Commits

pietschy committed 2ed26cd

refactored to support page items and non page tiems, added DateNavigationBuilder and added moved the old defult template to nav_bar and created a new simpler default template

Comments (0)

Files changed (11)

elements/navigation_builder/default/menu.php

     throw new Exception('Required parameter $navRoot is missing');
 }
 ?>
-<ul class="<?=$navRoot->getRenderOption('class','nav-bar')?>">
+<ul class="<?=$navRoot->getRenderOption('class','nav')?>">
     <?= $navRoot->renderChildItems() ?>
 </ul>

elements/navigation_builder/default/nav_item.php

 if ($navItem->depth() > 1) {
     $classNames[] = 'level-' . $navItem->depth();
 }
-if ($navItem->isHomePage()) {
-    $classNames[] = 'home-page';
-}
 $anchorClassNames = array();
 if ($navItem->hasChildren() && $navItem->pageAttribute('replace_link_with_first_in_nav')) {
     $anchorClassNames[] = 'redirects-to-first-child';
 <li class="<?= join(' ', $classNames)?>">
     <a href="<?=$navItem->getURL()?>" class="<?= join(' ', $anchorClassNames)?>" target="<?=$navItem->getTarget()?>"><?=$navItem->getName()?></a>
     <? if ($navItem->hasChildren()) { ?>
-    <a href="#" class="flyout-toggle"><span> </span></a>
-    <ul class="flyout">
-        <?= $navItem->renderChildItems() ?>
+    <a href="#" class="-flyout-toggle"><span> </span></a>
+    <ul class="-flyout">
+        <? $navItem->renderChildItems() ?>
     </ul>
     <? } ?>
 </li>

elements/navigation_builder/nav_bar/menu.php

+<?php defined('C5_EXECUTE') or die("Access Denied.");
+/** @var NavRoot $navRoot */
+if (!isset($navRoot)) {
+    throw new Exception('Required parameter $navRoot is missing');
+}
+?>
+<ul class="<?=$navRoot->getRenderOption('class','nav-bar')?>">
+    <?= $navRoot->renderChildItems() ?>
+</ul>

elements/navigation_builder/nav_bar/nav_item.php

+<?php defined('C5_EXECUTE') or die("Access Denied.");
+/** @var NavItem $navItem*/
+if (!isset($navItem)) {
+    throw new Exception('Required parameter $navItem is missing');
+}
+/** @var NavItem $navItem */
+$classNames = $navItem->getClassNames();
+if ($navItem->isFirstChild()) {
+    $classNames[] = 'first-child';
+}
+if ($navItem->isLastChild()) {
+    $classNames[] = 'last-child';
+}
+if ($navItem->isCurrent()) {
+    $classNames[] = 'active';
+    $classNames[] = 'nav-path-selected';
+}
+if ($navItem->isInPath()) {
+    $classNames[] = 'active';
+    $classNames[] = 'nav-path-selected';
+}
+if ($navItem->hasChildren()) {
+    $classNames[] = 'has-flyout';
+}
+if ($navItem->depth() < 2) {
+    $classNames[] = 'level-1';
+}
+if ($navItem->depth() > 1) {
+    $classNames[] = 'level-' . $navItem->depth();
+}
+if ($navItem->isHomePage()) {
+    $classNames[] = 'home-page';
+}
+$anchorClassNames = array();
+if ($navItem->hasChildren() && $navItem->pageAttribute('replace_link_with_first_in_nav')) {
+    $anchorClassNames[] = 'redirects-to-first-child';
+}
+?>
+<li class="<?= join(' ', $classNames)?>">
+    <a href="<?=$navItem->getURL()?>" class="<?= join(' ', $anchorClassNames)?>" target="<?=$navItem->getTarget()?>"><?=$navItem->getName()?></a>
+    <? if ($navItem->hasChildren()) { ?>
+    <a href="#" class="flyout-toggle"><span> </span></a>
+    <ul class="flyout">
+        <? $navItem->renderChildItems() ?>
+    </ul>
+    <? } ?>
+</li>

models/BestPathPredicate.php

+<?php
+Loader::model("/tree/Predicate", 'terarp_common');
+/**
+ * This predicate finds the best match path for a given page.  It handles the case where a the
+ * tree doesn't contain the actual page.  In such cases it checks if the last node in the tree is
+ * a parent of specified page.
+ */
+class BestPathPredicate implements Predicate
+{
+
+    /** @var Page $page */
+    private $activePage;
+    /** @var int[] $parentIDs */
+    private $parentIDs;
+
+    /**
+     * @param $activePage Page
+     */
+    function __construct($activePage)
+    {
+        $this->activePage = $activePage;
+        $this->parentIDs = $this->getParentIDs($activePage);
+    }
+
+    /**
+     * Checks if the specified page is equal to the current navItem or, if the navItem is a leaf
+     * in the tree it checks if the navItem is a parent of the specified page.
+     * @param $navItem NavItem
+     * @return boolean
+     */
+    function evaluate($navItem)
+    {
+        if ($navItem instanceof PageNavItem)
+        {
+            return $navItem->isItemForPage($this->activePage) ? true : $this->checkIfNavItemIsLastInPath($navItem);
+        }
+
+        return false;
+    }
+
+    /**
+     * @param $navItem PageNavItem
+     * @return bool
+     */
+    private function checkIfNavItemIsLastInPath($navItem)
+    {
+        // the home pages is the ancestor of everything so it's should never
+        // be used as 'the last nav in the path'.  You're either on the home page or
+        // you're somewhere else
+        if ($navItem->isHomePage()) return false;
+
+        // if the navItems is a leaf in the tree then see if the it's the parent of the
+        // current page.
+        if ($navItem->isLeaf()) {
+            return in_array($navItem->getPage()->getCollectionID(), $this->parentIDs);
+        }
+
+        return false;
+    }
+
+    /**
+     * @param $page Page
+     * @return int[]
+     */
+    private function getParentIDs($page)
+    {
+        $result = array();
+        $id = $page->getCollectionID();
+        while ($id != HOME_CID) {
+            $id = $page->getCollectionParentIDFromChildID($id);
+            // if were editing the page default we don't have any parents.
+            if ($id == 0) break;
+            // otherwise keep collecting until we've hit the home page.
+            $result[] = $id;
+        }
+
+        return $result;
+    }
+}

models/DateNavigationBuilder.php

+<?php
+Loader::model('page_list');
+
+/**
+ * Created by JetBrains PhpStorm.
+ * User: andrew
+ * Date: 5/09/12
+ * Time: 1:53 PM
+ * To change this template use File | Settings | File Templates.
+ */
+class DateNavigationBuilder
+{
+    /** @var PageList $pageList */
+    private $pageList;
+
+    /** @var NavItem[] $yearItems */
+    private $yearItems;
+
+    /** @var NavItem[] $monthItems */
+    private $monthItems;
+
+    function __construct(PageList $pageList)
+    {
+        $this->pageList = $pageList;
+    }
+
+    /**
+     * @return NavRoot
+     */
+    public function build()
+    {
+        $this->monthItems = array();
+        $this->yearItems = array();
+
+        $pages = $this->pageList->get(9999);
+
+        foreach ($pages as $page)
+        {
+            $navItem = $this->getMonthItem($page);
+            $navItem->add(new PageNavItem($page));
+        }
+
+        $navRoot = new NavRoot("");
+
+        // if there's only one year, then just show the months...
+        $items = count($this->yearItems) > 1 ? $this->yearItems : $this->monthItems;
+        foreach ($items as $item)
+        {
+            $navRoot->add($item);
+        }
+
+        $navRoot->configureActivePathFrom(Page::getCurrentPage());
+        return $navRoot;
+    }
+
+    /**
+     * @param Page $page
+     * @return \NavItem
+     */
+    private function getMonthItem($page)
+    {
+        $monthKey = $page->getCollectionDatePublic('Y-F');
+        $monthItem = $this->monthItems[$monthKey];
+
+        if (!isset($monthItem))
+        {
+            $monthItem = new NavItem($page->getCollectionDatePublic('F'));
+            $this->monthItems[$monthKey] = $monthItem;
+            $this->getYearItem($page)->add($monthItem);
+        }
+
+        return $monthItem;
+    }
+
+    /**
+     * @param Page $page
+     * @return \NavItem
+     */
+    private function getYearItem($page)
+    {
+        $yearKey = $page->getCollectionDatePublic('Y');
+        $yearItem = $this->yearItems[$yearKey];
+
+        if (!isset($yearItem))
+        {
+            $yearItem = new NavItem($yearKey);
+            $this->yearItems[$yearKey] = $yearItem;
+        }
+
+        return $yearItem;
+    }
+
+    private function getItem(&$collection, $key, $name)
+    {
+        $item = $collection[$key];
+
+        if (!isset($item))
+        {
+            $item = new NavItem($name);
+            $collection[$key] = $item;
+        }
+
+        return $item;
+    }
+
+}

models/NavItem.php

 
 class NavItem extends TreeNode
 {
-    /** @var \Page */
-    private $page;
-
     /** @var String */
     private $name;
+    /** @var String */
+    private $url;
 
     /** @var String[] */
     private $classNames;
     private $inPath = false;
 
     /**
-     * @param null|Page $page
      * @param null|String $name
+     * @param null|String $url
      */
-    public function __construct($page, $name = null)
+    public function __construct($name, $url = '#')
     {
-        $this->page = $page;
         $this->name = $name;
+        $this->url = $url;
         $this->classNames = array();
     }
 
     public function __toString()
     {
-        return get_class($this) . "[name='{$this->getName()}', path='{$this->getPage()->getCollectionPath()}']";
-    }
-
-    /**
-     * The Page NavItem this nav item represents.
-     * @return \Page The page associated with the NavItem
-     */
-    public function getPage()
-    {
-        return $this->page;
+        return get_class($this) . "[name='{$this->getName()}', url='{$this->getURL()}']";
     }
 
     /**
      */
     public function getName()
     {
-        return is_null($this->name) ? $this->getPageName() : $this->name;
+        return $this->name;
     }
 
     public function setName(String $name)
 
     public function getTarget()
     {
-        return $this->getTargetFor($this->getPage());
-    }
-
-    /**
-     * A unit testable version of getTarget.
-     * @param Page $page
-     * @return string
-     */
-    protected function getTargetFor($page)
-    {
-
-        if ($page->isExternalLink() && $page->openCollectionPointerExternalLinkInNewWindow()) {
-            return '_blank';
-        }
-
-        $targetAttribute = $page->getCollectionAttributeValue('nav_target');
-
-        return empty($targetAttribute) ? '_self' : $targetAttribute;
-    }
-
-    /**
-     * The pages name (i.e. it's collection name)
-     * @return String
-     */
-    public function getPageName()
-    {
-        return $this->page->getCollectionName();
-    }
-
-    /**
-     * Gets the page AttributeValue for this page.  Delegates to Page->getCollectionAttributeValue($handle);
-     * @param $attributeHandle the handle of the attribute, e.g. `exclude_nav`.
-     * @return AttributeValue|bool|CollectionAttributeValue
-     */
-    public function pageAttribute($attributeHandle)
-    {
-        return $this->getPage()->getCollectionAttributeValue($attributeHandle);
-    }
-
-    public function isRedirectingToFirstChild() {
-        return $this->pageAttribute('replace_link_with_first_in_nav');
+        return '_self';
     }
 
     /**
      */
     public function getURL()
     {
-        /** @var NavigationHelper $nh */
-        $nh = Loader::helper('navigation');
-        $page = $this->findFirstNonRedirectingPage($this->getPage());
-        return $page->getCollectionID() == 1 ? '/' . DIR_REL : $nh->getLinkToCollection($page);
-    }
-
-    /**
-     * Returns the first page at or below the specified page that doesn't have `replace_link_with_first_in_nav`.
-     * @param Page $page the page
-     * @return Page the page or the closest child that doesn't have `replace_link_with_first_in_nav`.
-     */
-    function findFirstNonRedirectingPage(Page $page)
-    {
-        $target_page = $page;
-        while ($target_page != null && $target_page->getCollectionAttributeValue('replace_link_with_first_in_nav')) {
-            $firstChild = $target_page->getFirstChild();
-            // need to make sure the page actually has children.
-            if ($firstChild == null)
-                break;
-
-            $target_page = $firstChild;
-        }
-
-        return $target_page;
-    }
-
-
-    /**
-     * Checks if the page in question is the same as this items page.
-     *
-     * @param $page Page the page in question
-     * @return bool true if the page in question is the same as this items page.
-     */
-    public function pageEquals($page)
-    {
-        // todo: what determines equality??? id and pointerId or page path???
-        return $this->getPage()->getCollectionPath() == $page->getCollectionPath();
-    }
-
-    public function isHomePage()
-    {
-        return $this->getPage()->getCollectionID() == HOME_CID;
+        return $this->url;
     }
 
     /**
         return $this->classNames;
     }
 
+    public function isItemForPage($page)
+    {
+        return false;
+    }
+
+    public function isHomePage()
+    {
+        return false;
+    }
+
+    public function pageAttribute($key)
+    {
+        return null;
+    }
 
     /**
      * DO NOT USE.. use {@link NavRoot::configureActivePathUsing} instead.  This method is only
         return $this->inPath;
     }
 
-    /**
-     *
-     */
     public function render()
     {
         Loader::packageElement("navigation_builder/{$this->getRenderTemplate()}/nav_item", 'navigation_builder', array('navItem' => $this));

models/NavRoot.php

 <?php
+require_once dirname(__FILE__) . '/BestPathPredicate.php';
+require_once dirname(__FILE__) . '/NavItem.php';
 /**
  *
  */
     private $renderTemplate = "default";
     public $renderOptions = array();
 
-    public function __construct($page = null)
+    public function __construct($name)
     {
-        parent::__construct($page);
+        parent::__construct($name);
+    }
+
+    /**
+     * Configures this navigation trees active path based on the current page.
+     * @param Page $page
+     * @return void
+     */
+    public function configureActivePathFrom($page)
+    {
+        $activeItem = $this->findNodeOrClosestAncestorOf($page);
+        // the active item may be null, especially if we're editing page defaults.
+        if (!is_null($activeItem)) {
+            $this->setActiveItem($activeItem);
+        }
     }
 
     /**
      * @param NavItem $activeItem
      * @return void
      */
-    public function configureCurrentPathUsing($activeItem)
+    public function setActiveItem($activeItem)
     {
         $this->activeItem = $activeItem;
         $this->callOnAll(function ($item) use ($activeItem)
 
 
 }
-
-/**
- * This predicate finds the best match path for a given page.  It handles the case where a the
- * tree doesn't contain the actual page.  In such cases it checks if the last node in the tree is
- * a parent of specified page.
- */
-class BestPathPredicate implements Predicate
-{
-
-    /** @var Page $page */
-    private $page;
-    /** @var int[] $parentIDs */
-    private $parentIDs;
-
-    /**
-     * @param $page Page
-     */
-    function __construct($page)
-    {
-        $this->page = $page;
-        $this->parentIDs = $this->getParentIDs($page);
-    }
-
-    /**
-     * Checks if the specified page is equal to the current navItem or, if the navItem is a leaf
-     * in the tree it checks if the navItem is a parent of the specified page.
-     * @param $navItem NavItem
-     * @return boolean
-     */
-    function evaluate($navItem)
-    {
-        return $navItem->pageEquals($this->page) || $this->checkIfNavItemIsLastInPath($navItem);
-    }
-
-    /**
-     * @param $navItem NavItem
-     * @return bool
-     */
-    private function checkIfNavItemIsLastInPath($navItem)
-    {
-        // the home pages is the ancestor of everything so it's should never
-        // be used as 'the last nav in the path'.  You're either on the home page or
-        // you're somewhere else
-        if ($navItem->isHomePage()) return false;
-
-        // if the navItems is a leaf in the tree then see if the it's the parent of the
-        // current page.
-        if ($navItem->isLeaf()) {
-            return in_array($navItem->getPage()->getCollectionID(), $this->parentIDs);
-        }
-
-        return false;
-    }
-
-    /**
-     * @param $page Page
-     * @return int[]
-     */
-    private function getParentIDs($page)
-    {
-        $result = array();
-        $id = $page->getCollectionID();
-        while ($id != HOME_CID) {
-            $id = $page->getCollectionParentIDFromChildID($id);
-            // if were editing the page default we don't have any parents.
-            if ($id == 0) break;
-            // otherwise keep collecting until we've hit the home page.
-            $result[] = $id;
-        }
-
-        return $result;
-    }
-}

models/NavigationBuilder.php

 <?php
-require_once dirname(__FILE__) . '/NavItem.php';
-require_once dirname(__FILE__) . '/NavRoot.php';
+require_once dirname(__FILE__) . '/PageNavItem.php';
+require_once dirname(__FILE__) . '/PageNavRoot.php';
 require_once dirname(__FILE__) . '/NavRenderer.php';
 
 /**
     /** @var TerarpCommonStringHelper $sh $*/
     private $sh;
 
+    private $excludeChildrenCallback = null;
+
     /**
      *
      */
     public function build($rootPage)
     {
         $navRoot = $this->buildTree($rootPage);
-        $this->configureActivePath($navRoot);
+        $navRoot->configureActivePathFrom(Page::getCurrentPage());
         return $navRoot;
     }
 
        $firstItem = $this->createNavItem($rootPage);
        $navRoot->insertChild($firstItem);
 
-       $this->configureActivePath($navRoot);
+       $navRoot->configureActivePathFrom(Page::getCurrentPage());
        return $navRoot;
     }
 
+    public function applyDefaultExcludes() {
+        $this->excludeChildrenWhen(function($page) {
+            return $page->getCollectionAttributeValue('exclude_children_from_nav') == true;
+        });
+    }
+
+    public function excludeChildrenWhen(Closure $filter)
+    {
+        $this->excludeChildrenCallback = $filter;
+    }
+
+
     private function buildTree($rootPage)
     {
         $navRoot = $this->createNavRoot($rootPage);
      */
     protected function shouldIncludeChildren($page)
     {
-        $exclude = $page->getCollectionAttributeValue('exclude_children_from_nav') == true;
+        if (!is_callable($this->excludeChildrenCallback)) return true;
+
+        $exclude = call_user_func($this->excludeChildrenCallback, $page);
 
         return !$exclude;
     }
      */
     protected function createNavItem($page)
     {
-        return new NavItem($page);
+        return new PageNavItem($page);
     }
 
     /**
      * Factory method to create a concrete instance of NavRoot.  You can create customised
      * subtypes here if you need to.
-     * @param null $page the page at the root of the tree.
-     * @return NavRoot
+     * @param Page $page the page at the root of the tree.
+     * @return PageNavRoot
      */
-    protected function createNavRoot($page = null)
+    protected function createNavRoot($page)
     {
-        return new NavRoot($page);
+        return new PageNavRoot($page);
     }
 
     /**
         return $this->maxDepth;
     }
 
-    /**
-     * @param NavRoot $navRoot
-     * @return void
-     */
-    private function configureActivePath($navRoot)
-    {
-        $activeItem = $navRoot->findNodeOrClosestAncestorOf(Page::getCurrentPage());
-        if (!is_null($activeItem)) {
-            $navRoot->configureCurrentPathUsing($activeItem);
-        }
-    }
-
 }

models/PageNavItem.php

+<?php
+/**
+ *
+ */
+require_once dirname(__FILE__) . '/NavItem.php';
+Loader::model("/tree/TreeNode", 'terarp_common');
+
+class PageNavItem extends NavItem
+{
+    /** @var \Page */
+    private $page;
+
+    /**
+     * @param null|Page $page
+     */
+    public function __construct($page)
+    {
+        parent::__construct(null);
+        $this->page = $page;
+    }
+
+    public function __toString()
+    {
+        return get_class($this) . "[name='{$this->getName()}', path='{$this->getPage()->getCollectionPath()}']";
+    }
+
+    /**
+     * The Page NavItem this nav item represents.
+     * @return \Page The page associated with the NavItem
+     */
+    public function getPage()
+    {
+        return $this->page;
+    }
+
+    /**
+     * Returns the name of this NavItem or if it hasn't been set it returns the page name.
+     * @return String
+     */
+    public function getName()
+    {
+        return is_null(parent::getName()) ? $this->getPageName() : parent::getName();
+    }
+
+    public function getTarget()
+    {
+        return $this->getTargetFor($this->getPage());
+    }
+
+    /**
+     * Checks if the page in question is the same as this items page.
+     *
+     * @param $page Page the page in question
+     * @return bool true if the page in question is the same as this items page.
+     */
+    public function isItemForPage($page)
+    {
+        // todo: what determines equality??? id and pointerId or page path???
+        return $this->getPage()->getCollectionPath() == $page->getCollectionPath();
+    }
+
+    public function isHomePage()
+    {
+        return $this->getPage()->getCollectionID() == HOME_CID;
+    }
+
+    /**
+     * A unit testable version of getTarget.
+     * @param Page $page
+     * @return string
+     */
+    protected function getTargetFor($page)
+    {
+
+        if ($page->isExternalLink() && $page->openCollectionPointerExternalLinkInNewWindow()) {
+            return '_blank';
+        }
+
+        $targetAttribute = $page->getCollectionAttributeValue('nav_target');
+
+        return empty($targetAttribute) ? '_self' : $targetAttribute;
+    }
+
+    /**
+     * The pages name (i.e. it's collection name)
+     * @return String
+     */
+    public function getPageName()
+    {
+        return $this->page->getCollectionName();
+    }
+
+    /**
+     * Gets the page AttributeValue for this page.  Delegates to Page->getCollectionAttributeValue($handle);
+     * @param $attributeHandle the handle of the attribute, e.g. `exclude_nav`.
+     * @return AttributeValue|bool|CollectionAttributeValue
+     */
+    public function pageAttribute($attributeHandle)
+    {
+        return $this->getPage()->getCollectionAttributeValue($attributeHandle);
+    }
+
+    public function isRedirectingToFirstChild()
+    {
+        return $this->pageAttribute('replace_link_with_first_in_nav');
+    }
+
+    /**
+     * Returns the URL for the page taking into account any redirect to first page setting on
+     * this page or any of it's children.  It also replaced the home page link with /DIR_REL.
+     *
+     * @return String the URL for the page taking into account any redirect to first page setting on
+     * this page or any of it's children.
+     */
+    public function getURL()
+    {
+        /** @var NavigationHelper $nh */
+        $nh = Loader::helper('navigation');
+        $page = $this->findFirstNonRedirectingPage($this->getPage());
+        return $page->getCollectionID() == 1 ? '/' . DIR_REL : $nh->getLinkToCollection($page);
+    }
+
+    /**
+     * Returns the first page at or below the specified page that doesn't have `replace_link_with_first_in_nav`.
+     * @param Page $page the page
+     * @return Page the page or the closest child that doesn't have `replace_link_with_first_in_nav`.
+     */
+    function findFirstNonRedirectingPage(Page $page)
+    {
+        $target_page = $page;
+        while ($target_page != null && $target_page->getCollectionAttributeValue('replace_link_with_first_in_nav')) {
+            $firstChild = $target_page->getFirstChild();
+            // need to make sure the page actually has children.
+            if ($firstChild == null)
+                break;
+
+            $target_page = $firstChild;
+        }
+
+        return $target_page;
+    }
+
+}

models/PageNavRoot.php

+<?php
+require_once dirname(__FILE__) . '/NavRoot.php';
+
+class PageNavRoot extends NavRoot
+{
+    private $page;
+    /**
+     * @param Page $page
+     */
+    public function __construct($page)
+    {
+        parent::__construct($page->getCollectionName());
+        $this->page = $page;
+    }
+
+    public function getPage()
+    {
+        return $this->page;
+    }
+}