681 lines
15 KiB
PHP
681 lines
15 KiB
PHP
<?php
|
|
/*
|
|
* Plugin Name: PrettyURLs
|
|
* Version: 3.0
|
|
* Plugin URI: https://www.flatpress.org
|
|
* Author: FlatPress
|
|
* Author URI: https://www.flatpress.org
|
|
* Description: Enables SEO friendly, pretty URLs (via htaccess). Part of the standard distribution.
|
|
*/
|
|
|
|
/**
|
|
* Place where the index is stored
|
|
*/
|
|
define('PRETTYURLS_TITLES', true);
|
|
define('PRETTYURLS_PATHINFO', !file_exists(ABS_PATH . '.htaccess'));
|
|
define('PRETTYURLS_CACHE', CACHE_DIR . '%%prettyurls-index.tmp');
|
|
define('PRETTYURLS_CATS', CACHE_DIR . '%%prettyurls-cats.tmp');
|
|
|
|
/**
|
|
* File existance check
|
|
*/
|
|
|
|
// memo
|
|
// register_plugin_setup('plugin_id', 'setup_func');
|
|
function plugin_prettyurls_setup() {
|
|
if (file_exists(ABS_PATH . '.htaccess'))
|
|
return 1;
|
|
|
|
if (!is_writable(ABS_PATH)) {
|
|
return -2;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
class Plugin_PrettyURLs {
|
|
|
|
var $index = array();
|
|
|
|
var $status = 0;
|
|
|
|
var $date_handled = false;
|
|
|
|
var $categories = null;
|
|
|
|
var $baseurl = null;
|
|
|
|
var $mode = null;
|
|
|
|
var $fp_params;
|
|
|
|
function categories($force = true) {
|
|
if ($this->categories)
|
|
return;
|
|
|
|
if ($force || !file_exists(PRETTYURLS_CATS)) {
|
|
$d = entry_categories_get('defs');
|
|
$list = array();
|
|
foreach ($d as $k => $v) {
|
|
$list [$k] = sanitize_title($v);
|
|
}
|
|
io_write_file(PRETTYURLS_CATS, serialize($list));
|
|
} else {
|
|
$f = io_load_file(PRETTYURLS_CATS);
|
|
$list = unserialize($f);
|
|
}
|
|
$this->categories = $list;
|
|
}
|
|
|
|
function md5($id, $title) {
|
|
$date = date_from_id($id);
|
|
return md5($date ['y'] . $date ['m'] . $date ['d'] . $title);
|
|
}
|
|
|
|
function permalink($str, $id) {
|
|
global $fpdb, $post;
|
|
|
|
if (isset($post) && PRETTYURLS_TITLES)
|
|
$title = sanitize_title($post ['subject']);
|
|
else
|
|
$title = $id;
|
|
$date = date_from_id($id);
|
|
// yeah, hackish, I know...
|
|
|
|
return $this->baseurl . "20{$date['y']}/{$date['m']}/{$date['d']}/$title/";
|
|
}
|
|
|
|
function commentlink($str, $id) {
|
|
$link = $this->permalink($str, $id);
|
|
return $link . "comments/";
|
|
}
|
|
|
|
function feedlink($str, $type) {
|
|
return $this->baseurl . "feed/{$type}/";
|
|
}
|
|
|
|
function commentsfeedlink($str, $type, $id) {
|
|
$link = $this->commentlink($str, $id);
|
|
return $link . "feed/{$type}/";
|
|
}
|
|
|
|
function staticlink($str, $id) {
|
|
return $this->baseurl . "$id/";
|
|
}
|
|
|
|
function categorylink($str, $catid) {
|
|
if (PRETTYURLS_TITLES) {
|
|
if (@$this->categories [$catid])
|
|
return $this->baseurl . "category/{$this->categories[$catid]}/";
|
|
else
|
|
return $str;
|
|
} else {
|
|
return $this->baseurl . "category/{$catid}/";
|
|
}
|
|
}
|
|
|
|
function yearlink($str, $y) {
|
|
return $this->baseurl . "20$y/";
|
|
}
|
|
|
|
function monthlink($str, $y, $m) {
|
|
return $this->yearlink($str, $y) . "$m/";
|
|
}
|
|
|
|
function daylink($str, $y, $m, $d) {
|
|
return $this->monthlink($str, $y, $m) . "$d/";
|
|
}
|
|
|
|
function cache_create() {
|
|
$this->index = array();
|
|
|
|
/*
|
|
* $o =& entry_init();
|
|
*
|
|
* $entries = $o->getList();
|
|
*/
|
|
|
|
$o = new FPDB_Query(array(
|
|
'start' => 0,
|
|
'count' => -1,
|
|
'fullparse' => false
|
|
), null);
|
|
|
|
// foreach ($entries as $id => $contents) {
|
|
while ($o->hasMore()) {
|
|
list ($id, $contents) = $o->getEntry();
|
|
$date = date_from_id($id);
|
|
echo $contents ['subject'], "\n";
|
|
$md5 = md5(sanitize_title($contents ['subject']));
|
|
$this->index [$date ['y']] [$date ['m']] [$date ['d']] [$md5] = $id;
|
|
}
|
|
// }
|
|
|
|
$this->cache_save();
|
|
io_write_file(PRETTYURLS_CACHE, 'dummy');
|
|
}
|
|
|
|
function handle_categories($matches) {
|
|
if (!$this->categories)
|
|
return;
|
|
|
|
if (PRETTYURLS_TITLES) {
|
|
if ($c = array_search($matches [1], $this->categories))
|
|
$this->fp_params ['cat'] = $c;
|
|
else
|
|
return $matches [0];
|
|
} else {
|
|
$this->fp_params ['cat'] = $matches [1];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* named matches are not supported here
|
|
*/
|
|
function handle_date($matches) {
|
|
$this->fp_params ['y'] = $matches [1];
|
|
if (isset($matches [3]))
|
|
$this->fp_params ['m'] = $matches [3];
|
|
if (isset($matches [5]))
|
|
$this->fp_params ['d'] = $matches [5];
|
|
|
|
$this->date_handled = true;
|
|
}
|
|
|
|
function handle_static($matches) {
|
|
$this->fp_params ['page'] = $matches [1];
|
|
$this->status = 2;
|
|
}
|
|
|
|
function handle_entry($matches) {
|
|
if (!PRETTYURLS_TITLES) {
|
|
$this->fp_params ['entry'] = $matches [1];
|
|
return;
|
|
}
|
|
|
|
// data is not as expected
|
|
if (!array_key_exists('y', $this->fp_params) || !array_key_exists('m', $this->fp_params) || !array_key_exists('d', $this->fp_params)) {
|
|
// a bit hackish: we make up a fake url when there is no match,
|
|
// so that at the higher level the system will 404...
|
|
$this->fp_params ['entry'] = 'a';
|
|
}
|
|
|
|
if ($this->cache_get($this->fp_params ['y'], $this->fp_params ['m'], $this->fp_params ['d'], md5($matches [1]))) {
|
|
$this->fp_params ['entry'] = $this->index [$this->fp_params ['y']] [$this->fp_params ['m']] [$this->fp_params ['d']] [md5($matches [1])];
|
|
} else {
|
|
// a bit hackish: we make up a fake url when there is no match,
|
|
// so that at the higher level the system will 404...
|
|
$this->fp_params ['entry'] = 'a';
|
|
}
|
|
}
|
|
|
|
function handle_page($matches) {
|
|
$this->fp_params ['paged'] = $matches [1];
|
|
$this->status = 2;
|
|
}
|
|
|
|
function handle_comment($matches) {
|
|
$this->fp_params ['comments'] = true;
|
|
}
|
|
|
|
function handle_feed($matches) {
|
|
$this->fp_params ['feed'] = isset($matches [2]) ? $matches [2] : 'rss2';
|
|
}
|
|
|
|
function get_url() {
|
|
$baseurl = BLOG_BASEURL;
|
|
$opt = plugin_getoptions('prettyurls', 'mode');
|
|
$url = substr($_SERVER ['REQUEST_URI'], strlen(BLOG_ROOT) - 1);
|
|
|
|
$urllenght = strlen($url);
|
|
|
|
if (isset($_SERVER ['PATH_INFO'])) {
|
|
$pathinfo = $_SERVER ['PATH_INFO'];
|
|
} else {
|
|
$pathinfo = '';
|
|
}
|
|
|
|
switch ($opt) {
|
|
case null:
|
|
case 0:
|
|
$opt = file_exists(ABS_PATH . '.htaccess') ? 3 : 1;
|
|
case 1:
|
|
$baseurl .= 'index.php/';
|
|
if ($urllenght < 2) {
|
|
$url = "/";
|
|
} else {
|
|
// $url = $_SERVER['PATH_INFO'];
|
|
$url = $pathinfo;
|
|
}
|
|
break;
|
|
case 2:
|
|
$baseurl .= '?u=/';
|
|
$url = @$_GET ['u'];
|
|
/* case 3: do nothing, it's BLOG_BASEURL */
|
|
}
|
|
|
|
$this->baseurl = $baseurl;
|
|
$this->mode = $opt;
|
|
|
|
return $url;
|
|
}
|
|
|
|
/*
|
|
* here is where the real work is done.
|
|
*
|
|
* First we load the cache if exists;
|
|
*
|
|
* We check then if the GET request contains a 'title'
|
|
* if so, we'll need date and time to construct the md5 sum
|
|
* with which we index the cache array
|
|
*
|
|
* If that entry exists, we set $_GET['entry'] to that ID,
|
|
* so that FlatPress can find it where it is expected
|
|
*
|
|
*/
|
|
function cache_init() {
|
|
global $fp_params;
|
|
|
|
$this->fp_params = &$fp_params;
|
|
$url = $this->get_url();
|
|
|
|
if (PRETTYURLS_TITLES) {
|
|
// if ($f = io_load_file(PRETTYURLS_CACHE))
|
|
$this->index = array(); // unserialize($f);
|
|
|
|
if (!file_exists(PRETTYURLS_CACHE))
|
|
$this->cache_create();
|
|
|
|
$this->categories(false);
|
|
}
|
|
|
|
if (!defined('MOD_INDEX'))
|
|
return;
|
|
|
|
// # this is not working if you reach flatpress via symlink
|
|
// # unless you don't edit manually defaults.php
|
|
// if (strpos($_SERVER['REQUEST_URI'], BLOG_ROOT)!==false) {
|
|
// $url = $_SERVER['REQUEST_URI'];
|
|
// $del = BLOG_ROOT;
|
|
// if (strpos($url, 'index.php')!==false)
|
|
// $del = $del . 'index.php/';
|
|
// $url = substr($url, strlen($del)-1);
|
|
// }
|
|
|
|
// removes querystrings
|
|
if (false !== $i = strpos($url, '?'))
|
|
$url = substr($url, 0, $i);
|
|
|
|
// removes anchors
|
|
if (false !== $i = strpos($url, '#'))
|
|
$url = substr($url, 0, $i);
|
|
|
|
if (strrpos($url, '/') != (strlen($url) - 1)) {
|
|
$url .= '/';
|
|
}
|
|
|
|
if ($url == '/')
|
|
return;
|
|
|
|
// date
|
|
$url = preg_replace_callback('!^/[0-9]{2}(?P<y>[0-9]{2})(/(?P<m>[0-9]{2})(/(?P<d>[0-9]{2}))?)?!', array(
|
|
&$this,
|
|
'handle_date'
|
|
), $url);
|
|
|
|
if (!$this->date_handled) {
|
|
// static page
|
|
$url = preg_replace_callback('|^/([a-zA-Z0-9_-]+)/$|', array(
|
|
&$this,
|
|
'handle_static'
|
|
), $url);
|
|
if ($this->status == 2)
|
|
return $this->check_url($url);
|
|
}
|
|
|
|
$url = preg_replace_callback('{category/([^/]+)/}', array(
|
|
&$this,
|
|
'handle_categories'
|
|
), $url);
|
|
|
|
$url = preg_replace_callback('|page/([0-9]+)/$|', array(
|
|
&$this,
|
|
'handle_page'
|
|
), $url);
|
|
if ($this->status == 2)
|
|
return $this->check_url($url);
|
|
|
|
if ($this->date_handled) {
|
|
$url = preg_replace_callback('|^/([^/]+)|', array(
|
|
&$this,
|
|
'handle_entry'
|
|
), $url);
|
|
// if status = 2
|
|
/*
|
|
* utils_error(404);
|
|
*/
|
|
|
|
$url = preg_replace_callback('|^/comments|', array(
|
|
&$this,
|
|
'handle_comment'
|
|
), $url);
|
|
}
|
|
|
|
$url = preg_replace_callback('|^/feed(/([^/]*))?|', array(
|
|
&$this,
|
|
'handle_feed'
|
|
), $url);
|
|
|
|
$this->check_url($url);
|
|
}
|
|
|
|
function check_url($url) {
|
|
if (!empty($url) && $url != '/') {
|
|
$this->fp_params = array(
|
|
'entry' => 'entry000000-000000'
|
|
);
|
|
$url = apply_filters('prettyurls_unhandled_url', $url);
|
|
}
|
|
}
|
|
|
|
function cache_delete_elem($id, $date) {
|
|
|
|
// is this a title change?
|
|
if (false !== ($ids = $this->cache_get($date ['y'], $date ['m'], $date ['d'])))
|
|
$hash = array_search($id, $ids);
|
|
else
|
|
return;
|
|
|
|
if ($hash) {
|
|
unset($this->index [$date ['y']] [$date ['m']] [$date ['d']] [$hash]);
|
|
|
|
if (empty($this->index [$date ['y']] [$date ['m']] [$date ['d']])) {
|
|
unset($this->index [$date ['y']] [$date ['m']] [$date ['d']]);
|
|
|
|
if (empty($this->index [$date ['y']] [$date ['m']])) {
|
|
unset($this->index [$date ['y']] [$date ['m']]);
|
|
|
|
if (empty($this->index [$date ['y']])) {
|
|
unset($this->index [$date ['y']]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->cache_save();
|
|
}
|
|
|
|
function cache_add($id, $arr) {
|
|
$date = date_from_id($id);
|
|
$title = sanitize_title($arr ['subject']);
|
|
|
|
$this->cache_delete_elem($id, $date);
|
|
|
|
// Add year and month keys to index, if not present already
|
|
if (!array_key_exists($date ['y'], $this->index) || !array_key_exists($date ['m'], $this->index [$date ['y']]) || $this->index [$date ['y']] [$date ['m']] === false) {
|
|
$this->index [$date ['y']] [$date ['m']] = array();
|
|
}
|
|
|
|
$this->index [$date ['y']] [$date ['m']] [$date ['d']] [md5($title)] = $id;
|
|
|
|
$this->cache_save();
|
|
|
|
return true;
|
|
}
|
|
|
|
function cache_get($y, $m, $d = null, $h = null) {
|
|
if (!isset($this->index [$y] [$m])) {
|
|
$s = @io_load_file(PRETTYURLS_CACHE . $y . $m);
|
|
$this->index [$y] [$m] = $s ? unserialize($s) : false;
|
|
}
|
|
|
|
if (is_null($d))
|
|
return $this->index [$y] [$m];
|
|
|
|
if (is_null($h))
|
|
return isset($this->index [$y] [$m] [$d]) ? $this->index [$y] [$m] [$d] : false;
|
|
|
|
if (isset($this->index [$y] [$m] [$d]))
|
|
return isset($this->index [$y] [$m] [$d] [$h]);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
function cache_delete($id) {
|
|
$date = date_from_id($id);
|
|
$this->cache_delete_elem($id, $date);
|
|
$this->cache_save();
|
|
}
|
|
|
|
function cache_save() {
|
|
if ($this->index) {
|
|
foreach ($this->index as $year => $months) {
|
|
foreach ($months as $month => $days)
|
|
io_write_file(PRETTYURLS_CACHE . $year . $month, serialize($days));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function nextprevlink($nextprev, $v) {
|
|
global $fpdb;
|
|
$q = &$fpdb->getQuery();
|
|
|
|
list ($caption, $id) = call_user_func(array(
|
|
&$q,
|
|
'get' . $nextprev
|
|
));
|
|
|
|
if (!$id)
|
|
return array();
|
|
|
|
if ($q->single) {
|
|
$date = date_from_id($id);
|
|
if (PRETTYURLS_TITLES)
|
|
$title = sanitize_title($caption);
|
|
else
|
|
$title = $id;
|
|
$url = $this->baseurl . "20{$date['y']}/{$date['m']}/{$date['d']}/$title/";
|
|
|
|
if ($v > 0)
|
|
$caption = $caption . ' » ';
|
|
else
|
|
$caption = ' « ' . $caption;
|
|
|
|
return array(
|
|
$caption,
|
|
$url
|
|
);
|
|
}
|
|
|
|
// else, we build a complete url
|
|
|
|
/* todo: clean up this mess... which means cleaning up the mess above. oh, my! */
|
|
|
|
$l = $this->baseurl;
|
|
|
|
if ((is_numeric($cid = @$this->fp_params ['category'])) || is_numeric($cid = @$this->fp_params ['cat']))
|
|
$l = $this->categorylink($l, $cid);
|
|
|
|
if (isset($this->fp_params ['y']) && $this->fp_params ['y']) {
|
|
$l .= '20' . $this->fp_params ['y'] . '/';
|
|
|
|
if (isset($this->fp_params ['m']) && $this->fp_params ['m']) {
|
|
$l .= $this->fp_params ['m'] . '/';
|
|
|
|
if (isset($this->fp_params ['d']) && $this->fp_params ['d'])
|
|
$l .= $this->fp_params ['d'] . '/';
|
|
}
|
|
}
|
|
|
|
$page = 1;
|
|
|
|
if (isset($this->fp_params ['paged']) && $this->fp_params ['paged'] > 1)
|
|
$page = $this->fp_params ['paged'];
|
|
|
|
$page += ($v . '');
|
|
|
|
if ($page > 0) {
|
|
$l .= 'page/' . $page . '/';
|
|
}
|
|
|
|
return array(
|
|
$caption,
|
|
$l
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
global $plugin_prettyurls;
|
|
$plugin_prettyurls = new Plugin_PrettyURLs();
|
|
$plugin_prettyurls->categories();
|
|
|
|
if (!defined('MOD_ADMIN_PANEL')) {
|
|
|
|
if (!function_exists('get_nextpage_link')) :
|
|
|
|
function get_nextpage_link() {
|
|
global $plugin_prettyurls;
|
|
return $plugin_prettyurls->nextprevlink('NextPage', 1);
|
|
}
|
|
|
|
function get_prevpage_link() {
|
|
global $plugin_prettyurls;
|
|
return $plugin_prettyurls->nextprevlink('PrevPage', -1);
|
|
}
|
|
|
|
endif;
|
|
|
|
}
|
|
|
|
add_filter('post_link', array(
|
|
&$plugin_prettyurls,
|
|
'permalink'
|
|
), 0, 2);
|
|
add_filter('comments_link', array(
|
|
&$plugin_prettyurls,
|
|
'commentlink'
|
|
), 0, 2);
|
|
add_filter('feed_link', array(
|
|
&$plugin_prettyurls,
|
|
'feedlink'
|
|
), 0, 2);
|
|
add_filter('post_comments_feed_link', array(
|
|
&$plugin_prettyurls,
|
|
'commentsfeedlink'
|
|
), 0, 3);
|
|
add_filter('category_link', array(
|
|
&$plugin_prettyurls,
|
|
'categorylink'
|
|
), 0, 2);
|
|
add_filter('page_link', array(
|
|
&$plugin_prettyurls,
|
|
'staticlink'
|
|
), 0, 2);
|
|
|
|
// date related functions
|
|
add_filter('year_link', array(
|
|
&$plugin_prettyurls,
|
|
'yearlink'
|
|
), 0, 2);
|
|
add_filter('month_link', array(
|
|
&$plugin_prettyurls,
|
|
'monthlink'
|
|
), 0, 3);
|
|
add_filter('day_link', array(
|
|
&$plugin_prettyurls,
|
|
'daylink'
|
|
), 0, 4);
|
|
|
|
if (PRETTYURLS_TITLES) {
|
|
add_filter('publish_post', array(
|
|
&$plugin_prettyurls,
|
|
'cache_add'
|
|
), 5, 2);
|
|
add_filter('delete_post', array(
|
|
&$plugin_prettyurls,
|
|
'cache_delete'
|
|
));
|
|
add_action('update_categories', array(
|
|
&$plugin_prettyurls,
|
|
'categories'
|
|
));
|
|
}
|
|
|
|
add_filter('init', array(
|
|
&$plugin_prettyurls,
|
|
'cache_init'
|
|
));
|
|
|
|
if (class_exists('AdminPanelAction')) {
|
|
|
|
class admin_plugin_prettyurls extends AdminPanelAction {
|
|
|
|
var $langres = 'plugin:prettyurls';
|
|
|
|
var $_config = array(
|
|
'mode' => 0
|
|
);
|
|
|
|
function setup() {
|
|
$this->smarty->assign('admin_resource', "plugin:prettyurls/admin.plugin.prettyurls");
|
|
$this->_config ['mode'] = plugin_getoptions('prettyurls', 'mode');
|
|
$this->smarty->assign('pconfig', $this->_config);
|
|
$blogroot = BLOG_ROOT;
|
|
$f = ABS_PATH . '.htaccess';
|
|
$txt = io_load_file($f);
|
|
if (!$txt) {
|
|
|
|
$txt = '
|
|
AddType application/x-httpd-php .php .htm .html
|
|
Options -Indexes
|
|
|
|
<IfModule mod_rewrite.c>
|
|
RewriteEngine On
|
|
RewriteBase ' . $blogroot . '
|
|
|
|
RewriteRule ^\.htaccess$ - [F]
|
|
|
|
RewriteCond %{REQUEST_FILENAME} !-f
|
|
RewriteCond %{REQUEST_FILENAME} !-d
|
|
RewriteRule . ' . $blogroot . 'index.php [L]
|
|
</IfModule>
|
|
';
|
|
}
|
|
|
|
$this->smarty->assign('cantsave', (!is_writable(ABS_PATH) || (file_exists($f) && !is_writable($f))));
|
|
$this->smarty->assign('htaccess', $txt);
|
|
}
|
|
|
|
function onsubmit($data = null) {
|
|
global $fp_config;
|
|
|
|
if (isset($_POST ['saveopt'])) {
|
|
$this->_config ['mode'] = (int) $_POST ['mode'];
|
|
plugin_addoption('prettyurls', 'mode', $this->_config ['mode']);
|
|
if (plugin_saveoptions())
|
|
$this->smarty->assign('success', 2);
|
|
else
|
|
$this->smarty->assign('success', -2);
|
|
}
|
|
|
|
if (isset($_POST ['htaccess-submit'])) {
|
|
if (!empty($_POST ['htaccess']) && io_write_file(ABS_PATH . '.htaccess', $_POST ['htaccess'])) {
|
|
$this->smarty->assign('success', 1);
|
|
} else {
|
|
$this->smarty->assign('success', -1);
|
|
}
|
|
}
|
|
|
|
return 2;
|
|
}
|
|
|
|
}
|
|
|
|
admin_addpanelaction('plugin', 'prettyurls', true);
|
|
}
|
|
|
|
?>
|