com.lokorin.lokorin.lib
[ class tree: com.lokorin.lokorin.lib ] [ index: com.lokorin.lokorin.lib ] [ all elements ]

Source for file lib_redirects.php

Documentation is available at lib_redirects.php

  1. <?php
  2. /*
  3. * Lokorin.com
  4. * Copyright 2004-2006
  5. * Licensed under the GNU LGPL. See COPYING for full terms.
  6. */
  7. /**
  8. * A library that contains everything related to the site's url redirect
  9. * system. The system maps incoming urls to actual files on the server. It
  10. * allows the site to show one structure to the outside while in reality using
  11. * a completly different structure on the server.
  12. * @author Andreas Launila
  13. * @version $Revision: 1.20 $
  14. * @package com.lokorin.lokorin.lib
  15. * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  16. */
  17.  
  18. /**
  19. * For access to common functions and variables.
  20. */
  21. require_once('common.php');
  22.  
  23. /**
  24. * Strips an url of any query string (if one exists).
  25. * @param string $url The url which's query string should be removed.
  26. * @return string The url without its query string.
  27. * @access public
  28. */
  29. function stripQueryString($url) {
  30. return preg_replace("/([^?]+)\?.*/", '$1', $url);
  31. }
  32.  
  33. /**
  34. * Attempts to get a url path to an existing file by starting from a specified
  35. * url and then translating the related redirects.
  36. * @param string $url The url, relative to the page root, that the search
  37. * should start from.
  38. * @param boolean $jump Whether or not redirects should be followed. I.e.
  39. * whether or not jumps should be executed. True if jumps should be
  40. * executed, false otherwise.
  41. * @return string The url to the file, relative to the page root and with query
  42. * string, that the url is resolved to. An empty string is returned if no
  43. * file could be found.
  44. * @access public
  45. */
  46. function getUrlPath($url, $jump = true) {
  47. $url = getRealUrl($url);
  48. $baseUrl = stripQueryString($url);
  49. $queryString = str_replace($baseUrl, '', $url);
  50. $filePath = DOC_ROOT.$baseUrl;
  51. //Look for redirects.
  52. $redirect = getRedirect($baseUrl);
  53. if(strlen($redirect) != 0) {
  54. if(!$jump) {
  55. return $redirect;
  56. }
  57. //A redirect was found, apply it.
  58. doPermanentRedirect($redirect.$queryString);
  59. exit;
  60. }
  61.  
  62. //Look for aliases.
  63. if(file_exists($filePath)) {
  64. if(is_dir($filePath)) {
  65. if((strlen($queryString) == 0) &&
  66. (getUrlPath($url.'/', $jump) != '')) {
  67. if(!$jump) {
  68. return $baseUrl.'/';
  69. }
  70. doRedirect($baseUrl.'/');
  71. exit;
  72. } else {
  73. return '';
  74. }
  75. } else {
  76. //A match has been found.
  77. return $url;
  78. }
  79. } else if(file_exists($filePath.'.php')) {
  80. //It's a match.
  81. return $url;
  82. } else {
  83. //Nothing was found.
  84. return '';
  85. }
  86. }
  87.  
  88. /**
  89. * Gets any redirects that apply to a specified url.
  90. * @param string $requestedUrl The url, relative to the site's root, which
  91. * has been requested by the user (no query string).
  92. * @return string The url that the specified url should be redirected to, or an
  93. * empty string if none was found.
  94. * @access private
  95. */
  96. function getRedirect($requestedUrl) {
  97. global $db;
  98.  
  99. $requestedUrl = str_replace('.php', '', $requestedUrl);
  100. try {
  101. $idFields = array(
  102. 'from' => $requestedUrl
  103. );
  104. return $db->querySelectFirst(TABLE_REDIRECTS, 'to',
  105. $db->buildSqlSelector($idFields));
  106. } catch(Exception $e) {
  107. return '';
  108. }
  109. }
  110.  
  111. /**
  112. * Gets the url that a specified url really leads to. For instance a user might
  113. * request one url while the request is actually transferred to an centralised
  114. * module behind the scenes.
  115. * @param string $requestedUrl The url, relative to the site's root, which
  116. * has been requested by the user (no query string).
  117. * @return string The actual url, relative to the site's root, that the call
  118. * should be directed to.
  119. * @access private
  120. */
  121. function getRealUrl($requestedUrl) {
  122. global $db;
  123. $idFields = array();
  124. $idFields[] = '(`from`='.$db->escape($requestedUrl).')';
  125. if((strlen($requestedUrl) == 0) ||
  126. ($requestedUrl{strlen($requestedUrl)-1} != '/')) {
  127. $idFields[] = '(`from`='.$db->escape($requestedUrl.'/').')';
  128. }
  129. $rows = $db->querySelect(TABLE_ALIASES, array('to'),
  130. 'WHERE '.implode(' OR ', $idFields));
  131.  
  132. if(sizeof($rows) > 0) {
  133. return $rows[0]['to'];
  134. } else if((strlen($requestedUrl) == 0) ||
  135. ($requestedUrl{strlen($requestedUrl)-1} === '/')) {
  136. return getRealUrl($requestedUrl.'index');
  137. } else {
  138. return $requestedUrl;
  139. }
  140. }
  141.  
  142. /**
  143. * Rebuilds the url aliases master table. It composes all necessary aliases
  144. * and resets the aliases master table with that info.
  145. * @access public
  146. */
  147. function buildUrlAliases() {
  148. /**
  149. * The printf pattern that should be used for building the news date from
  150. * value.
  151. * @var string
  152. * @access private
  153. */
  154. define('NEWS_DATE_PATTERN_FROM', 'news/%s/%s/%s');
  155. /**
  156. * The printf pattern that should be used for building the news date to
  157. * value.
  158. * @var string
  159. * @access private
  160. */
  161. define('NEWS_DATE_PATTERN_TO', 'news/archive.php?day=%s');
  162. /**
  163. * The printf pattern that should be used for building the news comments
  164. * from value.
  165. * @var string
  166. * @access private
  167. */
  168. define('NEWS_COMMENTS_PATTERN_FROM', 'news/comments/entry%s');
  169. /**
  170. * The printf pattern that should be used for building the news comments to
  171. * value.
  172. * @var string
  173. * @access private
  174. */
  175. define('NEWS_COMMENTS_PATTERN_TO', 'news/comments.php?news_id=%s');
  176. /**
  177. * The printf pattern that should be used for building the news title from
  178. * value.
  179. * @vars string
  180. * @access private
  181. */
  182. define('NEWS_TITLE_PATTERN_FROM', 'news/%s_%s');
  183. /**
  184. * The printf pattern that should be used for building the news id to
  185. * value.
  186. * @var string
  187. * @access private
  188. */
  189. define('NEWS_TITLE_PATTERN_TO', 'news/archive.php?id=%s');
  190. /**
  191. * The printf pattern that should be used for building the project
  192. * submenus' to value when it should point to the about module.
  193. * @var string
  194. * @access private
  195. */
  196. define('PROJECT_ABOUT_TO', '_includes/module_about.php');
  197. /**
  198. * The printf pattern that should be used for building the project
  199. * submenus' to value when it should point to the progress module.
  200. * @var string
  201. * @access private
  202. */
  203. define('PROJECT_PROGRESS_TO', '_includes/module_progress.php');
  204. /**
  205. * The printf pattern that should be used for building the project
  206. * submenus' to value when it should point to the download module.
  207. * @var string
  208. * @access private
  209. */
  210. define('PROJECT_DOWNLOAD_TO', '_includes/module_download.php');
  211. /**
  212. * The printf pattern that should be used for building the project
  213. * submenus' to value when it should point to the image module.
  214. * @var string
  215. * @access private
  216. */
  217. define('PROJECT_IMAGES_TO', '_includes/module_images.php');
  218. /**
  219. * The printf pattern that should be used for building the from values for
  220. * programming language related aliases.
  221. * @var string
  222. * @access private
  223. */
  224. define('LANG_PATTERN_FROM', 'languages/%s');
  225. /**
  226. * The printf pattern that should be used for building the project
  227. * submenus' to value when it should point to the download module.
  228. * @var string
  229. * @access private
  230. */
  231. define('LANG_PATTERN_TO', 'languages.php?id=%s');
  232. /**
  233. * The printf pattern that should be used for building the archive month
  234. * from value.
  235. * @var string
  236. * @access private
  237. */
  238. define('ARCHIVE_MONTH_PATTERN_FROM', 'news/%s/%s');
  239. /**
  240. * The printf pattern that should be used for building the archive month
  241. * to value.
  242. * @var string
  243. * @access private
  244. */
  245. define('ARCHIVE_MONTH_PATTERN_TO', 'news/archive.php?year=%s&month=%s');
  246. /**
  247. * The printf pattern that should be used for building the sitemap to
  248. * value.
  249. * @var string
  250. * @access private
  251. */
  252. define('SITEMAP_PATTERN_TO', 'sitemaps/sitemap.php?name=%s');
  253.  
  254. global $db;
  255. $aliases = array();
  256.  
  257. //Source 1: Custom aliases.
  258. $fields = array(
  259. 'from',
  260. 'to'
  261. );
  262. $rows = $db->querySelect(TABLE_CUSTOM_ALIASES, $fields);
  263. foreach($rows as $row) {
  264. $aliases[] = $row;
  265. }
  266. //Source 2: News
  267. doInclude('lib_util');
  268. $fields = array(
  269. 'id',
  270. 'caption',
  271. 'timestamp'
  272. );
  273. $rows = $db->querySelect(TABLE_NEWS, $fields);
  274. $datesFrom = array(); //Stores all redirected dates.
  275. foreach($rows as $row) {
  276. //The date alias.
  277. $dayOfMonth = date('j', $row['timestamp']);
  278. $month = date('n', $row['timestamp']);
  279. $year = date('Y', $row['timestamp']);
  280. $dateFrom = sprintf(NEWS_DATE_PATTERN_FROM, $year, $month,
  281. $dayOfMonth);
  282. if(!in_array($dateFrom, $datesFrom)) {
  283. $datesFrom[] = $dateFrom;
  284. $aliases[] = array(
  285. 'from' => $dateFrom,
  286. 'to' => sprintf(NEWS_DATE_PATTERN_TO,
  287. floor($row['timestamp'] / (3600*24)))
  288. );
  289. }
  290. //The comments alias.
  291. $aliases[] = array(
  292. 'from' => sprintf(NEWS_COMMENTS_PATTERN_FROM, $row['id']),
  293. 'to' => sprintf(NEWS_COMMENTS_PATTERN_TO, $row['id'])
  294. );
  295. //The title alias.
  296. $aliases[] = array(
  297. 'from' => sprintf(NEWS_TITLE_PATTERN_FROM,
  298. urlEncodeReadable($row['caption']), $row['id']),
  299. 'to' => sprintf(NEWS_TITLE_PATTERN_TO, $row['id'])
  300. );
  301. }
  302. //Source 3: Projects
  303. $result = $db->query("SELECT proj.relative_path, menu.internal_path " .
  304. "FROM ".TABLE_PROJECTS." AS proj, ".TABLE_PROJECT_SUBMENUS." AS menu ".
  305. "WHERE menu.project_id = proj.id");
  306. while(list($projPath, $menuPath) = mysql_fetch_row($result)) {
  307. if($menuPath == 'about') {
  308. //The about module.
  309. $aliases[] = array(
  310. 'from' => $projPath.$menuPath,
  311. 'to' => sprintf(PROJECT_ABOUT_TO)
  312. );
  313. } else if($menuPath == 'progress') {
  314. //The progress module.
  315. $aliases[] = array(
  316. 'from' => $projPath.$menuPath,
  317. 'to' => sprintf(PROJECT_PROGRESS_TO)
  318. );
  319. } else if($menuPath == 'download') {
  320. //The download module.
  321. $aliases[] = array(
  322. 'from' => $projPath.$menuPath,
  323. 'to' => sprintf(PROJECT_DOWNLOAD_TO)
  324. );
  325. } else if($menuPath == 'images') {
  326. //The images module.
  327. $aliases[] = array(
  328. 'from' => $projPath.$menuPath,
  329. 'to' => sprintf(PROJECT_IMAGES_TO)
  330. );
  331. }
  332. }
  333. //Source 4: Programming languages
  334. $fields = array(
  335. 'id',
  336. 'name'
  337. );
  338. $rows = $db->querySelect(TABLE_PROGRAM_LANGS, $fields);
  339. foreach($rows as $row) {
  340. $aliases[] = array(
  341. 'from' => sprintf(LANG_PATTERN_FROM, strtolower($row['name'])),
  342. 'to' => sprintf(LANG_PATTERN_TO, $row['id'])
  343. );
  344. }
  345. //Source 5: Archive months
  346. $result = $db->query(
  347. "SELECT min(timestamp), max(timestamp) FROM ".TABLE_NEWS);
  348. list($time, $upperBound) = mysql_fetch_row($result);
  349. do {
  350. $year = date('Y', $time);
  351. $month = date('n', $time);
  352. $aliases[] = array(
  353. 'from' => sprintf(ARCHIVE_MONTH_PATTERN_FROM, $year, $month),
  354. 'to' => sprintf(ARCHIVE_MONTH_PATTERN_TO, $year, $month)
  355. );
  356. $before = $time;
  357. $time = mktime(0, 0, 1, $month+1, 1, $year);
  358. } while($time <= $upperBound);
  359. //Source 6: Sitemaps
  360. doInclude('lib_sitemap');
  361. $index = new SitemapIndex();
  362. $aliases[] = array(
  363. 'from' => $index->getVirtualLocation(),
  364. 'to' => sprintf(SITEMAP_PATTERN_TO, '')
  365. );
  366. foreach($index->getSitemaps() as $sitemap) {
  367. $aliases[] = array(
  368. 'from' => $sitemap->getVirtualLocation(),
  369. 'to' => sprintf(SITEMAP_PATTERN_TO, $sitemap->getName())
  370. );
  371. }
  372. //Reset the master table.
  373. $db->query("TRUNCATE TABLE ".TABLE_ALIASES);
  374. foreach($aliases as $alias) {
  375. $db->queryInsert(TABLE_ALIASES, $alias);
  376. }
  377. }
  378.  
  379. /**
  380. * Rebuilds the url redirects master table. It composes all necessary redirects
  381. * and resets the redirects master table with that info.
  382. * @access public
  383. */
  384. function buildUrlRedirects() {
  385. /**
  386. * The printf pattern that should be used for building the projects' from
  387. * values.
  388. * @var string
  389. * @access private
  390. */
  391. define('PROJECT_FROM', '%sindex');
  392. /**
  393. * The printf pattern that should be used for building the projects' to
  394. * values.
  395. * @var string
  396. * @access private
  397. */
  398. define('PROJECT_TO', '%sabout');
  399. /**
  400. * The printf pattern that should be used for building the news id from
  401. * value.
  402. * @var string
  403. * @access private
  404. */
  405. define('NEWS_ID_PATTERN_FROM', 'news/entry%s');
  406. /**
  407. * The printf pattern that should be used for building the news id to
  408. * value.
  409. * @var string
  410. * @access private
  411. */
  412. define('NEWS_ID_PATTERN_TO', 'news/%s_%s');
  413. global $db;
  414. $redirects = array();
  415.  
  416. //Source 1: Custom redirects.
  417. $fields = array(
  418. 'from',
  419. 'to'
  420. );
  421. $rows = $db->querySelect(TABLE_CUSTOM_REDIRECTS, $fields);
  422. foreach($rows as $row) {
  423. $redirects[] = $row;
  424. }
  425. //Source 2: Projects
  426. $rows = $db->querySelect(TABLE_PROJECTS, array('relative_path'));
  427. foreach($rows as $row) {
  428. $path = $row['relative_path'];
  429. $redirects[] = array(
  430. 'from' => sprintf(PROJECT_FROM, $path),
  431. 'to' => sprintf(PROJECT_TO, $path)
  432. );
  433. }
  434. //Source 3: News
  435. doInclude('lib_util');
  436. $fields = array(
  437. 'id',
  438. 'caption',
  439. 'timestamp'
  440. );
  441. $rows = $db->querySelect(TABLE_NEWS, $fields);
  442. foreach($rows as $row) {
  443. //The id redirects.
  444. $redirects[] = array(
  445. 'from' => sprintf(NEWS_ID_PATTERN_FROM, $row['id']),
  446. 'to' => sprintf(NEWS_ID_PATTERN_TO,
  447. urlEncodeReadable($row['caption']), $row['id'])
  448. );
  449. }
  450. //Reset the master table.
  451. $db->query("TRUNCATE TABLE ".TABLE_REDIRECTS);
  452. foreach($redirects as $redirection) {
  453. $db->queryInsert(TABLE_REDIRECTS, $redirection);
  454. }
  455. }
  456.  
  457. /**
  458. * Gets all urls that are similar to a specified url. I.e. all urls that exist
  459. * and are subsets of the specified url.
  460. * @param string $url The url, relative to the page root, for which all similar
  461. * urls should be found. No query string is allowed.
  462. * @return array An array of string with each string representing a similar url
  463. * relative to the page root.
  464. * @access public
  465. */
  466. function getSimilarUrls($url) {
  467. $result = array();
  468. $parts = array_reverse(explode('/', $url));
  469. foreach($parts as $key => $part) {
  470. $baseUrl = getRealUrl(implode('/', array_reverse($parts)), false);
  471. $filePath = DOC_ROOT.$baseUrl;
  472. if(file_exists($filePath)) {
  473. if(is_dir($filePath)) {
  474. if(getUrlPath($baseUrl.'/', false) != '') {
  475. $result[] = $baseUrl.'/';
  476. }
  477. } else {
  478. //A match has been found.
  479. $result[] = $baseUrl;
  480. }
  481. }
  482. unset($parts[$key]);
  483. }
  484. $result[] = '';
  485. return $result;
  486. }
  487.  
  488. /**
  489. * Performs a delayed redirection to the specified url with the speicified
  490. * delay. The page will be redirected after the specified number of seconds
  491. * have elapsed.
  492. * @param string $url The url to the site that the user should be taken to.
  493. * @param integer $delay The number of seconds that the redirection should be
  494. * delayed.
  495. * @access public
  496. */
  497. function doDelayedRedirect($url, $delay = 2) {
  498. Page::getInstance()->addToHead(new HeadData(
  499. '<meta http-equiv="refresh" content="'.$delay.';url='.$url.'" />'));
  500. }
  501.  
  502. /**
  503. * Redirects the request to a specified page with a permanent redirect, i.e.
  504. * with a status code 301. The redirects is performed immedientally without
  505. * delay and is done via the header sent in response to the request.
  506. * @param string $destinationUrl The url to which the user should be
  507. * redirected. The url should be relative to the page root, relative to
  508. * the web root or be absolute, starting with http://.
  509. */
  510. function doPermanentRedirect($destinationUrl) {
  511. header("HTTP/1.1 301 Moved Permanently");
  512. doRedirect($destinationUrl);
  513. }
  514.  
  515. /**
  516. * Redirects the url to the specified page with a temporary redirect, i.e.
  517. * a redirect that should not be considered to be permanent by the client.
  518. * @param string $destinationUrl The url to which the user should be
  519. * redirected. The url should be relative to the page root, relative to
  520. * the web root or be absolute, starting with http://.
  521. */
  522. function doRedirect($destinationUrl) {
  523. if(strpos($destinationUrl, 'http://') !== 0) {
  524. //Local redirect.
  525. header("Location: ".ROOT_PATH.stripRootPath($destinationUrl));
  526. } else {
  527. //Absolute redirect.
  528. header("Location: ".$destinationUrl);
  529. }
  530. }
  531.  
  532. /**
  533. * Strips the root path from a specified url.
  534. * @param string $url The url from which the root path should be stripped.
  535. * @return string The url but without the first occurance of the root path.
  536. */
  537. function stripRootPath($url) {
  538. $strpos = strpos($url, ROOT_PATH);
  539. if($strpos === false || $strpos != 0) {
  540. //The url does not begin with the root path.
  541. return $url;
  542. } else {
  543. return substr($url, 0, $strpos).substr($url, $strpos.strlen(ROOT_PATH));
  544. }
  545. }
  546. ?>

Documentation generated on Sun, 16 Apr 2006 21:03:52 +0200 by phpDocumentor 1.3.0RC4