diff --git a/README.md b/README.md index 409ce7d..b52d54b 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ This module is intended to replicate and expand upon the functionality provided ## Requirements -SilverStripe 4.0 (3.1+ through previous releases) +SilverStripe 4.3 (3.1+ through previous releases) ## Installation @@ -29,8 +29,22 @@ Download, place the folder in your project root, rename it to 'betternavigator' ## How to use -Just place **$BetterNavigator** somewhere in your template(s). If your website uses caching, make sure BetterNavigator's output is excluded. +The navigator is auto-injected into your template, and no code changes are needed. +If your website uses caching, make sure BetterNavigator's output is excluded. + +## Disabling the navigator + +You can disable the navigator using your own custom logic by defining a `showBetterNavigator(): bool` +method in any controller with the extension applied. + +```php +public function showBetterNavigator() +{ + // A user-defined setting + return $this->ShowDebugTools; +} +``` **Access developer tools on a live website** You can mark certain CMS users as developers in your site's config, so they can access developer tools when logged in. Example YAML: @@ -55,10 +69,6 @@ All content, scripts and CSS are loaded via the BetterNavigator.ss template, so The BetterNavigator.ss template's scope is set to the page that is being viewed, so any methods available in your page controller will be available in the BetterNavigator.ss template. This should allow you to add custom links by page type and introduce complex logic if you want to. -## Known issues - - * Probably won't work in IE8 or lower. - ## Bonus: better debugging tools This module provide quick access to SilverStripe's built in [URL Variable Tools](http://doc.silverstripe.org/framework/en/reference/urlvariabletools) but reading their output isn't much fun. You can peek under SilverStripe's hood much more conveniently using the recently released [SilverStripe Clockwork](https://github.com/markguinn/silverstripe-clockwork) by Mark Guinn. Out of the box SQL Queries and controller events are logged. It's Chrome only for now. diff --git a/src/Extension/BetterNavigatorExtension.php b/src/Extension/BetterNavigatorExtension.php index 46000fe..43e97ce 100644 --- a/src/Extension/BetterNavigatorExtension.php +++ b/src/Extension/BetterNavigatorExtension.php @@ -8,82 +8,157 @@ use SilverStripe\Control\Controller; use SilverStripe\Control\Director; use SilverStripe\Core\Config\Config; use SilverStripe\ORM\DataExtension; +use SilverStripe\ORM\FieldType\DBField; use SilverStripe\Security\LogoutForm; use SilverStripe\Security\Member; use SilverStripe\Security\Permission; use SilverStripe\Security\Security; use SilverStripe\Versioned\Versioned; use SilverStripe\View\ArrayData; +use SilverStripe\ORM\FieldType\DBHTMLText; +use SilverStripe\View\Requirements; -class BetterNavigatorExtension extends DataExtension { +class BetterNavigatorExtension extends DataExtension +{ + + /** + * @var bool|null + */ + private $shouldDisplay = null; + + /** + * Load requirements in before final render. When the next extension point is called, it's too late. + * @return void + */ + public function beforeCallActionHandler() + { + if ($this->shouldDisplay()) { + Requirements::javascript('jonom/silverstripe-betternavigator: javascript/betternavigator.js'); + Requirements::css('jonom/silverstripe-betternavigator: css/betternavigator.css'); + } + } + + /** + * @param $request + * @param $action + * @param DBHTMLText $result + * @return DBHTMLText + */ + public function afterCallActionHandler($request, $action, $result) + { + if (!$this->shouldDisplay()) { + return $result; + } + + $html = $result->getValue(); + $navigatorHTML = $this->generateNavigator()->getValue(); + + // Inject the NavigatorHTML before the closing tag + $html = preg_replace( + '/(<\/body[^>]*>)/i', + $navigatorHTML . '\\1', + $html + ); + $result->setValue($html); + + return $result; + } + + /** + * Override on a per-controller basis to add custom logic + * @return bool + */ + public function showBetterNavigator() + { + return true; + } /** * Provides a front-end utility menu with administrative functions and developer tools * Relies on SilverStripeNavigator * - * @return string + * @return DBHTMLText */ - public function BetterNavigator() { + private function generateNavigator() + { + // Get SilverStripeNavigator links & stage info (CMS/Stage/Live/Archive) + $nav = []; + $viewing = ''; + $navigator = SilverStripeNavigator::create($this->owner->dataRecord); + $isDev = Director::isDev(); + + $items = $navigator->getItems(); + foreach ($items as $item) { + $name = $item->getName(); + $active = $item->isActive(); + $nav[$name] = [ + 'Link' => $item->getLink(), + 'Active' => $active + ]; + if ($active) { + if ($name == 'LiveLink') $viewing = 'Live'; + if ($name == 'StageLink') $viewing = 'Draft'; + if ($name == 'ArchiveLink') $viewing = 'Archived'; + } + } + // Only show edit link if user has permission to edit this page + $editLink = array_key_exists('CMSLink', $nav) + && ($isDev || $this->owner->dataRecord->canEdit() && Permission::check('CMS_ACCESS_CMSMain')) + ? $nav['CMSLink']['Link'] : false; + + // Is the logged in member nominated as a developer? + $member = Member::currentUser(); + $devs = Config::inst()->get('BetterNavigator', 'developers'); + $identifierField = Member::config()->unique_identifier_field; + $isDeveloper = $member && is_array($devs) ? in_array($member->{$identifierField}, $devs) : false; + + // Add other data for template + $backURL = '?BackURL=' . urlencode($this->owner->Link()); + $bNData = array_merge($nav, [ + 'Member' => $member, + 'Stage' => Versioned::get_stage(), + 'Viewing' => $viewing, // What we're viewing doesn't necessarily align with the active Stage + 'LoginLink' => Controller::join_links(Director::absoluteBaseURL(), Security::config()->login_url, $backURL), + 'LogoutLink' => Controller::join_links(Director::absoluteBaseURL() . Security::config()->logout_url, $backURL), + 'LogoutForm' => LogoutForm::create($this->owner)->setName('BetterNavigatorLogoutForm'), + 'EditLink' => $editLink, + 'Mode' => Director::get_environment_type(), + 'IsDeveloper' => $isDeveloper + ]); + + // Merge with page data, send to template and render + $navigator = new ArrayData($bNData); + + return $this->owner->customise($navigator)->renderWith('BetterNavigator\\BetterNavigator'); + } + + /** + * Internally compute and cache weather the navigator should display + * @return bool + */ + private function shouldDisplay() + { + if ($this->shouldDisplay !== null) { + return $this->shouldDisplay; + } // Make sure this is a page - if (!$this->isAPage()) return false; + if (!$this->isAPage() || !$this->owner->showBetterNavigator()) { + return $this->shouldDisplay = false; + } // Only show navigator to appropriate users $isDev = Director::isDev(); $canViewDraft = (Permission::check('VIEW_DRAFT_CONTENT') || Permission::check('CMS_ACCESS_CMSMain')); - if ($isDev || $canViewDraft) { - // Get SilverStripeNavigator links & stage info (CMS/Stage/Live/Archive) - $nav = []; - $viewing = ''; - $navigator = SilverStripeNavigator::create($this->owner->dataRecord); - $items = $navigator->getItems(); - foreach ($items as $item) { - $name = $item->getName(); - $active = $item->isActive(); - $nav[$name] = [ - 'Link' => $item->getLink(), - 'Active' => $active - ]; - if ($active) { - if ($name == 'LiveLink') $viewing = 'Live'; - if ($name == 'StageLink') $viewing = 'Draft'; - if ($name == 'ArchiveLink') $viewing = 'Archived'; - } - } - // Only show edit link if user has permission to edit this page - $editLink = array_key_exists('CMSLink', $nav) - && ($isDev || $this->owner->dataRecord->canEdit() && Permission::check('CMS_ACCESS_CMSMain')) - ? $nav['CMSLink']['Link'] : false; - // Is the logged in member nominated as a developer? - $member = Member::currentUser(); - $devs = Config::inst()->get('BetterNavigator', 'developers'); - $identifierField = Member::config()->unique_identifier_field; - $isDeveloper = $member && is_array($devs) ? in_array($member->{$identifierField}, $devs) : false; - - // Add other data for template - $backURL = '?BackURL=' . urlencode($this->owner->Link()); - $bNData = array_merge($nav, [ - 'Member' => $member, - 'Stage' => Versioned::get_stage(), - 'Viewing' => $viewing, // What we're viewing doesn't necessarily align with the active Stage - 'LoginLink' => Controller::join_links(Director::absoluteBaseURL(), Security::config()->login_url, $backURL), - 'LogoutLink' => Controller::join_links(Director::absoluteBaseURL() . Security::config()->logout_url, $backURL), - 'LogoutForm' => LogoutForm::create($this->owner)->setName('BetterNavigatorLogoutForm'), - 'EditLink' => $editLink, - 'Mode' => Director::get_environment_type(), - 'IsDeveloper' => $isDeveloper - ]); - - // Merge with page data, send to template and render - $bNData = new ArrayData($bNData); - $page = $this->owner->customise(['BetterNavigator' => $bNData]); - return $page->renderWith('BetterNavigator\\BetterNavigator'); - } - return false; + return $this->shouldDisplay = ($isDev || $canViewDraft); } - protected function isAPage() { + /** + * @return boolean + */ + private function isAPage() + { return $this->owner && $this->owner->dataRecord && $this->owner->dataRecord instanceof SiteTree diff --git a/templates/BetterNavigator/BetterNavigator.ss b/templates/BetterNavigator/BetterNavigator.ss index 67e1958..97baf63 100644 --- a/templates/BetterNavigator/BetterNavigator.ss +++ b/templates/BetterNavigator/BetterNavigator.ss @@ -1,21 +1,15 @@ -<% require javascript("jonom/silverstripe-betternavigator: javascript/betternavigator.js") %> -<% require css("jonom/silverstripe-betternavigator: css/betternavigator.css") %> -