PageRenderTime 129ms CodeModel.GetById 36ms RepoModel.GetById 2ms app.codeStats 0ms

/lib/Zend/View/Helper/Navigation/Menu.php

https://bitbucket.org/andrewjleavitt/magestudy
PHP | 647 lines | 305 code | 64 blank | 278 comment | 75 complexity | ad2dba2af0566dde40227fcff9c45ed0 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0, WTFPL
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_View
  17. * @subpackage Helper
  18. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: Menu.php 20096 2010-01-06 02:05:09Z bkarwin $
  21. */
  22. /**
  23. * @see Zend_View_Helper_Navigation_HelperAbstract
  24. */
  25. #require_once 'Zend/View/Helper/Navigation/HelperAbstract.php';
  26. /**
  27. * Helper for rendering menus from navigation containers
  28. *
  29. * @category Zend
  30. * @package Zend_View
  31. * @subpackage Helper
  32. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  33. * @license http://framework.zend.com/license/new-bsd New BSD License
  34. */
  35. class Zend_View_Helper_Navigation_Menu
  36. extends Zend_View_Helper_Navigation_HelperAbstract
  37. {
  38. /**
  39. * CSS class to use for the ul element
  40. *
  41. * @var string
  42. */
  43. protected $_ulClass = 'navigation';
  44. /**
  45. * Whether only active branch should be rendered
  46. *
  47. * @var bool
  48. */
  49. protected $_onlyActiveBranch = false;
  50. /**
  51. * Whether parents should be rendered when only rendering active branch
  52. *
  53. * @var bool
  54. */
  55. protected $_renderParents = true;
  56. /**
  57. * Partial view script to use for rendering menu
  58. *
  59. * @var string|array
  60. */
  61. protected $_partial = null;
  62. /**
  63. * View helper entry point:
  64. * Retrieves helper and optionally sets container to operate on
  65. *
  66. * @param Zend_Navigation_Container $container [optional] container to
  67. * operate on
  68. * @return Zend_View_Helper_Navigation_Menu fluent interface,
  69. * returns self
  70. */
  71. public function menu(Zend_Navigation_Container $container = null)
  72. {
  73. if (null !== $container) {
  74. $this->setContainer($container);
  75. }
  76. return $this;
  77. }
  78. // Accessors:
  79. /**
  80. * Sets CSS class to use for the first 'ul' element when rendering
  81. *
  82. * @param string $ulClass CSS class to set
  83. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  84. */
  85. public function setUlClass($ulClass)
  86. {
  87. if (is_string($ulClass)) {
  88. $this->_ulClass = $ulClass;
  89. }
  90. return $this;
  91. }
  92. /**
  93. * Returns CSS class to use for the first 'ul' element when rendering
  94. *
  95. * @return string CSS class
  96. */
  97. public function getUlClass()
  98. {
  99. return $this->_ulClass;
  100. }
  101. /**
  102. * Sets a flag indicating whether only active branch should be rendered
  103. *
  104. * @param bool $flag [optional] render only active
  105. * branch. Default is true.
  106. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  107. */
  108. public function setOnlyActiveBranch($flag = true)
  109. {
  110. $this->_onlyActiveBranch = (bool) $flag;
  111. return $this;
  112. }
  113. /**
  114. * Returns a flag indicating whether only active branch should be rendered
  115. *
  116. * By default, this value is false, meaning the entire menu will be
  117. * be rendered.
  118. *
  119. * @return bool whether only active branch should be rendered
  120. */
  121. public function getOnlyActiveBranch()
  122. {
  123. return $this->_onlyActiveBranch;
  124. }
  125. /**
  126. * Enables/disables rendering of parents when only rendering active branch
  127. *
  128. * See {@link setOnlyActiveBranch()} for more information.
  129. *
  130. * @param bool $flag [optional] render parents when
  131. * rendering active branch.
  132. * Default is true.
  133. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  134. */
  135. public function setRenderParents($flag = true)
  136. {
  137. $this->_renderParents = (bool) $flag;
  138. return $this;
  139. }
  140. /**
  141. * Returns flag indicating whether parents should be rendered when rendering
  142. * only the active branch
  143. *
  144. * By default, this value is true.
  145. *
  146. * @return bool whether parents should be rendered
  147. */
  148. public function getRenderParents()
  149. {
  150. return $this->_renderParents;
  151. }
  152. /**
  153. * Sets which partial view script to use for rendering menu
  154. *
  155. * @param string|array $partial partial view script or null. If
  156. * an array is given, it is
  157. * expected to contain two values;
  158. * the partial view script to use,
  159. * and the module where the script
  160. * can be found.
  161. * @return Zend_View_Helper_Navigation_Menu fluent interface, returns self
  162. */
  163. public function setPartial($partial)
  164. {
  165. if (null === $partial || is_string($partial) || is_array($partial)) {
  166. $this->_partial = $partial;
  167. }
  168. return $this;
  169. }
  170. /**
  171. * Returns partial view script to use for rendering menu
  172. *
  173. * @return string|array|null
  174. */
  175. public function getPartial()
  176. {
  177. return $this->_partial;
  178. }
  179. // Public methods:
  180. /**
  181. * Returns an HTML string containing an 'a' element for the given page if
  182. * the page's href is not empty, and a 'span' element if it is empty
  183. *
  184. * Overrides {@link Zend_View_Helper_Navigation_Abstract::htmlify()}.
  185. *
  186. * @param Zend_Navigation_Page $page page to generate HTML for
  187. * @return string HTML string for the given page
  188. */
  189. public function htmlify(Zend_Navigation_Page $page)
  190. {
  191. // get label and title for translating
  192. $label = $page->getLabel();
  193. $title = $page->getTitle();
  194. // translate label and title?
  195. if ($this->getUseTranslator() && $t = $this->getTranslator()) {
  196. if (is_string($label) && !empty($label)) {
  197. $label = $t->translate($label);
  198. }
  199. if (is_string($title) && !empty($title)) {
  200. $title = $t->translate($title);
  201. }
  202. }
  203. // get attribs for element
  204. $attribs = array(
  205. 'id' => $page->getId(),
  206. 'title' => $title,
  207. 'class' => $page->getClass()
  208. );
  209. // does page have a href?
  210. if ($href = $page->getHref()) {
  211. $element = 'a';
  212. $attribs['href'] = $href;
  213. $attribs['target'] = $page->getTarget();
  214. } else {
  215. $element = 'span';
  216. }
  217. return '<' . $element . $this->_htmlAttribs($attribs) . '>'
  218. . $this->view->escape($label)
  219. . '</' . $element . '>';
  220. }
  221. /**
  222. * Normalizes given render options
  223. *
  224. * @param array $options [optional] options to normalize
  225. * @return array normalized options
  226. */
  227. protected function _normalizeOptions(array $options = array())
  228. {
  229. if (isset($options['indent'])) {
  230. $options['indent'] = $this->_getWhitespace($options['indent']);
  231. } else {
  232. $options['indent'] = $this->getIndent();
  233. }
  234. if (isset($options['ulClass']) && $options['ulClass'] !== null) {
  235. $options['ulClass'] = (string) $options['ulClass'];
  236. } else {
  237. $options['ulClass'] = $this->getUlClass();
  238. }
  239. if (array_key_exists('minDepth', $options)) {
  240. if (null !== $options['minDepth']) {
  241. $options['minDepth'] = (int) $options['minDepth'];
  242. }
  243. } else {
  244. $options['minDepth'] = $this->getMinDepth();
  245. }
  246. if ($options['minDepth'] < 0 || $options['minDepth'] === null) {
  247. $options['minDepth'] = 0;
  248. }
  249. if (array_key_exists('maxDepth', $options)) {
  250. if (null !== $options['maxDepth']) {
  251. $options['maxDepth'] = (int) $options['maxDepth'];
  252. }
  253. } else {
  254. $options['maxDepth'] = $this->getMaxDepth();
  255. }
  256. if (!isset($options['onlyActiveBranch'])) {
  257. $options['onlyActiveBranch'] = $this->getOnlyActiveBranch();
  258. }
  259. if (!isset($options['renderParents'])) {
  260. $options['renderParents'] = $this->getRenderParents();
  261. }
  262. return $options;
  263. }
  264. // Render methods:
  265. /**
  266. * Renders the deepest active menu within [$minDepth, $maxDeth], (called
  267. * from {@link renderMenu()})
  268. *
  269. * @param Zend_Navigation_Container $container container to render
  270. * @param array $active active page and depth
  271. * @param string $ulClass CSS class for first UL
  272. * @param string $indent initial indentation
  273. * @param int|null $minDepth minimum depth
  274. * @param int|null $maxDepth maximum depth
  275. * @return string rendered menu
  276. */
  277. protected function _renderDeepestMenu(Zend_Navigation_Container $container,
  278. $ulClass,
  279. $indent,
  280. $minDepth,
  281. $maxDepth)
  282. {
  283. if (!$active = $this->findActive($container, $minDepth - 1, $maxDepth)) {
  284. return '';
  285. }
  286. // special case if active page is one below minDepth
  287. if ($active['depth'] < $minDepth) {
  288. if (!$active['page']->hasPages()) {
  289. return '';
  290. }
  291. } else if (!$active['page']->hasPages()) {
  292. // found pages has no children; render siblings
  293. $active['page'] = $active['page']->getParent();
  294. } else if (is_int($maxDepth) && $active['depth'] +1 > $maxDepth) {
  295. // children are below max depth; render siblings
  296. $active['page'] = $active['page']->getParent();
  297. }
  298. $ulClass = $ulClass ? ' class="' . $ulClass . '"' : '';
  299. $html = $indent . '<ul' . $ulClass . '>' . self::EOL;
  300. foreach ($active['page'] as $subPage) {
  301. if (!$this->accept($subPage)) {
  302. continue;
  303. }
  304. $liClass = $subPage->isActive(true) ? ' class="active"' : '';
  305. $html .= $indent . ' <li' . $liClass . '>' . self::EOL;
  306. $html .= $indent . ' ' . $this->htmlify($subPage) . self::EOL;
  307. $html .= $indent . ' </li>' . self::EOL;
  308. }
  309. $html .= $indent . '</ul>';
  310. return $html;
  311. }
  312. /**
  313. * Renders a normal menu (called from {@link renderMenu()})
  314. *
  315. * @param Zend_Navigation_Container $container container to render
  316. * @param string $ulClass CSS class for first UL
  317. * @param string $indent initial indentation
  318. * @param int|null $minDepth minimum depth
  319. * @param int|null $maxDepth maximum depth
  320. * @param bool $onlyActive render only active branch?
  321. * @return string
  322. */
  323. protected function _renderMenu(Zend_Navigation_Container $container,
  324. $ulClass,
  325. $indent,
  326. $minDepth,
  327. $maxDepth,
  328. $onlyActive)
  329. {
  330. $html = '';
  331. // find deepest active
  332. if ($found = $this->findActive($container, $minDepth, $maxDepth)) {
  333. $foundPage = $found['page'];
  334. $foundDepth = $found['depth'];
  335. } else {
  336. $foundPage = null;
  337. }
  338. // create iterator
  339. $iterator = new RecursiveIteratorIterator($container,
  340. RecursiveIteratorIterator::SELF_FIRST);
  341. if (is_int($maxDepth)) {
  342. $iterator->setMaxDepth($maxDepth);
  343. }
  344. // iterate container
  345. $prevDepth = -1;
  346. foreach ($iterator as $page) {
  347. $depth = $iterator->getDepth();
  348. $isActive = $page->isActive(true);
  349. if ($depth < $minDepth || !$this->accept($page)) {
  350. // page is below minDepth or not accepted by acl/visibilty
  351. continue;
  352. } else if ($onlyActive && !$isActive) {
  353. // page is not active itself, but might be in the active branch
  354. $accept = false;
  355. if ($foundPage) {
  356. if ($foundPage->hasPage($page)) {
  357. // accept if page is a direct child of the active page
  358. $accept = true;
  359. } else if ($foundPage->getParent()->hasPage($page)) {
  360. // page is a sibling of the active page...
  361. if (!$foundPage->hasPages() ||
  362. is_int($maxDepth) && $foundDepth + 1 > $maxDepth) {
  363. // accept if active page has no children, or the
  364. // children are too deep to be rendered
  365. $accept = true;
  366. }
  367. }
  368. }
  369. if (!$accept) {
  370. continue;
  371. }
  372. }
  373. // make sure indentation is correct
  374. $depth -= $minDepth;
  375. $myIndent = $indent . str_repeat(' ', $depth);
  376. if ($depth > $prevDepth) {
  377. // start new ul tag
  378. if ($ulClass && $depth == 0) {
  379. $ulClass = ' class="' . $ulClass . '"';
  380. } else {
  381. $ulClass = '';
  382. }
  383. $html .= $myIndent . '<ul' . $ulClass . '>' . self::EOL;
  384. } else if ($prevDepth > $depth) {
  385. // close li/ul tags until we're at current depth
  386. for ($i = $prevDepth; $i > $depth; $i--) {
  387. $ind = $indent . str_repeat(' ', $i);
  388. $html .= $ind . ' </li>' . self::EOL;
  389. $html .= $ind . '</ul>' . self::EOL;
  390. }
  391. // close previous li tag
  392. $html .= $myIndent . ' </li>' . self::EOL;
  393. } else {
  394. // close previous li tag
  395. $html .= $myIndent . ' </li>' . self::EOL;
  396. }
  397. // render li tag and page
  398. $liClass = $isActive ? ' class="active"' : '';
  399. $html .= $myIndent . ' <li' . $liClass . '>' . self::EOL
  400. . $myIndent . ' ' . $this->htmlify($page) . self::EOL;
  401. // store as previous depth for next iteration
  402. $prevDepth = $depth;
  403. }
  404. if ($html) {
  405. // done iterating container; close open ul/li tags
  406. for ($i = $prevDepth+1; $i > 0; $i--) {
  407. $myIndent = $indent . str_repeat(' ', $i-1);
  408. $html .= $myIndent . ' </li>' . self::EOL
  409. . $myIndent . '</ul>' . self::EOL;
  410. }
  411. $html = rtrim($html, self::EOL);
  412. }
  413. return $html;
  414. }
  415. /**
  416. * Renders helper
  417. *
  418. * Renders a HTML 'ul' for the given $container. If $container is not given,
  419. * the container registered in the helper will be used.
  420. *
  421. * Available $options:
  422. *
  423. *
  424. * @param Zend_Navigation_Container $container [optional] container to
  425. * create menu from. Default
  426. * is to use the container
  427. * retrieved from
  428. * {@link getContainer()}.
  429. * @param array $options [optional] options for
  430. * controlling rendering
  431. * @return string rendered menu
  432. */
  433. public function renderMenu(Zend_Navigation_Container $container = null,
  434. array $options = array())
  435. {
  436. if (null === $container) {
  437. $container = $this->getContainer();
  438. }
  439. $options = $this->_normalizeOptions($options);
  440. if ($options['onlyActiveBranch'] && !$options['renderParents']) {
  441. $html = $this->_renderDeepestMenu($container,
  442. $options['ulClass'],
  443. $options['indent'],
  444. $options['minDepth'],
  445. $options['maxDepth']);
  446. } else {
  447. $html = $this->_renderMenu($container,
  448. $options['ulClass'],
  449. $options['indent'],
  450. $options['minDepth'],
  451. $options['maxDepth'],
  452. $options['onlyActiveBranch']);
  453. }
  454. return $html;
  455. }
  456. /**
  457. * Renders the inner-most sub menu for the active page in the $container
  458. *
  459. * This is a convenience method which is equivalent to the following call:
  460. * <code>
  461. * renderMenu($container, array(
  462. * 'indent' => $indent,
  463. * 'ulClass' => $ulClass,
  464. * 'minDepth' => null,
  465. * 'maxDepth' => null,
  466. * 'onlyActiveBranch' => true,
  467. * 'renderParents' => false
  468. * ));
  469. * </code>
  470. *
  471. * @param Zend_Navigation_Container $container [optional] container to
  472. * render. Default is to render
  473. * the container registered in
  474. * the helper.
  475. * @param string $ulClass [optional] CSS class to
  476. * use for UL element. Default
  477. * is to use the value from
  478. * {@link getUlClass()}.
  479. * @param string|int $indent [optional] indentation as
  480. * a string or number of
  481. * spaces. Default is to use
  482. * the value retrieved from
  483. * {@link getIndent()}.
  484. * @return string rendered content
  485. */
  486. public function renderSubMenu(Zend_Navigation_Container $container = null,
  487. $ulClass = null,
  488. $indent = null)
  489. {
  490. return $this->renderMenu($container, array(
  491. 'indent' => $indent,
  492. 'ulClass' => $ulClass,
  493. 'minDepth' => null,
  494. 'maxDepth' => null,
  495. 'onlyActiveBranch' => true,
  496. 'renderParents' => false
  497. ));
  498. }
  499. /**
  500. * Renders the given $container by invoking the partial view helper
  501. *
  502. * The container will simply be passed on as a model to the view script
  503. * as-is, and will be available in the partial script as 'container', e.g.
  504. * <code>echo 'Number of pages: ', count($this->container);</code>.
  505. *
  506. * @param Zend_Navigation_Container $container [optional] container to
  507. * pass to view script. Default
  508. * is to use the container
  509. * registered in the helper.
  510. * @param string|array $partial [optional] partial view
  511. * script to use. Default is to
  512. * use the partial registered
  513. * in the helper. If an array
  514. * is given, it is expected to
  515. * contain two values; the
  516. * partial view script to use,
  517. * and the module where the
  518. * script can be found.
  519. * @return string helper output
  520. */
  521. public function renderPartial(Zend_Navigation_Container $container = null,
  522. $partial = null)
  523. {
  524. if (null === $container) {
  525. $container = $this->getContainer();
  526. }
  527. if (null === $partial) {
  528. $partial = $this->getPartial();
  529. }
  530. if (empty($partial)) {
  531. #require_once 'Zend/View/Exception.php';
  532. $e = new Zend_View_Exception(
  533. 'Unable to render menu: No partial view script provided'
  534. );
  535. $e->setView($this->view);
  536. throw $e;
  537. }
  538. $model = array(
  539. 'container' => $container
  540. );
  541. if (is_array($partial)) {
  542. if (count($partial) != 2) {
  543. #require_once 'Zend/View/Exception.php';
  544. $e = new Zend_View_Exception(
  545. 'Unable to render menu: A view partial supplied as '
  546. . 'an array must contain two values: partial view '
  547. . 'script and module where script can be found'
  548. );
  549. $e->setView($this->view);
  550. throw $e;
  551. }
  552. return $this->view->partial($partial[0], $partial[1], $model);
  553. }
  554. return $this->view->partial($partial, null, $model);
  555. }
  556. // Zend_View_Helper_Navigation_Helper:
  557. /**
  558. * Renders menu
  559. *
  560. * Implements {@link Zend_View_Helper_Navigation_Helper::render()}.
  561. *
  562. * If a partial view is registered in the helper, the menu will be rendered
  563. * using the given partial script. If no partial is registered, the menu
  564. * will be rendered as an 'ul' element by the helper's internal method.
  565. *
  566. * @see renderPartial()
  567. * @see renderMenu()
  568. *
  569. * @param Zend_Navigation_Container $container [optional] container to
  570. * render. Default is to
  571. * render the container
  572. * registered in the helper.
  573. * @return string helper output
  574. */
  575. public function render(Zend_Navigation_Container $container = null)
  576. {
  577. if ($partial = $this->getPartial()) {
  578. return $this->renderPartial($container, $partial);
  579. } else {
  580. return $this->renderMenu($container);
  581. }
  582. }
  583. }