mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Move CMS Howtos
This commit is contained in:
parent
fe62a2fa79
commit
e729131a63
@ -0,0 +1,333 @@
|
|||||||
|
title: ModelAdmin
|
||||||
|
summary: Create admin UI's for managing your data records.
|
||||||
|
|
||||||
|
# ModelAdmin
|
||||||
|
|
||||||
|
[api:ModelAdmin] provides a simple way to utilize the SilverStripe Admin UI with your own data models. It can create
|
||||||
|
searchables list and edit views of [api:DataObject] subclasses, and even provides import and export of your data.
|
||||||
|
|
||||||
|
It uses the framework's knowledge about the model to provide sensible defaults, allowing you to get started in a couple
|
||||||
|
of lines of code, while still providing a solid base for customization.
|
||||||
|
|
||||||
|
<div class="info" markdown="1">
|
||||||
|
The interface is mainly powered by the [api:GridField] class ([documentation](../forms/fields/gridfield)), which can
|
||||||
|
also be used in other areas of your application.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Let's assume we want to manage a simple product listing as a sample data model: A product can have a name, price, and
|
||||||
|
a category.
|
||||||
|
|
||||||
|
**mysite/code/Product.php**
|
||||||
|
|
||||||
|
:::php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Product extends DataObject {
|
||||||
|
|
||||||
|
private static $db = array(
|
||||||
|
'Name' => 'Varchar',
|
||||||
|
'ProductCode' => 'Varchar',
|
||||||
|
'Price' => 'Currency'
|
||||||
|
);
|
||||||
|
|
||||||
|
private static $has_one = array(
|
||||||
|
'Category' => 'Category'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
**mysite/code/Category.php**
|
||||||
|
|
||||||
|
:::php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Category extends DataObject {
|
||||||
|
|
||||||
|
private static $db = array(
|
||||||
|
'Title' => 'Text'
|
||||||
|
);
|
||||||
|
|
||||||
|
private static $has_many = array(
|
||||||
|
'Products' => 'Product'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
To create your own `ModelAdmin`, simply extend the base class, and edit the `$managed_models` property with the list of
|
||||||
|
DataObject's you want to scaffold an interface for. The class can manage multiple models in parallel, if required.
|
||||||
|
|
||||||
|
We'll name it `MyAdmin`, but the class name can be anything you want.
|
||||||
|
|
||||||
|
**mysite/code/MyAdmin.php**
|
||||||
|
|
||||||
|
:::php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class MyAdmin extends ModelAdmin {
|
||||||
|
|
||||||
|
private static $managed_models = array(
|
||||||
|
'Product',
|
||||||
|
'Category'
|
||||||
|
);
|
||||||
|
|
||||||
|
private static $url_segment = 'products';
|
||||||
|
|
||||||
|
private static $menu_title = 'My Product Admin';
|
||||||
|
}
|
||||||
|
|
||||||
|
This will automatically add a new menu entry to the SilverStripe Admin UI entitled `My Product Admin` and logged in
|
||||||
|
users will be able to upload and manage `Product` and `Category` instances through http://yoursite.com/admin/products.
|
||||||
|
|
||||||
|
<div class="alert" markdown="1">
|
||||||
|
After defining these classes, make sure you have rebuilt your SilverStripe database and flushed your cache.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Permissions
|
||||||
|
|
||||||
|
Each new `ModelAdmin` subclass creates its' own [permission code](../security), for the example above this would be
|
||||||
|
`CMS_ACCESS_MyAdmin`. Users with access to the Admin UI will need to have this permission assigned through
|
||||||
|
`admin/security/` or have the `ADMIN` permission code in order to gain access to the controller.
|
||||||
|
|
||||||
|
<div class="notice" markdown="1">
|
||||||
|
For more information on the security and permission system see the [Security Documentation](../security)
|
||||||
|
</div>
|
||||||
|
|
||||||
|
The [api:DataObject] API has more granular permission control, which is enforced in [api:ModelAdmin] by default.
|
||||||
|
Available checks are `canEdit()`, `canCreate()`, `canView()` and `canDelete()`. Models check for administrator
|
||||||
|
permissions by default. For most cases, less restrictive checks make sense, e.g. checking for general CMS access rights.
|
||||||
|
|
||||||
|
**mysite/code/Category.php**
|
||||||
|
|
||||||
|
:::php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Category extends DataObject {
|
||||||
|
// ...
|
||||||
|
public function canView($member = null) {
|
||||||
|
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canEdit($member = null) {
|
||||||
|
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canDelete($member = null) {
|
||||||
|
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canCreate($member = null) {
|
||||||
|
return Permission::check('CMS_ACCESS_MyAdmin', 'any', $member);
|
||||||
|
}
|
||||||
|
|
||||||
|
## Searching Records
|
||||||
|
|
||||||
|
[api:ModelAdmin] uses the [SearchContext](../search/searchcontext) class to provide a search form, as well as get the
|
||||||
|
searched results. Every [api:DataObject] can have its own context, based on the fields which should be searchable. The
|
||||||
|
class makes a guess at how those fields should be searched, e.g. showing a checkbox for any boolean fields in your
|
||||||
|
`$db` definition.
|
||||||
|
|
||||||
|
To remove, add or modify searchable fields, define a new `[api:DataObject::$searchable_fields]` static on your model
|
||||||
|
class (see [SearchContext](../search/searchcontext) docs for details).
|
||||||
|
|
||||||
|
**mysite/code/Product.php**
|
||||||
|
|
||||||
|
:::php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Product extends DataObject {
|
||||||
|
|
||||||
|
private static $searchable_fields = array(
|
||||||
|
'Name',
|
||||||
|
'ProductCode'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="hint" markdown="1">
|
||||||
|
[SearchContext](../search/searchcontext) documentation has more information on providing the search functionality.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Displaying Results
|
||||||
|
|
||||||
|
The results are shown in a tabular listing, powered by the [GridField](../forms/fields/gridfield), more specifically
|
||||||
|
the [api:GridFieldDataColumns] component. This component looks for a [api:DataObject::$summary_fields] static on your
|
||||||
|
model class, where you can add or remove columns. To change the title, use [api:DataObject::$field_labels].
|
||||||
|
|
||||||
|
**mysite/code/Page.php**
|
||||||
|
|
||||||
|
:::php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Product extends DataObject {
|
||||||
|
|
||||||
|
private static $field_labels = array(
|
||||||
|
'Price' => 'Cost' // renames the column to "Cost"
|
||||||
|
);
|
||||||
|
|
||||||
|
private static $summary_fields = array(
|
||||||
|
'Name',
|
||||||
|
'Price'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
The results list are retrieved from [api:SearchContext->getResults], based on the parameters passed through the search
|
||||||
|
form. If no search parameters are given, the results will show every record. Results are a [api:DataList] instance, so
|
||||||
|
can be customized by additional SQL filters, joins.
|
||||||
|
|
||||||
|
For example, we might want to exclude all products without prices in our sample `MyAdmin` implementation.
|
||||||
|
|
||||||
|
**mysite/code/MyAdmin.php**
|
||||||
|
|
||||||
|
:::php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class MyAdmin extends ModelAdmin {
|
||||||
|
|
||||||
|
public function getList() {
|
||||||
|
$list = parent::getList();
|
||||||
|
|
||||||
|
// Always limit by model class, in case you're managing multiple
|
||||||
|
if($this->modelClass == 'Product') {
|
||||||
|
$list = $list->exclude('Price', '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
You can also customize the search behavior directly on your `ModelAdmin` instance. For example, we might want to have a
|
||||||
|
checkbox which limits search results to expensive products (over $100).
|
||||||
|
|
||||||
|
**mysite/code/MyAdmin.php**
|
||||||
|
|
||||||
|
:::php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class MyAdmin extends ModelAdmin {
|
||||||
|
|
||||||
|
public function getSearchContext() {
|
||||||
|
$context = parent::getSearchContext();
|
||||||
|
|
||||||
|
if($this->modelClass == 'Product') {
|
||||||
|
$context->getFields()->push(new CheckboxField('q[ExpensiveOnly]', 'Only expensive stuff'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getList() {
|
||||||
|
$list = parent::getList();
|
||||||
|
|
||||||
|
$params = $this->request->requestVar('q'); // use this to access search parameters
|
||||||
|
|
||||||
|
if($this->modelClass == 'Product' && isset($params['ExpensiveOnly']) && $params['ExpensiveOnly']) {
|
||||||
|
$list = $list->exclude('Price:LessThan', '100');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
To alter how the results are displayed (via `[api:GridField]`), you can also overload the `getEditForm()` method. For
|
||||||
|
example, to add a new component.
|
||||||
|
|
||||||
|
**mysite/code/MyAdmin.php**
|
||||||
|
|
||||||
|
:::php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class MyAdmin extends ModelAdmin {
|
||||||
|
|
||||||
|
private static $managed_models = array(
|
||||||
|
'Product',
|
||||||
|
'Category'
|
||||||
|
);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
public function getEditForm($id = null, $fields = null) {
|
||||||
|
$form = parent::getEditForm($id, $fields);
|
||||||
|
|
||||||
|
// $gridFieldName is generated from the ModelClass, eg if the Class 'Product'
|
||||||
|
// is managed by this ModelAdmin, the GridField for it will also be named 'Product'
|
||||||
|
|
||||||
|
$gridFieldName = $this->sanitiseClassName($this->modelClass);
|
||||||
|
$gridField = $form->Fields()->fieldByName($gridFieldName);
|
||||||
|
|
||||||
|
// modify the list view.
|
||||||
|
$gridField->getConfig()->addComponent(new GridFieldFilterHeader());
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
The above example will add the component to all `GridField`s (of all managed models). Alternatively we can also add it
|
||||||
|
to only one specific `GridField`:
|
||||||
|
|
||||||
|
**mysite/code/MyAdmin.php**
|
||||||
|
|
||||||
|
:::php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class MyAdmin extends ModelAdmin {
|
||||||
|
|
||||||
|
private static $managed_models = array(
|
||||||
|
'Product',
|
||||||
|
'Category'
|
||||||
|
);
|
||||||
|
|
||||||
|
public function getEditForm($id = null, $fields = null) {
|
||||||
|
$form = parent::getEditForm($id, $fields);
|
||||||
|
|
||||||
|
$gridFieldName = 'Product';
|
||||||
|
$gridField = $form->Fields()->fieldByName($gridFieldName);
|
||||||
|
|
||||||
|
if ($gridField) {
|
||||||
|
$gridField->getConfig()->addComponent(new GridFieldFilterHeader());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
## Data Import
|
||||||
|
|
||||||
|
The `ModelAdmin` class provides import of CSV files through the [api:CsvBulkLoader] API. which has support for column
|
||||||
|
mapping, updating existing records, and identifying relationships - so its a powerful tool to get your data into a
|
||||||
|
SilverStripe database.
|
||||||
|
|
||||||
|
By default, each model management interface allows uploading a CSV file with all columns auto detected. To override
|
||||||
|
with a more specific importer implementation, use the [api:ModelAdmin::$model_importers] static.
|
||||||
|
|
||||||
|
## Data Export
|
||||||
|
|
||||||
|
Export is available as a CSV format through a button at the end of a results list. You can also export search results.
|
||||||
|
This is handled through the [api:GridFieldExportButton] component.
|
||||||
|
|
||||||
|
To customize the exported columns, create a new method called `getExportFields` in your `ModelAdmin`:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class MyAdmin extends ModelAdmin {
|
||||||
|
// ...
|
||||||
|
|
||||||
|
public function getExportFields() {
|
||||||
|
return array(
|
||||||
|
'Name' => 'Name',
|
||||||
|
'ProductCode' => 'Product Code',
|
||||||
|
'Category.Title' => 'Category'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
* [GridField](../forms/fields/gridfield)
|
||||||
|
* [Permissions](../security/permissions)
|
||||||
|
* [SeachContext](../search/seachcontext)
|
||||||
|
|
||||||
|
## API Documentation
|
||||||
|
|
||||||
|
* [api:ModelAdmin]
|
||||||
|
* [api:LeftAndMain]
|
||||||
|
* [api:GridField]
|
||||||
|
* [api:DataList]
|
||||||
|
* [api:CsvBulkLoader]
|
@ -1,6 +1,6 @@
|
|||||||
# CMS layout
|
title: Admin Layout
|
||||||
|
|
||||||
## Overview
|
# CMS layout
|
||||||
|
|
||||||
The CMS markup is structured into "panels", which are the base units containing interface components (or other panels),
|
The CMS markup is structured into "panels", which are the base units containing interface components (or other panels),
|
||||||
as declared by the class `cms-panel`. Panels can be made collapsible, and get the ability to be resized and aligned with
|
as declared by the class `cms-panel`. Panels can be made collapsible, and get the ability to be resized and aligned with
|
||||||
@ -8,7 +8,7 @@ a layout manager, in our case [jLayout](http://www.bramstein.com/projects/jlayou
|
|||||||
declarations (mostly dimensions and positioning) via JavaScript.
|
declarations (mostly dimensions and positioning) via JavaScript.
|
||||||
|
|
||||||
We've established a convention for a `redraw` method on each panel and UI component that need to update their content as
|
We've established a convention for a `redraw` method on each panel and UI component that need to update their content as
|
||||||
a result of changes to their position, size or visibility. This method would usually be invoked by the parent container.
|
a result of changes to their position, size or visibility. This method would usually be invoked by the parent container.
|
||||||
|
|
||||||
The layout manager does not dynamically track changes to panel sizes - we have to trigger laying out manually each time
|
The layout manager does not dynamically track changes to panel sizes - we have to trigger laying out manually each time
|
||||||
we need an update to happen (for example from `window::onresize` event, or panel toggling). It then cascades through the
|
we need an update to happen (for example from `window::onresize` event, or panel toggling). It then cascades through the
|
||||||
@ -21,7 +21,7 @@ The easiest way to update the layout of the CMS is to call `redraw` on the top-l
|
|||||||
|
|
||||||
This causes the framework to:
|
This causes the framework to:
|
||||||
|
|
||||||
* reset the _threeColumnCompressor_ algorithm with the current layout options (that can be set via
|
* reset the _threeColumnCompressor_ algorithm with the current layout options (that can be set via
|
||||||
`updateLayoutOptions`)
|
`updateLayoutOptions`)
|
||||||
* trigger `layout` which cascades into all children resizing and positioning subordinate elements (this is internal
|
* trigger `layout` which cascades into all children resizing and positioning subordinate elements (this is internal
|
||||||
to the layout manager)
|
to the layout manager)
|
||||||
@ -56,9 +56,9 @@ Layout manager will automatically apply algorithms to the children of `.cms-cont
|
|||||||
`data-layout-type` attribute. Let's take the content toolbar as an example of a second-level layout application:
|
`data-layout-type` attribute. Let's take the content toolbar as an example of a second-level layout application:
|
||||||
|
|
||||||
:::html
|
:::html
|
||||||
<div class="cms-content-tools west cms-panel cms-panel-layout"
|
<div class="cms-content-tools west cms-panel cms-panel-layout"
|
||||||
data-expandOnClick="true"
|
data-expandOnClick="true"
|
||||||
data-layout-type="border"
|
data-layout-type="border"
|
||||||
id="cms-content-tools-CMSMain">
|
id="cms-content-tools-CMSMain">
|
||||||
<%-- content utilising border's north, south, east, west and center classes --%>
|
<%-- content utilising border's north, south, east, west and center classes --%>
|
||||||
</div>
|
</div>
|
||||||
@ -67,15 +67,15 @@ For detailed discussion on available algorithms refer to
|
|||||||
[jLayout algorithms](https://github.com/bramstein/jlayout#layout-algorithms).
|
[jLayout algorithms](https://github.com/bramstein/jlayout#layout-algorithms).
|
||||||
|
|
||||||
Our [Howto: Extend the CMS Interface](../howto/extend-cms-interface) has a practical example on how to add a bottom
|
Our [Howto: Extend the CMS Interface](../howto/extend-cms-interface) has a practical example on how to add a bottom
|
||||||
panel to the CMS UI.
|
panel to the CMS UI.
|
||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
|
|
||||||
The following methods are available as an interface to underlying _threeColumnCompressor_ algorithm on the
|
The following methods are available as an interface to underlying _threeColumnCompressor_ algorithm on the
|
||||||
`.cms-container` entwine:
|
`.cms-container` entwine:
|
||||||
|
|
||||||
* **getLayoutOptions**: get currently used _threeColumnCompressor_ options.
|
* **getLayoutOptions**: get currently used _threeColumnCompressor_ options.
|
||||||
* **updateLayoutOptions**: change specified options and trigger the laying out:
|
* **updateLayoutOptions**: change specified options and trigger the laying out:
|
||||||
`$('.cms-container').updateLayoutOptions({mode: 'split'});`
|
`$('.cms-container').updateLayoutOptions({mode: 'split'});`
|
||||||
* **splitViewMode**: enable side by side editing.
|
* **splitViewMode**: enable side by side editing.
|
||||||
* **contentViewMode**: only menu and content areas are shown.
|
* **contentViewMode**: only menu and content areas are shown.
|
||||||
@ -91,7 +91,7 @@ You might have noticed that the top-level `.cms-container` has the `data-layout-
|
|||||||
_threeColumnCompressor_ algorithm for the layout of the menu, content and preview columns of the CMS. The annotated code
|
_threeColumnCompressor_ algorithm for the layout of the menu, content and preview columns of the CMS. The annotated code
|
||||||
for this algorithm can be found in `LeftAndMain.Layout.js`.
|
for this algorithm can be found in `LeftAndMain.Layout.js`.
|
||||||
|
|
||||||
Since the layout-type for the element is set to `custom` and will be ignored by the layout manager, we apply the
|
Since the layout-type for the element is set to `custom` and will be ignored by the layout manager, we apply the
|
||||||
_threeColumnCompressor_ explicitly `LeftAndMain::redraw`. This way we also get a chance to provide options expected
|
_threeColumnCompressor_ explicitly `LeftAndMain::redraw`. This way we also get a chance to provide options expected
|
||||||
by the algorithm that are initially taken from the `LeftAndMain::LayoutOptions` entwine variable.
|
by the algorithm that are initially taken from the `LeftAndMain::LayoutOptions` entwine variable.
|
||||||
|
|
@ -2,41 +2,41 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
With the addition of side-by-side editing, the preview has the ability to appear
|
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.
|
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
|
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
|
is saved, and relevant pages will be loaded for editing when the user navigates
|
||||||
around in the preview.
|
around in the preview.
|
||||||
|
|
||||||
The root element for preview is `.cms-preview` which maintains the internal
|
The root element for preview is `.cms-preview` which maintains the internal
|
||||||
states necessary for rendering within the entwine properties. It provides
|
states necessary for rendering within the entwine properties. It provides
|
||||||
function calls for transitioning between these states and has the ability to
|
function calls for transitioning between these states and has the ability to
|
||||||
update the appearance of the option selectors.
|
update the appearance of the option selectors.
|
||||||
|
|
||||||
In terms of backend support, it relies on `SilverStripeNavigator` to be rendered
|
In terms of backend support, it relies on `SilverStripeNavigator` to be rendered
|
||||||
into the `.cms-edit-form`. _LeftAndMain_ will automatically take care of
|
into the `.cms-edit-form`. _LeftAndMain_ will automatically take care of
|
||||||
generating it as long as the `*_SilverStripeNavigator` template is found -
|
generating it as long as the `*_SilverStripeNavigator` template is found -
|
||||||
first segment has to match current _LeftAndMain_-derived class (e.g.
|
first segment has to match current _LeftAndMain_-derived class (e.g.
|
||||||
`LeftAndMain_SilverStripeNavigator`).
|
`LeftAndMain_SilverStripeNavigator`).
|
||||||
|
|
||||||
We use `ss.preview` entwine namespace for all preview-related entwines.
|
We use `ss.preview` entwine namespace for all preview-related entwines.
|
||||||
|
|
||||||
<div class="notice" markdown='1'>
|
<div class="notice" markdown='1'>
|
||||||
Caveat: `SilverStripeNavigator` and `CMSPreviewable` interface currently only
|
Caveat: `SilverStripeNavigator` and `CMSPreviewable` interface currently only
|
||||||
support SiteTree objects that are _Versioned_. They are not general enough for
|
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
|
using on any other DataObject. That pretty much limits the extendability of the
|
||||||
feature.
|
feature.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Configuration and Defaults
|
## Configuration and Defaults
|
||||||
|
|
||||||
Like most of the CMS, the preview UI is powered by
|
Like most of the CMS, the preview UI is powered by
|
||||||
[jQuery entwine](https://github.com/hafriedlander/jquery.entwine). This means
|
[jQuery entwine](https://github.com/hafriedlander/jquery.entwine). This means
|
||||||
its defaults are configured through JavaScript, by setting entwine properties.
|
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 order to achieve this, create a new file `mysite/javascript/MyLeftAndMain.Preview.js`.
|
||||||
|
|
||||||
In the following example we configure three aspects:
|
In the following example we configure three aspects:
|
||||||
|
|
||||||
* Set the default mode from "split view" to a full "edit view"
|
* Set the default mode from "split view" to a full "edit view"
|
||||||
* Make a wider mobile preview
|
* Make a wider mobile preview
|
||||||
* Increase minimum space required by preview before auto-hiding
|
* Increase minimum space required by preview before auto-hiding
|
||||||
@ -83,32 +83,32 @@ To understand how layouts are handled in the CMS UI, have a look at the
|
|||||||
|
|
||||||
## Enabling preview
|
## Enabling preview
|
||||||
|
|
||||||
The frontend decides on the preview being enabled or disabled based on the
|
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
|
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.
|
will remain hidden, and the layout will stay in the _content_ mode.
|
||||||
|
|
||||||
If the class is found, frontend looks for the `SilverStripeNavigator` structure
|
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.
|
and moves it to the `.cms-preview-control` panel at the bottom of the preview.
|
||||||
This structure supplies preview options such as state selector.
|
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
|
If the navigator is not found, the preview appears in the GUI, but is shown as
|
||||||
"blocked" - i.e. displaying the "preview unavailable" overlay.
|
"blocked" - i.e. displaying the "preview unavailable" overlay.
|
||||||
|
|
||||||
The preview can be affected by calling `enablePreview` and `disablePreview`. You
|
The preview can be affected by calling `enablePreview` and `disablePreview`. You
|
||||||
can check if the preview is active by inspecting the `IsPreviewEnabled` entwine
|
can check if the preview is active by inspecting the `IsPreviewEnabled` entwine
|
||||||
property.
|
property.
|
||||||
|
|
||||||
## Preview states
|
## Preview states
|
||||||
|
|
||||||
States are the site stages: _live_, _stage_ etc. Preview states are picked up
|
States are the site stages: _live_, _stage_ etc. Preview states are picked up
|
||||||
from the `SilverStripeNavigator`. You can invoke the state change by calling:
|
from the `SilverStripeNavigator`. You can invoke the state change by calling:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
$('.cms-preview').entwine('.ss.preview').changeState('StageLink');
|
$('.cms-preview').entwine('.ss.preview').changeState('StageLink');
|
||||||
```
|
```
|
||||||
|
|
||||||
Note the state names come from `SilverStripeNavigatorItems` class names - thus
|
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
|
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
|
with the internal state. See `AllowedStates` in `.cms-preview` entwine for the
|
||||||
list of supported states.
|
list of supported states.
|
||||||
|
|
||||||
@ -120,8 +120,8 @@ You can get the current state by calling:
|
|||||||
|
|
||||||
## Preview sizes
|
## Preview sizes
|
||||||
|
|
||||||
This selector defines how the preview iframe is rendered, and try to emulate
|
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
|
different device sizes. The options are hardcoded. The option names map directly
|
||||||
to CSS classes applied to the `.cms-preview` and are as follows:
|
to CSS classes applied to the `.cms-preview` and are as follows:
|
||||||
|
|
||||||
* _auto_: responsive layout
|
* _auto_: responsive layout
|
||||||
@ -129,8 +129,8 @@ to CSS classes applied to the `.cms-preview` and are as follows:
|
|||||||
* _tablet_
|
* _tablet_
|
||||||
* _mobile_
|
* _mobile_
|
||||||
|
|
||||||
You can switch between different types of display sizes programmatically, which
|
You can switch between different types of display sizes programmatically, which
|
||||||
has the benefit of redrawing the related selector and maintaining a consistent
|
has the benefit of redrawing the related selector and maintaining a consistent
|
||||||
internal state:
|
internal state:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@ -145,15 +145,15 @@ You can find out current size by calling:
|
|||||||
|
|
||||||
## Preview modes
|
## Preview modes
|
||||||
|
|
||||||
Preview modes map to the modes supported by the _threeColumnCompressor_ layout
|
Preview modes map to the modes supported by the _threeColumnCompressor_ layout
|
||||||
algorithm, see [layout reference](../reference/layout) for more details. You
|
algorithm, see [layout reference](../reference/layout) for more details. You
|
||||||
can change modes by calling:
|
can change modes by calling:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
$('.cms-preview').entwine('.ss.preview').changeMode('preview');
|
$('.cms-preview').entwine('.ss.preview').changeMode('preview');
|
||||||
```
|
```
|
||||||
|
|
||||||
Currently active mode is stored on the `.cms-container` along with related
|
Currently active mode is stored on the `.cms-container` along with related
|
||||||
internal states of the layout. You can reach it by calling:
|
internal states of the layout. You can reach it by calling:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@ -161,10 +161,10 @@ internal states of the layout. You can reach it by calling:
|
|||||||
```
|
```
|
||||||
|
|
||||||
<div class="notice" markdown='1'>
|
<div class="notice" markdown='1'>
|
||||||
Caveat: the `.preview-mode-selector` appears twice, once in the preview and
|
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
|
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
|
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
|
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.
|
option selectors, even if they try to appear as one horizontal bar.
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
A lot can be achieved in SilverStripe by adding properties and form fields
|
A lot can be achieved in SilverStripe by adding properties and form fields
|
||||||
to your own page types (via `[api:SiteTree->getCMSFields()]`), as well as creating
|
to your own page types (via `[api:SiteTree->getCMSFields()]`), as well as creating
|
||||||
your own data management interfaces through `[api:ModelAdmin]`. But sometimes
|
your own data management interfaces through `[api:ModelAdmin]`. But sometimes
|
||||||
you'll want to go deeper and tailor the underlying interface to your needs as well.
|
you'll want to go deeper and tailor the underlying interface to your needs as well.
|
||||||
@ -22,7 +22,7 @@ While SilverStripe is intended to work with JavaScript only,
|
|||||||
we're following the principles of "[Progressive Enhancement](http://en.wikipedia.org/wiki/Progressive_enhancement)"
|
we're following the principles of "[Progressive Enhancement](http://en.wikipedia.org/wiki/Progressive_enhancement)"
|
||||||
where feasible, relying on a comparatively light layer of JavaScript to enhance
|
where feasible, relying on a comparatively light layer of JavaScript to enhance
|
||||||
forms and markup generated on the server. This allows seamless customization of
|
forms and markup generated on the server. This allows seamless customization of
|
||||||
aspects like form fields. We're explaining this philosophy in more detail
|
aspects like form fields. We're explaining this philosophy in more detail
|
||||||
on our [blog](http://www.silverstripe.org/the-3-0-ui-a-better-framework-for-your-ideas/)).
|
on our [blog](http://www.silverstripe.org/the-3-0-ui-a-better-framework-for-your-ideas/)).
|
||||||
|
|
||||||
All CSS in the CMS UI is written in the [SCSS language extensions](http://sass-lang.com/)
|
All CSS in the CMS UI is written in the [SCSS language extensions](http://sass-lang.com/)
|
||||||
@ -30,7 +30,7 @@ and the [Compass framework](http://compass-style.org/), which helps
|
|||||||
us maintain expressive and concise style declarations. The files are located in `framework/admin/scss`
|
us maintain expressive and concise style declarations. The files are located in `framework/admin/scss`
|
||||||
(and if you have the `cms` module installed, in `cms/scss`), and are compiled to a `css` folder on the
|
(and if you have the `cms` module installed, in `cms/scss`), and are compiled to a `css` folder on the
|
||||||
same directory path. Changes to the SCSS files can be automatically converted by installing
|
same directory path. Changes to the SCSS files can be automatically converted by installing
|
||||||
the ["compass" module](https://github.com/silverstripe-labs/silverstripe-compass) for SilverStripe,
|
the ["compass" module](https://github.com/silverstripe-labs/silverstripe-compass) for SilverStripe,
|
||||||
although [installing the compass framework](http://compass-style.org/install/) directly works as well.
|
although [installing the compass framework](http://compass-style.org/install/) directly works as well.
|
||||||
Each file describes its purpose at the top of the declarations. Note that you can write
|
Each file describes its purpose at the top of the declarations. Note that you can write
|
||||||
plain CSS without SCSS for your custom CMS interfaces as well, we just mandate SCSS for core usage.
|
plain CSS without SCSS for your custom CMS interfaces as well, we just mandate SCSS for core usage.
|
||||||
@ -44,7 +44,7 @@ As there's a whole lot of CSS driving the CMS, we have certain best practives ar
|
|||||||
(which might change later on). A more structural name could be `cms-menu` (or `cms-tools-menu` for a more specific version)
|
(which might change later on). A more structural name could be `cms-menu` (or `cms-tools-menu` for a more specific version)
|
||||||
* Class naming: Use the `cms-` class prefix for major components in the cms interface,
|
* Class naming: Use the `cms-` class prefix for major components in the cms interface,
|
||||||
and the `ss-ui-` prefix for extensions to jQuery UI. Don't use the `ui-` class prefix, its reserved for jQuery UI built-in styles.
|
and the `ss-ui-` prefix for extensions to jQuery UI. Don't use the `ui-` class prefix, its reserved for jQuery UI built-in styles.
|
||||||
* Use jQuery UI's built-in styles where possible, e.g. `ui-widget` for a generic container, or `ui-state-highlight`
|
* Use jQuery UI's built-in styles where possible, e.g. `ui-widget` for a generic container, or `ui-state-highlight`
|
||||||
to highlight a specific component. See the [jQuery UI Theming API](http://jqueryui.com/docs/Theming/API) for a full list.
|
to highlight a specific component. See the [jQuery UI Theming API](http://jqueryui.com/docs/Theming/API) for a full list.
|
||||||
|
|
||||||
See our [system requirements](../installation/server-requirements) for a list of supported browsers.
|
See our [system requirements](../installation/server-requirements) for a list of supported browsers.
|
||||||
@ -67,7 +67,7 @@ We can use this to create a different base template with `LeftAndMain.ss`
|
|||||||
(which corresponds to the `LeftAndMain` PHP controller class).
|
(which corresponds to the `LeftAndMain` PHP controller class).
|
||||||
In case you want to retain the main CMS structure (which is recommended),
|
In case you want to retain the main CMS structure (which is recommended),
|
||||||
just create your own "Content" template (e.g. `MyCMSController_Content.ss`),
|
just create your own "Content" template (e.g. `MyCMSController_Content.ss`),
|
||||||
which is in charge of rendering the main content area apart from the CMS menu.
|
which is in charge of rendering the main content area apart from the CMS menu.
|
||||||
|
|
||||||
Depending on the complexity of your layout, you'll also need to overload the
|
Depending on the complexity of your layout, you'll also need to overload the
|
||||||
"EditForm" template (e.g. `MyCMSController_EditForm.ss`), e.g. to implement
|
"EditForm" template (e.g. `MyCMSController_EditForm.ss`), e.g. to implement
|
||||||
@ -77,9 +77,9 @@ This requires manual assignment of the template to your form instance, see `[api
|
|||||||
Often its useful to have a "tools" panel in between the menu and your content,
|
Often its useful to have a "tools" panel in between the menu and your content,
|
||||||
usually occupied by a search form or navigational helper.
|
usually occupied by a search form or navigational helper.
|
||||||
In this case, you can either overload the full base template as described above.
|
In this case, you can either overload the full base template as described above.
|
||||||
To avoid duplicating all this template code, you can also use the special `[api:LeftAndMain->Tools()]` and
|
To avoid duplicating all this template code, you can also use the special `[api:LeftAndMain->Tools()]` and
|
||||||
`[api:LeftAndMain->EditFormTools()]` methods available in `LeftAndMain`.
|
`[api:LeftAndMain->EditFormTools()]` methods available in `LeftAndMain`.
|
||||||
These placeholders are populated by auto-detected templates,
|
These placeholders are populated by auto-detected templates,
|
||||||
with the naming convention of "<controller classname>_Tools.ss" and "<controller classname>_EditFormTools.ss".
|
with the naming convention of "<controller classname>_Tools.ss" and "<controller classname>_EditFormTools.ss".
|
||||||
So to add or "subclass" a tools panel, simply create this file and it's automatically picked up.
|
So to add or "subclass" a tools panel, simply create this file and it's automatically picked up.
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ e.g. after saving a record (which requires a form refresh), or switching the sec
|
|||||||
Depending on where in the DOM hierarchy you want to use a form,
|
Depending on where in the DOM hierarchy you want to use a form,
|
||||||
custom templates and additional CSS classes might be required for correct operation.
|
custom templates and additional CSS classes might be required for correct operation.
|
||||||
For example, the "EditForm" has specific view and logic JavaScript behaviour
|
For example, the "EditForm" has specific view and logic JavaScript behaviour
|
||||||
which can be enabled via adding the "cms-edit-form" class.
|
which can be enabled via adding the "cms-edit-form" class.
|
||||||
In order to set the correct layout classes, we also need a custom template.
|
In order to set the correct layout classes, we also need a custom template.
|
||||||
To obey the inheritance chain, we use `$this->getTemplatesWithSuffix('_EditForm')` for
|
To obey the inheritance chain, we use `$this->getTemplatesWithSuffix('_EditForm')` for
|
||||||
selecting the most specific template (so `MyAdmin_EditForm.ss`, if it exists).
|
selecting the most specific template (so `MyAdmin_EditForm.ss`, if it exists).
|
||||||
@ -115,7 +115,7 @@ Basic example form in a CMS controller subclass:
|
|||||||
class MyAdmin extends LeftAndMain {
|
class MyAdmin extends LeftAndMain {
|
||||||
function getEditForm() {
|
function getEditForm() {
|
||||||
return CMSForm::create(
|
return CMSForm::create(
|
||||||
$this,
|
$this,
|
||||||
'EditForm',
|
'EditForm',
|
||||||
new FieldSet(
|
new FieldSet(
|
||||||
TabSet::create(
|
TabSet::create(
|
||||||
@ -139,14 +139,14 @@ Basic example form in a CMS controller subclass:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Note: Usually you don't need to worry about these settings,
|
Note: Usually you don't need to worry about these settings,
|
||||||
and will simply call `parent::getEditForm()` to modify an existing,
|
and will simply call `parent::getEditForm()` to modify an existing,
|
||||||
correctly configured form.
|
correctly configured form.
|
||||||
|
|
||||||
## JavaScript through jQuery.entwine
|
## JavaScript through jQuery.entwine
|
||||||
|
|
||||||
[jQuery.entwine](https://github.com/hafriedlander/jquery.entwine) is a thirdparty library
|
[jQuery.entwine](https://github.com/hafriedlander/jquery.entwine) is a thirdparty library
|
||||||
which allows us to attach behaviour to DOM elements in a flexible and structured mannger.
|
which allows us to attach behaviour to DOM elements in a flexible and structured mannger.
|
||||||
It replaces the `behaviour.js` library used in previous versions of the CMS interface.
|
It replaces the `behaviour.js` library used in previous versions of the CMS interface.
|
||||||
See [Topics: JavaScript](../topics/javascript) for more information on how to use it.
|
See [Topics: JavaScript](../topics/javascript) for more information on how to use it.
|
||||||
In the CMS interface, all entwine rules should be placed in the "ss" entwine namespace.
|
In the CMS interface, all entwine rules should be placed in the "ss" entwine namespace.
|
||||||
@ -154,13 +154,13 @@ If you want to call methods defined within these rules outside of entwine logic,
|
|||||||
you have to use this namespace, e.g. `$('.cms-menu').entwine('ss').collapse()`.
|
you have to use this namespace, e.g. `$('.cms-menu').entwine('ss').collapse()`.
|
||||||
|
|
||||||
Note that only functionality that is custom to the CMS application needs to be built
|
Note that only functionality that is custom to the CMS application needs to be built
|
||||||
in jQuery.entwine, we're trying to reuse library code wherever possible.
|
in jQuery.entwine, we're trying to reuse library code wherever possible.
|
||||||
The most prominent example of this is the usage of [jQuery UI](http://jqueryui.com) for
|
The most prominent example of this is the usage of [jQuery UI](http://jqueryui.com) for
|
||||||
dialogs and buttons.
|
dialogs and buttons.
|
||||||
|
|
||||||
The CMS includes the jQuery.entwine inspector. Press Ctrl+` ("backtick") to bring down the inspector.
|
The CMS includes the jQuery.entwine inspector. Press Ctrl+` ("backtick") to bring down the inspector.
|
||||||
You can then click on any element in the CMS to see which entwine methods are bound to
|
You can then click on any element in the CMS to see which entwine methods are bound to
|
||||||
any particular element.
|
any particular element.
|
||||||
|
|
||||||
## JavaScript and CSS dependencies via Requirements and Ajax
|
## JavaScript and CSS dependencies via Requirements and Ajax
|
||||||
|
|
||||||
@ -181,23 +181,23 @@ so don't place a rule applying to all form buttons inside `ModelAdmin.js`.
|
|||||||
|
|
||||||
The CMS relies heavily on Ajax-loading of interfaces, so each interface and the JavaScript
|
The CMS relies heavily on Ajax-loading of interfaces, so each interface and the JavaScript
|
||||||
driving it have to assume its underlying DOM structure is appended via an Ajax callback
|
driving it have to assume its underlying DOM structure is appended via an Ajax callback
|
||||||
rather than being available when the browser window first loads.
|
rather than being available when the browser window first loads.
|
||||||
jQuery.entwine is effectively an advanced version of [jQuery.live](http://api.jquery.com/live/)
|
jQuery.entwine is effectively an advanced version of [jQuery.live](http://api.jquery.com/live/)
|
||||||
and [jQuery.delegate](http://api.jquery.com/delegate/), so takes care of dynamic event binding.
|
and [jQuery.delegate](http://api.jquery.com/delegate/), so takes care of dynamic event binding.
|
||||||
|
|
||||||
Most interfaces will require their own JavaScript and CSS files, so the Ajax loading has
|
Most interfaces will require their own JavaScript and CSS files, so the Ajax loading has
|
||||||
to ensure they're loaded unless already present. A custom-built library called
|
to ensure they're loaded unless already present. A custom-built library called
|
||||||
`jQuery.ondemand` (located in `framework/thirdparty`) takes care of this transparently -
|
`jQuery.ondemand` (located in `framework/thirdparty`) takes care of this transparently -
|
||||||
so as a developer just declare your dependencies through the `[api:Requirements]` API.
|
so as a developer just declare your dependencies through the `[api:Requirements]` API.
|
||||||
|
|
||||||
## Ajax Loading and Browser History
|
## Ajax Loading and Browser History
|
||||||
|
|
||||||
SilverStripe uses the HTML5 browser history to modify the URL without a complete window refresh,
|
SilverStripe uses the HTML5 browser history to modify the URL without a complete window refresh,
|
||||||
and load its UI via Ajax by hooking into browser navigation events (through the
|
and load its UI via Ajax by hooking into browser navigation events (through the
|
||||||
[history.js](https://github.com/balupton/History.js/) wrapper library).
|
[history.js](https://github.com/balupton/History.js/) wrapper library).
|
||||||
This technique has an impact on how any Ajax load needs to happen:
|
This technique has an impact on how any Ajax load needs to happen:
|
||||||
In order to support browser history (and change the URL state),
|
In order to support browser history (and change the URL state),
|
||||||
a CMS developer needs to fire a navigation event rather than invoking the Ajax call directly.
|
a CMS developer needs to fire a navigation event rather than invoking the Ajax call directly.
|
||||||
|
|
||||||
The main point of contact here is `$('.cms-container').loadPanel(<url>, <title>, <data>)`
|
The main point of contact here is `$('.cms-container').loadPanel(<url>, <title>, <data>)`
|
||||||
in `LeftAndMain.js`. The `data` object can contain additional state which is required
|
in `LeftAndMain.js`. The `data` object can contain additional state which is required
|
||||||
@ -352,7 +352,7 @@ Note: To avoid double processing, the first response body is usually empty.
|
|||||||
|
|
||||||
By loading mostly HTML responses, we don't have an easy way to communicate
|
By loading mostly HTML responses, we don't have an easy way to communicate
|
||||||
information which can't be directly contained in the produced HTML.
|
information which can't be directly contained in the produced HTML.
|
||||||
For example, the currently used controller class might've changed due to a "redirect",
|
For example, the currently used controller class might've changed due to a "redirect",
|
||||||
which affects the currently active menu entry. We're using HTTP response headers to contain this data
|
which affects the currently active menu entry. We're using HTTP response headers to contain this data
|
||||||
without affecting the response body.
|
without affecting the response body.
|
||||||
|
|
||||||
@ -388,7 +388,7 @@ SilverStripe automatically applies a [jQuery UI button style](http://jqueryui.co
|
|||||||
to all elements with the class `.ss-ui-button`. We've extended the jQuery UI widget a bit
|
to all elements with the class `.ss-ui-button`. We've extended the jQuery UI widget a bit
|
||||||
to support defining icons via HTML5 data attributes (see `ssui.core.js`).
|
to support defining icons via HTML5 data attributes (see `ssui.core.js`).
|
||||||
These icon identifiers relate to icon files in `framework/admin/images/btn-icons`,
|
These icon identifiers relate to icon files in `framework/admin/images/btn-icons`,
|
||||||
and are sprited into a single file through SCSS and the Compass framework
|
and are sprited into a single file through SCSS and the Compass framework
|
||||||
(see [tutorial](http://compass-style.org/help/tutorials/spriting/)).
|
(see [tutorial](http://compass-style.org/help/tutorials/spriting/)).
|
||||||
Compass also creates the correct CSS classes to show those sprites via background images
|
Compass also creates the correct CSS classes to show those sprites via background images
|
||||||
(see `framework/admin/scss/_sprites.scss`).
|
(see `framework/admin/scss/_sprites.scss`).
|
||||||
@ -435,7 +435,7 @@ by the [jstree](http://jstree.com) library. It is configured through
|
|||||||
HTML5 metadata generated on its container (see the `data-hints` attribute).
|
HTML5 metadata generated on its container (see the `data-hints` attribute).
|
||||||
For more information, see the [Howto: Customize the CMS tree](../howto/customize-cms-tree).
|
For more information, see the [Howto: Customize the CMS tree](../howto/customize-cms-tree).
|
||||||
|
|
||||||
Note that a similar tree logic is also used for the
|
Note that a similar tree logic is also used for the
|
||||||
form fields to select one or more entries from those hierarchies
|
form fields to select one or more entries from those hierarchies
|
||||||
(`[api:TreeDropdownField]` and `[api:TreeMultiselectField]`).
|
(`[api:TreeDropdownField]` and `[api:TreeMultiselectField]`).
|
||||||
|
|
||||||
@ -487,7 +487,7 @@ Form template with custom tab navigation (trimmed down):
|
|||||||
<% loop Fields %>$FieldHolder<% end_loop %>
|
<% loop Fields %>$FieldHolder<% end_loop %>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
Tabset template without tab navigation (e.g. `CMSTabset.ss`)
|
Tabset template without tab navigation (e.g. `CMSTabset.ss`)
|
@ -0,0 +1,28 @@
|
|||||||
|
title: WYSIWYG Styles
|
||||||
|
summary: Add custom CSS properties to the rich-text editor.
|
||||||
|
|
||||||
|
# WYSIWYG Styles
|
||||||
|
|
||||||
|
SilverStripe lets you customize the style of content in the CMS. This is done by setting up a CSS file called
|
||||||
|
`editor.css` in either your theme or in your `mysite` folder. This is set through
|
||||||
|
|
||||||
|
:::php
|
||||||
|
HtmlEditorConfig::get('cms')->setOption('ContentCSS', project() . '/css/editor.css');
|
||||||
|
|
||||||
|
Will load the `mysite/css/editor.css` file.
|
||||||
|
|
||||||
|
Any CSS classes within this file will be automatically added to the `WYSIWYG` editors 'style' dropdown. For instance, to
|
||||||
|
add the color 'red' as an option within the `WYSIWYG` add the following to the `editor.css`
|
||||||
|
|
||||||
|
:::css
|
||||||
|
.red {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="notice" markdown="1">
|
||||||
|
After you have defined the `editor.css` make sure you clear your SilverStripe cache for it to take effect.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## API Documentation
|
||||||
|
|
||||||
|
* [api:HtmlEditorConfig]
|
@ -23,7 +23,7 @@ jQuery allows you to write complex behavior in a couple of lines of JavaScript.
|
|||||||
be reused can be custom code without further encapsulation. For example, a button rollover effect doesn't require a full
|
be reused can be custom code without further encapsulation. For example, a button rollover effect doesn't require a full
|
||||||
plugin. See "[How jQuery Works](http://docs.jquery.com/How_jQuery_Works)" for a good introduction.
|
plugin. See "[How jQuery Works](http://docs.jquery.com/How_jQuery_Works)" for a good introduction.
|
||||||
|
|
||||||
You should write all your custom jQuery code in a closure.
|
You should write all your custom jQuery code in a closure.
|
||||||
|
|
||||||
:::javascript
|
:::javascript
|
||||||
(function($) {
|
(function($) {
|
||||||
@ -34,7 +34,7 @@ You should write all your custom jQuery code in a closure.
|
|||||||
|
|
||||||
## jQuery Plugins
|
## jQuery Plugins
|
||||||
|
|
||||||
A jQuery Plugin is essentially a method call which can act on a collection of DOM elements. It is contained within the
|
A jQuery Plugin is essentially a method call which can act on a collection of DOM elements. It is contained within the
|
||||||
`jQuery.fn` namespace, and attaches itself automatically to all jQuery collections. The basics for are outlined in the
|
`jQuery.fn` namespace, and attaches itself automatically to all jQuery collections. The basics for are outlined in the
|
||||||
official [jQuery Plugin Authoring](http://docs.jquery.com/Plugins/Authoring) documentation.
|
official [jQuery Plugin Authoring](http://docs.jquery.com/Plugins/Authoring) documentation.
|
||||||
|
|
@ -59,7 +59,7 @@ Here we initialise the button based on the backend check, and assume that the bu
|
|||||||
}
|
}
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
## Frontend support ##
|
## Frontend support ##
|
||||||
|
|
||||||
As with the *Save* and *Save & publish* buttons, you might want to add some scripted reactions to user actions on the
|
As with the *Save* and *Save & publish* buttons, you might want to add some scripted reactions to user actions on the
|
||||||
@ -116,7 +116,7 @@ extras.
|
|||||||
|
|
||||||
Continuing our example let's add a "constructive" style to our *Clean-up* button. First you need to be able to add
|
Continuing our example let's add a "constructive" style to our *Clean-up* button. First you need to be able to add
|
||||||
custom JS code into the CMS. You can do this by adding a new source file, here
|
custom JS code into the CMS. You can do this by adding a new source file, here
|
||||||
`mysite/javascript/CMSMain.CustomActionsExtension.js`, and requiring it
|
`mysite/javascript/CMSMain.CustomActionsExtension.js`, and requiring it
|
||||||
through a YAML configuration value: `LeftAndMain.extra_requirements_javascript`.
|
through a YAML configuration value: `LeftAndMain.extra_requirements_javascript`.
|
||||||
Set it to 'mysite/javascript/CMSMain.CustomActionsExtension.js'.
|
Set it to 'mysite/javascript/CMSMain.CustomActionsExtension.js'.
|
||||||
|
|
@ -14,7 +14,7 @@ at the last position within the field, and expects unescaped HTML content.
|
|||||||
->setDescription('More <strong>detailed</strong> help');
|
->setDescription('More <strong>detailed</strong> help');
|
||||||
|
|
||||||
To show the help text as a tooltip instead of inline,
|
To show the help text as a tooltip instead of inline,
|
||||||
add a `.cms-description-tooltip` class.
|
add a `.cms-description-tooltip` class.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
TextField::create('MyText', 'My Text Label')
|
TextField::create('MyText', 'My Text Label')
|
@ -2,53 +2,53 @@
|
|||||||
|
|
||||||
## Adding a administration panel
|
## Adding a administration panel
|
||||||
|
|
||||||
Every time you add a new extension of the `[api:LeftAndMain]` class to the CMS,
|
Every time you add a new extension of the `[api:LeftAndMain]` class to the CMS,
|
||||||
SilverStripe will automatically create a new `[api:CMSMenuItem]` for it
|
SilverStripe will automatically create a new `[api:CMSMenuItem]` for it
|
||||||
|
|
||||||
The most popular extension of LeftAndMain is a `[api:ModelAdmin]` class, so
|
The most popular extension of LeftAndMain is a `[api:ModelAdmin]` class, so
|
||||||
for a more detailed introduction to creating new `ModelAdmin` interfaces, read
|
for a more detailed introduction to creating new `ModelAdmin` interfaces, read
|
||||||
the [ModelAdmin reference](../reference/modeladmin).
|
the [ModelAdmin reference](../reference/modeladmin).
|
||||||
|
|
||||||
In this document we'll take the `ProductAdmin` class used in the
|
In this document we'll take the `ProductAdmin` class used in the
|
||||||
[ModelAdmin reference](../reference/modeladmin#setup) and so how we can change
|
[ModelAdmin reference](../reference/modeladmin#setup) and so how we can change
|
||||||
the menu behaviour by using the `$menu_title` and `$menu_icon` statics to
|
the menu behaviour by using the `$menu_title` and `$menu_icon` statics to
|
||||||
provide a custom title and icon.
|
provide a custom title and icon.
|
||||||
|
|
||||||
### Defining a Custom Icon
|
### Defining a Custom Icon
|
||||||
|
|
||||||
First we'll need a custom icon. For this purpose SilverStripe uses 16x16
|
First we'll need a custom icon. For this purpose SilverStripe uses 16x16
|
||||||
black-and-transparent PNG graphics. In this case we'll place the icon in
|
black-and-transparent PNG graphics. In this case we'll place the icon in
|
||||||
`mysite/images`, but you are free to use any location.
|
`mysite/images`, but you are free to use any location.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
class ProductAdmin extends ModelAdmin {
|
class ProductAdmin extends ModelAdmin {
|
||||||
// ...
|
// ...
|
||||||
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
|
If its not defined, the CMS falls back to using the class name of the
|
||||||
controller, 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 {
|
||||||
// ...
|
// ...
|
||||||
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
|
In order to localize the menu title in different languages, use the
|
||||||
`<classname>.MENUTITLE` entity name, which is automatically created when running
|
`<classname>.MENUTITLE` entity name, which is automatically created when running
|
||||||
the i18n text collection.
|
the i18n text collection.
|
||||||
|
|
||||||
For more information on language and translations, please refer to the
|
For more information on language and translations, please refer to the
|
||||||
[i18n](../reference/ii8n) docs.
|
[i18n](../reference/ii8n) docs.
|
||||||
|
|
||||||
## Adding an external link to the menu
|
## Adding an external link to the menu
|
||||||
|
|
||||||
On top of your administration windows, the menu can also have external links
|
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
|
(e.g. to external reference). In this example, we're going to add a link to
|
||||||
Google to the menu.
|
Google to the menu.
|
||||||
|
|
||||||
First, we need to define a `[api:LeftAndMainExtension]` which will contain our
|
First, we need to define a `[api:LeftAndMainExtension]` which will contain our
|
||||||
@ -69,7 +69,7 @@ button configuration.
|
|||||||
// the link you want to item to go to
|
// the link you want to item to go to
|
||||||
$link = 'http://google.com';
|
$link = 'http://google.com';
|
||||||
|
|
||||||
// priority controls the ordering of the link in the stack. The
|
// priority controls the ordering of the link in the stack. The
|
||||||
// lower the number, the lower in the list
|
// lower the number, the lower in the list
|
||||||
$priority = -2;
|
$priority = -2;
|
||||||
|
|
||||||
@ -83,14 +83,14 @@ button configuration.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
To have the link appear, make sure you add the extension to the `LeftAndMain`
|
To have the link appear, make sure you add the extension to the `LeftAndMain`
|
||||||
class. For more information about configuring extensions see the
|
class. For more information about configuring extensions see the
|
||||||
[DataExtension reference](../reference/dataextension).
|
[DataExtension reference](../reference/dataextension).
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
LeftAndMain::add_extension('CustomLeftAndMain')
|
LeftAndMain::add_extension('CustomLeftAndMain')
|
||||||
|
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
* [How to extend the CMS interface](extend-cms-interface)
|
* [How to extend the CMS interface](extend-cms-interface)
|
@ -39,11 +39,11 @@ code like this:
|
|||||||
...
|
...
|
||||||
</ul>
|
</ul>
|
||||||
...
|
...
|
||||||
|
|
||||||
By applying the proper style sheet, the snippet html above could produce the look of:
|
By applying the proper style sheet, the snippet html above could produce the look of:
|
||||||
![Page Node Screenshot](../_images/tree_node.png "Page Node")
|
![Page Node Screenshot](../_images/tree_node.png "Page Node")
|
||||||
|
|
||||||
SiteTree is a `[api:DataObject]` which is versioned by `[api:Versioned]` extension.
|
SiteTree is a `[api:DataObject]` which is versioned by `[api:Versioned]` extension.
|
||||||
Each node can optionally have publication status flags, e.g. "Removed from draft".
|
Each node can optionally have publication status flags, e.g. "Removed from draft".
|
||||||
Each flag has a unique identifier, which is also used as a CSS class for easier styling.
|
Each flag has a unique identifier, which is also used as a CSS class for easier styling.
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ __Example: using a subclass__
|
|||||||
public function getScheduledToPublish(){
|
public function getScheduledToPublish(){
|
||||||
// return either true or false
|
// return either true or false
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStatusFlags($cached = true) {
|
public function getStatusFlags($cached = true) {
|
||||||
$flags = parent::getStatusFlags($cached);
|
$flags = parent::getStatusFlags($cached);
|
||||||
$flags['scheduledtopublish'] = "Scheduled To Publish";
|
$flags['scheduledtopublish'] = "Scheduled To Publish";
|
@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
## Introduction ##
|
## Introduction ##
|
||||||
|
|
||||||
The CMS interface works just like any other part of your website: It consists of
|
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
|
PHP controllers, templates, CSS stylesheets and JavaScript. Because it uses the
|
||||||
same base elements, it is relatively easy to extend.
|
same base elements, it is relatively easy to extend.
|
||||||
|
|
||||||
As an example, we're going to add a permanent "bookmarks" link list to popular pages
|
As an example, we're going to add a permanent "bookmarks" link list to popular pages
|
||||||
into the main CMS menu. A page can be bookmarked by a CMS author through a
|
into the main CMS menu. A page can be bookmarked by a CMS author through a
|
||||||
simple checkbox.
|
simple checkbox.
|
||||||
|
|
||||||
For a deeper introduction to the inner workings of the CMS, please refer to our
|
For a deeper introduction to the inner workings of the CMS, please refer to our
|
||||||
@ -15,8 +15,8 @@ guide on [CMS Architecture](../reference/cms-architecture).
|
|||||||
|
|
||||||
## Overload a CMS template ##
|
## Overload a CMS template ##
|
||||||
|
|
||||||
If you place a template with an identical name into your application 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
|
directory (usually `mysite/templates/`), it'll take priority over the built-in
|
||||||
one.
|
one.
|
||||||
|
|
||||||
CMS templates are inherited based on their controllers, similar to subclasses of
|
CMS templates are inherited based on their controllers, similar to subclasses of
|
||||||
@ -24,10 +24,10 @@ the common `Page` object (a new PHP class `MyPage` will look for a `MyPage.ss` t
|
|||||||
We can use this to create a different base template with `LeftAndMain.ss`
|
We can use this to create a different base template with `LeftAndMain.ss`
|
||||||
(which corresponds to the `LeftAndMain` PHP controller class).
|
(which corresponds to the `LeftAndMain` PHP controller class).
|
||||||
|
|
||||||
Copy the template markup of the base implementation at `framework/admin/templates/Includes/LeftAndMain_Menu.ss`
|
Copy the template markup of the base implementation at `framework/admin/templates/Includes/LeftAndMain_Menu.ss`
|
||||||
into `mysite/templates/Includes/LeftAndMain_Menu.ss`. It will automatically be picked up by
|
into `mysite/templates/Includes/LeftAndMain_Menu.ss`. It will automatically be picked up by
|
||||||
the CMS logic. Add a new section into the `<ul class="cms-menu-list">`
|
the CMS logic. Add a new section into the `<ul class="cms-menu-list">`
|
||||||
|
|
||||||
:::ss
|
:::ss
|
||||||
...
|
...
|
||||||
<ul class="cms-menu-list">
|
<ul class="cms-menu-list">
|
||||||
@ -40,15 +40,15 @@ the CMS logic. Add a new section into the `<ul class="cms-menu-list">`
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
...
|
...
|
||||||
|
|
||||||
Refresh the CMS interface with `admin/?flush=all`, and you should see those
|
Refresh the CMS interface with `admin/?flush=all`, and you should see those
|
||||||
hardcoded links underneath the left-hand menu. We'll make these dynamic further down.
|
hardcoded links underneath the left-hand menu. We'll make these dynamic further down.
|
||||||
|
|
||||||
## Include custom CSS in the CMS
|
## Include custom CSS in the CMS
|
||||||
|
|
||||||
In order to show the links a bit separated from the other menu entries,
|
In order to show the links a bit separated from the other menu entries,
|
||||||
we'll add some CSS, and get it to load
|
we'll add some CSS, and get it to load
|
||||||
with the CMS interface. Paste the following content into a new file called
|
with the CMS interface. Paste the following content into a new file called
|
||||||
`mysite/css/BookmarkedPages.css`:
|
`mysite/css/BookmarkedPages.css`:
|
||||||
|
|
||||||
:::css
|
:::css
|
||||||
@ -64,9 +64,9 @@ Load the new CSS file into the CMS, by setting the `LeftAndMain.extra_requiremen
|
|||||||
|
|
||||||
## Create a "bookmark" flag on pages ##
|
## Create a "bookmark" flag on pages ##
|
||||||
|
|
||||||
Now we'll define which pages are actually bookmarked, a flag that is stored in
|
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
|
the database. For this we need to decorate the page record with a
|
||||||
`DataExtension`. Create a new file called `mysite/code/BookmarkedPageExtension.php`
|
`DataExtension`. Create a new file called `mysite/code/BookmarkedPageExtension.php`
|
||||||
and insert the following code.
|
and insert the following code.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
@ -77,7 +77,7 @@ and insert the following code.
|
|||||||
private static $db = array(
|
private static $db = array(
|
||||||
'IsBookmarked' => 'Boolean'
|
'IsBookmarked' => 'Boolean'
|
||||||
);
|
);
|
||||||
|
|
||||||
public function updateCMSFields(FieldList $fields) {
|
public function updateCMSFields(FieldList $fields) {
|
||||||
$fields->addFieldToTab('Root.Main',
|
$fields->addFieldToTab('Root.Main',
|
||||||
new CheckboxField('IsBookmarked', "Show in CMS bookmarks?")
|
new CheckboxField('IsBookmarked', "Show in CMS bookmarks?")
|
||||||
@ -98,8 +98,8 @@ Refresh the CMS, open a page for editing and you should see the new checkbox.
|
|||||||
## Retrieve the list of bookmarks from the database
|
## Retrieve the list of bookmarks from the database
|
||||||
|
|
||||||
One piece in the puzzle is still missing: How do we get the list of bookmarked
|
One piece in the puzzle is still missing: How do we get the list of bookmarked
|
||||||
pages from the database into the template we've already created (with hardcoded
|
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
|
links)? Again, we extend a core class: The main CMS controller called
|
||||||
`LeftAndMain`.
|
`LeftAndMain`.
|
||||||
|
|
||||||
Add the following code to a new file `mysite/code/BookmarkedLeftAndMainExtension.php`;
|
Add the following code to a new file `mysite/code/BookmarkedLeftAndMainExtension.php`;
|
||||||
@ -113,7 +113,7 @@ Add the following code to a new file `mysite/code/BookmarkedLeftAndMainExtension
|
|||||||
return Page::get()->filter("IsBookmarked", 1);
|
return Page::get()->filter("IsBookmarked", 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Enable the extension in your [configuration file](/topics/configuration)
|
Enable the extension in your [configuration file](/topics/configuration)
|
||||||
|
|
||||||
:::yml
|
:::yml
|
||||||
@ -137,50 +137,50 @@ and replace it with the following:
|
|||||||
|
|
||||||
## Extending the CMS actions
|
## Extending the CMS actions
|
||||||
|
|
||||||
CMS actions follow a principle similar to the CMS fields: they are built in the
|
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
|
backend with the help of `FormFields` and `FormActions`, and the frontend is
|
||||||
responsible for applying a consistent styling.
|
responsible for applying a consistent styling.
|
||||||
|
|
||||||
The following conventions apply:
|
The following conventions apply:
|
||||||
|
|
||||||
* New actions can be added by redefining `getCMSActions`, or adding an extension
|
* New actions can be added by redefining `getCMSActions`, or adding an extension
|
||||||
with `updateCMSActions`.
|
with `updateCMSActions`.
|
||||||
* It is required the actions are contained in a `FieldSet` (`getCMSActions`
|
* It is required the actions are contained in a `FieldSet` (`getCMSActions`
|
||||||
returns this already).
|
returns this already).
|
||||||
* Standalone buttons are created by adding a top-level `FormAction` (no such
|
* Standalone buttons are created by adding a top-level `FormAction` (no such
|
||||||
button is added by default).
|
button is added by default).
|
||||||
* Button groups are created by adding a top-level `CompositeField` with
|
* Button groups are created by adding a top-level `CompositeField` with
|
||||||
`FormActions` in it.
|
`FormActions` in it.
|
||||||
* A `MajorActions` button group is already provided as a default.
|
* A `MajorActions` button group is already provided as a default.
|
||||||
* Drop ups with additional actions that appear as links are created via a
|
* Drop ups with additional actions that appear as links are created via a
|
||||||
`TabSet` and `Tabs` with `FormActions` inside.
|
`TabSet` and `Tabs` with `FormActions` inside.
|
||||||
* A `ActionMenus.MoreOptions` tab is already provided as a default and contains
|
* A `ActionMenus.MoreOptions` tab is already provided as a default and contains
|
||||||
some minor actions.
|
some minor actions.
|
||||||
* You can override the actions completely by providing your own
|
* You can override the actions completely by providing your own
|
||||||
`getAllCMSFields`.
|
`getAllCMSFields`.
|
||||||
|
|
||||||
Let's walk through a couple of examples of adding new CMS actions in `getCMSActions`.
|
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
|
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
|
we are inserting it in the front of all other actions. We could also add a
|
||||||
button group (`CompositeField`) in a similar fashion.
|
button group (`CompositeField`) in a similar fashion.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$fields->unshift(FormAction::create('normal', 'Normal button'));
|
$fields->unshift(FormAction::create('normal', 'Normal button'));
|
||||||
|
|
||||||
We can affect the existing button group by manipulating the `CompositeField`
|
We can affect the existing button group by manipulating the `CompositeField`
|
||||||
already present in the `FieldList`.
|
already present in the `FieldList`.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$fields->fieldByName('MajorActions')->push(FormAction::create('grouped', 'New group button'));
|
$fields->fieldByName('MajorActions')->push(FormAction::create('grouped', 'New group button'));
|
||||||
|
|
||||||
Another option is adding actions into the drop-up - best place for placing
|
Another option is adding actions into the drop-up - best place for placing
|
||||||
infrequently used minor actions.
|
infrequently used minor actions.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
$fields->addFieldToTab('ActionMenus.MoreOptions', FormAction::create('minor', 'Minor action'));
|
$fields->addFieldToTab('ActionMenus.MoreOptions', FormAction::create('minor', 'Minor action'));
|
||||||
|
|
||||||
We can also easily create new drop-up menus by defining new tabs within the
|
We can also easily create new drop-up menus by defining new tabs within the
|
||||||
`TabSet`.
|
`TabSet`.
|
||||||
|
|
||||||
:::php
|
:::php
|
||||||
@ -190,12 +190,12 @@ We can also easily create new drop-up menus by defining new tabs within the
|
|||||||
Empty tabs will be automatically removed from the `FieldList` to prevent clutter.
|
Empty tabs will be automatically removed from the `FieldList` to prevent clutter.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
New actions will need associated controller handlers to work. You can use a
|
New actions will need associated controller handlers to work. You can use a
|
||||||
`LeftAndMainExtension` to provide one. Refer to [Controller documentation](../topics/controller)
|
`LeftAndMainExtension` to provide one. Refer to [Controller documentation](../topics/controller)
|
||||||
for instructions on setting up handlers.
|
for instructions on setting up handlers.
|
||||||
|
|
||||||
To make the actions more user-friendly you can also use alternating buttons as
|
To make the actions more user-friendly you can also use alternating buttons as
|
||||||
detailed in the [CMS Alternating Button](../reference/cms-alternating-button)
|
detailed in the [CMS Alternating Button](../reference/cms-alternating-button)
|
||||||
how-to.
|
how-to.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
@ -0,0 +1,22 @@
|
|||||||
|
## Extending existing ModelAdmin
|
||||||
|
|
||||||
|
Sometimes you'll work with ModelAdmins from other modules. To customize these interfaces, you can always subclass. But there's
|
||||||
|
also another tool at your disposal: The `[api:Extension]` API.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
class MyAdminExtension extends Extension {
|
||||||
|
// ...
|
||||||
|
public function updateEditForm(&$form) {
|
||||||
|
$form->Fields()->push(/* ... */)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Now enable this extension through your `[config.yml](/topics/configuration)` file.
|
||||||
|
|
||||||
|
:::yml
|
||||||
|
MyAdmin:
|
||||||
|
extensions:
|
||||||
|
- MyAdminExtension
|
||||||
|
|
||||||
|
The following extension points are available: `updateEditForm()`, `updateSearchContext()`,
|
||||||
|
`updateSearchForm()`, `updateList()`, `updateImportForm`.
|
@ -0,0 +1,13 @@
|
|||||||
|
title: Customising the Admin Interface
|
||||||
|
summary: Extend the admin view to provide custom behavior or new features for CMS and admin users.
|
||||||
|
introduction: The Admin interface can be extended to provide additional functionality to users and custom interfaces for managing data.
|
||||||
|
|
||||||
|
The Admin interface is bundled within the SilverStripe Framework but is most commonly used in conjunction with the `CMS`
|
||||||
|
module. The main class for displaying the interface is a specialized [api:Controller] called [api:LeftAndMain], named
|
||||||
|
as it is designed around a left hand navigation and a main edit form.
|
||||||
|
|
||||||
|
[CHILDREN]
|
||||||
|
|
||||||
|
## How to's
|
||||||
|
|
||||||
|
[CHILDREN Folder="How_Tos"]
|
@ -1,106 +0,0 @@
|
|||||||
# Typography
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
SilverStripe lets you customise the style of content in the CMS.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
This is done by setting up a CSS file called (projectname)/css/typography.css.
|
|
||||||
|
|
||||||
You also need to create a file called (projectname)/css/editor.css with the following content:
|
|
||||||
|
|
||||||
:::css
|
|
||||||
/**
|
|
||||||
* This support file is used to style the WYSIWYG editor in the CMS
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "typography.css";
|
|
||||||
|
|
||||||
body.mceContentBody {
|
|
||||||
min-height: 200px;
|
|
||||||
font-size: 62.5%;
|
|
||||||
}
|
|
||||||
body.mceContentBody a.broken {
|
|
||||||
background-color: #FF7B71;
|
|
||||||
border: 1px red solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
In typography.css you can define styles of any of the tags that will get created by the editor:
|
|
||||||
|
|
||||||
* P, BLOCKQUOTE
|
|
||||||
* H1-6
|
|
||||||
* UL, OL, LI
|
|
||||||
* TABLE
|
|
||||||
* STRONG, EM, U
|
|
||||||
* A
|
|
||||||
|
|
||||||
It's important to realise that this CSS file is included directly into the CMS system, and if you aren't careful, you
|
|
||||||
can alter the styling of other parts of the interface. While this is novel, it can be dangerous and is probably not
|
|
||||||
what you're after.
|
|
||||||
|
|
||||||
The way around this is to limit all your styling selectors to elements inside something with `class="typography"`. The
|
|
||||||
other half of this is to put `class="typography"` onto any area in your template where you would like the styling to be
|
|
||||||
applied.
|
|
||||||
|
|
||||||
**WRONG**
|
|
||||||
|
|
||||||
:::css
|
|
||||||
CSS:
|
|
||||||
h1, h2 {
|
|
||||||
color: #F77;
|
|
||||||
}
|
|
||||||
|
|
||||||
Template:
|
|
||||||
<div>
|
|
||||||
$Content
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
**RIGHT**
|
|
||||||
|
|
||||||
:::css
|
|
||||||
CSS:
|
|
||||||
.typography h1, .typography h2 {
|
|
||||||
color: #F77;
|
|
||||||
}
|
|
||||||
|
|
||||||
Template:
|
|
||||||
<div class="typography">
|
|
||||||
$Content
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
If you would to include different styles for different sections of your site, you can use class names the same as the
|
|
||||||
name of the data fields. This example sets up different paragraph styles for 2 HTML editor fields called Content and
|
|
||||||
OtherContent:
|
|
||||||
|
|
||||||
:::css
|
|
||||||
.Content.typography p {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.OtherContent.typography p {
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
### Removing the typography class
|
|
||||||
|
|
||||||
Sometimes, it's not enough to add a class, you also want to remove the typography class. You can use the
|
|
||||||
`[api:HTMLEditorField]` method setCSSClass.
|
|
||||||
|
|
||||||
This example sets another CSS class typographybis:
|
|
||||||
|
|
||||||
:::php
|
|
||||||
public function getCMSFields() {
|
|
||||||
...
|
|
||||||
$htmleditor = new HTMLEditorField("ContentBis", "Content Bis");
|
|
||||||
$htmleditor->setCSSClass('typographybis');
|
|
||||||
$fields->addFieldToTab("Root.Content", $htmleditor);
|
|
||||||
...
|
|
||||||
return $fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
**Note:** This functionality will be available in the version 2.0.2 of the CMS.
|
|
@ -1,301 +0,0 @@
|
|||||||
# ModelAdmin
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
Provides a simple way to utilize the SilverStripe CMS UI with your own data models,
|
|
||||||
and create searchable list and edit views of them, and even providing import and export of your data.
|
|
||||||
|
|
||||||
It uses the framework's knowledge about the model to provide sensible defaults,
|
|
||||||
allowing you to get started in a couple of lines of code,
|
|
||||||
while still providing a solid base for customization.
|
|
||||||
|
|
||||||
The interface is mainly powered by the [GridField](/reference/grid-field) class,
|
|
||||||
which can also be used in other CMS areas (e.g. to manage a relation on a `SiteTree`
|
|
||||||
record in the standard CMS interface).
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
Let's assume we want to manage a simple product listing as a sample data model:
|
|
||||||
A product can have a name, price, and a category.
|
|
||||||
|
|
||||||
:::php
|
|
||||||
class Product extends DataObject {
|
|
||||||
private static $db = array('Name' => 'Varchar', 'ProductCode' => 'Varchar', 'Price' => 'Currency');
|
|
||||||
private static $has_one = array('Category' => 'Category');
|
|
||||||
}
|
|
||||||
class Category extends DataObject {
|
|
||||||
private static $db = array('Title' => 'Text');
|
|
||||||
private static $has_many = array('Products' => 'Product');
|
|
||||||
}
|
|
||||||
|
|
||||||
To create your own `ModelAdmin`, simply extend the base class,
|
|
||||||
and edit the `$managed_models` property with the list of
|
|
||||||
data objects you want to scaffold an interface for.
|
|
||||||
The class can manage multiple models in parallel, if required.
|
|
||||||
We'll name it `MyAdmin`, but the class name can be anything you want.
|
|
||||||
|
|
||||||
:::php
|
|
||||||
class MyAdmin extends ModelAdmin {
|
|
||||||
private static $managed_models = array('Product','Category'); // Can manage multiple models
|
|
||||||
private static $url_segment = 'products'; // Linked as /admin/products/
|
|
||||||
private static $menu_title = 'My Product Admin';
|
|
||||||
}
|
|
||||||
|
|
||||||
This will automatically add a new menu entry to the CMS, and you're ready to go!
|
|
||||||
Try opening http://localhost/admin/products/?flush=all.
|
|
||||||
|
|
||||||
## Permissions
|
|
||||||
|
|
||||||
Each new `ModelAdmin` subclass creates its own [permission code](/reference/permission),
|
|
||||||
for the example above this would be `CMS_ACCESS_MyAdmin`. Users with access to the CMS
|
|
||||||
need to have this permission assigned through `admin/security/` in order to gain
|
|
||||||
access to the controller (unless they're admins).
|
|
||||||
|
|
||||||
The `DataObject` API has more granular permission control, which is enforced in ModelAdmin by default.
|
|
||||||
Available checks are `canEdit()`, `canCreate()`, `canView()` and `canDelete()`.
|
|
||||||
Models check for administrator permissions by default. For most cases,
|
|
||||||
less restrictive checks make sense, e.g. checking for general CMS access rights.
|
|
||||||
|
|
||||||
:::php
|
|
||||||
class Category extends DataObject {
|
|
||||||
// ...
|
|
||||||
public function canView($member = null) {
|
|
||||||
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
|
||||||
}
|
|
||||||
public function canEdit($member = null) {
|
|
||||||
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
|
||||||
}
|
|
||||||
public function canDelete($member = null) {
|
|
||||||
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
|
||||||
}
|
|
||||||
public function canCreate($member = null) {
|
|
||||||
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
|
|
||||||
}
|
|
||||||
|
|
||||||
## Search Fields
|
|
||||||
|
|
||||||
ModelAdmin uses the [SearchContext](/reference/searchcontext) class to provide
|
|
||||||
a search form, as well as get the searched results. Every DataObject can have its own context,
|
|
||||||
based on the fields which should be searchable. The class makes a guess at how those fields
|
|
||||||
should be searched, e.g. showing a checkbox for any boolean fields in your `$db` definition.
|
|
||||||
|
|
||||||
To remove, add or modify searchable fields, define a new `[api:DataObject::$searchable_fields]`
|
|
||||||
static on your model class (see [SearchContext](/reference/searchcontext) docs for details).
|
|
||||||
|
|
||||||
:::php
|
|
||||||
class Product extends DataObject {
|
|
||||||
// ...
|
|
||||||
private static $searchable_fields = array(
|
|
||||||
'Name',
|
|
||||||
'ProductCode'
|
|
||||||
// leaves out the 'Price' field, removing it from the search
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
For a more sophisticated customization, for example configuring the form fields
|
|
||||||
for the search form, override `DataObject->getCustomSearchContext()` on your model class.
|
|
||||||
|
|
||||||
## Result Columns
|
|
||||||
|
|
||||||
The results are shown in a tabular listing, powered by the [GridField](/reference/grid-field),
|
|
||||||
more specifically the `[api:GridFieldDataColumns]` component.
|
|
||||||
It looks for a `[api:DataObject::$summary_fields]` static on your model class,
|
|
||||||
where you can add or remove columns. To change the title, use `[api:DataObject::$field_labels]`.
|
|
||||||
|
|
||||||
:::php
|
|
||||||
class Product extends DataObject {
|
|
||||||
// ...
|
|
||||||
private static $field_labels = array(
|
|
||||||
'Price' => 'Cost' // renames the column to "Cost"
|
|
||||||
);
|
|
||||||
private static $summary_fields = array(
|
|
||||||
'Name',
|
|
||||||
'Price',
|
|
||||||
// leaves out the 'ProductCode' field, removing the column
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
## Results Customization
|
|
||||||
|
|
||||||
The results are retrieved from `[api:SearchContext->getResults()]`,
|
|
||||||
based on the parameters passed through the search form.
|
|
||||||
If no search parameters are given, the results will show every record.
|
|
||||||
Results are a `[api:DataList]` instance, so can be customized by additional
|
|
||||||
SQL filters, joins, etc (see [datamodel](/topics/datamodel) for more info).
|
|
||||||
|
|
||||||
For example, we might want to exclude all products without prices in our sample `MyAdmin` implementation.
|
|
||||||
|
|
||||||
:::php
|
|
||||||
class MyAdmin extends ModelAdmin {
|
|
||||||
// ...
|
|
||||||
public function getList() {
|
|
||||||
$list = parent::getList();
|
|
||||||
// Always limit by model class, in case you're managing multiple
|
|
||||||
if($this->modelClass == 'Product') {
|
|
||||||
$list = $list->exclude('Price', '0');
|
|
||||||
}
|
|
||||||
return $list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
You can also customize the search behaviour directly on your `ModelAdmin` instance.
|
|
||||||
For example, we might want to have a checkbox which limits search results to expensive products (over $100).
|
|
||||||
|
|
||||||
:::php
|
|
||||||
class MyAdmin extends ModelAdmin {
|
|
||||||
// ...
|
|
||||||
public function getSearchContext() {
|
|
||||||
$context = parent::getSearchContext();
|
|
||||||
if($this->modelClass == 'Product') {
|
|
||||||
$context->getFields()->push(new CheckboxField('q[ExpensiveOnly]', 'Only expensive stuff'));
|
|
||||||
}
|
|
||||||
return $context;
|
|
||||||
}
|
|
||||||
public function getList() {
|
|
||||||
$list = parent::getList();
|
|
||||||
$params = $this->request->requestVar('q'); // use this to access search parameters
|
|
||||||
if($this->modelClass == 'Product' && isset($params['ExpensiveOnly']) && $params['ExpensiveOnly']) {
|
|
||||||
$list = $list->exclude('Price:LessThan', '100');
|
|
||||||
}
|
|
||||||
return $list;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
### GridField Customization
|
|
||||||
|
|
||||||
To alter how the results are displayed (via `[api:GridField]`), you can also overload the `getEditForm()` method. For example, to add a new component.
|
|
||||||
|
|
||||||
:::php
|
|
||||||
class MyAdmin extends ModelAdmin {
|
|
||||||
private static $managed_models = array('Product','Category');
|
|
||||||
// ...
|
|
||||||
public function getEditForm($id = null, $fields = null) {
|
|
||||||
$form = parent::getEditForm($id, $fields);
|
|
||||||
// $gridFieldName is generated from the ModelClass, eg if the Class 'Product'
|
|
||||||
// is managed by this ModelAdmin, the GridField for it will also be named 'Product'
|
|
||||||
$gridFieldName = $this->sanitiseClassName($this->modelClass);
|
|
||||||
$gridField = $form->Fields()->fieldByName($gridFieldName);
|
|
||||||
$gridField->getConfig()->addComponent(new GridFieldFilterHeader());
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
The above example will add the component to all `GridField`s (of all managed models). Alternatively we can also add it to only one specific `GridField`:
|
|
||||||
|
|
||||||
:::php
|
|
||||||
class MyAdmin extends ModelAdmin {
|
|
||||||
private static $managed_models = array('Product','Category');
|
|
||||||
// ...
|
|
||||||
public function getEditForm($id = null, $fields = null) {
|
|
||||||
$form = parent::getEditForm($id, $fields);
|
|
||||||
$gridFieldName = 'Product';
|
|
||||||
$gridField = $form->Fields()->fieldByName($gridFieldName);
|
|
||||||
if ($gridField) {
|
|
||||||
$gridField->getConfig()->addComponent(new GridFieldFilterHeader());
|
|
||||||
}
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
## Managing Relationships
|
|
||||||
|
|
||||||
Has-one relationships are simply implemented as a `[api:DropdownField]` by default.
|
|
||||||
Consider replacing it with a more powerful interface in case you have many records
|
|
||||||
(through customizing `[api:DataObject->getCMSFields]`).
|
|
||||||
|
|
||||||
Has-many and many-many relationships are usually handled via the [GridField](/reference/grid-field) class,
|
|
||||||
more specifically the `[api:GridFieldAddExistingAutocompleter]` and `[api:GridFieldRelationDelete]` components.
|
|
||||||
They provide a list/detail interface within a single record edited in your ModelAdmin.
|
|
||||||
The [GridField](/reference/grid-field) docs also explain how to manage
|
|
||||||
extra relation fields on join tables through its detail forms.
|
|
||||||
The autocompleter can also search attributes on relations,
|
|
||||||
based on the search fields defined through `[api:DataObject::searchableFields()]`.
|
|
||||||
|
|
||||||
## Permissions
|
|
||||||
|
|
||||||
`ModelAdmin` respects the permissions set on the model, through methods on your `DataObject` implementations:
|
|
||||||
`canView()`, `canEdit()`, `canDelete()`, and `canCreate`.
|
|
||||||
|
|
||||||
In terms of access control to the interface itself, every `ModelAdmin` subclass
|
|
||||||
creates its own "[permission code](/reference/permissions)", which can be assigned
|
|
||||||
to groups through the `admin/security` management interface. To further limit
|
|
||||||
permission, either override checks in `ModelAdmin->init()`, or define
|
|
||||||
more permission codes through the `ModelAdmin::$required_permission_codes` static.
|
|
||||||
|
|
||||||
## Data Import
|
|
||||||
|
|
||||||
The `ModelAdmin` class provides import of CSV files through the `[api:CsvBulkLoader]` API.
|
|
||||||
which has support for column mapping, updating existing records,
|
|
||||||
and identifying relationships - so its a powerful tool to get your data into a SilverStripe database.
|
|
||||||
|
|
||||||
By default, each model management interface allows uploading a CSV file
|
|
||||||
with all columns autodetected. To override with a more specific importer implementation,
|
|
||||||
use the `[api:ModelAdmin::$model_importers] static.
|
|
||||||
|
|
||||||
## Data Export
|
|
||||||
|
|
||||||
Export is also available, although at the moment only to the CSV format,
|
|
||||||
through a button at the end of a results list. You can also export search results.
|
|
||||||
It is handled through the `[api:GridFieldExportButton]` component.
|
|
||||||
|
|
||||||
To customize the exported columns, create a new method called `getExportFields` in your `ModelAdmin`:
|
|
||||||
|
|
||||||
:::php
|
|
||||||
class MyAdmin extends ModelAdmin {
|
|
||||||
// ...
|
|
||||||
public function getExportFields() {
|
|
||||||
return array(
|
|
||||||
'Name' => 'Name',
|
|
||||||
'ProductCode' => 'Product Code',
|
|
||||||
'Category.Title' => 'Category'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Dot syntax support allows you to select a field on a related `has_one` object.
|
|
||||||
|
|
||||||
## Extending existing ModelAdmins
|
|
||||||
|
|
||||||
Sometimes you'll work with ModelAdmins from other modules, e.g. the product management
|
|
||||||
of an ecommerce module. To customize this, you can always subclass. But there's
|
|
||||||
also another tool at your disposal: The `[api:Extension]` API.
|
|
||||||
|
|
||||||
:::php
|
|
||||||
class MyAdminExtension extends Extension {
|
|
||||||
// ...
|
|
||||||
public function updateEditForm(&$form) {
|
|
||||||
$form->Fields()->push(/* ... */)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Now enable this extension through your `[config.yml](/topics/configuration)` file.
|
|
||||||
|
|
||||||
:::yml
|
|
||||||
MyAdmin:
|
|
||||||
extensions:
|
|
||||||
- MyAdminExtension
|
|
||||||
|
|
||||||
The following extension points are available: `updateEditForm()`, `updateSearchContext()`,
|
|
||||||
`updateSearchForm()`, `updateList()`, `updateImportForm`.
|
|
||||||
|
|
||||||
## Customizing the interface
|
|
||||||
|
|
||||||
Interfaces like `ModelAdmin` can be customized in many ways:
|
|
||||||
|
|
||||||
* JavaScript behaviour (e.g. overwritten jQuery.entwine rules)
|
|
||||||
* CSS styles
|
|
||||||
* HTML markup through templates
|
|
||||||
|
|
||||||
In general, use your `ModelAdmin->init()` method to add additional requirements
|
|
||||||
through the [Requirements](/reference/requirements) API.
|
|
||||||
For an introduction how to customize the CMS templates, see our [CMS Architecture Guide](/reference/cms-architecture).
|
|
||||||
|
|
||||||
## Related
|
|
||||||
|
|
||||||
* [GridField](../reference/grid-field): The UI component powering ModelAdmin
|
|
||||||
* [Tutorial 5: Dataobject Relationship Management](../tutorials/5-dataobject-relationship-management)
|
|
||||||
* `[api:SearchContext]`
|
|
||||||
* [genericviews Module](http://silverstripe.org/generic-views-module)
|
|
||||||
* [Presentation about ModelAdmin at SupperHappyDevHouse Wellington](http://www.slideshare.net/chillu/modeladmin-in-silverstripe-23)
|
|
||||||
* [Reference: CMS Architecture](../reference/cms-architecture)
|
|
||||||
* [Howto: Extend the CMS Interface](../howto/extend-cms-interface)
|
|
@ -1,7 +0,0 @@
|
|||||||
summary: Extend the admin view to provide custom behavior or new features for CMS users.
|
|
||||||
|
|
||||||
[CHILDREN]
|
|
||||||
|
|
||||||
## How-to
|
|
||||||
|
|
||||||
[CHILDREN How_To]
|
|
Loading…
Reference in New Issue
Block a user