PageRenderTime 60ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/library/XenForo/Model/AdminNavigation.php

https://github.com/hanguyenhuu/DTUI_201105
PHP | 666 lines | 413 code | 81 blank | 172 comment | 43 complexity | d7998b80193cc0f0224502434f5ee91f MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, BSD-3-Clause
  1. <?php
  2. /**
  3. * Model for admin navigation.
  4. *
  5. * @package XenForo_AdminNavigation
  6. */
  7. class XenForo_Model_AdminNavigation extends XenForo_Model
  8. {
  9. const FETCH_ADDON = 0x01;
  10. /**
  11. * Gets all admin navigation entries, in parent-display order.
  12. *
  13. * @param array Fetch options
  14. *
  15. * @return array [navigation id] => info
  16. */
  17. public function getAdminNavigationEntries(array $fetchOptions = array())
  18. {
  19. $joinOptions = $this->prepareAdminNavigationFetchOptions($fetchOptions);
  20. return $this->fetchAllKeyed('
  21. SELECT admin_navigation.*
  22. ' . $joinOptions['selectFields'] . '
  23. FROM xf_admin_navigation AS admin_navigation
  24. ' . $joinOptions['joinTables'] . '
  25. ORDER BY
  26. admin_navigation.parent_navigation_id,
  27. admin_navigation.display_order
  28. ', 'navigation_id');
  29. }
  30. /**
  31. * Gets all admin navigation entries with the specified parent, in display order.
  32. *
  33. * @param string $parentId
  34. * @param array Fetch options
  35. *
  36. * @return array [navigation id] => info
  37. */
  38. public function getAdminNavigationEntriesWithParent($parentId, array $fetchOptions = array())
  39. {
  40. $joinOptions = $this->prepareAdminNavigationFetchOptions($fetchOptions);
  41. return $this->fetchAllKeyed('
  42. SELECT admin_navigation.*
  43. ' . $joinOptions['selectFields'] . '
  44. FROM xf_admin_navigation AS admin_navigation
  45. ' . $joinOptions['joinTables'] . '
  46. WHERE
  47. admin_navigation.parent_navigation_id = ?
  48. ORDER BY
  49. admin_navigation.display_order
  50. ', 'navigation_id', $parentId);
  51. }
  52. /**
  53. * Gets all admin navigation entries in the specified add-on.
  54. *
  55. * @param string $addOnId
  56. * @param array Fetch options
  57. *
  58. * @return array [navigation id] => info
  59. */
  60. public function getAdminNavigationEntriesInAddOn($addOnId, array $fetchOptions = array())
  61. {
  62. $joinOptions = $this->prepareAdminNavigationFetchOptions($fetchOptions);
  63. return $this->fetchAllKeyed('
  64. SELECT admin_navigation.*
  65. ' . $joinOptions['selectFields'] . '
  66. FROM xf_admin_navigation AS admin_navigation
  67. ' . $joinOptions['joinTables'] . '
  68. WHERE
  69. admin_navigation.addon_id = ?
  70. ', 'navigation_id', $addOnId);
  71. }
  72. /**
  73. * Gets the specified admin navigation entry.
  74. *
  75. * @param string $navigationId
  76. * @param array Fetch options
  77. *
  78. * @return array|false
  79. */
  80. public function getAdminNavigationEntryById($navigationId, array $fetchOptions = array())
  81. {
  82. $joinOptions = $this->prepareAdminNavigationFetchOptions($fetchOptions);
  83. return $this->_getDb()->fetchRow('
  84. SELECT admin_navigation.*
  85. ' . $joinOptions['selectFields'] . '
  86. FROM xf_admin_navigation AS admin_navigation
  87. ' . $joinOptions['joinTables'] . '
  88. WHERE
  89. admin_navigation.navigation_id = ?
  90. ', $navigationId);
  91. }
  92. /**
  93. * Gets the specified admin navigation entries.
  94. *
  95. * @param array $navigationIds
  96. * @param array Fetch options
  97. *
  98. * @return array [navigation id] => info
  99. */
  100. public function getAdminNavigationEntriesByIds(array $navigationIds, array $fetchOptions = array())
  101. {
  102. if (!$navigationIds)
  103. {
  104. return array();
  105. }
  106. $joinOptions = $this->prepareAdminNavigationFetchOptions($fetchOptions);
  107. return $this->fetchAllKeyed('
  108. SELECT admin_navigation.*
  109. ' . $joinOptions['selectFields'] . '
  110. FROM xf_admin_navigation AS admin_navigation
  111. ' . $joinOptions['joinTables'] . '
  112. WHERE
  113. admin_navigation.navigation_id IN (' . $this->_getDb()->quote($navigationIds) . ')
  114. ', 'navigation_id');
  115. }
  116. /**
  117. * Prepares additional parameters for admin navigation fetch queries.
  118. *
  119. * @param array $fetchOptions
  120. *
  121. * @return array
  122. */
  123. public function prepareAdminNavigationFetchOptions(array $fetchOptions)
  124. {
  125. $selectFields = '';
  126. $joinTables = '';
  127. if (!empty($fetchOptions['join']))
  128. {
  129. if ($fetchOptions['join'] & self::FETCH_ADDON)
  130. {
  131. $selectFields .= ',
  132. addon.*,addon.title AS addon_title, admin_navigation.addon_id';
  133. $joinTables .= '
  134. LEFT JOIN xf_addon AS addon ON
  135. (addon.addon_id = admin_navigation.addon_id)';
  136. }
  137. }
  138. return array(
  139. 'selectFields' => $selectFields,
  140. 'joinTables' => $joinTables,
  141. );
  142. }
  143. /**
  144. * Group admin navigation entries by their parent.
  145. *
  146. * @param array $navigation List of navigation entries to group
  147. *
  148. * @return array [parent navigation id][navigation id] => info
  149. */
  150. public function groupAdminNavigation(array $navigation)
  151. {
  152. $output = array(
  153. '' => array()
  154. );
  155. foreach ($navigation AS $nav)
  156. {
  157. $output[$nav['parent_navigation_id']][$nav['navigation_id']] = $nav;
  158. }
  159. return $output;
  160. }
  161. /**
  162. * Get the admin navigation list in the correct display order. This can be processed
  163. * linearly with depth markers to visually represent the tree.
  164. *
  165. * @param array|null $navigation Navigation entries; if null, grabbed automatically
  166. * @param string $root Root node to traverse from
  167. * @param integer $depth Depth to start at
  168. *
  169. * @return array [navigation id] => info, with depth key set
  170. */
  171. public function getAdminNavigationInOrder(array $navigation = null, $root = '', $depth = 0)
  172. {
  173. if (!is_array($navigation))
  174. {
  175. $navigation = $this->groupAdminNavigation($this->getAdminNavigationEntries());
  176. }
  177. if (!isset($navigation[$root]))
  178. {
  179. return array();
  180. }
  181. $output = array();
  182. foreach ($navigation[$root] AS $nav)
  183. {
  184. $nav['depth'] = $depth;
  185. $output[$nav['navigation_id']] = $nav;
  186. $output += $this->getAdminNavigationInOrder($navigation, $nav['navigation_id'], $depth + 1);
  187. }
  188. return $output;
  189. }
  190. /**
  191. * Gets the admin navigation as simple options for use in a select. Uses depth markers for tree display.
  192. *
  193. * @param array|null $navigation Navigation entries; if null, grabbed automatically
  194. * @param string $root Root node to traverse from
  195. * @param integer $depth Depth to start at
  196. *
  197. * @return array [] => [value, label, depth]
  198. */
  199. public function getAdminNavigationOptions(array $navigation = null, $root = '', $depth = 0)
  200. {
  201. $navList = $this->prepareAdminNavigationEntries(
  202. $this->getAdminNavigationInOrder($navigation, $root, $depth)
  203. );
  204. $options = array();
  205. foreach ($navList AS $nav)
  206. {
  207. $options[] = array(
  208. 'value' => $nav['navigation_id'],
  209. 'label' => $nav['title'],
  210. 'depth' => $nav['depth']
  211. );
  212. }
  213. return $options;
  214. }
  215. /**
  216. * Gets the admin navigation breadcrumb.
  217. *
  218. * @param string $breadCrumbId Final piece in the breadcrumb
  219. * @param array $navigationList List of all navigation entries
  220. *
  221. * @return array Breadcrumb pieces (array: [link, title]), from the top ("tab") down to the given breadcrumb ID
  222. */
  223. public function getAdminNavigationBreadCrumb($breadCrumbId, array $navigationList)
  224. {
  225. $breadCrumb = array();
  226. while (isset($navigationList[$breadCrumbId]))
  227. {
  228. $navigation = $navigationList[$breadCrumbId];
  229. $breadCrumbId = $navigation['parent_navigation_id'];
  230. if (!$navigation['link'])
  231. {
  232. continue;
  233. }
  234. $navigation = $this->prepareAdminNavigationEntry($navigation);
  235. $breadCrumb[$navigation['navigation_id']] = array(
  236. 'link' => $navigation['link'],
  237. 'title' => $navigation['title']
  238. );
  239. $breadCrumbId = $navigation['parent_navigation_id'];
  240. }
  241. return array_reverse($breadCrumb, true);
  242. }
  243. /**
  244. * Determines if the specified parent navigation ID is a valid parent for the navigation ID.
  245. *
  246. * @param string $potentialParentId
  247. * @param string $navigationId
  248. * @param array|null $navigation Navigation entries; if null, grabbed automatically
  249. *
  250. * @return boolean
  251. */
  252. public function isAdminNavigationEntryValidParent($potentialParentId, $navigationId, array $navigation = null)
  253. {
  254. if ($potentialParentId == $navigationId)
  255. {
  256. return false;
  257. }
  258. else if ($potentialParentId === '')
  259. {
  260. return true;
  261. }
  262. if (!is_array($navigation))
  263. {
  264. $navigation = $this->getAdminNavigationEntries();
  265. }
  266. if (!isset($navigation[$potentialParentId]))
  267. {
  268. return false;
  269. }
  270. else if (!isset($navigation[$navigationId]))
  271. {
  272. return true;
  273. }
  274. $walkId = $potentialParentId;
  275. do
  276. {
  277. $walkNav = $navigation[$walkId];
  278. if ($walkNav['navigation_id'] == $navigationId)
  279. {
  280. return false;
  281. }
  282. $walkId = $walkNav['parent_navigation_id'];
  283. }
  284. while (isset($navigation[$walkId]));
  285. return true;
  286. }
  287. /**
  288. * Filter out unviewable admin navigation details based on the visitor and application state.
  289. *
  290. * @param array $navigation List of navigation entries
  291. *
  292. * @return array Navigation entries filtered
  293. */
  294. public function filterUnviewableAdminNavigation(array $navigation)
  295. {
  296. $isDebug = XenForo_Application::debugMode();
  297. $visitor = XenForo_Visitor::getInstance();
  298. $childCount = array();
  299. foreach ($navigation AS $key => $nav)
  300. {
  301. if ($this->getModelFromCache('XenForo_Model_AddOn')->isAddOnDisabled($nav))
  302. {
  303. // XenForo or "" add-on elements can't be disabled, otherwise ensure add-on is enabled
  304. unset($navigation[$key]);
  305. }
  306. else if ($nav['debug_only'] && !$isDebug)
  307. {
  308. unset($navigation[$key]);
  309. }
  310. else if ($nav['admin_permission_id'] && !$visitor->hasAdminPermission($nav['admin_permission_id']))
  311. {
  312. unset($navigation[$key]);
  313. }
  314. else
  315. {
  316. if (isset($childCount[$nav['parent_navigation_id']]))
  317. {
  318. $childCount[$nav['parent_navigation_id']]++;
  319. }
  320. else
  321. {
  322. $childCount[$nav['parent_navigation_id']] = 1;
  323. }
  324. }
  325. }
  326. $traverse = array_keys($navigation);
  327. while ($traverse)
  328. {
  329. $navId = array_shift($traverse);
  330. if ($navId === '' || !isset($navigation[$navId]))
  331. {
  332. continue;
  333. }
  334. $nav = $navigation[$navId];
  335. if (!$nav['hide_no_children'])
  336. {
  337. continue;
  338. }
  339. if (!isset($childCount[$navId]) || $childCount[$navId] <= 0)
  340. {
  341. unset($navigation[$navId]);
  342. $childCount[$nav['parent_navigation_id']]--;
  343. $traverse[] = $nav['parent_navigation_id'];
  344. }
  345. }
  346. return $navigation;
  347. }
  348. /**
  349. * Get the admin navigation for the display of the admin container.
  350. *
  351. * @param string $breadCrumbId Farthest point down the bread crumb; used to build bread crumb and select tab/section
  352. *
  353. * @return array Keys: breadCrumb, tabs, sideLinks, sideLinksRoot
  354. */
  355. public function getAdminNavigationForDisplay($breadCrumbId)
  356. {
  357. $navigation = $this->getAdminNavigationEntries(array(
  358. 'join' => XenForo_Model_AdminNavigation::FETCH_ADDON
  359. ));
  360. $navigation = $this->filterUnviewableAdminNavigation($navigation);
  361. $groupedNavigation = $this->groupAdminNavigation($navigation);
  362. if (isset($navigation[$breadCrumbId]))
  363. {
  364. $breadCrumb = $this->getAdminNavigationBreadCrumb($breadCrumbId, $navigation);
  365. $keys = array_keys($breadCrumb);
  366. $selectedTabId = reset($keys);
  367. }
  368. else
  369. {
  370. $breadCrumb = array();
  371. $selectedTabId = false;
  372. }
  373. $tabs = $this->prepareAdminNavigationEntries($groupedNavigation['']);
  374. if (!$selectedTabId || !isset($groupedNavigation[$selectedTabId]))
  375. {
  376. $selectedTabId = 'setup';
  377. }
  378. if (isset($tabs[$selectedTabId]))
  379. {
  380. $tabs[$selectedTabId]['selected'] = true;
  381. }
  382. if (isset($groupedNavigation[$selectedTabId]))
  383. {
  384. $sideLinks = array();
  385. $traverseList = array($selectedTabId);
  386. while ($traverseList)
  387. {
  388. $traverseId = array_shift($traverseList);
  389. if (!isset($groupedNavigation[$traverseId]))
  390. {
  391. continue;
  392. }
  393. foreach ($groupedNavigation[$traverseId] AS $traverse)
  394. {
  395. if (isset($groupedNavigation[$traverse['navigation_id']]))
  396. {
  397. $traverseList[] = $traverse['navigation_id'];
  398. }
  399. $traverse = $this->prepareAdminNavigationEntry($traverse);
  400. $sideLinks[$traverse['parent_navigation_id']][$traverse['navigation_id']] = $traverse;
  401. }
  402. }
  403. }
  404. else
  405. {
  406. $sideLinks = array();
  407. }
  408. return array(
  409. 'breadCrumb' => $breadCrumb,
  410. 'tabs' => $tabs,
  411. 'sideLinks' => $sideLinks,
  412. 'sideLinksRoot' => $selectedTabId
  413. );
  414. }
  415. /**
  416. * Prepares an admin navigation entry for display.
  417. *
  418. * @param array $entry
  419. *
  420. * @return array
  421. */
  422. public function prepareAdminNavigationEntry(array $entry)
  423. {
  424. $entry['title'] = new XenForo_Phrase($this->getAdminNavigationPhraseName($entry['navigation_id']));
  425. return $entry;
  426. }
  427. /**
  428. * Prepares a list of admin navigation entries for display.
  429. *
  430. * @param array $entries
  431. *
  432. * @return array
  433. */
  434. public function prepareAdminNavigationEntries(array $entries)
  435. {
  436. foreach ($entries AS &$entry)
  437. {
  438. $entry = $this->prepareAdminNavigationEntry($entry);
  439. }
  440. return $entries;
  441. }
  442. /**
  443. * Gets the name of an admin navigation phrase.
  444. *
  445. * @param string $navigationId
  446. *
  447. * @return string
  448. */
  449. public function getAdminNavigationPhraseName($navigationId)
  450. {
  451. return 'admin_navigation_' . $navigationId;
  452. }
  453. /**
  454. * Gets the master title phrase value for the specified admin navigation entry.
  455. *
  456. * @param string $navigationId
  457. *
  458. * @return string
  459. */
  460. public function getAdminNavigationMasterTitlePhraseValue($navigationId)
  461. {
  462. $phraseName = $this->getAdminNavigationPhraseName($navigationId);
  463. return $this->_getPhraseModel()->getMasterPhraseValue($phraseName);
  464. }
  465. /**
  466. * Gets the file name for the development output.
  467. *
  468. * @return string
  469. */
  470. public function getAdminNavigationDevelopmentFileName()
  471. {
  472. $config = XenForo_Application::get('config');
  473. if (!$config->debug || !$config->development->directory)
  474. {
  475. return '';
  476. }
  477. return XenForo_Application::getInstance()->getRootDir()
  478. . '/' . $config->development->directory . '/file_output/admin_navigation.xml';
  479. }
  480. /**
  481. * Gets the development admin navigation XML document.
  482. *
  483. * @return DOMDocument
  484. */
  485. public function getAdminNavigationDevelopmentXml()
  486. {
  487. $document = new DOMDocument('1.0', 'utf-8');
  488. $document->formatOutput = true;
  489. $rootNode = $document->createElement('admin_navigation');
  490. $document->appendChild($rootNode);
  491. $this->appendAdminNavigationAddOnXml($rootNode, 'XenForo');
  492. return $document;
  493. }
  494. /**
  495. * Appends the add-on admin navigation XML to a given DOM element.
  496. *
  497. * @param DOMElement $rootNode Node to append all navigation elements to
  498. * @param string $addOnId Add-on ID to be exported
  499. */
  500. public function appendAdminNavigationAddOnXml(DOMElement $rootNode, $addOnId)
  501. {
  502. $navigation = $this->getAdminNavigationEntriesInAddOn($addOnId);
  503. ksort($navigation);
  504. $document = $rootNode->ownerDocument;
  505. foreach ($navigation AS $nav)
  506. {
  507. $navNode = $document->createElement('navigation');
  508. $navNode->setAttribute('navigation_id', $nav['navigation_id']);
  509. $navNode->setAttribute('parent_navigation_id', $nav['parent_navigation_id']);
  510. $navNode->setAttribute('display_order', $nav['display_order']);
  511. $navNode->setAttribute('link', $nav['link']);
  512. $navNode->setAttribute('admin_permission_id', $nav['admin_permission_id']);
  513. $navNode->setAttribute('debug_only', $nav['debug_only']);
  514. $navNode->setAttribute('hide_no_children', $nav['hide_no_children']);
  515. $rootNode->appendChild($navNode);
  516. }
  517. }
  518. /**
  519. * Deletes the admin navigation that belongs to the specified add-on.
  520. *
  521. * @param string $addOnId
  522. */
  523. public function deleteAdminNavigationForAddOn($addOnId)
  524. {
  525. $db = $this->_getDb();
  526. $db->delete('xf_admin_navigation', 'addon_id = ' . $db->quote($addOnId));
  527. }
  528. /**
  529. * Imports the development admin navigation XML data.
  530. *
  531. * @param string $fileName File to read the XML from
  532. */
  533. public function importAdminNavigationDevelopmentXml($fileName)
  534. {
  535. $document = new SimpleXMLElement($fileName, 0, true);
  536. $this->importAdminNavigationAddOnXml($document, 'XenForo');
  537. }
  538. /**
  539. * Imports the add-on admin navigation XML.
  540. *
  541. * @param SimpleXMLElement $xml XML element pointing to the root of the navigation data
  542. * @param string $addOnId Add-on to import for
  543. */
  544. public function importAdminNavigationAddOnXml(SimpleXMLElement $xml, $addOnId)
  545. {
  546. $db = $this->_getDb();
  547. XenForo_Db::beginTransaction($db);
  548. $this->deleteAdminNavigationForAddOn($addOnId);
  549. $xmlNav = XenForo_Helper_DevelopmentXml::fixPhpBug50670($xml->navigation);
  550. $navIds = array();
  551. foreach ($xmlNav AS $nav)
  552. {
  553. $navIds[] = (string)$nav['navigation_id'];
  554. }
  555. $existingNavigation = $this->getAdminNavigationEntriesByIds($navIds);
  556. foreach ($xmlNav AS $nav)
  557. {
  558. $navId = (string)$nav['navigation_id'];
  559. $dw = XenForo_DataWriter::create('XenForo_DataWriter_AdminNavigation');
  560. if (isset($existingNavigation[$navId]))
  561. {
  562. $dw->setExistingData($existingNavigation[$navId], true);
  563. }
  564. $dw->bulkSet(array(
  565. 'navigation_id' => $navId,
  566. 'parent_navigation_id' => (string)$nav['parent_navigation_id'],
  567. 'display_order' => (string)$nav['display_order'],
  568. 'link' => (string)$nav['link'],
  569. 'admin_permission_id' => (string)$nav['admin_permission_id'],
  570. 'debug_only' => (string)$nav['debug_only'],
  571. 'hide_no_children' => (string)$nav['hide_no_children'],
  572. 'addon_id' => $addOnId
  573. ));
  574. $dw->save();
  575. }
  576. XenForo_Db::commit($db);
  577. }
  578. /**
  579. * @return XenForo_Model_Phrase
  580. */
  581. protected function _getPhraseModel()
  582. {
  583. return $this->getModelFromCache('XenForo_Model_Phrase');
  584. }
  585. }