diff --git a/e107_core/url/news/rewrite/e_url.php b/e107_core/url/news/rewrite/e_url.php new file mode 100644 index 000000000..f58fe21ab --- /dev/null +++ b/e107_core/url/news/rewrite/e_url.php @@ -0,0 +1,271 @@ + array( + 'noSingleEntry' => false, // [optional] default false; disallow this module to be shown via single entry point when this config is used + 'legacy' => '{e_BASE}news.php', // [optional] default empty; if it's a legacy module (no single entry point support) - URL to the entry point script + 'format' => 'path', // get|path - notify core for the current URL format, if set to 'get' rules will be ignored + 'selfParse' => true, // [optional] default false; use only this->parse() method, no core routine URL parsing + 'selfCreate' => true, // [optional] default false; use only this->create() method, no core routine URL creating + 'defaultRoute' => 'list/items', // [optional] default empty; route (no leading module) used when module is found with no additional controller/action information e.g. /news/ + 'errorRoute' => '', // [optional] default empty; route (no leading module) used when module is found but no inner route is matched, leave empty to force error 404 page + 'urlSuffix' => '', // [optional] default empty; string to append to the URL (e.g. .html) + ), + + 'rules' => array() // rule set array + ); + } + + /** + * When returning array, module or it's corresponding alias will be prefixed + * Create link so that it can be mapped by the parse() method + * - view/item?id=xxx -> news/xxx + * - list/items[?page=xxx] -> news[?page=xxx] + * - list/category?id=xxx[&page=xxx] -> news/Category/xxx?page=xxx + * - list/category?id=0[&page=xxx] -> news?page=xxx + * - list/short?id=xxx[&page=xxx] -> news/Short/xxx?page=xxx + * - list/category?id=xxx[&page=xxx] -> news?page=xxx + * - list/day?id=xxx -> news/Day-id + * - list/month?id=xxx -> news/Month-id + * - list/year?id=xxx -> news/Year-id + * - list/nextprev?route=xxx -> PARSED_ROUTE?page=[FROM] (recursive parse() call) + */ + public function create($route, $params = array(), $options = array()) + { + $page = $params['page'] ? intval($params['page']) : '0'; + if(!$route) $route = 'item/default'; + + if(is_string($route)) $route = explode('/', $route, 2); + $r = array(); + $parm = array(); + + ## news are passing array as it is retrieved from the DB, map vars to proper values + if(isset($params['news_id']) && !empty($params['news_id'])) $params['id'] = $params['news_id']; + if(isset($params['news_title']) && !empty($params['news_title'])) $params['id'] = $params['news_title']; // TODO - news_sef + if(isset($params['category_name']) && !empty($params['category_name'])) $params['category'] = $params['category_name']; + + if($route[0] == 'view') + { + switch ($route[1]) + { + case 'item': + $r[0] = $params['id']; // news/ID + break; + + default: + + break; + } + } + elseif($route[0] == 'list') + { + switch ($route[1]) + { + + case 'items': + $r[0] = ''; + if($page) $parm = array('page' => $page); // news?page=xxx + break; + + case 'category': + case 'list': + if(!vartrue($params['id'])) + { + $r[0] = ''; + if($page) $parm = array('page' => $page); // news?page=xxx + } + else + { + // news/Category/Category-Name?page=xxx + // news/Short/Category-Name?page=xxx + $r[0] = $route[1] == 'category' ? 'Short' : 'Category'; + $r[1] = $params['id']; + if($page) $parm = array('page' => $page); + } + break; + + case 'day': + case 'month': + case 'year': + $r[0] = $route[1].'-'.$params['id']; + break; + + case 'nextprev': + $route = $params['route']; + unset($params['route']); + // prevent dead loop + if($route != 'list/nextprev') + { + $tmp = $this->create($route, $params); + $r = $tmp[0]; + $parm = $tmp[1]; + $parm['page'] = '[FROM]'; + unset($tmp); + } + break; + + default: + + break; + } + } + + if(empty($r)) return false; + + return array($r, $parm); + } + + /** + * Manually parse request + * Pathinfo DOESN'T contain leading 'module' (e.g news or alias 'Blog') + * Retruned route shouldn't contain module as well, unless you manipulate $request directly and set $request->routed to true + * Mapped URLs: + * - news/News-Item -> extend.xxx + * - news/Category/Category-Name?page=10 -> list.xxx.10 + * - news/Day|Month-xxx -> day|month-xxx + */ + public function parse($pathInfo, $params, $request, $router, $config) + { + $page = $params['page'] ? intval($params['page']) : '0'; + if(!$pathInfo) + { + ## this var is used by default from legacy() method + ## you may override legacy() method + ## Keep in mind legacy() is not triggered at all if parse() returns false or $request->routed is set to true + $this->legacyQueryString = $page ? 'default.0.'.$page : ''; + return $config['defaultRoute']; + } + + ## no controller/action pair - news item view - map to extend.xxx + if(strpos($pathInfo, '/') === false) + { + $route = 'view/item'; + $id = is_numeric($pathInfo) ? intval($pathInfo) : $this->itemIdByTitle($pathInfo); + if(!$id) + { + ## let news.php handle missing news item + $this->legacyQueryString = 'extend.0'; + return $route; + } + $this->legacyQueryString = 'extend.'.$id; + return $route; + } + + $parts = explode('/', $pathInfo, 2); + + switch (strtolower($parts[0])) + { + # map to list.xxx.xxx + case 'short': + case 'category': + # Hardcoded leading string for categories, could be pref or LAN constant + if(!vartrue($parts[1])) + { + ## force not found as we don't want to have duplicated content (default.0.xxx) + return false; + } + else + { + if(!is_numeric($parts[1])) $id = $this->categoryIdByTitle($parts[1]); + else $id = intval($parts[1]); + } + if(!$id) + { + # let news.php handle it + $id = 0; + } + $action = $parts[0] == 'short' ? 'cat' : 'list'; + $this->legacyQueryString = $action.'.'.$id.'.'.$page; + return 'item/list'; + break; + + # could be pref or LAN constant + case 'day': + if(!vartrue($parts[1])) $id = 0; + else $id = intval($parts[1]); + + $this->legacyQueryString = 'day-'.$id; + break; + + # could be pref or LAN constant + case 'month': + if(!vartrue($parts[1])) $id = 0; + else $id = intval($parts[1]); + + $this->legacyQueryString = 'month-'.$id; + break; + + # could be pref or LAN constant + case 'year': + if(!vartrue($parts[1])) $id = 0; + else $id = intval($parts[1]); + + $this->legacyQueryString = 'year-'.$id; + break; + + # force not found + default: + return false; + break; + } + + return false; + } + + /** + * Admin callback + * Language file not loaded as all language data is inside the lan_eurl.php (loaded by default on administration URL page) + */ + public function admin() + { + // static may be used for performance + static $admin = array( + 'labels' => array( + 'name' => LAN_EURL_CORE_NEWS, // Module name + 'label' => LAN_EURL_NEWS_REWRITE_LABEL, // Current profile name + 'description' => LAN_EURL_NEWS_REWRITE_DESCR, // + ), + 'form' => array(), // Under construction - additional configuration options + 'callbacks' => array(), // Under construction - could be used for e.g. URL generator functionallity + ); + + return $admin; + } + + ### CUSTOM METHODS ### + + //retrieve news_id by Title (XXX - news_sef column, equals to news_title if not set explicit) + public function itemIdByTitle($id) + { + $sql = e107::getDb('url'); + $tp = e107::getParser(); + $id = $tp->toDB($id); + if($sql->db_Select('news', 'news_id', "news_title='{$id}'")) // TODO - it'll be news_url (new) field + { + $id = $sql->db_Fetch(); + return $id['news_id']; + } + return false; + } + + //retrieve category_id by Title (XXX - category_sef column, equals to category_name if not set explicit) + public function categoryIdByTitle($id) + { + $sql = e107::getDb('url'); + $tp = e107::getParser(); + $id = $tp->toDB($id); + if($sql->db_Select('news_category', 'category_id', "category_name='{$id}'")) // TODO - it'll be category_url (new) field + { + $id = $sql->db_Fetch(); + return $id['category_id']; + } + return false; + } +} diff --git a/e107_core/url/news/rewrite_extended/e_url.php b/e107_core/url/news/rewrite_extended/e_url.php new file mode 100644 index 000000000..0b67016db --- /dev/null +++ b/e107_core/url/news/rewrite_extended/e_url.php @@ -0,0 +1,137 @@ + array( + 'legacy' => '{e_BASE}news.php', // [optional] default empty; if it's a legacy module (no single entry point support) - URL to the entry point script; override per rule is allowed + 'format' => 'path', // get|path - notify core for the current URL format, if set to 'get' rules will be ignored + 'defaultRoute' => 'list/items', // [optional] default empty; route (no leading module) used when module is found with no additional controller/action information e.g. /news/ + 'legacyQuery' => '', // [optional] default null; default legacy query string template, parsed (simpleParse) with requestParams values (request object) and GET vars part of allowVars array (rule); override per rule is allowed + + ### default vars mapping (create URL), override per rule is allowed + 'mapVars' => array( + 'news_id' => 'id', + 'news_title' => 'name', + ), + + ### Numerical array containing allowed vars by default (create URL, used for legacyQuery parsing in parse routine as well), + ### false means - disallow all vars beside those required by the rules + ### Override per rule is allowed + 'allowVars' => false, + ), + + 'rules' => array( + ### simple matches first - PERFORMANCE + '' => array('list/items', 'allowVars' => array('page'), 'legacyQuery' => 'default.0.{page}', ), + 'Category' => array('list/items', 'allowVars' => array('page'), 'legacyQuery' => 'default.0.{page}', ), + + ## URL with ID and Title - no DB call, balanced performance! + 'Category//' => array('list/items', 'allowVars' => array('page'), 'mapVars' => array('category_id' => 'id', 'category_title' => 'name'), 'legacyQuery' => 'list.{id}.{page}'), + ## URL with ID only - best performance! + // 'Category/' => array('list/items', 'allowVars' => array('page'), 'legacyQuery' => 'list.{id}.{page}', 'mapVars' => array('category_id' => 'id')), + ## URL with Title only - prettiest and slowest! + ##'Category/' => array('list/items', 'allowVars' => array('page'), 'mapVars' => array('category_title' => 'name'), 'legacyQuery' => 'list.{id}.{page}', 'parseCallback' => 'categoryIdByTitle'), + + ### View item requested by id or string, if you remove the catch ALL example, uncomment at least on row from this block + ### leading category name example - could be enabled together with the next example to handle creating of URLs without knowing the category title + // 'View//' => array('view/item', 'mapVars' => array('news_title' => 'name', 'category_name' => 'category'), 'legacyQuery' => 'extend.{name}', 'parseCallback' => 'itemIdByTitle'), + // to be noted here - value 'name' is replaced by item id within the callback method; TODO replace news_title with news_sef field + // 'View/' => array('view/item', 'mapVars' => array('news_title' => 'name'), 'legacyQuery' => 'extend.{name}', 'parseCallback' => 'itemIdByTitle'), + // 'View/' => array('view/item', 'mapVars' => array('news_id' => 'id'), 'legacyQuery' => 'extend.{id}'), + + + // less used after + 'Brief/' => array('list/short', 'allowVars' => array('page'), 'legacyQuery' => 'cat.{id}.{page}', 'mapVars' => array('news_id' => 'id')), + 'Day/' => array('list/day', 'legacyQuery' => 'day-{id}'), + 'Month/' => array('list/month', 'legacyQuery' => 'month-{id}'), + 'Year/' => array('list/year', 'legacyQuery' => 'year-{id}'), + + ### View news item - kinda catch all - very bad performance when News is chosen as default namespace - two additional DB queries on every site call! + // Leading category name - uncomment to enable + '/' => array('view/item', 'mapVars' => array('news_title' => 'name', 'category_name' => 'category'), 'legacyQuery' => 'extend.{name}', 'parseCallback' => 'itemIdByTitle'), + // Base location as item view - uncomment to enable + // '' => array('view/item', 'mapVars' => array('news_title' => 'name'), 'legacyQuery' => 'extend.{name}', 'parseCallback' => 'itemIdByTitle'), + + + ) + ); + } + + /** + * Query mapping in format route?params: + * - item/vew?id=xxx -> ?extend.id + * - list/items[?page=xxx] -> default.0.page + * - list/category?id=xxx[&page=xxx] -> list.id.page + * - list/category?id=0[&page=xxx] -> default.0.page + * - list/short?id=xxx[&page=xxx] -> cat.id.page + * - list/day?id=xxx -> ?day-id + * - list/month?id=xxx -> ?month-id + * - list/year?id=xxx -> ?year-id + * - list/nextprev?route=xxx -> PARSED_ROUTE.[FROM] (recursive parse() call) + */ + + + /** + * Admin callback + * Language file not loaded as all language data is inside the lan_eurl.php (loaded by default on administration URL page) + */ + public function admin() + { + // static may be used for performance + static $admin = array( + 'labels' => array( + 'name' => LAN_EURL_CORE_NEWS, // Module name + 'label' => LAN_EURL_NEWS_REWRITEX_LABEL, // Current profile name + 'description' => LAN_EURL_NEWS_REWRITEX_DESCR, // + ), + 'form' => array(), // Under construction - additional configuration options + 'callbacks' => array(), // Under construction - could be used for e.g. URL generator functionallity + ); + + return $admin; + } + + ### CUSTOM METHODS ### + + /** + * view/item by name callback + * @param eRequest $request + */ + public function itemIdByTitle(eRequest $request) + { + $name = $request->getRequestParam('name'); + if(!$name || is_numeric($name)) return; + + $sql = e107::getDb('url'); + $name = e107::getParser()->toDB($name); + if($sql->db_Select('news', 'news_id', "news_title='{$name}'")) // TODO - it'll be news_url (new) field + { + $name = $sql->db_Fetch(); + $request->setRequestParam('name', $name['news_id']); + } + } + + /** + * list/items by name callback + * @param eRequest $request + */ + public function categoryIdByTitle(eRequest $request) + { + $name = $request->getRequestParam('name'); + if(!$name || is_numeric($name)) return; + + $sql = e107::getDb('url'); + $id = e107::getParser()->toDB($name); + if($sql->db_Select('news_category', 'category_id', "category_name='{$name}'")) // TODO - it'll be category_url (new) field + { + $name = $sql->db_Fetch(); + $request->setRequestParam('name', $name['category_id']); + } + } +} \ No newline at end of file diff --git a/e107_core/url/search/rewrite/e_url.php b/e107_core/url/search/rewrite/e_url.php new file mode 100644 index 000000000..d1629d77d --- /dev/null +++ b/e107_core/url/search/rewrite/e_url.php @@ -0,0 +1,42 @@ + array( + 'legacy' => '{e_BASE}search.php', // [optional] default empty; if it's a legacy module (no single entry point support) - URL to the entry point script to be included + 'format' => 'path', // get|path - notify core for the current URL format, if set to 'get' rules will be ignored + 'defaultRoute' => 'index/index', // [optional] default empty; route (no leading module) used when module is found with no additional controller/action information e.g. /news/ + + ), + + // rule set array + 'rules' => array( + '' => array('index/index', 'defaultVars' => array('id' => 0)), + ) + ); + } + + /** + * Admin callback + * Language file not loaded as all language data is inside the lan_eurl.php (loaded by default on administration URL page) + */ + public function admin() + { + // static may be used for performance + static $admin = array( + 'labels' => array( + 'name' => LAN_EURL_CORE_SEARCH, // Module name + 'label' => LAN_EURL_SEARCH_REWRITE_LABEL, // Current profile name + 'description' => LAN_EURL_SEARCH_REWRITE_DESCR, // + ), + 'form' => array(), // Under construction - additional configuration options + 'callbacks' => array(), // Under construction - could be used for e.g. URL generator functionallity + ); + + return $admin; + } +} diff --git a/e107_core/url/system/rewrite/e_url.php b/e107_core/url/system/rewrite/e_url.php new file mode 100644 index 000000000..6542301e6 --- /dev/null +++ b/e107_core/url/system/rewrite/e_url.php @@ -0,0 +1,41 @@ + array( + 'format' => 'path', // get|path - notify core for the current URL format, if set to 'get' rules will be ignored + 'defaultRoute' => 'error/notfound', // [optional] default empty; route (no leading module) used when module is found with no additional controller/action information e.g. /news/ + + ), + + // rule set array + 'rules' => array( + 'error404' => 'error/notfound', + ) + ); + } + + /** + * Admin callback + * Language file not loaded as all language data is inside the lan_eurl.php (loaded by default on administration URL page) + */ + public function admin() + { + // static may be used for performance + static $admin = array( + 'labels' => array( + 'name' => LAN_EURL_CORE_SYSTEM, // Module name + 'label' => LAN_EURL_SYSTEM_REWRITE_LABEL, // Current profile name + 'description' => LAN_EURL_SYSTEM_REWRITE_DESCR, // + ), + 'form' => array(), // Under construction - additional configuration options + 'callbacks' => array(), // Under construction - could be used for e.g. URL generator functionallity + ); + + return $admin; + } +} diff --git a/e107_core/url/user/rewrite/e_url.php b/e107_core/url/user/rewrite/e_url.php new file mode 100644 index 000000000..00b1e2ca1 --- /dev/null +++ b/e107_core/url/user/rewrite/e_url.php @@ -0,0 +1,86 @@ + array( + 'noSingleEntry' => false, // [optional] default false; disallow this module to be shown via single entry point when this config is used + 'legacy' => '{e_BASE}user.php', // [optional] default empty; if it's a legacy module (no single entry point support) - URL to the entry point script to be included + 'format' => 'path', // get|path - notify core for the current URL format, if set to 'get' rules will be ignored + 'selfParse' => false, // [optional] default false; use only this->parse() method, no core routine URL parsing + 'selfCreate' => false, // [optional] default false; use only this->create() method, no core routine URL creating + 'defaultRoute' => 'myprofile/view', // [optional] default empty; route (no leading module) used when module is found with no additional controller/action information e.g. /news/ + 'errorRoute' => '', // [optional] default empty; route (no leading module) used when module is found but no inner route is matched, leave empty to force error 404 page + 'urlSuffix' => '', // [optional] default empty; string to append to the URL (e.g. .html) + 'mapVars' => array( // vars mapping (create URL) + 'user_id' => 'id', + 'user_name' => 'name', + ), + 'allowVars' => false, // allowed vars (create URL, used for legacyQuery parsing in parse routine as well), false means - disallow all vars beside those required by the rules + 'legacyQuery' => '' // default legacy query string template, null to disable, override possible by rule + ), + + // rule set array + 'rules' => array( + // simple matches first - PERFORMANCE + '' => array('myprofile/view', 'defaultVars' => array('id' => 0)), + 'Settings' => array('myprofile/edit', 'defaultVars' => array('id' => 0), 'legacy' => '{e_BASE}usersettings.php'), + 'List' => array('profile/list', 'allowVars' => array('page'), 'legacyQuery' => '{page}'), + 'Login' => array('login/index', 'legacy' => '{e_BASE}login.php'), + 'Register' => array('register/index', 'legacy' => '{e_BASE}signup.php'), + + // Regex involved next + //'' => array('profile/view', 'legacyQuery' => 'id.{id}'), + 'Edit/' => array('profile/edit', 'legacy' => '{e_BASE}usersettings.php', 'legacyQuery' => '{id}'), + + // Named requests - important to be in the end in this order! + 'Edit/' => array('profile/edit', 'legacy' => '{e_BASE}usersettings.php', 'legacyQuery' => '{id}', 'parseCallback' => 'idByName'), + // Last one - close to catch all! + '' => array('profile/view', 'legacyQuery' => 'id.{id}', 'parseCallback' => 'idByName'), + ) + ); + } + + /** + * Admin callback + * Language file not loaded as all language data is inside the lan_eurl.php (loaded by default on administration URL page) + */ + public function admin() + { + // static may be used for performance + static $admin = array( + 'labels' => array( + 'name' => LAN_EURL_CORE_USER, // Module name + 'label' => LAN_EURL_USER_REWRITE_LABEL, // Current profile name + 'description' => LAN_EURL_USER_REWRITE_DESCR, // + ), + 'form' => array(), // Under construction - additional configuration options + 'callbacks' => array(), // Under construction - could be used for e.g. URL generator functionallity + ); + + return $admin; + } + + ### CUSTOM METHODS ### + + /** + * profile/edit & profile/view callback + * @param eRequest $request + */ + public function idByName(eRequest $request) + { + $name = $request->getRequestParam('name'); + if(!$name) return; + + $sql = e107::getDb('url'); + $name = e107::getParser()->toDB($name); + if($sql->db_Select('user', 'user_id', "user_name='{$name}'")) // XXX - new user_sef field? Discuss. + { + $name = $sql->db_Fetch(); + $request->setRequestParam('id', $name['user_id']); + } + } +}