PageRenderTime 65ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/www/framework/Cms/XmlNav.php

https://github.com/depage/depage-cms
PHP | 479 lines | 265 code | 50 blank | 164 comment | 69 complexity | b8efcbc64658f43debf203deaffa21bf MD5 | raw file
  1. <?php
  2. namespace Depage\Cms;
  3. class XmlNav {
  4. const ACTIVE_STATUS = 'active';
  5. const PARENT_STATUS = 'parent-of-active';
  6. private $xml;
  7. /**
  8. * @brief rout
  9. **/
  10. public $routeHtmlThroughPhp = false;
  11. protected $xmldb = null;
  12. protected $docRefByPageId = [];
  13. protected $nodeByDocRef = [];
  14. protected $pageIdByUrl = [];
  15. protected $pageIdByDocRef = [];
  16. protected $pageInfoByDocRef = [];
  17. protected $pageOrderByDocRef = [];
  18. protected $pagedataIdByPageId = [];
  19. protected $urlsByPageId = [];
  20. // {{{ constructor
  21. /**
  22. * initializes xmlnav object
  23. *
  24. * @param $xml filename to load as navigation xml or navigation as \DOMDocument
  25. */
  26. public function __construct($xmlgetter = null, $xml = '') {
  27. if (!empty($xmlgetter)) {
  28. $this->setXmlGetter($xmlgetter);
  29. }
  30. if (!empty($xml)) {
  31. $this->setPageXml($xml);
  32. }
  33. }
  34. // }}}
  35. // {{{ setXmlGetter()
  36. /**
  37. * @brief setXmlGetter
  38. *
  39. * @param mixed $xmlgetter
  40. * @return void
  41. **/
  42. public function setXmlGetter($xmlgetter)
  43. {
  44. $this->xmldb = $xmlgetter;
  45. }
  46. // }}}
  47. // {{{ setPageXml()
  48. /**
  49. * @brief setPageXml
  50. *
  51. * @param mixed $xml
  52. * @return void
  53. **/
  54. public function setPageXml($xml)
  55. {
  56. if ($xml != '' && is_string($xml)) {
  57. $this->loadXmlFromFile($xml);
  58. } else if ($xml instanceof \DOMDocument) {
  59. $this->xml = $xml;
  60. }
  61. $this->docRefByPageId = [];
  62. $this->nodeByDocRef = [];
  63. $this->pageIdByUrl = [];
  64. $this->pageIdByDocRef = [];
  65. $this->pageInfoByDocRef = [];
  66. $this->pageOrderByDocRef = [];
  67. $this->pagedataIdByPageId = [];
  68. $this->urlsByPageId = [];
  69. list($xml, $node) = \Depage\Xml\Document::getDocAndNode($this->xml);
  70. $xpath = new \DOMXPath($xml);
  71. $xpath->registerNamespace("pg", "http://cms.depagecms.net/ns/page");
  72. $pages = $xpath->query("//pg:*[@url]");
  73. if ($pages->length == 0) {
  74. // url attribute not available -> add now
  75. $this->addUrlAttributes($xml);
  76. $pages = $xpath->query("//pg:*[@url]");
  77. }
  78. $i = 0;
  79. foreach ($pages as $node) {
  80. $id = (int) $node->getAttribute("db:id");
  81. $docref = $node->getAttributeNS("http://cms.depagecms.net/ns/database", "docref");
  82. // base mappings
  83. $this->urlsByPageId[$id] = $node->getAttribute("url");
  84. $this->docRefByPageId[$id] = $docref;
  85. $this->pageIdByDocRef[$docref] = $id;
  86. $this->nodeByDocRef[$docref] = $node;
  87. // only for pages and redirects
  88. if ($node->nodeName != "pg:page" && $node->nodeName != "pg:redirect") {
  89. continue;
  90. }
  91. $this->pageIdByUrl[$node->getAttribute("url")] = $id;
  92. $this->pageOrderByDocRef[$docref] = $i;
  93. $i++;
  94. }
  95. }
  96. // }}}
  97. // {{{ getPageXml()
  98. /**
  99. * @brief getPageXml
  100. *
  101. * @param mixed
  102. * @return void
  103. **/
  104. public function getPageXml()
  105. {
  106. return $this->xml;
  107. }
  108. // }}}
  109. // {{{ loadXmlFromFile()
  110. /**
  111. * loads navigation xml from file
  112. *
  113. * @param string $path
  114. */
  115. public function loadXmlFromFile($path) {
  116. $this->xml = new \DOMDocument();
  117. if (!$this->xml->load($path)) {
  118. throw new \exception('Could not load the navigation XML file.');
  119. }
  120. }
  121. // }}}
  122. // {{{ getPages()
  123. /**
  124. * @brief getPages
  125. *
  126. * @param mixed
  127. * @return void
  128. **/
  129. public function getPages()
  130. {
  131. foreach ($this->nodeByDocRef as $docref => $pos) {
  132. $this->getPageInfo($docref);
  133. }
  134. return array_filter($this->pageInfoByDocRef, function($page) {
  135. return $page->pageType != "pg:folder";
  136. });
  137. }
  138. // }}}
  139. // {{{ getRecentlyChangedPages()
  140. /**
  141. * @brief getRecentlyChangedPages
  142. *
  143. * @param max
  144. * @return array
  145. **/
  146. public function getRecentlyChangedPages($max = null)
  147. {
  148. $pages = $this->getPages();
  149. usort($pages, function($a, $b) {
  150. if (!$a->released && $b->released) {
  151. return -1;
  152. } else if ($a->released && !$b->released) {
  153. return 1;
  154. }
  155. return $b->lastchange->getTimestamp() <=> $a->lastchange->getTimestamp();
  156. });
  157. if ($max > 0) {
  158. $pages = array_splice($pages, 0, $max);
  159. }
  160. return $pages;
  161. }
  162. // }}}
  163. // {{{ getPublicPages()
  164. /**
  165. * @brief getPublicPages
  166. *
  167. * @param max
  168. * @return array
  169. **/
  170. public function getPublicPages($lastpublishDate = false)
  171. {
  172. $pages = $this->getPages();
  173. $pages = array_filter($pages, function($page) {
  174. return $page->released || $page->published;
  175. });
  176. usort($pages, function($a, $b) use ($lastpublishDate) {
  177. if (!$a->released && $b->released) {
  178. return -1;
  179. } else if ($a->released && !$b->released) {
  180. return 1;
  181. } else if (
  182. $lastpublishDate &&
  183. $a->lastrelease->getTimestamp() <= $lastpublishDate->getTimestamp() &&
  184. $b->lastrelease->getTimestamp() <= $lastpublishDate->getTimestamp()
  185. ) {
  186. return $a->pageOrder <=> $b->pageOrder;
  187. }
  188. return $b->lastchange->getTimestamp() <=> $a->lastchange->getTimestamp();
  189. });
  190. return $pages;
  191. }
  192. // }}}
  193. // {{{ getUnreleasedPages()
  194. /**
  195. * @brief getUnreleasedPages
  196. *
  197. * @param mixed
  198. * @return void
  199. **/
  200. public function getUnreleasedPages()
  201. {
  202. $pages = $this->getRecentlyChangedPages();
  203. $pages = array_filter($pages, function($page) {
  204. return $page->released == false;
  205. });
  206. return $pages;
  207. }
  208. // }}}
  209. // {{{ getUnpublishedPages()
  210. /**
  211. * @brief getUnpublishedPages
  212. *
  213. * @param mixed
  214. * @return void
  215. **/
  216. public function getUnpublishedPages($lastpublishDate, $released = null)
  217. {
  218. $pages = $this->getRecentlyChangedPages();
  219. if ($lastpublishDate === false) {
  220. return $pages;
  221. }
  222. $pages = array_filter($pages, function($page) use ($lastpublishDate, $released) {
  223. $r = true;
  224. if ($released === true) {
  225. $r = $page->released == $released;
  226. }
  227. if (!$page->lastrelease) {
  228. return false;
  229. }
  230. return $page->lastrelease->getTimestamp() > $lastpublishDate->getTimestamp() && $r;
  231. });
  232. return $pages;
  233. }
  234. // }}}
  235. // {{{ getUrl()
  236. /**
  237. * @brief getUrl
  238. *
  239. * @param mixed $
  240. * @return void
  241. **/
  242. public function getUrl($pageId)
  243. {
  244. return $this->urlsByPageId[$pageId] ?? false;
  245. }
  246. // }}}
  247. // {{{ getPageId()
  248. /**
  249. * @brief getPageId
  250. *
  251. * @param mixed $url
  252. * @return void
  253. **/
  254. public function getPageId($url)
  255. {
  256. return $this->pageIdByUrl[$url] ?? false;
  257. }
  258. // }}}
  259. // {{{ getPageDataId()
  260. /**
  261. * @brief getPageDataId
  262. *
  263. * @param mixed $pageId
  264. * @return void
  265. **/
  266. public function getPageDataId($pageId)
  267. {
  268. $this->getPageInfo($this->getDocRef($pageId));
  269. return $this->pagedataIdByPageId[$pageId] ?? false;
  270. }
  271. // }}}
  272. // {{{ getDocRef()
  273. /**
  274. * @brief getDocRef
  275. *
  276. * @param mixed getDocRef
  277. * @return void
  278. **/
  279. public function getDocRef($pageId)
  280. {
  281. return $this->docRefByPageId[$pageId] ?? false;
  282. }
  283. // }}}
  284. // {{{ getPageInfo()
  285. /**
  286. * @brief getPageInfo
  287. *
  288. * @param mixed $docref
  289. * @return void
  290. **/
  291. public function getPageInfo($docref)
  292. {
  293. if (!$this->xmldb) {
  294. return false;
  295. }
  296. if (!empty($this->pageInfoByDocRef[$docref])) {
  297. return $this->pageInfoByDocRef[$docref];
  298. }
  299. if (empty($this->nodeByDocRef[$docref])) {
  300. return false;
  301. }
  302. $node = $this->nodeByDocRef[$docref];
  303. $docInfo = $this->xmldb->getDocInfo($docref);
  304. $this->pagedataIdByPageId[$this->pageIdByDocRef[$docref]] = $docInfo->id;
  305. $docInfo->pageId = $node->getAttribute("db:id");
  306. $docInfo->pageOrder = $this->pageOrderByDocRef[$docref] ?? null;
  307. $docInfo->url = $node->getAttribute("url");
  308. $docInfo->fileType = $node->getAttribute("file_type");
  309. $docInfo->published = $node->getAttribute("db:published") == "true";
  310. $docInfo->released = $node->getAttribute("db:released") == "true";
  311. $docInfo->protected = $node->getAttribute("db:protected") == "true";
  312. $docInfo->pageType = $node->nodeName;
  313. $docInfo->nav = [];
  314. $docInfo->tags = [];
  315. foreach ($node->attributes as $name => $attrNode) {
  316. if (substr($name, 0, 4) == 'nav_') {
  317. $docInfo->nav[substr($name, 4)] = $attrNode->value;
  318. } else if (substr($name, 0, 4) == 'tag_') {
  319. $docInfo->tags[substr($name, 4)] = $attrNode->value;
  320. }
  321. }
  322. $this->pageInfoByDocRef[$docref] = $docInfo;
  323. return $this->pageInfoByDocRef[$docref] ?? false;
  324. }
  325. // }}}
  326. // addUrlAttributes() {{{
  327. /**
  328. *zM Add Urls Attributes
  329. *
  330. * Adds a url attribute to each page in the XML DOM tree.
  331. * The url is built from the page name and the names of ancestor folders.
  332. * e.g. folder1/folder2/page1
  333. *
  334. * @param \DOMNode $xml
  335. *
  336. * @return (string) last url
  337. */
  338. public function addUrlAttributes(\DOMNode $node, $url = "") {
  339. list($xml, $node) = \Depage\Xml\Document::getDocAndNode($node);
  340. // get current part of url from name
  341. if (in_array($node->nodeName, ['pg:folder', 'pg:page', 'pg:redirect'])) {
  342. $url .= \Depage\Html\Html::getEscapedUrl(mb_strtolower($node->getAttribute('name')));
  343. }
  344. if (in_array($node->nodeName, ['proj:folder'])) {
  345. $url .= $node->getAttribute('name');
  346. }
  347. // loop through child nodes
  348. if ($node->hasChildNodes()) {
  349. foreach($node->childNodes as $child){
  350. if ($child instanceof \DOMElement) {
  351. $this->addUrlAttributes($child, $url . "/");
  352. }
  353. }
  354. }
  355. // get url based on current node
  356. if ($node->getAttribute("isIndex") == "true") {
  357. // set url as empty when index page
  358. $url = "";
  359. } elseif ($node->nodeName == 'pg:folder') {
  360. // set url of folders to url of first child page
  361. $xpath = new \DOMXpath($xml);
  362. $urlNodes = $xpath->query("(.//pg:page/@url)[1]", $node, true);
  363. $url = $urlNodes->item(0)->value ?? "";
  364. } elseif ($node->nodeName == 'pg:redirect') {
  365. $url = $url . ".php";
  366. } elseif ($node->nodeName == 'pg:page') {
  367. if ($ext = $node->getAttribute("file_type")) {
  368. if ($this->routeHtmlThroughPhp && $ext == "html") {
  369. $ext = "php";
  370. }
  371. $url = $url . "." . $ext;
  372. } else {
  373. $url = $url . "/";
  374. }
  375. } elseif ($node->nodeName == 'proj:folder') {
  376. $url = $url . "/";
  377. } else {
  378. $url = null;
  379. }
  380. if (!is_null($url) && $node->getAttribute("url") !== $url) {
  381. $node->setAttribute("url", $url);
  382. }
  383. }
  384. // }}}
  385. // {{{ addStatusAttributes()
  386. /**
  387. * Add Status
  388. *
  389. * Checks the request URL and sets the status of the active page
  390. * in the XML DOM tree.
  391. *
  392. * Sets parent folder statuses to 'parent-of-active'.
  393. *
  394. * @param \DOMDocument $xml
  395. */
  396. public function addStatusAttributes(\DOMNode $node, $url) {
  397. list($xml, $node) = \Depage\Xml\Document::getDocAndNode($node);
  398. $xpath = new \DOMXpath($xml);
  399. $page = null;
  400. $pages = $xpath->query("//pg:page[@url='{$url}'] | //pg:redirect[@url='{$url}']");
  401. if ($pages && $pages->length) {
  402. // a page has activeUrl
  403. $page = $pages->item(0);
  404. $page->setAttribute('status', $this::ACTIVE_STATUS);
  405. $page = $page->parentNode;
  406. while ($page && $page->nodeType == XML_ELEMENT_NODE) {
  407. // loop to top
  408. $page->setAttribute('status', $this::PARENT_STATUS);
  409. $page = $page->parentNode;
  410. }
  411. }
  412. }
  413. // }}}
  414. // {{{ addLocalizedAttributes()
  415. /**
  416. * Add localized name
  417. *
  418. * @param \DOMDocument $xml
  419. */
  420. private function addLocalizedAttributes(\DOMDocument $xml, $lang) {
  421. $xpath = new \DOMXpath($xml);
  422. $nodes = $xpath->query("//*[@name]");
  423. foreach ($nodes as $node) {
  424. $node->setAttribute('localized', _($node->getAttribute('name')));
  425. }
  426. }
  427. // }}}
  428. }
  429. // vim:set ft=php sw=4 sts=4 fdm=marker et :