Merge pull request #2474 from wilr/externalcmslink

API: Add attributes argument for CMSMenuItem for external links
This commit is contained in:
Ingo Schommer 2013-10-02 02:38:56 -07:00
commit e7953f3b41
7 changed files with 198 additions and 40 deletions

View File

@ -1,16 +1,21 @@
<?php <?php
/** /**
* The object manages the main CMS menu. See {@link LeftAndMain::init()} for example usage. * The object manages the main CMS menu. See {@link LeftAndMain::init()} for
* example usage.
* *
* The menu will be automatically populated with menu items for subclasses of {@link LeftAndMain}. * The menu will be automatically populated with menu items for subclasses of
* That is, for each class in the CMS that creates an administration panel, a CMS menu item will be created. * {@link LeftAndMain}. That is, for each class in the CMS that creates an
* The default configuration will also include a 'help' link to the SilverStripe user documentation. * administration panel, a CMS menu item will be created. The default
* configuration will also include a 'help' link to the SilverStripe user
* documentation.
*
* Additional CMSMenu items can be added through {@link LeftAndMainExtension::init()}
* extensions added to {@link LeftAndMain}.
* *
* @package cms * @package framework
* @subpackage content * @subpackage admin
*/ */
class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider {
{
/** /**
* An array of changes to be made to the menu items, in the order that the changes should be * An array of changes to be made to the menu items, in the order that the changes should be
@ -79,10 +84,12 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider
* @param string $url The url of the link * @param string $url The url of the link
* @param integer $priority The menu priority (sorting order) of the menu item. Higher priorities will be further * @param integer $priority The menu priority (sorting order) of the menu item. Higher priorities will be further
* left. * left.
* @param array $attributes an array of attributes to include on the link.
*
* @return boolean The result of the operation. * @return boolean The result of the operation.
*/ */
public static function add_link($code, $menuTitle, $url, $priority = -1) { public static function add_link($code, $menuTitle, $url, $priority = -1, $attributes = null) {
return self::add_menu_item($code, $menuTitle, $url, null, $priority); return self::add_menu_item($code, $menuTitle, $url, null, $priority, $attributes);
} }
/** /**
@ -97,13 +104,17 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider
* @param string $controllerClass The controller class for this menu, used to check permisssions. * @param string $controllerClass The controller class for this menu, used to check permisssions.
* If blank, it's assumed that this is public, and always shown to users who * If blank, it's assumed that this is public, and always shown to users who
* have the rights to access some other part of the admin area. * have the rights to access some other part of the admin area.
* @param array $attributes an array of attributes to include on the link.
*
* @return boolean Success * @return boolean Success
*/ */
public static function add_menu_item($code, $menuTitle, $url, $controllerClass = null, $priority = -1) { public static function add_menu_item($code, $menuTitle, $url, $controllerClass = null, $priority = -1, $attributes = null) {
// If a class is defined, then force the use of that as a code. This helps prevent menu item duplication // If a class is defined, then force the use of that as a code. This helps prevent menu item duplication
if($controllerClass) $code = $controllerClass; if($controllerClass) {
$code = $controllerClass;
}
return self::replace_menu_item($code, $menuTitle, $url, $controllerClass, $priority); return self::replace_menu_item($code, $menuTitle, $url, $controllerClass, $priority, $attributes);
} }
/** /**
@ -223,18 +234,26 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider
* @param string $controllerClass The controller class for this menu, used to check permisssions. * @param string $controllerClass The controller class for this menu, used to check permisssions.
* If blank, it's assumed that this is public, and always shown to users who * If blank, it's assumed that this is public, and always shown to users who
* have the rights to access some other part of the admin area. * have the rights to access some other part of the admin area.
* @param array $attributes an array of attributes to include on the link.
*
* @return boolean Success * @return boolean Success
*/ */
public static function replace_menu_item($code, $menuTitle, $url, $controllerClass = null, $priority = -1) { public static function replace_menu_item($code, $menuTitle, $url, $controllerClass = null, $priority = -1, $attributes = null) {
$item = new CMSMenuItem($menuTitle, $url, $controllerClass, $priority);
if($attributes) {
$item->setAttributes($attributes);
}
self::$menu_item_changes[] = array( self::$menu_item_changes[] = array(
'type' => 'add', 'type' => 'add',
'code' => $code, 'code' => $code,
'item' => new CMSMenuItem($menuTitle, $url, $controllerClass, $priority), 'item' => $item,
); );
} }
/** /**
* Add a previously built menuitem object to the menu * Add a previously built menu item object to the menu
*/ */
protected static function add_menu_item_obj($code, $cmsMenuItem) { protected static function add_menu_item_obj($code, $cmsMenuItem) {
self::$menu_item_changes[] = array( self::$menu_item_changes[] = array(

View File

@ -1,12 +1,18 @@
<?php <?php
/** /**
* A simple CMS menu item * A simple CMS menu item.
*
* Items can be added to the menu through custom {@link LeftAndMainExtension}
* classes and {@link CMSMenu}.
* *
* @package cms * @see CMSMenu
* @subpackage content *
* @package framework
* @subpackage admin
*/ */
class CMSMenuItem extends Object class CMSMenuItem extends Object {
{
/** /**
* The (translated) menu title * The (translated) menu title
* @var string $title * @var string $title
@ -31,8 +37,17 @@ class CMSMenuItem extends Object
*/ */
public $priority; public $priority;
/**
* Attributes for the link. For instance, custom data attributes or standard
* HTML anchor properties.
*
* @var string
*/
protected $attributes = array();
/** /**
* Create a new CMS Menu Item * Create a new CMS Menu Item
*
* @param string $title * @param string $title
* @param string $url * @param string $url
* @param string $controller Controller class name * @param string $controller Controller class name
@ -43,7 +58,41 @@ class CMSMenuItem extends Object
$this->url = $url; $this->url = $url;
$this->controller = $controller; $this->controller = $controller;
$this->priority = $priority; $this->priority = $priority;
parent::__construct(); parent::__construct();
} }
/**
* @param array $attributes
*/
public function setAttributes($attributes) {
$this->attributes = $attributes;
}
/**
* @param array
*
* @return HTML
*/
public function getAttributesHTML($attrs = null) {
$exclude = (is_string($attrs)) ? func_get_args() : null;
if(!$attrs || is_string($attrs)) {
$attrs = $this->attributes;
}
// Remove empty
$attrs = array_filter((array)$attrs, function($v) {
return ($v || $v === 0 || $v === '0');
});
// Create markkup
$parts = array();
foreach($attrs as $name => $value) {
$parts[] = ($value === true) ? "{$name}=\"{$name}\"" : "{$name}=\"" . Convert::raw2att($value) . "\"";
}
return implode(' ', $parts);
}
} }

View File

@ -219,7 +219,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
'Help', 'Help',
_t('LeftAndMain.HELP', 'Help', 'Menu title'), _t('LeftAndMain.HELP', 'Help', 'Menu title'),
$this->config()->help_link, $this->config()->help_link,
-2 -2,
array(
'target' => '_blank'
)
); );
// Allow customisation of the access check by a extension // Allow customisation of the access check by a extension
@ -616,6 +619,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
if($menuItems) { if($menuItems) {
foreach($menuItems as $code => $menuItem) { foreach($menuItems as $code => $menuItem) {
// alternate permission checks (in addition to LeftAndMain->canView()) // alternate permission checks (in addition to LeftAndMain->canView())
if( if(
isset($menuItem->controller) isset($menuItem->controller)
&& $this->hasMethod('alternateMenuDisplayCheck') && $this->hasMethod('alternateMenuDisplayCheck')
@ -661,9 +665,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
$menuIcon = LeftAndMain::menu_icon_for_class($menuItem->controller); $menuIcon = LeftAndMain::menu_icon_for_class($menuItem->controller);
if (!empty($menuIcon)) $menuIconStyling .= $menuIcon; if (!empty($menuIcon)) $menuIconStyling .= $menuIcon;
} }
$menu->push(new ArrayData(array( $menu->push(new ArrayData(array(
"MenuItem" => $menuItem, "MenuItem" => $menuItem,
"AttributesHTML" => $menuItem->getAttributesHTML(),
"Title" => Convert::raw2xml($title), "Title" => Convert::raw2xml($title),
"Code" => DBField::create_field('Text', $code), "Code" => DBField::create_field('Text', $code),
"Link" => $menuItem->url, "Link" => $menuItem->url,

View File

@ -230,6 +230,13 @@
// Ignore external links, fallback to standard link behaviour // Ignore external links, fallback to standard link behaviour
var isExternal = $.path.isExternal(this.attr('href')); var isExternal = $.path.isExternal(this.attr('href'));
if(e.which > 1 || isExternal) return; if(e.which > 1 || isExternal) return;
// if the developer has this to open in a new window, handle
// that
if(this.attr('target') == "_blank") {
return;
}
e.preventDefault(); e.preventDefault();
var item = this.getMenuItem(); var item = this.getMenuItem();

View File

@ -24,7 +24,7 @@
<ul class="cms-menu-list"> <ul class="cms-menu-list">
<% loop $MainMenu %> <% loop $MainMenu %>
<li class="$LinkingMode $FirstLast <% if $LinkingMode == 'link' %><% else %>opened<% end_if %>" id="Menu-$Code" title="$Title.ATT"> <li class="$LinkingMode $FirstLast <% if $LinkingMode == 'link' %><% else %>opened<% end_if %>" id="Menu-$Code" title="$Title.ATT">
<a href="$Link" <% if $Code == 'Help' %>target="_blank"<% end_if%>> <a href="$Link" $AttributesHTML>
<span class="icon icon-16 icon-{$Code.LowerCase}">&nbsp;</span> <span class="icon icon-16 icon-{$Code.LowerCase}">&nbsp;</span>
<span class="text">$Title</span> <span class="text">$Title</span>
</a> </a>

View File

@ -1,6 +1,6 @@
<?php <?php
/** /**
* @package cms * @package framework
* @subpackage tests * @subpackage tests
*/ */
class CMSMenuTest extends SapphireTest implements TestOnly { class CMSMenuTest extends SapphireTest implements TestOnly {
@ -35,7 +35,21 @@ class CMSMenuTest extends SapphireTest implements TestOnly {
$this->assertNull($menuItem->controller, 'Link menu item has no controller class'); $this->assertNull($menuItem->controller, 'Link menu item has no controller class');
$this->assertEquals($menuItem->priority, -1, 'Link menu item has the correct priority'); $this->assertEquals($menuItem->priority, -1, 'Link menu item has the correct priority');
CMSMenu::clear_menu(); CMSMenu::clear_menu();
}
public function testLinkWithExternalAttributes() {
CMSMenu::clear_menu();
CMSMenu::add_link('LinkCode', 'link title', 'http://www.example.com', -2, array(
'target' => '_blank'
));
$menuItems = CMSMenu::get_menu_items();
$menuItem = $menuItems['LinkCode'];
$this->assertEquals('target="_blank"', $menuItem->getAttributesHTML());
CMSMenu::clear_menu();
} }
public function testCmsClassDetection() { public function testCmsClassDetection() {
@ -81,8 +95,15 @@ class CMSMenuTest extends SapphireTest implements TestOnly {
} }
/**
* @package framework
* @subpackage tests
*/
class CMSMenuTest_LeftAndMainController extends LeftAndMain implements TestOnly { class CMSMenuTest_LeftAndMainController extends LeftAndMain implements TestOnly {
private static $url_segment = 'CMSMenuTest_LeftAndMainController'; private static $url_segment = 'CMSMenuTest_LeftAndMainController';
private static $menu_title = 'CMSMenuTest_LeftAndMainController'; private static $menu_title = 'CMSMenuTest_LeftAndMainController';
private static $menu_priority = 50; private static $menu_priority = 50;
} }

View File

@ -1,16 +1,24 @@
# How to customize the CMS Menu # # How to customize the CMS Menu
## Defining a Custom Icon ## ## Adding a administration panel
Every time you add a new extension of the `api:LeftAndMain` class to the CMS, SilverStripe will automatically create a new menu-item for it, with a default title and icon. Every time you add a new extension of the `[api:LeftAndMain]` class to the CMS,
We can easily change that behaviour by using the static `$menu_title` and `$menu_icon` statics to SilverStripe will automatically create a new `[api:CMSMenuItem]` for it
The most popular extension of LeftAndMain is a `[api:ModelAdmin]` class, so
for a more detailed introduction to creating new `ModelAdmin` interfaces, read
the [ModelAdmin referencee](../reference/modeladmin).
In this document we'll take the `ProductAdmin` class used in the
[ModelAdmin referencee](../reference/modeladmin#setup) and so how we can change
the menu behaviour by using the static `$menu_title` and `$menu_icon` statics to
provide a custom title and icon. provide a custom title and icon.
The most popular extension of LeftAndMain is the `api:ModelAdmin` class, so we'll use that for an example. ### Defining a Custom Icon
We'll take the `ProductAdmin` class used in the [ModelAdmin reference](../reference/modeladmin#setup).
First we'll need a custom icon. For this purpose SilverStripe uses 16x16 black-and-transparent PNG graphics. First we'll need a custom icon. For this purpose SilverStripe uses 16x16
In this case we'll place the icon in `mysite/images`, but you are free to use any location. black-and-transparent PNG graphics. In this case we'll place the icon in
`mysite/images`, but you are free to use any location.
:::php :::php
class ProductAdmin extends ModelAdmin { class ProductAdmin extends ModelAdmin {
@ -18,11 +26,11 @@ In this case we'll place the icon in `mysite/images`, but you are free to use an
private static $menu_icon = 'mysite/images/product-icon.png'; private static $menu_icon = 'mysite/images/product-icon.png';
} }
## Defining a Custom Title ## ### Defining a Custom Title
The title of menu entries is configured through the `$menu_title` static. The title of menu entries is configured through the `$menu_title` static.
If its not defined, the CMS falls back to using the class name of the controller, If its not defined, the CMS falls back to using the class name of the
removing the "Admin" bit at the end. controller, removing the "Admin" bit at the end.
:::php :::php
class ProductAdmin extends ModelAdmin { class ProductAdmin extends ModelAdmin {
@ -30,9 +38,58 @@ removing the "Admin" bit at the end.
private static $menu_title = 'My Custom Admin'; private static $menu_title = 'My Custom Admin';
} }
In order to localize the menu title in different languages, use the `<classname>.MENUTITLE` In order to localize the menu title in different languages, use the
entity name, which is automatically created when running the i18n text collection. `<classname>.MENUTITLE` entity name, which is automatically created when running
For more information on language and translations, please refer to the [i18n](../reference/ii8n) docs. the i18n text collection.
For more information on language and translations, please refer to the
[i18n](../reference/ii8n) docs.
## Adding an external link to the menu
On top of your administration windows, the menu can also have external links
(e.g to external reference). In this example, we're going to add a link to
Google to the menu.
First, we need to define a `[api:LeftAndMainExtension]` which will contain our
button configuration.
:::php
<?php
class CustomLeftAndMain extends LeftAndMainExtension {
public function init() {
// unique identifier for this item. Will have an ID of Menu-$ID
$id = 'LinkToGoogle';
// your 'nice' title
$title = 'Google';
// the link you want to item to go to
$link = 'http://google.com';
// priority controls the ordering of the link in the stack. The
// lower the number, the lower in the list
$priority = -2;
// Add your own attributes onto the link. In our case, we want to
// open the link in a new window (not the original)
$attributes = array(
'target' => '_blank'
);
CMSMenu::add_link($id, $title, $link, $priority, $attributes);
}
}
To have the link appear, make sure you add the extension to the `LeftAndMain`
class. For more information about configuring extensions see the
[DataExtension referencee](../reference/dataextension).
:::php
LeftAndMain::add_extension('CustomLeftAndMain')
## Related ## Related