NEW: Auto-injection into template (#43)

* `$BetterNavigator` no longer required in template
* Uses locally scoped render for navigator
* Added shouldShowBetterNavigator() method
This commit is contained in:
Aaron Carlino 2019-08-24 05:10:19 +12:00 committed by Jono Menz
parent db3c7d8e39
commit a5e3f0d88f
3 changed files with 147 additions and 69 deletions

View File

@ -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.

View File

@ -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 </body> 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

View File

@ -1,21 +1,15 @@
<% require javascript("jonom/silverstripe-betternavigator: javascript/betternavigator.js") %>
<% require css("jonom/silverstripe-betternavigator: css/betternavigator.css") %>
<div id="BetterNavigator" class="collapsed">
<% with $BetterNavigator %>
<div id="BetterNavigatorStatus" class="$Viewing">
<span class="bn-icon-cog"></span>
$Viewing
<span class="bn-icon-close"></span>
</div>
<% end_with %>
<div id="BetterNavigatorContent">
<div class="bn-links">
<% with $BetterNavigator %>
<% if $ArchiveLink.Active %>
<% if $EditLink %><a href="$EditLink" target="_blank"><span class="bn-icon-edit"></span>Restore</a><% end_if %>
<% else %>
@ -42,19 +36,18 @@
<% else %>
<a href="$LoginLink"><span class="bn-icon-user"></span>Log in</a>
<% end_if %>
<% end_with %>
</div>
<% include BetterNavigator\BetterNavigatorExtraContent %>
<% if $BetterNavigator.Mode=='dev' || $BetterNavigator.IsDeveloper %>
<% if $Mode=='dev' || $IsDeveloper %>
<div class="bn-heading">Developer tools</div>
<div class="bn-links">
<% if $BetterNavigator.Mode='dev' %>
<% if $Mode='dev' %>
<span class="bn-disabled" title="Log out to end Dev Mode"><span class="bn-icon-tick"></span>Dev mode on</span>
<% else %>
<a href="{$AbsoluteLink}?isDev=1"><span class="bn-icon-devmode"></span>Dev mode</a>