diff --git a/docs/en/02_Developer_Guides/09_Security/02_Permission.md b/docs/en/02_Developer_Guides/09_Security/02_Permissions.md similarity index 100% rename from docs/en/02_Developer_Guides/09_Security/02_Permission.md rename to docs/en/02_Developer_Guides/09_Security/02_Permissions.md diff --git a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/01_ModelAdmin.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/01_ModelAdmin.md new file mode 100644 index 000000000..0d3c7ba3d --- /dev/null +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/01_ModelAdmin.md @@ -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. + +
+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. +
+ +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 + 'Varchar', + 'ProductCode' => 'Varchar', + 'Price' => 'Currency' + ); + + private static $has_one = array( + 'Category' => 'Category' + ); + } + +**mysite/code/Category.php** + + :::php + '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 + +After defining these classes, make sure you have rebuilt your SilverStripe database and flushed your cache. + + +## 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. + +
+For more information on the security and permission system see the [Security Documentation](../security) +
+ +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 + +[SearchContext](../search/searchcontext) documentation has more information on providing the search functionality. + + +## 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 + '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 + 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 + 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 + 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 + 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 + '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] diff --git a/docs/en/02_Developer_Guides/15_Customising_the_CMS/03_CMS_Layout.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/03_CMS_Layout.md similarity index 93% rename from docs/en/02_Developer_Guides/15_Customising_the_CMS/03_CMS_Layout.md rename to docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/03_CMS_Layout.md index 22d529fd0..45929c356 100644 --- a/docs/en/02_Developer_Guides/15_Customising_the_CMS/03_CMS_Layout.md +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/03_CMS_Layout.md @@ -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), 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. 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 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: -* 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`) * trigger `layout` which cascades into all children resizing and positioning subordinate elements (this is internal 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: :::html -
<%-- content utilising border's north, south, east, west and center classes --%>
@@ -67,15 +67,15 @@ For detailed discussion on available algorithms refer to [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 -panel to the CMS UI. +panel to the CMS UI. ### 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: * **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'});` * **splitViewMode**: enable side by side editing. * **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 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 by the algorithm that are initially taken from the `LeftAndMain::LayoutOptions` entwine variable. diff --git a/docs/en/02_Developer_Guides/15_Customising_the_CMS/04_Preview.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/04_Preview.md similarity index 91% rename from docs/en/02_Developer_Guides/15_Customising_the_CMS/04_Preview.md rename to docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/04_Preview.md index 6c23293d0..f949d9717 100644 --- a/docs/en/02_Developer_Guides/15_Customising_the_CMS/04_Preview.md +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/04_Preview.md @@ -2,41 +2,41 @@ ## Overview -With the addition of side-by-side editing, the preview has the ability to appear -within the CMS window when editing content in the _Pages_ section of the CMS. -The site is rendered into an iframe. It will update itself whenever the content -is saved, and relevant pages will be loaded for editing when the user navigates +With the addition of side-by-side editing, the preview has the ability to appear +within the CMS window when editing content in the _Pages_ section of the CMS. +The site is rendered into an iframe. It will update itself whenever the content +is saved, and relevant pages will be loaded for editing when the user navigates around in the preview. -The root element for preview is `.cms-preview` which maintains the internal -states necessary for rendering within the entwine properties. It provides -function calls for transitioning between these states and has the ability to +The root element for preview is `.cms-preview` which maintains the internal +states necessary for rendering within the entwine properties. It provides +function calls for transitioning between these states and has the ability to update the appearance of the option selectors. -In terms of backend support, it relies on `SilverStripeNavigator` to be rendered -into the `.cms-edit-form`. _LeftAndMain_ will automatically take care of +In terms of backend support, it relies on `SilverStripeNavigator` to be rendered +into the `.cms-edit-form`. _LeftAndMain_ will automatically take care of generating it as long as the `*_SilverStripeNavigator` template is found - -first segment has to match current _LeftAndMain_-derived class (e.g. +first segment has to match current _LeftAndMain_-derived class (e.g. `LeftAndMain_SilverStripeNavigator`). We use `ss.preview` entwine namespace for all preview-related entwines.
-Caveat: `SilverStripeNavigator` and `CMSPreviewable` interface currently only -support SiteTree objects that are _Versioned_. They are not general enough for -using on any other DataObject. That pretty much limits the extendability of the +Caveat: `SilverStripeNavigator` and `CMSPreviewable` interface currently only +support SiteTree objects that are _Versioned_. They are not general enough for +using on any other DataObject. That pretty much limits the extendability of the feature.
## Configuration and Defaults -Like most of the CMS, the preview UI is powered by -[jQuery entwine](https://github.com/hafriedlander/jquery.entwine). This means +Like most of the CMS, the preview UI is powered by +[jQuery entwine](https://github.com/hafriedlander/jquery.entwine). This means its defaults are configured through JavaScript, by setting entwine properties. In order to achieve this, create a new file `mysite/javascript/MyLeftAndMain.Preview.js`. In the following example we configure three aspects: - + * Set the default mode from "split view" to a full "edit view" * Make a wider mobile preview * 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 -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 +The frontend decides on the preview being enabled or disabled based on the +presence of the `.cms-previewable` class. If this class is not found the preview will remain hidden, and the layout will stay in the _content_ mode. -If the class is found, frontend looks for the `SilverStripeNavigator` structure -and moves it to the `.cms-preview-control` panel at the bottom of the preview. +If the class is found, frontend looks for the `SilverStripeNavigator` structure +and moves it to the `.cms-preview-control` panel at the bottom of the preview. This structure supplies preview options such as state selector. -If the navigator is not found, the preview appears in the GUI, but is shown as +If the navigator is not found, the preview appears in the GUI, but is shown as "blocked" - i.e. displaying the "preview unavailable" overlay. -The preview can be affected by calling `enablePreview` and `disablePreview`. You -can check if the preview is active by inspecting the `IsPreviewEnabled` entwine +The preview can be affected by calling `enablePreview` and `disablePreview`. You +can check if the preview is active by inspecting the `IsPreviewEnabled` entwine property. ## Preview states -States are the site stages: _live_, _stage_ etc. Preview states are picked up +States are the site stages: _live_, _stage_ etc. Preview states are picked up from the `SilverStripeNavigator`. You can invoke the state change by calling: ```js $('.cms-preview').entwine('.ss.preview').changeState('StageLink'); ``` -Note the state names come from `SilverStripeNavigatorItems` class names - thus -the _Link_ in their names. This call will also redraw the state selector to fit +Note the state names come from `SilverStripeNavigatorItems` class names - thus +the _Link_ in their names. This call will also redraw the state selector to fit with the internal state. See `AllowedStates` in `.cms-preview` entwine for the list of supported states. @@ -120,8 +120,8 @@ You can get the current state by calling: ## Preview sizes -This selector defines how the preview iframe is rendered, and try to emulate -different device sizes. The options are hardcoded. The option names map directly +This selector defines how the preview iframe is rendered, and try to emulate +different device sizes. The options are hardcoded. The option names map directly to CSS classes applied to the `.cms-preview` and are as follows: * _auto_: responsive layout @@ -129,8 +129,8 @@ to CSS classes applied to the `.cms-preview` and are as follows: * _tablet_ * _mobile_ -You can switch between different types of display sizes programmatically, which -has the benefit of redrawing the related selector and maintaining a consistent +You can switch between different types of display sizes programmatically, which +has the benefit of redrawing the related selector and maintaining a consistent internal state: ```js @@ -145,15 +145,15 @@ You can find out current size by calling: ## Preview modes -Preview modes map to the modes supported by the _threeColumnCompressor_ layout -algorithm, see [layout reference](../reference/layout) for more details. You -can change modes by calling: +Preview modes map to the modes supported by the _threeColumnCompressor_ layout +algorithm, see [layout reference](../reference/layout) for more details. You +can change modes by calling: ```js $('.cms-preview').entwine('.ss.preview').changeMode('preview'); ``` -Currently active mode is stored on the `.cms-container` along with related +Currently active mode is stored on the `.cms-container` along with related internal states of the layout. You can reach it by calling: ```js @@ -161,10 +161,10 @@ internal states of the layout. You can reach it by calling: ```
-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 +Caveat: the `.preview-mode-selector` appears twice, once in the preview and +second time in the CMS actions area as `#preview-mode-dropdown-in-cms`. This is done because the user should still have access to the mode selector even if -preview is not visible. Currently CMS Actions are a separate area to the preview +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.
diff --git a/docs/en/02_Developer_Guides/15_Customising_the_CMS/05_CMS_Architecture.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/05_CMS_Architecture.md similarity index 97% rename from docs/en/02_Developer_Guides/15_Customising_the_CMS/05_CMS_Architecture.md rename to docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/05_CMS_Architecture.md index 226d57da2..0db706f50 100644 --- a/docs/en/02_Developer_Guides/15_Customising_the_CMS/05_CMS_Architecture.md +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/05_CMS_Architecture.md @@ -2,7 +2,7 @@ ## 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 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. @@ -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)" where feasible, relying on a comparatively light layer of JavaScript to enhance 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/)). 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` (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 -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. 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. @@ -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) * 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. - * 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. 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). In case you want to retain the main CMS structure (which is recommended), 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 "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, usually occupied by a search form or navigational helper. 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`. -These placeholders are populated by auto-detected templates, +These placeholders are populated by auto-detected templates, with the naming convention of "_Tools.ss" and "_EditFormTools.ss". 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, custom templates and additional CSS classes might be required for correct operation. 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. To obey the inheritance chain, we use `$this->getTemplatesWithSuffix('_EditForm')` for 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 { function getEditForm() { return CMSForm::create( - $this, + $this, 'EditForm', new FieldSet( 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, correctly configured form. ## JavaScript through jQuery.entwine [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. 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. @@ -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()`. 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 dialogs and buttons. 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 -any particular element. +any particular element. ## 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 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/) 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 -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 - 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, -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). This technique has an impact on how any Ajax load needs to happen: -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. +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. The main point of contact here is `$('.cms-container').loadPanel(, , <data>)` 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 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 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 support defining icons via HTML5 data attributes (see `ssui.core.js`). 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/)). Compass also creates the correct CSS classes to show those sprites via background images (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). 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 (`[api:TreeDropdownField]` and `[api:TreeMultiselectField]`). @@ -487,7 +487,7 @@ Form template with custom tab navigation (trimmed down): <% loop Fields %>$FieldHolder<% end_loop %> </fieldset> </div> - + </form> Tabset template without tab navigation (e.g. `CMSTabset.ss`) diff --git a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/05_Typography.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/05_Typography.md new file mode 100644 index 000000000..3c53ceb09 --- /dev/null +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/05_Typography.md @@ -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] diff --git a/docs/en/02_Developer_Guides/15_Customising_the_CMS/Javascript_Development.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/06_Javascript_Development.md similarity index 99% rename from docs/en/02_Developer_Guides/15_Customising_the_CMS/Javascript_Development.md rename to docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/06_Javascript_Development.md index 770904ec5..a91150247 100644 --- a/docs/en/02_Developer_Guides/15_Customising_the_CMS/Javascript_Development.md +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/06_Javascript_Development.md @@ -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 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 (function($) { @@ -34,7 +34,7 @@ You should write all your custom jQuery code in a closure. ## 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 official [jQuery Plugin Authoring](http://docs.jquery.com/Plugins/Authoring) documentation. diff --git a/docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/CMS_Alternating_Button.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/CMS_Alternating_Button.md similarity index 99% rename from docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/CMS_Alternating_Button.md rename to docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/CMS_Alternating_Button.md index 4590448df..cd97566b9 100644 --- a/docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/CMS_Alternating_Button.md +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/CMS_Alternating_Button.md @@ -59,7 +59,7 @@ Here we initialise the button based on the backend check, and assume that the bu } // ... } - + ## Frontend support ## 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 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`. Set it to 'mysite/javascript/CMSMain.CustomActionsExtension.js'. diff --git a/docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/CMS_Formfield_Help_Text.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/CMS_Formfield_Help_Text.md similarity index 96% rename from docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/CMS_Formfield_Help_Text.md rename to docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/CMS_Formfield_Help_Text.md index b8ee1e437..00c682414 100644 --- a/docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/CMS_Formfield_Help_Text.md +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/CMS_Formfield_Help_Text.md @@ -14,7 +14,7 @@ at the last position within the field, and expects unescaped HTML content. ->setDescription('More <strong>detailed</strong> help'); To show the help text as a tooltip instead of inline, -add a `.cms-description-tooltip` class. +add a `.cms-description-tooltip` class. :::php TextField::create('MyText', 'My Text Label') diff --git a/docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/Customise_CMS_Menu.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Customise_CMS_Menu.md similarity index 90% rename from docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/Customise_CMS_Menu.md rename to docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Customise_CMS_Menu.md index af3a1841b..1701378d9 100644 --- a/docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/Customise_CMS_Menu.md +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Customise_CMS_Menu.md @@ -2,53 +2,53 @@ ## 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 -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 the [ModelAdmin reference](../reference/modeladmin). -In this document we'll take the `ProductAdmin` class used in the -[ModelAdmin reference](../reference/modeladmin#setup) and so how we can change -the menu behaviour by using the `$menu_title` and `$menu_icon` statics to +In this document we'll take the `ProductAdmin` class used in the +[ModelAdmin reference](../reference/modeladmin#setup) and so how we can change +the menu behaviour by using the `$menu_title` and `$menu_icon` statics to provide a custom title and icon. ### Defining a Custom Icon -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 +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 `mysite/images`, but you are free to use any location. :::php 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 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. :::php 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 -`<classname>.MENUTITLE` entity name, which is automatically created when running + +In order to localize the menu title in different languages, use the +`<classname>.MENUTITLE` entity name, which is automatically created when running 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. ## Adding an external link to the menu -On top of your administration windows, the menu can also have external links -(e.g. to external reference). In this example, we're going to add a link to +On top of your administration windows, the menu can also have external links +(e.g. to external reference). In this example, we're going to add a link to Google to the menu. First, we need to define a `[api:LeftAndMainExtension]` which will contain our @@ -69,7 +69,7 @@ button configuration. // the link you want to item to go to $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 $priority = -2; @@ -83,14 +83,14 @@ button configuration. } } -To have the link appear, make sure you add the extension to the `LeftAndMain` -class. For more information about configuring extensions see the +To have the link appear, make sure you add the extension to the `LeftAndMain` +class. For more information about configuring extensions see the [DataExtension reference](../reference/dataextension). :::php LeftAndMain::add_extension('CustomLeftAndMain') - + ## Related * [How to extend the CMS interface](extend-cms-interface) diff --git a/docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/Customise_CMS_Pages_List.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Customise_CMS_Pages_List.md similarity index 100% rename from docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/Customise_CMS_Pages_List.md rename to docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Customise_CMS_Pages_List.md diff --git a/docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/Customise_CMS_Tree.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Customise_CMS_Tree.md similarity index 99% rename from docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/Customise_CMS_Tree.md rename to docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Customise_CMS_Tree.md index 742d3b547..64aefc98b 100644 --- a/docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/Customise_CMS_Tree.md +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Customise_CMS_Tree.md @@ -39,11 +39,11 @@ code like this: ... </ul> ... - + By applying the proper style sheet, the snippet html above could produce the look of: ![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 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(){ // return either true or false } - + public function getStatusFlags($cached = true) { $flags = parent::getStatusFlags($cached); $flags['scheduledtopublish'] = "Scheduled To Publish"; diff --git a/docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/Extend_CMS_Interface.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extend_CMS_Interface.md similarity index 90% rename from docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/Extend_CMS_Interface.md rename to docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extend_CMS_Interface.md index 74d80f04a..5475f6d24 100644 --- a/docs/en/02_Developer_Guides/15_Customising_the_CMS/How_To/Extend_CMS_Interface.md +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extend_CMS_Interface.md @@ -2,12 +2,12 @@ ## Introduction ## -The CMS interface works just like any other part of your website: It consists of -PHP controllers, templates, CSS stylesheets and JavaScript. Because it uses the -same base elements, it is relatively easy to extend. +The CMS interface works just like any other part of your website: It consists of +PHP controllers, templates, CSS stylesheets and JavaScript. Because it uses the +same base elements, it is relatively easy to extend. -As an example, we're going to add a permanent "bookmarks" link list to popular pages -into the main CMS menu. A page can be bookmarked by a CMS author through a +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 simple checkbox. 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 ## -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 +If you place a template with an identical name into your application template +directory (usually `mysite/templates/`), it'll take priority over the built-in one. CMS templates are inherited based on their controllers, similar to subclasses of @@ -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` (which corresponds to the `LeftAndMain` PHP controller class). -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 +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 the CMS logic. Add a new section into the `<ul class="cms-menu-list">` - + :::ss ... <ul class="cms-menu-list"> @@ -40,15 +40,15 @@ the CMS logic. Add a new section into the `<ul class="cms-menu-list">` </li> </ul> ... - + 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 -In order to show the links a bit separated from the other menu entries, -we'll add some CSS, and get it to load -with the CMS interface. Paste the following content into a new file called +In order to show the links a bit separated from the other menu entries, +we'll add some CSS, and get it to load +with the CMS interface. Paste the following content into a new file called `mysite/css/BookmarkedPages.css`: :::css @@ -64,9 +64,9 @@ Load the new CSS file into the CMS, by setting the `LeftAndMain.extra_requiremen ## Create a "bookmark" flag on pages ## -Now we'll define which pages are actually bookmarked, a flag that is stored in -the database. For this we need to decorate the page record with a -`DataExtension`. Create a new file called `mysite/code/BookmarkedPageExtension.php` +Now we'll define which pages are actually bookmarked, a flag that is stored in +the database. For this we need to decorate the page record with a +`DataExtension`. Create a new file called `mysite/code/BookmarkedPageExtension.php` and insert the following code. :::php @@ -77,7 +77,7 @@ and insert the following code. private static $db = array( 'IsBookmarked' => 'Boolean' ); - + public function updateCMSFields(FieldList $fields) { $fields->addFieldToTab('Root.Main', 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 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 -links)? Again, we extend a core class: The main CMS controller called +pages from the database into the template we've already created (with hardcoded +links)? Again, we extend a core class: The main CMS controller called `LeftAndMain`. Add the following code to a new file `mysite/code/BookmarkedLeftAndMainExtension.php`; @@ -113,7 +113,7 @@ Add the following code to a new file `mysite/code/BookmarkedLeftAndMainExtension return Page::get()->filter("IsBookmarked", 1); } } - + Enable the extension in your [configuration file](/topics/configuration) :::yml @@ -137,50 +137,50 @@ and replace it with the following: ## Extending the CMS actions -CMS actions follow a principle similar to the CMS fields: they are built in the -backend with the help of `FormFields` and `FormActions`, and the frontend is +CMS actions follow a principle similar to the CMS fields: they are built in the +backend with the help of `FormFields` and `FormActions`, and the frontend is responsible for applying a consistent styling. The following conventions apply: -* New actions can be added by redefining `getCMSActions`, or adding an extension +* New actions can be added by redefining `getCMSActions`, or adding an extension with `updateCMSActions`. -* It is required the actions are contained in a `FieldSet` (`getCMSActions` +* It is required the actions are contained in a `FieldSet` (`getCMSActions` returns this already). -* Standalone buttons are created by adding a top-level `FormAction` (no such +* Standalone buttons are created by adding a top-level `FormAction` (no such button is added by default). -* Button groups are created by adding a top-level `CompositeField` with +* Button groups are created by adding a top-level `CompositeField` with `FormActions` in it. * A `MajorActions` button group is already provided as a default. -* Drop ups with additional actions that appear as links are created via a +* Drop ups with additional actions that appear as links are created via a `TabSet` and `Tabs` with `FormActions` inside. -* A `ActionMenus.MoreOptions` tab is already provided as a default and contains +* A `ActionMenus.MoreOptions` tab is already provided as a default and contains some minor actions. -* You can override the actions completely by providing your own +* You can override the actions completely by providing your own `getAllCMSFields`. Let's walk through a couple of examples of adding new CMS actions in `getCMSActions`. -First of all we can add a regular standalone button anywhere in the set. Here -we are inserting it in the front of all other actions. We could also add a +First of all we can add a regular standalone button anywhere in the set. Here +we are inserting it in the front of all other actions. We could also add a button group (`CompositeField`) in a similar fashion. :::php $fields->unshift(FormAction::create('normal', 'Normal button')); -We can affect the existing button group by manipulating the `CompositeField` +We can affect the existing button group by manipulating the `CompositeField` already present in the `FieldList`. :::php $fields->fieldByName('MajorActions')->push(FormAction::create('grouped', 'New group button')); -Another option is adding actions into the drop-up - best place for placing +Another option is adding actions into the drop-up - best place for placing infrequently used minor actions. :::php $fields->addFieldToTab('ActionMenus.MoreOptions', FormAction::create('minor', 'Minor action')); -We can also easily create new drop-up menus by defining new tabs within the +We can also easily create new drop-up menus by defining new tabs within the `TabSet`. :::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. </div> -New actions will need associated controller handlers to work. You can use a -`LeftAndMainExtension` to provide one. Refer to [Controller documentation](../topics/controller) +New actions will need associated controller handlers to work. You can use a +`LeftAndMainExtension` to provide one. Refer to [Controller documentation](../topics/controller) for instructions on setting up handlers. -To make the actions more user-friendly you can also use alternating buttons as -detailed in the [CMS Alternating Button](../reference/cms-alternating-button) +To make the actions more user-friendly you can also use alternating buttons as +detailed in the [CMS Alternating Button](../reference/cms-alternating-button) how-to. ## Summary diff --git a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extending_An_Existing_ModelAdmin.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extending_An_Existing_ModelAdmin.md new file mode 100644 index 000000000..f1cc60c2f --- /dev/null +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/How_Tos/Extending_An_Existing_ModelAdmin.md @@ -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`. \ No newline at end of file diff --git a/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/index.md b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/index.md new file mode 100644 index 000000000..a721db394 --- /dev/null +++ b/docs/en/02_Developer_Guides/15_Customising_the_Admin_Interface/index.md @@ -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"] \ No newline at end of file diff --git a/docs/en/02_Developer_Guides/15_Customising_the_CMS/00_Typography.md b/docs/en/02_Developer_Guides/15_Customising_the_CMS/00_Typography.md deleted file mode 100644 index dc2f42863..000000000 --- a/docs/en/02_Developer_Guides/15_Customising_the_CMS/00_Typography.md +++ /dev/null @@ -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. \ No newline at end of file diff --git a/docs/en/02_Developer_Guides/15_Customising_the_CMS/01_ModelAdmin.md b/docs/en/02_Developer_Guides/15_Customising_the_CMS/01_ModelAdmin.md deleted file mode 100644 index d29cd73bf..000000000 --- a/docs/en/02_Developer_Guides/15_Customising_the_CMS/01_ModelAdmin.md +++ /dev/null @@ -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) diff --git a/docs/en/02_Developer_Guides/15_Customising_the_CMS/index.md b/docs/en/02_Developer_Guides/15_Customising_the_CMS/index.md deleted file mode 100644 index d986a35db..000000000 --- a/docs/en/02_Developer_Guides/15_Customising_the_CMS/index.md +++ /dev/null @@ -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] \ No newline at end of file