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

Source for file lib_sitemap.php

Documentation is available at lib_sitemap.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 functions and classes related to the site's sitemap.
  9. * The sitemap is primarily used by indexing bots, e.g. googlebot.
  10. * @author Andreas Launila
  11. * @version $Revision: 1.2 $
  12. * @package com.lokorin.lokorin.lib
  13. * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  14. */
  15.  
  16. /**
  17. * For access to common constants and functions.
  18. */
  19. require_once('common.php');
  20.  
  21. /**
  22. * The virtual location of the sitemap index file, the file that should
  23. * contain the whole sitemap index.
  24. * @var string
  25. * @access private
  26. */
  27. define('SITEMAP_INDEX', 'sitemap.xml');
  28. /**
  29. * The virtual locations that should be used for sitemap files. The name is a
  30. * printf pattern that accepts one string parameter, the name. The string
  31. * returned is the location relative to the site root.
  32. * @var string
  33. * @access private
  34. */
  35. define('SITEMAP_NAMES', 'sitemap_%s.xml');
  36.  
  37. /**#@+
  38. * Sitemap names, they have to be unique.
  39. * @access private
  40. * @var string
  41. */
  42. define('SITEMAP_NEWS', "news");
  43. define('SITEMAP_PROJECTS', "projects");
  44. define('SITEMAP_FILES', "files");
  45. define('SITEMAP_FORUM', "forum");
  46. /**#@-*/ * The counter that should be used to count the number of the times that the
  47. * sitemap index has been requested.
  48. * @var integer
  49. * @access private
  50. */
  51. define('SITEMAP_INDEX_COUNTER', 58);
  52.  
  53. /**
  54. * Formats the paramamters into a well-formed XML url element as specified
  55. * by the google sitemap schema. The last three parameters are optional,
  56. * they may be left out.
  57. * @param string $loc The url relative the root.
  58. * @param integer $lastmod The timestamp that describes the last point in
  59. * time when the page was modified.
  60. * @param float $priority The priority of the page [0,1].
  61. * @param string $changefreq The change frequency as specified by the
  62. * schema.
  63. * @return string A well-formed XML url element.
  64. */
  65. function formatSitemapElement($loc, $lastmod = false, $priority = false,
  66. $changefreq = false) {
  67. $element = '<url><loc>http://'.$_SERVER['HTTP_HOST'].ROOT_PATH.$loc.'</loc>';
  68. if($lastmod !== false) {
  69. $element .= '<lastmod>'.date(DATE_SITEMAP, $lastmod).'</lastmod>';
  70. }
  71. if($priority !== false) {
  72. $element .= '<priority>'.$priority.'</priority>';
  73. }
  74. if($changefreq !== false) {
  75. $element .= '<changefreq>'.$changefreq.'</changefreq>';
  76. }
  77. $element .= '</url>';
  78. return $element;
  79. }
  80.  
  81. /**
  82. * Notifies that the contents of a table has been altered. This should be
  83. * called in order for the sitemaps to stay current.
  84. * @param string $table The name of the table which's contents changed.
  85. * @access public
  86. */
  87. function notifyTableChanged($table) {
  88. global $db;
  89.  
  90. //Get all sitemaps that are backed by the table.
  91. $idFields = array(
  92. 'backing_table_name' => $table
  93. );
  94. $rows = $db->querySelect(TABLE_SITEMAP, array('name'),
  95. $db->buildSqlSelector($idFields));
  96. foreach($rows as $row) {
  97. //Invalidate the sitemap.
  98. $sitemap = new Sitemap($row['name']);
  99. $sitemap->invalidate();
  100. }
  101. }
  102.  
  103. /**
  104. * Describes a sitemap index.
  105. * @author Andreas Launila
  106. * @package com.lokorin.lokorin.lib
  107. */
  108. class SitemapIndex {
  109. /**
  110. * An array of all sitemaps contained in the index.
  111. * @var array
  112. */
  113. private $sitemaps;
  114. /**
  115. * Constructs the sitemap index.
  116. */
  117. public function __construct() {
  118. $this->sitemaps = $this->getAllSitemaps();
  119. }
  120. /**
  121. * Gets all available sitemaps.
  122. * @return array An array of Sitemap instances, each representing one of
  123. * the available sitemaps.
  124. */
  125. private function getAllSitemaps() {
  126. global $db;
  127. $sitemaps = array();
  128. $rows = $db->querySelect(TABLE_SITEMAP, array('name'));
  129. foreach($rows as $row) {
  130. $sitemaps[] = new Sitemap($row['name']);
  131. }
  132. return $sitemaps;
  133. }
  134. /**
  135. * Gets the complete sitemap index as XML as specified by the google
  136. * sitemap schema.
  137. * @return string A complete XML document.
  138. */
  139. public function getAsXml() {
  140. //Increase the counter.
  141. doInclude('lib_counters');
  142. try {
  143. $counter = new Counter(SITEMAP_INDEX_COUNTER);
  144. $counter->recordHit();
  145. } catch(IllegalArgumentException $e) {}
  146. //Generate the index contents.
  147. ob_start();
  148. echo '<?xml version="1.0" encoding="UTF-8"?>'.
  149. '<sitemapindex xmlns="http://www.google.com/schemas/sitemap/0.84" '.
  150. 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '.
  151. 'xsi:schemaLocation="http://www.google.com/schemas/sitemap/0.84 '.
  152. 'http://www.google.com/schemas/sitemap/0.84/siteindex.xsd">'."\n";
  153. foreach($this->sitemaps as $sitemap) {
  154. echo $sitemap->getAsElementForIndex();
  155. }
  156. //Footer
  157. echo '</sitemapindex>';
  158. return ob_get_clean();
  159. }
  160. /**
  161. * Gets all sitemaps that belong to the index.
  162. * @return array An array of Sitemap instances, each representing one of
  163. * the sitemaps that belong to the index.
  164. */
  165. public function getSitemaps() {
  166. return $this->sitemaps;
  167. }
  168. /**
  169. * Gets the virtual location for the sitemap index.
  170. * @return string A location, relative to the root, for the virtual sitemap
  171. * index.
  172. */
  173. public function getVirtualLocation() {
  174. return SITEMAP_INDEX;
  175. }
  176. }
  177.  
  178. /**
  179. * Describes a sitemap.
  180. * @author Andreas Launila
  181. * @package com.lokorin.lokorin.lib
  182. */
  183. class Sitemap extends TableEntry {
  184. /**
  185. * The unqiue name of the sitemap.
  186. * @var string
  187. */
  188. private $name;
  189. /**
  190. * A unix timestamp that describes the point in time when the sitemap was
  191. * last updated.
  192. * @var integer
  193. */
  194. private $timestampUpdated;
  195. /**
  196. * The actual XML document describing the sitemap.
  197. * @var string
  198. */
  199. private $xmlContents;
  200. /**
  201. * Whether or not the XML contents is valid. Contents that is not valid
  202. * should be regenerated rather than reused. True if the current XML
  203. * contents is valid, false otherwise.
  204. * @var boolean
  205. */
  206. private $isValid;
  207. /**
  208. * The name of the database table that is backing the sitemap, if any. If
  209. * no table is backing the sitemap then this variable should be empty. The
  210. * sitemap should be invalidated if the backing table's contents is
  211. * changed.
  212. * @var string
  213. */
  214. private $backingTable;
  215. /**
  216. * Constructs the sitemap.
  217. * @param string $name The unique name of the sitemap that should be
  218. * constructed.
  219. * @throws IllegalArgumentException If the name is not a valid sitemap
  220. * name.
  221. */
  222. public function __construct($name) {
  223. $fields = array(
  224. 'timestamp_updated',
  225. 'xml_contents',
  226. 'is_valid',
  227. 'backing_table_name'
  228. );
  229. parent::__construct(TABLE_SITEMAP, $name, $fields, 'name');
  230. }
  231. /**
  232. * @see com.lokorin.lokorin.TableEntry.readRow($row)
  233. */
  234. protected function readRow($row) {
  235. $this->name = $row['name'];
  236. $this->timestampUpdated = $row['timestamp_updated'];
  237. $this->xmlContents = $row['xml_contents'];
  238. $this->isValid = ($row['is_valid'] != 0) && ($row['is_valid'] != '0');
  239. $this->backingTable = $row['backing_table_name'];
  240. }
  241. /**
  242. * Invalidates the sitemape, causing it to be regenerated by the next time
  243. * it's requested.
  244. */
  245. public function invalidate() {
  246. global $db;
  247. $vars = array(
  248. 'is_valid' => 0
  249. );
  250. $idFields = array(
  251. 'name' => $this->name
  252. );
  253. $db->queryUpdate(TABLE_SITEMAP, $vars, $db->buildSqlSelector($idFields));
  254. }
  255. /**
  256. * Updates the sitemap with the latest available contents.
  257. */
  258. private function update() {
  259. //Generate the XML document.
  260. ob_start();
  261. //Header.
  262. echo '<?xml version="1.0" encoding="UTF-8"?>'."\n".
  263. '<urlset xmlns="http://www.google.com/schemas/sitemap/0.84" '.
  264. 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '.
  265. 'xsi:schemaLocation="http://www.google.com/schemas/sitemap/0.84 '.
  266. 'http://www.google.com/schemas/sitemap/0.84/sitemap.xsd">'."\n";
  267. //Handle all the names
  268. switch($this->name) {
  269. case 'news':
  270. echo $this->getNewsBody();
  271. break;
  272. case 'projects':
  273. echo $this->getProjectsBody();
  274. break;
  275. case 'menu':
  276. echo $this->getFilesBody();
  277. break;
  278. default:
  279. logException(new IllegalStateException("Unknown name (".$this->name.")"));
  280. }
  281. //Footer.
  282. echo '</urlset>';
  283. //Update the data.
  284. $this->xmlContents = ob_get_clean();
  285. $this->isValid = true;
  286. $this->timestampUpdated = time();
  287.  
  288. //Update the database.
  289. global $db;
  290. $vars = array(
  291. 'timestamp_updated' => $this->timestampUpdated,
  292. 'xml_contents' => $this->xmlContents,
  293. 'is_valid' => '1',
  294. );
  295. $idFields = array(
  296. 'name' => $this->name
  297. );
  298. $db->queryUpdate(TABLE_SITEMAP, $vars, $db->buildSqlSelector($idFields));
  299. }
  300. /**
  301. * Gets the XML body contents for the news sitemap.
  302. * @return string A number of well formed XML elements.
  303. */
  304. private function getNewsBody() {
  305. doInclude('lib_news');
  306.  
  307. $output = '';
  308. $news = getNews(""); //Get all news entries.
  309. foreach($news as $entry) {
  310. $output .= $entry->getAsSitemapEntry()."\n";
  311. }
  312. return $output;
  313. }
  314. /**
  315. * Gets the XML body contents for the projects sitemap.
  316. * @return string A number of well formed XML elements.
  317. */
  318. private function getProjectsBody() {
  319. doInclude('lib_projects');
  320. $output = '';
  321. $projects = getAllProjects();
  322. foreach($projects as $project) {
  323. $output .= $project->getAsSitemapEntries()."\n";
  324. }
  325. return $output;
  326. }
  327. /**
  328. * Gets the XML body contents for everything in the main menu.
  329. * @return string A number of well formed XML elements.
  330. */
  331. private function getFilesBody() {
  332. doInclude('lib_menu');
  333. $output = '';
  334. $menus = getAllRootMenus();
  335. foreach($menus as $menu) {
  336. $output .= $menu->getAsSitemapEntry();
  337. foreach($menu->getChildren() as $child) {
  338. $output .= $child->getAsSitemapEntry();
  339. }
  340. }
  341. return $output;
  342. }
  343. /**
  344. * Gets the contents of the sitemap as an XML document as specified by the
  345. * google sitemap schema.
  346. * @return string A complete XML document.
  347. */
  348. public function getAsXml() {
  349. if(!$this->isValid) {
  350. //Invalid contents, regenerate it.
  351. $this->update();
  352. }
  353. return $this->xmlContents;
  354. }
  355. /**
  356. * Gets the sitemap reference elemenet that should be used to represent
  357. * the sitemap in sitemap indices.
  358. * @return string A single well-formed XML sitemap element as specified by
  359. * the google sitemap schema.
  360. */
  361. public function getAsElementForIndex() {
  362. doInclude('lib_util');
  363. return sprintf('<sitemap><loc>%s</loc><lastmod>%s</lastmod></sitemap>',
  364. 'http://'.$_SERVER['HTTP_HOST'].ROOT_PATH.$this->getVirtualLocation(),
  365. date(DATE_SITEMAP, $this->timestampUpdated));
  366. }
  367. /**
  368. * Gets the virtual location for the sitemap.
  369. * @return string A location, relative to the root, for the virtual
  370. * sitemap.
  371. */
  372. public function getVirtualLocation() {
  373. return sprintf(SITEMAP_NAMES, $this->name);
  374. }
  375. /**
  376. * Gets the unique name of the sitemap.
  377. * @return string A name that is unique for the sitemap.
  378. */
  379. public function getName() {
  380. return $this->name;
  381. }
  382. }
  383.  
  384. ?>

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