- <?php
- /*
- * Lokorin.com
- * Copyright 2004-2006
- * Licensed under the GNU LGPL. See COPYING for full terms.
- */
- /**
- * A library that contains everything related to the site's url redirect
- * system. The system maps incoming urls to actual files on the server. It
- * allows the site to show one structure to the outside while in reality using
- * a completly different structure on the server.
- * @author Andreas Launila
- * @version $Revision: 1.20 $
- * @package com.lokorin.lokorin.lib
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- */
-
- /**
- * For access to common functions and variables.
- */
- require_once('common.php');
-
- /**
- * Strips an url of any query string (if one exists).
- * @param string $url The url which's query string should be removed.
- * @return string The url without its query string.
- * @access public
- */
- function stripQueryString($url) {
- return preg_replace("/([^?]+)\?.*/", '$1', $url);
- }
-
- /**
- * Attempts to get a url path to an existing file by starting from a specified
- * url and then translating the related redirects.
- * @param string $url The url, relative to the page root, that the search
- * should start from.
- * @param boolean $jump Whether or not redirects should be followed. I.e.
- * whether or not jumps should be executed. True if jumps should be
- * executed, false otherwise.
- * @return string The url to the file, relative to the page root and with query
- * string, that the url is resolved to. An empty string is returned if no
- * file could be found.
- * @access public
- */
- function getUrlPath($url, $jump = true) {
- $url = getRealUrl($url);
- $baseUrl = stripQueryString($url);
- $queryString = str_replace($baseUrl, '', $url);
- $filePath = DOC_ROOT.$baseUrl;
-
- //Look for redirects.
- $redirect = getRedirect($baseUrl);
- if(strlen($redirect) != 0) {
- if(!$jump) {
- return $redirect;
- }
- //A redirect was found, apply it.
- doPermanentRedirect($redirect.$queryString);
- exit;
- }
-
- //Look for aliases.
- if(file_exists($filePath)) {
- if(is_dir($filePath)) {
- if((strlen($queryString) == 0) &&
- (getUrlPath($url.'/', $jump) != '')) {
- if(!$jump) {
- return $baseUrl.'/';
- }
- doRedirect($baseUrl.'/');
- exit;
- } else {
- return '';
- }
- } else {
- //A match has been found.
- return $url;
- }
- } else if(file_exists($filePath.'.php')) {
- //It's a match.
- return $url;
- } else {
- //Nothing was found.
- return '';
- }
- }
-
- /**
- * Gets any redirects that apply to a specified url.
- * @param string $requestedUrl The url, relative to the site's root, which
- * has been requested by the user (no query string).
- * @return string The url that the specified url should be redirected to, or an
- * empty string if none was found.
- * @access private
- */
- function getRedirect($requestedUrl) {
- global $db;
-
- $requestedUrl = str_replace('.php', '', $requestedUrl);
- try {
- $idFields = array(
- 'from' => $requestedUrl
- );
- return $db->querySelectFirst(TABLE_REDIRECTS, 'to',
- $db->buildSqlSelector($idFields));
- } catch(Exception $e) {
- return '';
- }
- }
-
- /**
- * Gets the url that a specified url really leads to. For instance a user might
- * request one url while the request is actually transferred to an centralised
- * module behind the scenes.
- * @param string $requestedUrl The url, relative to the site's root, which
- * has been requested by the user (no query string).
- * @return string The actual url, relative to the site's root, that the call
- * should be directed to.
- * @access private
- */
- function getRealUrl($requestedUrl) {
- global $db;
-
- $idFields = array();
- $idFields[] = '(`from`='.$db->escape($requestedUrl).')';
- if((strlen($requestedUrl) == 0) ||
- ($requestedUrl{strlen($requestedUrl)-1} != '/')) {
- $idFields[] = '(`from`='.$db->escape($requestedUrl.'/').')';
- }
- $rows = $db->querySelect(TABLE_ALIASES, array('to'),
- 'WHERE '.implode(' OR ', $idFields));
-
- if(sizeof($rows) > 0) {
- return $rows[0]['to'];
- } else if((strlen($requestedUrl) == 0) ||
- ($requestedUrl{strlen($requestedUrl)-1} === '/')) {
- return getRealUrl($requestedUrl.'index');
- } else {
- return $requestedUrl;
- }
- }
-
- /**
- * Rebuilds the url aliases master table. It composes all necessary aliases
- * and resets the aliases master table with that info.
- * @access public
- */
- function buildUrlAliases() {
- /**
- * The printf pattern that should be used for building the news date from
- * value.
- * @var string
- * @access private
- */
- define('NEWS_DATE_PATTERN_FROM', 'news/%s/%s/%s');
- /**
- * The printf pattern that should be used for building the news date to
- * value.
- * @var string
- * @access private
- */
- define('NEWS_DATE_PATTERN_TO', 'news/archive.php?day=%s');
- /**
- * The printf pattern that should be used for building the news comments
- * from value.
- * @var string
- * @access private
- */
- define('NEWS_COMMENTS_PATTERN_FROM', 'news/comments/entry%s');
- /**
- * The printf pattern that should be used for building the news comments to
- * value.
- * @var string
- * @access private
- */
- define('NEWS_COMMENTS_PATTERN_TO', 'news/comments.php?news_id=%s');
- /**
- * The printf pattern that should be used for building the news title from
- * value.
- * @vars string
- * @access private
- */
- define('NEWS_TITLE_PATTERN_FROM', 'news/%s_%s');
- /**
- * The printf pattern that should be used for building the news id to
- * value.
- * @var string
- * @access private
- */
- define('NEWS_TITLE_PATTERN_TO', 'news/archive.php?id=%s');
- /**
- * The printf pattern that should be used for building the project
- * submenus' to value when it should point to the about module.
- * @var string
- * @access private
- */
- define('PROJECT_ABOUT_TO', '_includes/module_about.php');
- /**
- * The printf pattern that should be used for building the project
- * submenus' to value when it should point to the progress module.
- * @var string
- * @access private
- */
- define('PROJECT_PROGRESS_TO', '_includes/module_progress.php');
- /**
- * The printf pattern that should be used for building the project
- * submenus' to value when it should point to the download module.
- * @var string
- * @access private
- */
- define('PROJECT_DOWNLOAD_TO', '_includes/module_download.php');
- /**
- * The printf pattern that should be used for building the project
- * submenus' to value when it should point to the image module.
- * @var string
- * @access private
- */
- define('PROJECT_IMAGES_TO', '_includes/module_images.php');
- /**
- * The printf pattern that should be used for building the from values for
- * programming language related aliases.
- * @var string
- * @access private
- */
- define('LANG_PATTERN_FROM', 'languages/%s');
- /**
- * The printf pattern that should be used for building the project
- * submenus' to value when it should point to the download module.
- * @var string
- * @access private
- */
- define('LANG_PATTERN_TO', 'languages.php?id=%s');
- /**
- * The printf pattern that should be used for building the archive month
- * from value.
- * @var string
- * @access private
- */
- define('ARCHIVE_MONTH_PATTERN_FROM', 'news/%s/%s');
- /**
- * The printf pattern that should be used for building the archive month
- * to value.
- * @var string
- * @access private
- */
- define('ARCHIVE_MONTH_PATTERN_TO', 'news/archive.php?year=%s&month=%s');
- /**
- * The printf pattern that should be used for building the sitemap to
- * value.
- * @var string
- * @access private
- */
- define('SITEMAP_PATTERN_TO', 'sitemaps/sitemap.php?name=%s');
-
- global $db;
- $aliases = array();
-
- //Source 1: Custom aliases.
- $fields = array(
- 'from',
- 'to'
- );
- $rows = $db->querySelect(TABLE_CUSTOM_ALIASES, $fields);
- foreach($rows as $row) {
- $aliases[] = $row;
- }
-
- //Source 2: News
- doInclude('lib_util');
- $fields = array(
- 'id',
- 'caption',
- 'timestamp'
- );
- $rows = $db->querySelect(TABLE_NEWS, $fields);
- $datesFrom = array(); //Stores all redirected dates.
- foreach($rows as $row) {
- //The date alias.
- $dayOfMonth = date('j', $row['timestamp']);
- $month = date('n', $row['timestamp']);
- $year = date('Y', $row['timestamp']);
- $dateFrom = sprintf(NEWS_DATE_PATTERN_FROM, $year, $month,
- $dayOfMonth);
- if(!in_array($dateFrom, $datesFrom)) {
- $datesFrom[] = $dateFrom;
- $aliases[] = array(
- 'from' => $dateFrom,
- 'to' => sprintf(NEWS_DATE_PATTERN_TO,
- floor($row['timestamp'] / (3600*24)))
- );
- }
-
- //The comments alias.
- $aliases[] = array(
- 'from' => sprintf(NEWS_COMMENTS_PATTERN_FROM, $row['id']),
- 'to' => sprintf(NEWS_COMMENTS_PATTERN_TO, $row['id'])
- );
-
- //The title alias.
- $aliases[] = array(
- 'from' => sprintf(NEWS_TITLE_PATTERN_FROM,
- urlEncodeReadable($row['caption']), $row['id']),
- 'to' => sprintf(NEWS_TITLE_PATTERN_TO, $row['id'])
- );
- }
-
- //Source 3: Projects
- $result = $db->query("SELECT proj.relative_path, menu.internal_path " .
- "FROM ".TABLE_PROJECTS." AS proj, ".TABLE_PROJECT_SUBMENUS." AS menu ".
- "WHERE menu.project_id = proj.id");
- while(list($projPath, $menuPath) = mysql_fetch_row($result)) {
- if($menuPath == 'about') {
- //The about module.
- $aliases[] = array(
- 'from' => $projPath.$menuPath,
- 'to' => sprintf(PROJECT_ABOUT_TO)
- );
- } else if($menuPath == 'progress') {
- //The progress module.
- $aliases[] = array(
- 'from' => $projPath.$menuPath,
- 'to' => sprintf(PROJECT_PROGRESS_TO)
- );
- } else if($menuPath == 'download') {
- //The download module.
- $aliases[] = array(
- 'from' => $projPath.$menuPath,
- 'to' => sprintf(PROJECT_DOWNLOAD_TO)
- );
- } else if($menuPath == 'images') {
- //The images module.
- $aliases[] = array(
- 'from' => $projPath.$menuPath,
- 'to' => sprintf(PROJECT_IMAGES_TO)
- );
- }
- }
-
- //Source 4: Programming languages
- $fields = array(
- 'id',
- 'name'
- );
- $rows = $db->querySelect(TABLE_PROGRAM_LANGS, $fields);
- foreach($rows as $row) {
- $aliases[] = array(
- 'from' => sprintf(LANG_PATTERN_FROM, strtolower($row['name'])),
- 'to' => sprintf(LANG_PATTERN_TO, $row['id'])
- );
- }
-
- //Source 5: Archive months
- $result = $db->query(
- "SELECT min(timestamp), max(timestamp) FROM ".TABLE_NEWS);
- list($time, $upperBound) = mysql_fetch_row($result);
- do {
- $year = date('Y', $time);
- $month = date('n', $time);
- $aliases[] = array(
- 'from' => sprintf(ARCHIVE_MONTH_PATTERN_FROM, $year, $month),
- 'to' => sprintf(ARCHIVE_MONTH_PATTERN_TO, $year, $month)
- );
- $before = $time;
- $time = mktime(0, 0, 1, $month+1, 1, $year);
- } while($time <= $upperBound);
-
- //Source 6: Sitemaps
- doInclude('lib_sitemap');
- $index = new SitemapIndex();
- $aliases[] = array(
- 'from' => $index->getVirtualLocation(),
- 'to' => sprintf(SITEMAP_PATTERN_TO, '')
- );
- foreach($index->getSitemaps() as $sitemap) {
- $aliases[] = array(
- 'from' => $sitemap->getVirtualLocation(),
- 'to' => sprintf(SITEMAP_PATTERN_TO, $sitemap->getName())
- );
- }
-
- //Reset the master table.
- $db->query("TRUNCATE TABLE ".TABLE_ALIASES);
- foreach($aliases as $alias) {
- $db->queryInsert(TABLE_ALIASES, $alias);
- }
- }
-
- /**
- * Rebuilds the url redirects master table. It composes all necessary redirects
- * and resets the redirects master table with that info.
- * @access public
- */
- function buildUrlRedirects() {
- /**
- * The printf pattern that should be used for building the projects' from
- * values.
- * @var string
- * @access private
- */
- define('PROJECT_FROM', '%sindex');
- /**
- * The printf pattern that should be used for building the projects' to
- * values.
- * @var string
- * @access private
- */
- define('PROJECT_TO', '%sabout');
- /**
- * The printf pattern that should be used for building the news id from
- * value.
- * @var string
- * @access private
- */
- define('NEWS_ID_PATTERN_FROM', 'news/entry%s');
- /**
- * The printf pattern that should be used for building the news id to
- * value.
- * @var string
- * @access private
- */
- define('NEWS_ID_PATTERN_TO', 'news/%s_%s');
-
- global $db;
- $redirects = array();
-
- //Source 1: Custom redirects.
- $fields = array(
- 'from',
- 'to'
- );
- $rows = $db->querySelect(TABLE_CUSTOM_REDIRECTS, $fields);
- foreach($rows as $row) {
- $redirects[] = $row;
- }
-
- //Source 2: Projects
- $rows = $db->querySelect(TABLE_PROJECTS, array('relative_path'));
- foreach($rows as $row) {
- $path = $row['relative_path'];
- $redirects[] = array(
- 'from' => sprintf(PROJECT_FROM, $path),
- 'to' => sprintf(PROJECT_TO, $path)
- );
- }
-
- //Source 3: News
- doInclude('lib_util');
- $fields = array(
- 'id',
- 'caption',
- 'timestamp'
- );
- $rows = $db->querySelect(TABLE_NEWS, $fields);
- foreach($rows as $row) {
- //The id redirects.
- $redirects[] = array(
- 'from' => sprintf(NEWS_ID_PATTERN_FROM, $row['id']),
- 'to' => sprintf(NEWS_ID_PATTERN_TO,
- urlEncodeReadable($row['caption']), $row['id'])
- );
- }
-
- //Reset the master table.
- $db->query("TRUNCATE TABLE ".TABLE_REDIRECTS);
- foreach($redirects as $redirection) {
- $db->queryInsert(TABLE_REDIRECTS, $redirection);
- }
- }
-
- /**
- * Gets all urls that are similar to a specified url. I.e. all urls that exist
- * and are subsets of the specified url.
- * @param string $url The url, relative to the page root, for which all similar
- * urls should be found. No query string is allowed.
- * @return array An array of string with each string representing a similar url
- * relative to the page root.
- * @access public
- */
- function getSimilarUrls($url) {
- $result = array();
- $parts = array_reverse(explode('/', $url));
- foreach($parts as $key => $part) {
- $baseUrl = getRealUrl(implode('/', array_reverse($parts)), false);
- $filePath = DOC_ROOT.$baseUrl;
-
- if(file_exists($filePath)) {
- if(is_dir($filePath)) {
- if(getUrlPath($baseUrl.'/', false) != '') {
- $result[] = $baseUrl.'/';
- }
- } else {
- //A match has been found.
- $result[] = $baseUrl;
- }
- }
- unset($parts[$key]);
- }
-
- $result[] = '';
- return $result;
- }
-
- /**
- * Performs a delayed redirection to the specified url with the speicified
- * delay. The page will be redirected after the specified number of seconds
- * have elapsed.
- * @param string $url The url to the site that the user should be taken to.
- * @param integer $delay The number of seconds that the redirection should be
- * delayed.
- * @access public
- */
- function doDelayedRedirect($url, $delay = 2) {
- Page::getInstance()->addToHead(new HeadData(
- '<meta http-equiv="refresh" content="'.$delay.';url='.$url.'" />'));
- }
-
- /**
- * Redirects the request to a specified page with a permanent redirect, i.e.
- * with a status code 301. The redirects is performed immedientally without
- * delay and is done via the header sent in response to the request.
- * @param string $destinationUrl The url to which the user should be
- * redirected. The url should be relative to the page root, relative to
- * the web root or be absolute, starting with http://.
- */
- function doPermanentRedirect($destinationUrl) {
- header("HTTP/1.1 301 Moved Permanently");
- doRedirect($destinationUrl);
- }
-
- /**
- * Redirects the url to the specified page with a temporary redirect, i.e.
- * a redirect that should not be considered to be permanent by the client.
- * @param string $destinationUrl The url to which the user should be
- * redirected. The url should be relative to the page root, relative to
- * the web root or be absolute, starting with http://.
- */
- function doRedirect($destinationUrl) {
- if(strpos($destinationUrl, 'http://') !== 0) {
- //Local redirect.
- header("Location: ".ROOT_PATH.stripRootPath($destinationUrl));
- } else {
- //Absolute redirect.
- header("Location: ".$destinationUrl);
- }
- }
-
- /**
- * Strips the root path from a specified url.
- * @param string $url The url from which the root path should be stripped.
- * @return string The url but without the first occurance of the root path.
- */
- function stripRootPath($url) {
- $strpos = strpos($url, ROOT_PATH);
- if($strpos === false || $strpos != 0) {
- //The url does not begin with the root path.
- return $url;
- } else {
- return substr($url, 0, $strpos).substr($url, $strpos.strlen(ROOT_PATH));
- }
- }
- ?>