diff --git a/admin/code/LeftAndMain.php b/admin/code/LeftAndMain.php
index 750153883..25189dbc0 100644
--- a/admin/code/LeftAndMain.php
+++ b/admin/code/LeftAndMain.php
@@ -378,29 +378,58 @@ class LeftAndMain extends Controller implements PermissionProvider {
$ie = isset($_SERVER['HTTP_USER_AGENT']) ? strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') : false;
if($ie) {
$version = substr($_SERVER['HTTP_USER_AGENT'], $ie + 5, 3);
- if($version == 7) Requirements::css(FRAMEWORK_ADMIN_DIR . '/css/ie7.css');
- else if($version == 8) Requirements::css(FRAMEWORK_ADMIN_DIR . '/css/ie8.css');
+
+ if($version == 7) {
+ Requirements::css(FRAMEWORK_ADMIN_DIR . '/css/ie7.css');
+ } else if($version == 8) {
+ Requirements::css(FRAMEWORK_ADMIN_DIR . '/css/ie8.css');
+ }
}
// Custom requirements
$extraJs = $this->stat('extra_requirements_javascript');
- if($extraJs) foreach($extraJs as $file => $config) {
- Requirements::javascript($file);
+ if($extraJs) {
+ foreach($extraJs as $file => $config) {
+ if(is_numeric($file)) {
+ $file = $config;
+ }
+
+ Requirements::javascript($file);
+ }
}
+
$extraCss = $this->stat('extra_requirements_css');
- if($extraCss) foreach($extraCss as $file => $config) {
- Requirements::css($file, isset($config['media']) ? $config['media'] : null);
+
+ if($extraCss) {
+ foreach($extraCss as $file => $config) {
+ if(is_numeric($file)) {
+ $file = $config;
+ $config = array();
+ }
+
+ Requirements::css($file, isset($config['media']) ? $config['media'] : null);
+ }
}
+
$extraThemedCss = $this->stat('extra_requirements_themedCss');
- if($extraThemedCss) foreach ($extraThemedCss as $file => $config) {
- Requirements::themedCSS($file, isset($config['media']) ? $config['media'] : null);
+
+ if($extraThemedCss) {
+ foreach ($extraThemedCss as $file => $config) {
+ if(is_numeric($file)) {
+ $file = $config;
+ $config = array();
+ }
+
+ Requirements::themedCSS($file, isset($config['media']) ? $config['media'] : null);
+ }
}
$dummy = null;
$this->extend('init', $dummy);
- // The user's theme shouldn't affect the CMS, if, for example, they have replaced TableListField.ss or Form.ss.
+ // The user's theme shouldn't affect the CMS, if, for example, they have
+ // replaced TableListField.ss or Form.ss.
Config::inst()->update('SSViewer', 'theme_enabled', false);
}
diff --git a/admin/tests/LeftAndMainTest.php b/admin/tests/LeftAndMainTest.php
index 2b971a335..23ae15fb9 100644
--- a/admin/tests/LeftAndMainTest.php
+++ b/admin/tests/LeftAndMainTest.php
@@ -1,6 +1,7 @@
autoFollowRedirection = false;
CMSMenu::populate_menu();
+
+ $this->backupCss = Config::inst()->get('LeftAndMain', 'extra_requirements_css');
+ $this->backupJs = Config::inst()->get('LeftAndMain', 'extra_requirements_javascript');
+ $this->backupCombined = Requirements::get_combined_files_enabled();
+
+ Config::inst()->update('LeftAndMain', 'extra_requirements_css', array(
+ FRAMEWORK_DIR . '/tests/assets/LeftAndMainTest.css'
+ ));
+
+ Config::inst()->update('LeftAndMain', 'extra_requirements_javascript', array(
+ FRAMEWORK_DIR . '/tests/assets/LeftAndMainTest.js'
+ ));
+
+ Requirements::set_combined_files_enabled(false);
+ }
+
+ public function tearDown() {
+ parent::tearDown();
+
+ Config::inst()->update('LeftAndMain', 'extra_requirements_css', $this->backupCss);
+ Config::inst()->update('LeftAndMain', 'extra_requirements_javascript', $this->backupJs);
+
+ Requirements::set_combined_files_enabled($this->backupCombined);
}
+
+ public function testExtraCssAndJavascript() {
+ $admin = $this->objFromFixture('Member', 'admin');
+ $this->session()->inst_set('loggedInAs', $admin->ID);
+ $response = $this->get('LeftAndMainTest_Controller');
+
+ $this->assertRegExp('/tests\/assets\/LeftAndMainTest.css/i', $response->getBody(), "body should contain custom css");
+ $this->assertRegExp('/tests\/assets\/LeftAndMainTest.js/i', $response->getBody(), "body should contain custom js");
+ }
+
/**
* Note: This test would typically rely on SiteTree and CMSMain, but is mocked by
* LeftAndMain_Controller and LeftAndMain_Object here to remove this dependency.
@@ -158,15 +194,22 @@ class LeftAndMainTest extends FunctionalTest {
$this->session()->inst_set('loggedInAs', null);
}
-
}
+/**
+ * @package framework
+ * @subpackage tests
+ */
class LeftAndMainTest_Controller extends LeftAndMain implements TestOnly {
protected $template = 'BlankPage';
private static $tree_class = 'LeftAndMainTest_Object';
}
+/**
+ * @package framework
+ * @subpackage tests
+ */
class LeftAndMainTest_Object extends DataObject implements TestOnly {
private static $db = array(
diff --git a/docs/en/howto/extend-cms-interface.md b/docs/en/howto/extend-cms-interface.md
index 5460583d6..9001b971f 100644
--- a/docs/en/howto/extend-cms-interface.md
+++ b/docs/en/howto/extend-cms-interface.md
@@ -2,28 +2,31 @@
## Introduction ##
-The CMS interface works just like any other part of your website: It consists of PHP controllers,
-templates, CSS stylesheets and JavaScript. Because it uses the same base elements,
-it is relatively easy to extend.
-As an example, we're going to add a permanent "bookmarks" bar to popular pages at the bottom of the CMS.
-A page can be bookmarked by a CMS author through a simple checkbox.
+The CMS interface works just like any other part of your website: It consists of
+PHP controllers, templates, CSS stylesheets and JavaScript. Because it uses the
+same base elements, it is relatively easy to extend.
+
+As an example, we're going to add a permanent "bookmarks" bar to popular pages
+at the bottom of the CMS. A page can be bookmarked by a CMS author through a
+simple checkbox.
For a deeper introduction to the inner workings of the CMS, please refer to our
guide on [CMS Architecture](../reference/cms-architecture).
## Overload a CMS template ##
-If you place a template with an identical name into your application template directory
-(usually `mysite/templates/`), it'll take priority over the built-in one.
+If you place a template with an identical name into your application template
+directory (usually `mysite/templates/`), it'll take priority over the built-in
+one.
CMS templates are inherited based on their controllers, similar to subclasses of
the common `Page` object (a new PHP class `MyPage` will look for a `MyPage.ss` template).
We can use this to create a different base template with `LeftAndMain.ss`
(which corresponds to the `LeftAndMain` PHP controller class).
-Copy the template markup of the base implementation at `framework/admin/templates/LeftAndMain.ss` into
-`mysite/templates/LeftAndMain.ss`. It will automatically be picked up by the CMS logic. Add a new section after the
-`$Content` tag:
+Copy the template markup of the base implementation at `framework/admin/templates/LeftAndMain.ss`
+into `mysite/templates/LeftAndMain.ss`. It will automatically be picked up by
+the CMS logic. Add a new section after the `$Content` tag:
:::ss
...
@@ -39,21 +42,24 @@ Copy the template markup of the base implementation at `framework/admin/template
...
-Refresh the CMS interface with `admin/?flush=all`, and you should see the new bottom bar with some hardcoded links.
-We'll make these dynamic further down.
+Refresh the CMS interface with `admin/?flush=all`, and you should see the new
+bottom bar with some hardcoded links. We'll make these dynamic further down.
-You might have noticed that we didn't write any JavaScript to add our layout manager.
-The important piece of information is the `south` class in our new `
` structure,
-plus the height value in our CSS. It instructs the existing parent layout how to render the element.
-This layout manager ([jLayout](http://www.bramstein.com/projects/jlayout/))
-allows us to build complex layouts with minimal JavaScript configuration.
+You might have noticed that we didn't write any JavaScript to add our layout
+manager. The important piece of information is the `south` class in our new
+`
` structure, plus the height value in our CSS. It instructs the existing
+parent layout how to render the element. This layout manager
+([jLayout](http://www.bramstein.com/projects/jlayout/)) allows us to build
+complex layouts with minimal JavaScript configuration.
-See [layout reference](../reference/layout) for more specific information on CMS layouting.
+See [layout reference](../reference/layout) for more specific information on
+CMS layouting.
## Include custom CSS in the CMS
-In order to show the links in one line, we'll add some CSS, and get it to load with the CMS interface.
-Paste the following content into a new file called `mysite/css/BookmarkedPages.css`:
+In order to show the links in one line, we'll add some CSS, and get it to load
+with the CMS interface. Paste the following content into a new file called
+`mysite/css/BookmarkedPages.css`:
:::css
.cms-bottom-bar {height: 20px; padding: 5px; background: #C6D7DF;}
@@ -67,18 +73,23 @@ Load the new CSS file into the CMS, by setting the `LeftAndMain.extra_requiremen
:::yml
LeftAndMain:
extra_requirements_css:
- mysite/css/BookmarkedPages.css:
+ - mysite/css/BookmarkedPages.css:
## Create a "bookmark" flag on pages ##
-Now we'll define which pages are actually bookmarked, a flag that is stored in the database.
-For this we need to decorate the page record with a `DataExtension`.
-Create a new file called `mysite/code/BookmarkedPageExtension.php` and insert the following code.
+Now we'll define which pages are actually bookmarked, a flag that is stored in
+the database. For this we need to decorate the page record with a
+`DataExtension`. Create a new file called `mysite/code/BookmarkedPageExtension.php`
+and insert the following code.
:::php
'Boolean');
+
+ private static $db = array(
+ 'IsBookmarked' => 'Boolean'
+ );
public function updateCMSFields(FieldList $fields) {
$fields->addFieldToTab('Root.Main',
@@ -100,14 +111,17 @@ Refresh the CMS, open a page for editing and you should see the new checkbox.
## Retrieve the list of bookmarks from the database
One piece in the puzzle is still missing: How do we get the list of bookmarked
-pages from the datbase into the template we've already created (with hardcoded links)?
-Again, we extend a core class: The main CMS controller called `LeftAndMain`.
+pages from the database into the template we've already created (with hardcoded
+links)? Again, we extend a core class: The main CMS controller called
+`LeftAndMain`.
Add the following code to a new file `mysite/code/BookmarkedLeftAndMainExtension.php`;
:::php
filter("IsBookmarked", 1);
}
@@ -133,39 +147,51 @@ and replace it with the following:
## Extending the CMS actions
-CMS actions follow a principle similar to the CMS fields: they are built in the backend with the help of `FormFields`
-and `FormActions`, and the frontend is responsible for applying a consistent styling.
+CMS actions follow a principle similar to the CMS fields: they are built in the
+backend with the help of `FormFields` and `FormActions`, and the frontend is
+responsible for applying a consistent styling.
The following conventions apply:
-* New actions can be added by redefining `getCMSActions`, or adding an extension with `updateCMSActions`.
-* It is required the actions are contained in a `FieldSet` (`getCMSActions` returns this already).
-* Standalone buttons are created by adding a top-level `FormAction` (no such button is added by default).
-* Button groups are created by adding a top-level `CompositeField` with `FormActions` in it.
+* New actions can be added by redefining `getCMSActions`, or adding an extension
+with `updateCMSActions`.
+* It is required the actions are contained in a `FieldSet` (`getCMSActions`
+returns this already).
+* Standalone buttons are created by adding a top-level `FormAction` (no such
+button is added by default).
+* Button groups are created by adding a top-level `CompositeField` with
+`FormActions` in it.
* A `MajorActions` button group is already provided as a default.
-* Drop ups with additional actions that appear as links are created via a `TabSet` and `Tabs` with `FormActions` inside.
-* A `ActionMenus.MoreOptions` tab is already provided as a default and contains some minor actions.
-* You can override the actions completely by providing your own `getAllCMSFields`.
+* Drop ups with additional actions that appear as links are created via a
+`TabSet` and `Tabs` with `FormActions` inside.
+* A `ActionMenus.MoreOptions` tab is already provided as a default and contains
+some minor actions.
+* You can override the actions completely by providing your own
+`getAllCMSFields`.
Let's walk through a couple of examples of adding new CMS actions in `getCMSActions`.
-First of all we can add a regular standalone button anywhere in the set. Here we are inserting it in the front of all
-other actions. We could also add a button group (`CompositeField`) in a similar fashion.
+First of all we can add a regular standalone button anywhere in the set. Here
+we are inserting it in the front of all other actions. We could also add a
+button group (`CompositeField`) in a similar fashion.
:::php
$fields->unshift(FormAction::create('normal', 'Normal button'));
-We can affect the existing button group by manipulating the `CompositeField` already present in the `FieldList`.
+We can affect the existing button group by manipulating the `CompositeField`
+already present in the `FieldList`.
:::php
$fields->fieldByName('MajorActions')->push(FormAction::create('grouped', 'New group button'));
-Another option is adding actions into the drop-up - best place for placing infrequently used minor actions.
+Another option is adding actions into the drop-up - best place for placing
+infrequently used minor actions.
:::php
$fields->addFieldToTab('ActionMenus.MoreOptions', FormAction::create('minor', 'Minor action'));
-We can also easily create new drop-up menus by defining new tabs within the `TabSet`.
+We can also easily create new drop-up menus by defining new tabs within the
+`TabSet`.
:::php
$fields->addFieldToTab('ActionMenus.MyDropUp', FormAction::create('minor', 'Minor action in a new drop-up'));
@@ -174,15 +200,18 @@ We can also easily create new drop-up menus by defining new tabs within the `Tab
Empty tabs will be automatically removed from the `FieldList` to prevent clutter.
-New actions will need associated controller handlers to work. You can use a `LeftAndMainExtension` to provide one. Refer
-to [Controller documentation](../topics/controller) for instructions on setting up handlers.
+New actions will need associated controller handlers to work. You can use a
+`LeftAndMainExtension` to provide one. Refer to [Controller documentation](../topics/controller)
+for instructions on setting up handlers.
-To make the actions more user-friendly you can also use alternating buttons as detailed in the [CMS Alternating
-Button](../reference/cms-alternating-button) how-to.
+To make the actions more user-friendly you can also use alternating buttons as
+detailed in the [CMS Alternating Button](../reference/cms-alternating-button)
+how-to.
## Summary
In a few lines of code, we've customized the look and feel of the CMS.
+
While this example is only scratching the surface, it includes most building
blocks and concepts for more complex extensions as well.
diff --git a/docs/en/reference/preview.md b/docs/en/reference/preview.md
index fde31481d..9a48a7111 100644
--- a/docs/en/reference/preview.md
+++ b/docs/en/reference/preview.md
@@ -2,31 +2,37 @@
## Overview
-With the addition of side-by-side editing, the preview has the ability to appear within the CMS window when editing
-content in the _Pages_ section of the CMS. The site is rendered into an iframe. It will update itself whenever the
-content is saved, and relevant pages will be loaded for editing when the user navigates around in the preview.
+With the addition of side-by-side editing, the preview has the ability to appear
+within the CMS window when editing content in the _Pages_ section of the CMS.
+The site is rendered into an iframe. It will update itself whenever the content
+is saved, and relevant pages will be loaded for editing when the user navigates
+around in the preview.
-The root element for preview is `.cms-preview` which maintains the internal states neccessary for rendering within the
-entwine properties. It provides function calls for transitioning between these states and has the ability to update the
-appearance of the option selectors.
+The root element for preview is `.cms-preview` which maintains the internal
+states necessary for rendering within the entwine properties. It provides
+function calls for transitioning between these states and has the ability to
+update the appearance of the option selectors.
-In terms of backend support, it relies on `SilverStripeNavigator` to be rendered into the `.cms-edit-form`.
-_LeftAndMain_ will automatically take care of generating it as long as the `*_SilverStripeNavigator` template is found -
-first segment has to match current _LeftAndMain_-derived class (e.g. `LeftAndMain_SilverStripeNavigator`).
+In terms of backend support, it relies on `SilverStripeNavigator` to be rendered
+into the `.cms-edit-form`. _LeftAndMain_ will automatically take care of
+generating it as long as the `*_SilverStripeNavigator` template is found -
+first segment has to match current _LeftAndMain_-derived class (e.g.
+`LeftAndMain_SilverStripeNavigator`).
We use `ss.preview` entwine namespace for all preview-related entwines.
-Caveat: `SilverStripeNavigator` and `CMSPreviewable` interface currently only support SiteTree objects that are
-_Versioned_. They are not general enough for using on any other DataObject. That pretty much limits the extendability
-of the feature.
+Caveat: `SilverStripeNavigator` and `CMSPreviewable` interface currently only
+support SiteTree objects that are _Versioned_. They are not general enough for
+using on any other DataObject. That pretty much limits the extendability of the
+feature.
## Configuration and Defaults
Like most of the CMS, the preview UI is powered by
-[jQuery entwine](https://github.com/hafriedlander/jquery.entwine).
-This means its defaults are configured through JavaScript, by setting entwine properties.
+[jQuery entwine](https://github.com/hafriedlander/jquery.entwine). This means
+its defaults are configured through JavaScript, by setting entwine properties.
In order to achieve this, create a new file `mysite/javascript/MyLeftAndMain.Preview.js`.
In the following example we configure three aspects:
@@ -69,7 +75,7 @@ to the `LeftAndMain.extra_requirements_javascript` [configuration value](/topics
:::yml
LeftAndMain
extra_requirements_javascript:
- mysite/javascript/MyLeftAndMain.Preview.js:
+ - mysite/javascript/MyLeftAndMain.Preview.js:
In order to find out which configuration values are available, the source code
is your best reference at the moment - have a look in `framework/admin/javascript/LeftAndMain.Preview.js`.
@@ -78,30 +84,33 @@ To understand how layouts are handled in the CMS UI, have a look at the
## Enabling preview
-The frontend decides on the preview being enabled or disabled based on the presnce of the `.cms-previewable` class. If
-this class is not found the preview will remain hidden, and the layout will stay in the _content_ mode.
+The frontend decides on the preview being enabled or disabled based on the
+presence of the `.cms-previewable` class. If this class is not found the preview
+will remain hidden, and the layout will stay in the _content_ mode.
-If the class is found, frontend looks for the `SilverStripeNavigator` structure and moves it to the
-`.cms-preview-control` panel at the bottom of the preview. This structure supplies preview options such as state
-selector.
+If the class is found, frontend looks for the `SilverStripeNavigator` structure
+and moves it to the `.cms-preview-control` panel at the bottom of the preview.
+This structure supplies preview options such as state selector.
-If the navigator is not found, the preview appears in the GUI, but is shown as "blocked" - i.e. displaying the "preview
-unavailable" overlay.
+If the navigator is not found, the preview appears in the GUI, but is shown as
+"blocked" - i.e. displaying the "preview unavailable" overlay.
-The preview can be affected by calling `enablePreview` and `disablePreview`. You can check if the preview is active by
-inspecting the `IsPreviewEnabled` entwine property.
+The preview can be affected by calling `enablePreview` and `disablePreview`. You
+can check if the preview is active by inspecting the `IsPreviewEnabled` entwine
+property.
## Preview states
-States are the site stages: _live_, _stage_ etc. Preview states are picked up from the `SilverStripeNavigator`.
-You can invoke the state change by calling:
+States are the site stages: _live_, _stage_ etc. Preview states are picked up
+from the `SilverStripeNavigator`. You can invoke the state change by calling:
```js
$('.cms-preview').entwine('.ss.preview').changeState('StageLink');
```
-Note the state names come from `SilverStripeNavigatorItems` class names - thus the _Link_ in their names. This call will
-also redraw the state selector to fit with the internal state. See `AllowedStates` in `.cms-preview` entwine for the
+Note the state names come from `SilverStripeNavigatorItems` class names - thus
+the _Link_ in their names. This call will also redraw the state selector to fit
+with the internal state. See `AllowedStates` in `.cms-preview` entwine for the
list of supported states.
You can get the current state by calling:
@@ -112,16 +121,18 @@ You can get the current state by calling:
## Preview sizes
-This selector defines how the preview iframe is rendered, and try to emulate different device sizes. The options are
-hardcoded. The option names map directly to CSS classes applied to the `.cms-preview` and are as follows:
+This selector defines how the preview iframe is rendered, and try to emulate
+different device sizes. The options are hardcoded. The option names map directly
+to CSS classes applied to the `.cms-preview` and are as follows:
* _auto_: responsive layout
* _desktop_
* _tablet_
* _mobile_
-You can switch between different types of display sizes programmatically, which has the benefit of redrawing the
-related selector and maintaining a consistent internal state:
+You can switch between different types of display sizes programmatically, which
+has the benefit of redrawing the related selector and maintaining a consistent
+internal state:
```js
$('.cms-preview').entwine('.ss.preview').changeSize('auto');
@@ -135,25 +146,27 @@ You can find out current size by calling:
## Preview modes
-Preview modes map to the modes supported by the _threeColumnCompressor_ layout algorithm, see
-[layout reference](../reference/layout) for more details. You can change modes by calling:
+Preview modes map to the modes supported by the _threeColumnCompressor_ layout
+algorithm, see [layout reference](../reference/layout) for more details. You
+can change modes by calling:
```js
$('.cms-preview').entwine('.ss.preview').changeMode('preview');
```
-Currently active mode is stored on the `.cms-container` along with related internal states of the layout. You can reach
-it by calling:
+Currently active mode is stored on the `.cms-container` along with related
+internal states of the layout. You can reach it by calling:
```js
$('.cms-container').entwine('.ss').getLayoutOptions().mode;
```
-Caveat: the `.preview-mode-selector` appears twice, once in the preview and second time in the CMS actions area as
-`#preview-mode-dropdown-in-cms`. This is done because the user should still have access to the mode selector even if
-preview is not visible. Currently CMS Actions are a separate area to the preview option selectors, even if they try
-to appear as one horizontal bar.
+Caveat: the `.preview-mode-selector` appears twice, once in the preview and
+second time in the CMS actions area as `#preview-mode-dropdown-in-cms`. This is
+done because the user should still have access to the mode selector even if
+preview is not visible. Currently CMS Actions are a separate area to the preview
+option selectors, even if they try to appear as one horizontal bar.
## Preview API
diff --git a/tests/assets/LeftAndMainTest.css b/tests/assets/LeftAndMainTest.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/assets/LeftAndMainTest.js b/tests/assets/LeftAndMainTest.js
new file mode 100644
index 000000000..e69de29bb