Compare commits
199 Commits
Author | SHA1 | Date |
---|---|---|
Guy Sartorelli | eacb02c297 | |
Guy Sartorelli | afe1cc5da0 | |
Guy Sartorelli | f2b92e8f83 | |
Guy Sartorelli | 2b53f5095c | |
Maxime Rainville | d27f656e97 | |
Steve Boyd | b615184c54 | |
Guy Sartorelli | a9a20ab1c0 | |
Guy Sartorelli | 8ad0537473 | |
Guy Sartorelli | 02d5a17e1f | |
Guy Sartorelli | 53e7646700 | |
Steve Boyd | 812f3dc282 | |
Steve Boyd | 9934adecdb | |
Steve Boyd | 08cdad1290 | |
Guy Sartorelli | 08554e7882 | |
Steve Boyd | 57b1d87577 | |
Steve Boyd | 3fd8120b74 | |
Steve Boyd | d907c77ed6 | |
Guy Sartorelli | 65ef1bbc50 | |
Steve Boyd | d16e4770da | |
Steve Boyd | 3685869917 | |
Steve Boyd | 3d89a29628 | |
Guy Sartorelli | d78326987c | |
Steve Boyd | 51d29b3d5a | |
Maxime Rainville | 5aca443a7c | |
Steve Boyd | 2b9536e5ae | |
Steve Boyd | a08aa08f4d | |
Maxime Rainville | 6c2b654f19 | |
Maxime Rainville | 6e163beb44 | |
Steve Boyd | d4ae7ff9ab | |
Steve Boyd | 92f90fbea5 | |
Maxime Rainville | f759a8b56b | |
Maxime Rainville | 81b59ab4b9 | |
Steve Boyd | 0f37c09531 | |
Steve Boyd | c3fc2cab68 | |
Serge Latyntsev | 0fda5eec16 | |
Steve Boyd | 1265ce99a6 | |
Garion Herman | 8db05ea5f0 | |
Serge Latyntsev | 007b6fcdfa | |
Garion Herman | 42af10671e | |
Garion Herman | f396ee7242 | |
Robbie Averill | ade8047461 | |
wernerkrauss | 5d2f0570d8 | |
Robbie Averill | 7d2dd46159 | |
Dylan Wagstaff | 41c9ecffe6 | |
Robbie Averill | 35df96f502 | |
Robbie Averill | 6ce962607f | |
Robbie Averill | eb81b3c8d9 | |
Raissa North | 129a650729 | |
Dylan Wagstaff | d17c577609 | |
Scott Hutchinson | 294f9b60b5 | |
Robbie Averill | 76b8b3f891 | |
Raissa North | ab8349209a | |
Dylan Wagstaff | 0a3c280c3d | |
Robbie Averill | 8583b894a1 | |
Robbie Averill | 5ca288c9a3 | |
Robbie Averill | e1a0469c46 | |
Robbie Averill | a0b1f9b1cf | |
Robbie Averill | a2f4594597 | |
Raissa North | de7473e9ea | |
Raissa North | 58e26969a3 | |
Raissa North | 285b46fafb | |
Raissa North | 0916970951 | |
Robbie Averill | 5287ebcbe5 | |
Robbie Averill | c5511726ef | |
Robbie Averill | 4dccc49a41 | |
Robbie Averill | 646cd1a210 | |
Robbie Averill | 7767292d50 | |
Robbie Averill | c28c3c1d8d | |
Dylan Wagstaff | f8f10e96ff | |
Robbie Averill | bf9fbe3aa7 | |
Robbie Averill | 406f925beb | |
Robbie Averill | 712fb56897 | |
Adrian Stein | e9e30b6489 | |
Damian Mooyman | 023eef23c6 | |
Robbie Averill | fb91acbe3a | |
Robbie Averill | 3f02a0b295 | |
Dylan Wagstaff | 18296d00e4 | |
Dylan Wagstaff | 36a5840d45 | |
Franco Springveldt | 26f1c2bd74 | |
Robbie Averill | 9b12b602f5 | |
Robbie Averill | 7c85b1b5d9 | |
Robbie Averill | ad6a965526 | |
Robbie Averill | 33f173d39b | |
maks feltrin | 33ef8b9d2c | |
Daniel Hensby | 5437f6b2a5 | |
Daniel Hensby | cc7d188927 | |
Daniel Hensby | 4c87ff3896 | |
Rastislav Brandobur | a434245cfa | |
Robbie Averill | 8f0427922b | |
Robbie Averill | 9aa40a0611 | |
Rastislav Brandobur | 80029afafb | |
Brett Tasker | df4844d45f | |
Damian Mooyman | 1fe4c3db70 | |
Robbie Averill | 8dae0a6f95 | |
Robbie Averill | ec9421ba65 | |
Robbie Averill | e7de0c61bf | |
Robbie Averill | 8eee397b9a | |
Robbie Averill | defb4bf134 | |
Daniel Hensby | f3b14df4e4 | |
Daniel Hensby | ef40ca30aa | |
Daniel Hensby | 1bbaba52e0 | |
Daniel Hensby | 616bcdb364 | |
Daniel Hensby | 8c705392e9 | |
Daniel Hensby | 661e7ee48a | |
Daniel Hensby | cc591f2c56 | |
Daniel Hensby | 40af14906d | |
Garion Herman | 04dbb41125 | |
Damian Mooyman | 6dd4ff8525 | |
Damian Mooyman | 58aa2a6a35 | |
Daniel Hensby | 0986537be7 | |
rasstislav | 86bf12a181 | |
Damian Mooyman | 8d5203c08a | |
Daniel Hensby | cbebc589b2 | |
torleif | 3bc304b844 | |
Daniel Hensby | 36ccdf2ebc | |
torleif | 987d29341e | |
Damian Mooyman | 9e2b4f1586 | |
torleif | e6ea99ac12 | |
Stevie Mayhew | 4aa66af42d | |
torleif | 92e3fd6d9a | |
torleif | 1719ee3248 | |
Damian Mooyman | 90d6402185 | |
Damian Mooyman | 04131d5b09 | |
Damian Mooyman | 9aac12ab66 | |
Daniel Hensby | 5ef0ef34a7 | |
Damian Mooyman | 9026821f29 | |
Damian Mooyman | 783f41dffa | |
helpfulrobot | cd434c3e25 | |
Stefan Eickhoff | 65b72e6474 | |
Daniel Hensby | db6500c3b0 | |
helpfulrobot | c53a1d5931 | |
Daniel Hensby | 79f5ebb167 | |
Damian Mooyman | 7d0e8f2bd0 | |
Daniel Hensby | 453ce12c2d | |
Daniel Hensby | dfe1236eef | |
Daniel Hensby | a77ba66858 | |
Daniel Hensby | 712a655cd8 | |
Daniel Hensby | 3e217a8d69 | |
Daniel Hensby | 5b8574eedd | |
helpfulrobot | 085d8f881f | |
helpfulrobot | 0fce21beed | |
helpfulrobot | 08a2b444cf | |
Scott Hutchinson | ecce5327a8 | |
Damian Mooyman | 6104f17220 | |
helpfulrobot | cc836f3be4 | |
helpfulrobot | 6e9e3c9aaa | |
Daniel Hensby | 9fcc75b4d6 | |
Daniel Hensby | dbafc880a3 | |
helpfulrobot | 2dd717cb84 | |
helpfulrobot | 130e9d06a1 | |
helpfulrobot | b41ad4cf71 | |
Damian Mooyman | d66a641934 | |
Damian Mooyman | 799c2f1bf0 | |
John Milmine | 0f207180b4 | |
Damian Mooyman | 2fe849365f | |
ezero | ce9095cf71 | |
Christopher Pitt | ae90bb1591 | |
Christopher Pitt | f17f56acd3 | |
Christopher Pitt | bf4d36d40b | |
Shoaib Ali | a5b37aa204 | |
Shoaib Ali | ed56b29fe8 | |
Shoaib Ali | 6136b4f1ea | |
Shoaib Ali | 4e5f1bf3e2 | |
Christopher Pitt | 705367eb32 | |
Shoaib Ali | 9cd3837dbc | |
Shoaib Ali | 210ffc9645 | |
Christopher Pitt | 936cb63826 | |
Shoaib Ali | a586a034d1 | |
Shoaib Ali | a397764ad7 | |
Cam Findlay | c202259bc1 | |
Christopher Pitt | ff39cd6eb4 | |
Christopher Pitt | eee39bca4e | |
Damian Mooyman | 79db693835 | |
Scott Hutchinson | c16db15e9e | |
Damian Mooyman | ca1bb3d037 | |
☃ Stephen Shkardoon ☃ | 17c94ff9ce | |
Matthew Hailwood | ff0178330a | |
Damian Mooyman | c332a6ab8b | |
Daniel Hensby | 4d9fc0056c | |
Damian Mooyman | b95f204374 | |
Christopher Pitt | bf5a08da76 | |
Christopher Pitt | 791d32a303 | |
Damian Mooyman | ed6114a7dc | |
Christopher Pitt | cdfec4c7dc | |
Damian Mooyman | ed8f7d867a | |
Damian Mooyman | ccea929cb9 | |
Ingo Schommer | 25a1a35a0b | |
Ingo Schommer | d0ce44401f | |
Ingo Schommer | f2f6b6e18c | |
Will Rossiter | 557a7219f4 | |
UndefinedOffset | 54599b98d1 | |
Christopher Pitt | 59e4ba50f2 | |
Damian Mooyman | b23606b498 | |
Christopher Pitt | 871ba84d97 | |
Damian Mooyman | 9c678ca540 | |
Damian Mooyman | f35800541e | |
Will Rossiter | 674ea5d591 | |
Daniel Hensby | 1b515b7dea | |
UndefinedOffset | 512a6aafee |
|
@ -0,0 +1,17 @@
|
|||
# For more information about the properties used in this file,
|
||||
# please see the EditorConfig documentation:
|
||||
# http://editorconfig.org
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[{*.yml,package.json}]
|
||||
indent_size = 2
|
||||
|
||||
# The indent size used in the package.json file cannot be changed:
|
||||
# https://github.com/npm/npm/pull/3180#issuecomment-16336516
|
|
@ -0,0 +1,7 @@
|
|||
/tests export-ignore
|
||||
/docs export-ignore
|
||||
/.gitattributes export-ignore
|
||||
/.gitignore export-ignore
|
||||
/.travis.yml export-ignore
|
||||
/.scrutinizer.yml export-ignore
|
||||
/codecov.yml export-ignore
|
|
@ -0,0 +1,11 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
name: CI
|
||||
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1
|
|
@ -0,0 +1,16 @@
|
|||
name: Dispatch CI
|
||||
|
||||
on:
|
||||
# At 1:00 PM UTC, only on Saturday and Sunday
|
||||
schedule:
|
||||
- cron: '0 13 * * 6,0'
|
||||
|
||||
jobs:
|
||||
dispatch-ci:
|
||||
name: Dispatch CI
|
||||
# Only run cron on the silverstripe account
|
||||
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Dispatch CI
|
||||
uses: silverstripe/gha-dispatch-ci@v1
|
|
@ -0,0 +1,17 @@
|
|||
name: Keepalive
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# The 4th of every month at 10:50am UTC
|
||||
schedule:
|
||||
- cron: '50 10 4 * *'
|
||||
|
||||
jobs:
|
||||
keepalive:
|
||||
name: Keepalive
|
||||
# Only run cron on the silverstripe account
|
||||
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Keepalive
|
||||
uses: silverstripe/gha-keepalive@v1
|
22
.travis.yml
22
.travis.yml
|
@ -1,22 +0,0 @@
|
|||
# See https://github.com/silverstripe-labs/silverstripe-travis-support for setup details
|
||||
|
||||
language: php
|
||||
php:
|
||||
- 5.3
|
||||
|
||||
env:
|
||||
- DB=MYSQL CORE_RELEASE=3.1
|
||||
- DB=PGSQL CORE_RELEASE=master
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.4
|
||||
env: DB=MYSQL CORE_RELEASE=master
|
||||
|
||||
before_script:
|
||||
- git clone git://github.com/silverstripe-labs/silverstripe-travis-support.git ~/travis-support
|
||||
- php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss
|
||||
- cd ~/builds/ss
|
||||
|
||||
script:
|
||||
- phpunit widgets/tests/
|
|
@ -1,8 +1,9 @@
|
|||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[silverstripe-widgets.master]
|
||||
[o:silverstripe:p:silverstripe-widgets:r:master]
|
||||
file_filter = lang/<lang>.yml
|
||||
source_file = lang/en.yml
|
||||
source_lang = en
|
||||
type = YML
|
||||
type = YML
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
mappings:
|
||||
WidgetContentControllerExtension: SilverStripe\Widgets\Controllers\WidgetContentControllerExtension
|
||||
Widget_Controller: SilverStripe\Widgets\Models\WidgetController
|
||||
WidgetPageExtension: SilverStripe\Widgets\Extensions\WidgetPageExtension
|
||||
WidgetAreaEditor: SilverStripe\Widgets\Forms\WidgetAreaEditor
|
||||
Widget: SilverStripe\Widgets\Model\Widget
|
||||
WidgetArea: SilverStripe\Widgets\Model\WidgetArea
|
24
LICENSE
24
LICENSE
|
@ -1,24 +0,0 @@
|
|||
* Copyright (c) 2011, SilverStripe Ltd
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the <organization> nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY SilverStripe Ltd. ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL Silverstripe Ltd. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
442
README.md
442
README.md
|
@ -1,34 +1,38 @@
|
|||
# Widgets Module
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/silverstripe/silverstripe-widgets.png?branch=master)](http://travis-ci.org/silverstripe/silverstripe-widgets)
|
||||
[![CI](https://github.com/silverstripe/silverstripe-widgets/actions/workflows/ci.yml/badge.svg)](https://github.com/silverstripe/silverstripe-widgets/actions/workflows/ci.yml)
|
||||
[![Silverstripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
|
||||
|
||||
## Introduction
|
||||
## Overview
|
||||
|
||||
[Widgets](http://silverstripe.org/widgets) are small pieces of functionality such as showing the latest comments or Flickr photos. They normally display on
|
||||
the sidebar of your website. To check out a what a [Widget](http://silverstripe.org/widgets) can do watch the
|
||||
[Widget video](http://silverstripe.com/assets/screencasts/SilverStripe-Blog-DragDrop-Widgets.swf) and try out the
|
||||
[demo site](http://demo.silverstripe.org/)
|
||||
Widgets are small pieces of functionality such as showing the latest comments or Flickr photos. They normally display on
|
||||
the sidebar of your website.
|
||||
|
||||
## Requirements
|
||||
|
||||
* SilverStripe 3.1
|
||||
* Silverstripe 4.0
|
||||
|
||||
**Note:** this version is compatible with Silverstripe 4. For Silverstripe 3, please see [the 1.x release line](https://github.com/silverstripe/silverstripe-widgets/tree/1.3).
|
||||
|
||||
### Installation
|
||||
|
||||
Install the module through [composer](http://getcomposer.org):
|
||||
|
||||
composer require silverstripe/widgets
|
||||
```
|
||||
$ composer require silverstripe/widgets
|
||||
```
|
||||
|
||||
Widgets are essentially database relations to other models, mostly page types.
|
||||
By default, they're not added to any of your own models. The easiest and most common
|
||||
way to get started would be to create a single collection of widgets under the
|
||||
way to get started would be to create a single collection of widgets under the
|
||||
name "SideBar" on your `Page` class. This is handled by an extension which you
|
||||
can enable through your `config.yml`:
|
||||
|
||||
:::yml
|
||||
Page:
|
||||
extensions:
|
||||
- WidgetPageExtension
|
||||
```yaml
|
||||
Page:
|
||||
extensions:
|
||||
- SilverStripe\Widgets\Extensions\WidgetPageExtension
|
||||
```
|
||||
|
||||
Run a `dev/build`, and adjust your templates to include the resulting sidebar view.
|
||||
The placeholder is called `$SideBarView`, and loops through all widgets assigned
|
||||
|
@ -36,43 +40,60 @@ to the current page.
|
|||
|
||||
Alternatively, you can add one or more widget collections to your own page types.
|
||||
Here's an example on how to just add widgets to a `MyPage` type, and call it
|
||||
`MyWidgetArea` instead.
|
||||
`MyWidgetArea` instead. Please ensure you add the correct namespaces for your module.
|
||||
|
||||
### Installing a widget
|
||||
|
||||
By following the "Packaging" rules below, widgets are easily installed. This example uses the Blog module which by default has widgets already enabled.
|
||||
|
||||
* Install the [blog module](http://www.silverstripe.org/blog-module/).
|
||||
* Download the widget and unzip to the main folder of your SilverStripe website, e.g. to `/widget_<widget-name>/`. The folder
|
||||
|
||||
* Install the [blog module](https://github.com/silverstripe/silverstripe-blog/).
|
||||
* Download the widget and unzip to the main folder of your Silverstripe website, e.g. to `/widget_<widget-name>/`. The folder
|
||||
will contain a few files, which generally won't need editing or reading.
|
||||
* Run `http://my-website.com/dev/build`
|
||||
* Login to the CMS and go to the 'Blog' page. Choose the "widgets" tab and click the new widget to activate it.
|
||||
* Your blog will now have the widget shown
|
||||
|
||||
## Documentation
|
||||
|
||||
### Adding widgets to other pages
|
||||
See the [docs/en](docs/en/introduction.md) folder.
|
||||
|
||||
You have to do a couple things to get a Widget to work on a page.
|
||||
## Versioning
|
||||
|
||||
This library follows [Semver](http://semver.org). According to Semver, you will be able to upgrade to any minor or patch version of this library without any breaking changes to the public API. Semver also requires that we clearly define the public API for this library.
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
Please [create an issue](http://github.com/silverstripe/silverstripe-widgets/issues) for any bugs you've found, or features you're missing.
|
||||
|
||||
* Install the Widgets Module, see above.
|
||||
* Add a WidgetArea field to your Page.
|
||||
* Add a new tab to the CMS with a WidgetAreaEditor field for managing the widgets.
|
||||
* Add a WidgetArea field to your Page.
|
||||
* Add a new tab to the CMS with a WidgetAreaEditor field for managing the widgets.
|
||||
e.g.
|
||||
|
||||
**mysite/code/Page.php**
|
||||
|
||||
class Page extends SiteTree {
|
||||
...
|
||||
private static $has_one = array(
|
||||
"MyWidgetArea" => "WidgetArea",
|
||||
);
|
||||
|
||||
public function getCMSFields() {
|
||||
$fields = parent::getCMSFields();
|
||||
$fields->addFieldToTab("Root.Widgets", new WidgetAreaEditor("MyWidgetArea"));
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
```php
|
||||
<?php
|
||||
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Widgets\Forms\WidgetAreaEditor;
|
||||
use SilverStripe\Widgets\Model\WidgetArea;
|
||||
|
||||
class Page extends SiteTree
|
||||
{
|
||||
// ...
|
||||
private static $has_one = array(
|
||||
'MyWidgetArea' => WidgetArea::class
|
||||
);
|
||||
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = parent::getCMSFields();
|
||||
$fields->addFieldToTab('Root.Widgets', new WidgetAreaEditor('MyWidgetArea'));
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this case, you need to alter your templates to include the `$MyWidgetArea` placeholder.
|
||||
|
||||
|
@ -85,84 +106,105 @@ blank). Each widget should be in its own folder like widgets_widgetName/
|
|||
After installing or creating a new widget, **make sure to run db/build?flush=1** at the end of the URL, *before*
|
||||
attempting to use it.
|
||||
|
||||
The class should extend the Widget class, and must specify three static variables - $title, the title that will appear
|
||||
in the rendered widget (eg Photos), $cmsTitle, a more descriptive title that will appear in the cms editor (eg Flickr
|
||||
Photos), and $description, a short description that will appear in the cms editor (eg This widget shows photos from
|
||||
The class should extend the Widget class, and must specify three config variables:
|
||||
|
||||
* `title`: The title that will appear in the rendered widget (eg Photos). This can be customised by the CMS admin
|
||||
* `cmsTitle`: a more descriptive title that will appear in the cms editor (eg Flickr Photos)
|
||||
* `description`: a short description that will appear in the cms editor (eg This widget shows photos from
|
||||
Flickr). The class may also specify functions to be used in the template like a page type can.
|
||||
|
||||
If a Widget has configurable options, then it can specify a number of database fields to store these options in via the
|
||||
static $db array, and also specify a getCMSFields function that returns a !FieldList, much the same way as a page type
|
||||
static `$db` array, and also specify a `getCMSFields` function that returns a `FieldList`, much the same way as a page type
|
||||
does.
|
||||
|
||||
An example widget is below:
|
||||
|
||||
**FlickrWidget.php**
|
||||
|
||||
:::php
|
||||
<?php
|
||||
class FlickrWidget extends Widget {
|
||||
private static $db = array(
|
||||
"User" => "Varchar",
|
||||
"Photoset" => "Varchar",
|
||||
"Tags" => "Varchar",
|
||||
"NumberToShow" => "Int"
|
||||
);
|
||||
|
||||
|
||||
private static $defaults = array(
|
||||
"NumberToShow" => 8
|
||||
);
|
||||
|
||||
private static $title = "Photos";
|
||||
private static $cmsTitle = "Flickr Photos";
|
||||
private static $description = "Shows flickr photos.";
|
||||
|
||||
public function Photos() {
|
||||
Requirements::javascript(THIRDPARTY_DIR . "/prototype/prototype.js");
|
||||
Requirements::javascript(THIRDPARTY_DIR . "/scriptaculous/effects.js");
|
||||
Requirements::javascript("mashups/javascript/lightbox.js");
|
||||
Requirements::css("mashups/css/lightbox.css");
|
||||
|
||||
$flickr = new FlickrService();
|
||||
if($this->Photoset == "") {
|
||||
$photos = $flickr->getPhotos($this->Tags, $this->User, $this->NumberToShow, 1);
|
||||
} else {
|
||||
$photos = $flickr->getPhotoSet($this->Photoset, $this->User, $this->NumberToShow, 1);
|
||||
}
|
||||
|
||||
$output = new ArrayList();
|
||||
foreach($photos->PhotoItems as $photo) {
|
||||
$output->push(new ArrayData(array(
|
||||
"Title" => $photo->title,
|
||||
"Link" => "http://farm1.static.flickr.com/" . $photo->image_path .".jpg",
|
||||
"Image" => "http://farm1.static.flickr.com/" .$photo->image_path. "_s.jpg"
|
||||
)));
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function getCMSFields() {
|
||||
return new FieldList(
|
||||
new TextField("User", "User"),
|
||||
new TextField("PhotoSet", "Photo Set"),
|
||||
new TextField("Tags", "Tags"),
|
||||
new NumericField("NumberToShow", "Number to Show")
|
||||
);
|
||||
}
|
||||
}
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Yourname\MyWidget;
|
||||
|
||||
use FlickrService;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\Forms\NumericField;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\View\ArrayData;
|
||||
use SilverStripe\View\Requirements;
|
||||
|
||||
class FlickrWidget extends Widget
|
||||
{
|
||||
private static $db = array(
|
||||
'User' => 'Varchar',
|
||||
'Photoset' => 'Varchar',
|
||||
'Tags' => 'Varchar',
|
||||
'NumberToShow' => 'Int'
|
||||
);
|
||||
|
||||
private static $defaults = array(
|
||||
'NumberToShow' => 8
|
||||
);
|
||||
|
||||
private static $title = 'Photos';
|
||||
private static $cmsTitle = 'Flickr Photos';
|
||||
private static $description = 'Shows flickr photos.';
|
||||
|
||||
public function Photos()
|
||||
{
|
||||
// You'll need to install these yourself
|
||||
Requirements::javascript(THIRDPARTY_DIR . '/prototype/prototype.js');
|
||||
Requirements::javascript(THIRDPARTY_DIR . '/scriptaculous/effects.js');
|
||||
Requirements::javascript('mashups/javascript/lightbox.js');
|
||||
Requirements::css('mashups/css/lightbox.css');
|
||||
|
||||
$flickr = new FlickrService();
|
||||
if ($this->Photoset == '') {
|
||||
$photos = $flickr->getPhotos($this->Tags, $this->User, $this->NumberToShow, 1);
|
||||
} else {
|
||||
$photos = $flickr->getPhotoSet($this->Photoset, $this->User, $this->NumberToShow, 1);
|
||||
}
|
||||
|
||||
$output = new ArrayList();
|
||||
foreach ($photos->PhotoItems as $photo) {
|
||||
$output->push(
|
||||
new ArrayData(
|
||||
array(
|
||||
'Title' => $photo->title,
|
||||
'Link' => 'http://farm1.static.flickr.com/' . $photo->image_path .'.jpg',
|
||||
'Image' => 'http://farm1.static.flickr.com/' .$photo->image_path. '_s.jpg'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function getCMSFields()
|
||||
{
|
||||
return new FieldList(
|
||||
new TextField('User', 'User'),
|
||||
new TextField('PhotoSet', 'Photo Set'),
|
||||
new TextField('Tags', 'Tags'),
|
||||
new NumericField('NumberToShow', 'Number to Show')
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**FlickrWidget.ss**
|
||||
|
||||
:::ss
|
||||
<% control Photos %>
|
||||
<a href="$Link" rel="lightbox" title="$Title"><img src="$Image" alt="$Title" /></a>
|
||||
<% end_control %>
|
||||
|
||||
```
|
||||
<% control Photos %>
|
||||
<a href="$Link" rel="lightbox" title="$Title"><img src="$Image" alt="$Title" /></a>
|
||||
<% end_control %>
|
||||
```
|
||||
|
||||
## Releasing a widget
|
||||
|
||||
Follow the [standard procedures defined for releasing a SilverStripe module](http://doc.silverstripe.org/framework/en/3.1/topics/module-development).
|
||||
Follow the [standard procedures defined for releasing a Silverstripe module](https://docs.silverstripe.org/en/4/developer_guides/extending/how_tos/publish_a_module).
|
||||
|
||||
Here is a composer template you can use.
|
||||
|
||||
|
@ -182,8 +224,8 @@ You need to finish off / change:
|
|||
"type": "silverstripe-module",
|
||||
"keywords" : ["widget"],
|
||||
"require": {
|
||||
"silverstripe/framework": "3.*",
|
||||
"silverstripe/cms": "3.*"
|
||||
"silverstripe/framework": "^4.0",
|
||||
"silverstripe/cms": "^4.0"
|
||||
},
|
||||
"license": "BSD-2-Clause",
|
||||
"authors": [
|
||||
|
@ -194,6 +236,11 @@ You need to finish off / change:
|
|||
],
|
||||
"extra" : {
|
||||
"installer-name": "widgets_"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Yourname\\MyWidget\\": "src/"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -203,59 +250,64 @@ You need to finish off / change:
|
|||
### Rendering a $Widget Individually
|
||||
|
||||
To call a single Widget in a page - without adding a widget area in the CMS for you to add / delete the widgets, you can
|
||||
define a merge variable in the Page Controller and include it in the Page Template.
|
||||
define a merge variable in the Page Controller and include it in the Page Template.
|
||||
|
||||
This example creates an RSSWidget with the SilverStripe blog feed.
|
||||
This example creates an RSSWidget with the Silverstripe blog feed.
|
||||
|
||||
:::php
|
||||
public function SilverStripeFeed() {
|
||||
$widget = new RSSWidget();
|
||||
$widget->RssUrl = "http://feeds.feedburner.com/silverstripe-blog";
|
||||
return $widget->renderWith("WidgetHolder");
|
||||
}
|
||||
```php
|
||||
public function SilverStripeFeed()
|
||||
{
|
||||
$widget = new RSSWidget();
|
||||
$widget->RssUrl = 'http://feeds.feedburner.com/silverstripe-blog';
|
||||
return $widget->renderWith('WidgetHolder');
|
||||
}
|
||||
```
|
||||
|
||||
To render the widget, simply include $SilverStripeFeed in your template:
|
||||
To render the widget, simply include `$SilverStripeFeed` in your template:
|
||||
|
||||
$SilverStripeFeed
|
||||
```
|
||||
$SilverStripeFeed
|
||||
```
|
||||
|
||||
As directed in the definition of `SilverStripeFeed()`, the Widget will be rendered through the WidgetHolder template. This
|
||||
is pre-defined at `widgets/templates/WidgetHolder.ss` and simply consists of:
|
||||
|
||||
As directed in the definition of SilverStripeFeed(), the Widget will be rendered through the WidgetHolder template. This
|
||||
is pre-defined at `framework/templates/WidgetHolder.ss` and simply consists of:
|
||||
|
||||
:::ss
|
||||
<div class="WidgetHolder">
|
||||
<h3>$Title</h3>
|
||||
$Content
|
||||
</div>
|
||||
|
||||
```
|
||||
<div class="WidgetHolder">
|
||||
<h3>$Title</h3>
|
||||
$Content
|
||||
</div>
|
||||
```
|
||||
|
||||
You can override the WidgetHolder.ss and Widget.ss templates in your theme too by adding WidgetHolder and Widget
|
||||
templates to `themes/myThemeName/templates/Includes/`
|
||||
|
||||
### Changing the title of your widget
|
||||
|
||||
To change the title of your widget, you need to override the Title() method. By default, this simply returns the $title
|
||||
To change the title of your widget, you need to override the `getTitle()` method. By default, this simply returns the `$title`
|
||||
variable. For example, to set your widgets title to 'Hello World!', you could use:
|
||||
|
||||
**widgets_yourWidget/YourWidgetWidget.php**
|
||||
**widgets_yourWidget/src/YourWidgetWidget.php**
|
||||
|
||||
:::php
|
||||
public function Title() {
|
||||
return "Hello World!";
|
||||
}
|
||||
```php
|
||||
public function getTitle()
|
||||
{
|
||||
return 'Hello World!';
|
||||
}
|
||||
```
|
||||
|
||||
but, you can do exactly the same by setting your `$title` variable.
|
||||
|
||||
but, you can do exactly the same by setting your $title variable.
|
||||
|
||||
A more common reason for overriding Title() is to allow the title to be set in the CMS. Say you had a text field in your
|
||||
A more common reason for overriding `getTitle()` is to allow the title to be set in the CMS. Say you had a text field in your
|
||||
widget called WidgetTitle, that you wish to use as your title. If nothing is set, then you'll use your default title.
|
||||
This is similar to the RSS Widget in the blog module.
|
||||
|
||||
:::php
|
||||
public function Title() {
|
||||
return $this->WidgetTitle ? $this->WidgetTitle : self::$title;
|
||||
}
|
||||
|
||||
```php
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->WidgetTitle ? $this->WidgetTitle : self::$title;
|
||||
}
|
||||
```
|
||||
|
||||
This returns the value inputted in the CMS, if it's set or what is in the $title variable if it isn't.
|
||||
|
||||
|
@ -267,74 +319,100 @@ sure that your controller follows the usual naming conventions, and it will be a
|
|||
|
||||
**mysite/code/MyWidget.php**
|
||||
|
||||
:::php
|
||||
class MyWidget extends Widget {
|
||||
private static $db = array(
|
||||
'TestValue' => 'Text'
|
||||
);
|
||||
}
|
||||
|
||||
class MyWidget_Controller extends WidgetController {
|
||||
public function MyFormName() {
|
||||
return new Form(
|
||||
$this,
|
||||
'MyFormName',
|
||||
new FieldList(
|
||||
new TextField('TestValue')
|
||||
),
|
||||
new FieldList(
|
||||
new FormAction('doAction')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function doAction($data, $form) {
|
||||
// $this->widget points to the widget
|
||||
}
|
||||
}
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Yourname\MyWidget;
|
||||
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
class MyWidget extends Widget
|
||||
{
|
||||
private static $db = array(
|
||||
'TestValue' => 'Text'
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Yourname\MyWidget;
|
||||
|
||||
use SilverStripe\Widgets\Controllers\WidgetController;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\FormAction;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\TextField;
|
||||
|
||||
class MyWidgetController extends WidgetController
|
||||
{
|
||||
public function MyFormName()
|
||||
{
|
||||
return new Form(
|
||||
$this,
|
||||
'MyFormName',
|
||||
new FieldList(
|
||||
new TextField('TestValue')
|
||||
),
|
||||
new FieldList(
|
||||
new FormAction('doAction')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function doAction($data, $form)
|
||||
{
|
||||
// $this->widget points to the widget
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To output this form, modify your widget template.
|
||||
|
||||
**mysite/templates/MyWidget.ss**
|
||||
**mysite/templates/Yourname/MyWidget/MyWidget.ss**
|
||||
|
||||
:::ss
|
||||
$Content
|
||||
$MyFormName
|
||||
```
|
||||
$Content
|
||||
$MyFormName
|
||||
```
|
||||
|
||||
**Note:** The necessary controller actions are only present in subclasses of `Page_Controller`. To use
|
||||
widget forms in other controller subclasses, have a look at *ContentController->handleWidget()* and
|
||||
*ContentController::$url_handlers*.
|
||||
**Note:** The necessary controller actions are only present in subclasses of `PageController`. To use
|
||||
widget forms in other controller subclasses, have a look at `ContentController->handleWidget()` and
|
||||
`ContentController::$url_handlers`.
|
||||
|
||||
## But what if I have widgets on my blog currently??
|
||||
|
||||
**Note:** This applies to old versions of the blog module. The latest version of this module does not contain `BlogHolder.php`.
|
||||
|
||||
If you currently have a blog installed, the widget fields are going to double up on those pages (as the blog extends the
|
||||
Page class). One way to fix this is to comment out line 30 in BlogHolder.php and remove the DB entry by running a
|
||||
Page class). One way to fix this is to comment out line 30 in `BlogHolder.php` and remove the DB entry by running a
|
||||
`http://www.mysite.com/db/build`.
|
||||
|
||||
**blog/code/BlogHolder.php**
|
||||
|
||||
:::php
|
||||
<?php
|
||||
class BlogHolder extends Page {
|
||||
|
||||
........
|
||||
static $has_one = array(
|
||||
// "Sidebar" => "WidgetArea", COMMENT OUT
|
||||
'Newsletter' => 'NewsletterType'
|
||||
.......
|
||||
public function getCMSFields() {
|
||||
$fields = parent::getCMSFields();
|
||||
$fields->removeFieldFromTab("Root.Content","Content");
|
||||
// $fields->addFieldToTab("Root.Widgets", new WidgetAreaEditor("Sidebar")); COMMENT OUT
|
||||
|
||||
........
|
||||
|
||||
```php
|
||||
<?php
|
||||
class BlogHolder extends Page
|
||||
{
|
||||
// ........
|
||||
static $has_one = array(
|
||||
// "Sidebar" => "WidgetArea", COMMENT OUT
|
||||
'Newsletter' => 'NewsletterType'
|
||||
);
|
||||
// .......
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = parent::getCMSFields();
|
||||
$fields->removeFieldFromTab("Root.Content","Content");
|
||||
// $fields->addFieldToTab("Root.Widgets", new WidgetAreaEditor("Sidebar")); COMMENT OUT
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Then you can use the Widget area you defined on Page.php
|
||||
|
||||
## Contributing
|
||||
|
||||
### Translations
|
||||
|
||||
Translations of the natural language strings are managed through a
|
||||
|
@ -345,4 +423,4 @@ and any new translations will be merged back to the project source code.
|
|||
Please use [https://www.transifex.com/projects/p/silverstripe-widgets/](https://www.transifex.com/projects/p/silverstripe-widgets/) to contribute translations,
|
||||
rather than sending pull requests with YAML files.
|
||||
|
||||
See the ["i18n" topic](http://doc.silverstripe.org/framework/en/trunk/topics/i18n) on doc.silverstripe.org for more details.
|
||||
See the ["i18n" topic](https://docs.silverstripe.org/en/4/developer_guides/i18n/) on doc.silverstripe.org for more details.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<?php
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
Name: contentcontrollerurlhandler
|
||||
Name: widgetscontentcontrollerurlhandler
|
||||
---
|
||||
ContentController:
|
||||
SilverStripe\CMS\Controllers\ContentController:
|
||||
extensions:
|
||||
- WidgetContentControllerExtension
|
||||
- SilverStripe\Widgets\Controllers\WidgetContentControllerExtension
|
||||
url_handlers:
|
||||
'widget/$ID!': 'handleWidget'
|
||||
'widget/$ID!': 'handleWidget'
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
Name: widgetslegacymapping
|
||||
---
|
||||
SilverStripe\ORM\DatabaseAdmin:
|
||||
classname_value_remapping:
|
||||
Widget: SilverStripe\Widgets\Model\Widget
|
||||
WidgetArea: SilverStripe\Widgets\Model\WidgetArea
|
|
@ -1,3 +1,6 @@
|
|||
Director:
|
||||
---
|
||||
Name: widgetsroutes
|
||||
---
|
||||
SilverStripe\Control\Director:
|
||||
rules:
|
||||
'WidgetController//$Action/$ID/$OtherID': 'WidgetController'
|
||||
'WidgetController//$Action/$ID/$OtherID': 'SilverStripe\Widgets\Model\WidgetController'
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
# Change Log
|
||||
|
||||
## [1.2.2](https://github.com/silverstripe/silverstripe-widgets/tree/1.2.2) (2016-02-04)
|
||||
[Full Changelog](https://github.com/silverstripe/silverstripe-widgets/compare/1.2.1...1.2.2)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Broken Links in README [\#119](https://github.com/silverstripe/silverstripe-widgets/issues/119)
|
||||
- Loop Children / with has\_one \(page\) not working [\#81](https://github.com/silverstripe/silverstripe-widgets/issues/81)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix readme.md links [\#120](https://github.com/silverstripe/silverstripe-widgets/pull/120) ([tractorcow](https://github.com/tractorcow))
|
||||
- Updated license year [\#118](https://github.com/silverstripe/silverstripe-widgets/pull/118) ([helpfulrobot](https://github.com/helpfulrobot))
|
||||
- ItemsToRender might return NULL // avoid warnings [\#117](https://github.com/silverstripe/silverstripe-widgets/pull/117) ([steiha](https://github.com/steiha))
|
||||
- Added standard code of conduct [\#116](https://github.com/silverstripe/silverstripe-widgets/pull/116) ([helpfulrobot](https://github.com/helpfulrobot))
|
||||
- Remove invalid 3.1 test fixture [\#115](https://github.com/silverstripe/silverstripe-widgets/pull/115) ([tractorcow](https://github.com/tractorcow))
|
||||
- Added standard git attributes [\#114](https://github.com/silverstripe/silverstripe-widgets/pull/114) ([helpfulrobot](https://github.com/helpfulrobot))
|
||||
- Added license to composer.json [\#113](https://github.com/silverstripe/silverstripe-widgets/pull/113) ([helpfulrobot](https://github.com/helpfulrobot))
|
||||
- Added standard license [\#112](https://github.com/silverstripe/silverstripe-widgets/pull/112) ([helpfulrobot](https://github.com/helpfulrobot))
|
||||
- Update changelog for 1.2.1 [\#111](https://github.com/silverstripe/silverstripe-widgets/pull/111) ([tractorcow](https://github.com/tractorcow))
|
||||
- Added standard Travis config [\#110](https://github.com/silverstripe/silverstripe-widgets/pull/110) ([helpfulrobot](https://github.com/helpfulrobot))
|
||||
- Added standard editor config [\#109](https://github.com/silverstripe/silverstripe-widgets/pull/109) ([helpfulrobot](https://github.com/helpfulrobot))
|
||||
- Converted to PSR-2 [\#107](https://github.com/silverstripe/silverstripe-widgets/pull/107) ([helpfulrobot](https://github.com/helpfulrobot))
|
||||
|
||||
## [1.2.1](https://github.com/silverstripe/silverstripe-widgets/tree/1.2.1) (2015-11-18)
|
||||
[Full Changelog](https://github.com/silverstripe/silverstripe-widgets/compare/1.1.2...1.2.1)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Making widgets able to be used in a non page controller [\#105](https://github.com/silverstripe/silverstripe-widgets/pull/105) ([pitchandtone](https://github.com/pitchandtone))
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Added standard Travis config [\#108](https://github.com/silverstripe/silverstripe-widgets/pull/108) ([helpfulrobot](https://github.com/helpfulrobot))
|
||||
- Added standard Scrutinizer config [\#106](https://github.com/silverstripe/silverstripe-widgets/pull/106) ([helpfulrobot](https://github.com/helpfulrobot))
|
||||
- OSS-905 Moved contributions of Widgets to contributing.md [\#103](https://github.com/silverstripe/silverstripe-widgets/pull/103) ([shoaibali](https://github.com/shoaibali))
|
||||
- OSS-905 Moved docs to docs/en/ [\#102](https://github.com/silverstripe/silverstripe-widgets/pull/102) ([shoaibali](https://github.com/shoaibali))
|
||||
- OSS-905 Added code of conduct [\#101](https://github.com/silverstripe/silverstripe-widgets/pull/101) ([shoaibali](https://github.com/shoaibali))
|
||||
- OSS-905 Added changelog [\#100](https://github.com/silverstripe/silverstripe-widgets/pull/100) ([shoaibali](https://github.com/shoaibali))
|
||||
- OSS-905 Moved contributing section to contributing.md [\#99](https://github.com/silverstripe/silverstripe-widgets/pull/99) ([shoaibali](https://github.com/shoaibali))
|
||||
- OSS-905 Added gitattributes [\#98](https://github.com/silverstripe/silverstripe-widgets/pull/98) ([shoaibali](https://github.com/shoaibali))
|
||||
- Added Scrutinizer support [\#96](https://github.com/silverstripe/silverstripe-widgets/pull/96) ([assertchris](https://github.com/assertchris))
|
||||
- BUG Prevent non-widget tests from loading WidgetAreaEditorTest\_FakePage every query [\#95](https://github.com/silverstripe/silverstripe-widgets/pull/95) ([tractorcow](https://github.com/tractorcow))
|
||||
- Tests for php 5.6 and framework 3.2 [\#94](https://github.com/silverstripe/silverstripe-widgets/pull/94) ([tractorcow](https://github.com/tractorcow))
|
||||
- Support widget forms with InheritSidebar [\#93](https://github.com/silverstripe/silverstripe-widgets/pull/93) ([hailwood](https://github.com/hailwood))
|
||||
|
||||
## [1.1.2](https://github.com/silverstripe/silverstripe-widgets/tree/1.1.2) (2015-08-20)
|
||||
[Full Changelog](https://github.com/silverstripe/silverstripe-widgets/compare/1.1.1...1.1.2)
|
||||
|
||||
## [1.1.1](https://github.com/silverstripe/silverstripe-widgets/tree/1.1.1) (2015-05-19)
|
||||
[Full Changelog](https://github.com/silverstripe/silverstripe-widgets/compare/1.2.0...1.1.1)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Updated translations [\#90](https://github.com/silverstripe/silverstripe-widgets/pull/90) ([assertchris](https://github.com/assertchris))
|
||||
- BUG Fix incorrect extension variable [\#89](https://github.com/silverstripe/silverstripe-widgets/pull/89) ([tractorcow](https://github.com/tractorcow))
|
||||
|
||||
## [1.2.0](https://github.com/silverstripe/silverstripe-widgets/tree/1.2.0) (2015-05-04)
|
||||
[Full Changelog](https://github.com/silverstripe/silverstripe-widgets/compare/1.1.0...1.2.0)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Move 3.2+ compat version to 1.2 [\#87](https://github.com/silverstripe/silverstripe-widgets/pull/87) ([tractorcow](https://github.com/tractorcow))
|
||||
|
||||
## [1.1.0](https://github.com/silverstripe/silverstripe-widgets/tree/1.1.0) (2015-05-03)
|
||||
[Full Changelog](https://github.com/silverstripe/silverstripe-widgets/compare/0.2.0...1.1.0)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Tests fail under silverstripe-framework 3 branch [\#86](https://github.com/silverstripe/silverstripe-widgets/issues/86)
|
||||
- Widgets not clickable or draggable [\#82](https://github.com/silverstripe/silverstripe-widgets/issues/82)
|
||||
- Please start using tags [\#79](https://github.com/silverstripe/silverstripe-widgets/issues/79)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- BUGFIX: Fixed issue where Widget::CMSEditor\(\) can't rename the enabled checkbox [\#85](https://github.com/silverstripe/silverstripe-widgets/pull/85) ([UndefinedOffset](https://github.com/UndefinedOffset))
|
||||
- API Enable Title to be CMS driven [\#84](https://github.com/silverstripe/silverstripe-widgets/pull/84) ([tractorcow](https://github.com/tractorcow))
|
||||
- Remove reliance on $\_REQUEST global [\#83](https://github.com/silverstripe/silverstripe-widgets/pull/83) ([tractorcow](https://github.com/tractorcow))
|
||||
- Adding .editorconfig [\#80](https://github.com/silverstripe/silverstripe-widgets/pull/80) ([dhensby](https://github.com/dhensby))
|
||||
- BUG: Fixed issue where beforeSave\(\) was not being called when saving [\#77](https://github.com/silverstripe/silverstripe-widgets/pull/77) ([UndefinedOffset](https://github.com/UndefinedOffset))
|
||||
|
||||
## [0.2.0](https://github.com/silverstripe/silverstripe-widgets/tree/0.2.0) (2014-08-06)
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Coding conventions [\#18](https://github.com/silverstripe/silverstripe-widgets/issues/18)
|
||||
- Adapt the CMS UI to fit the SS 3.0 UI [\#17](https://github.com/silverstripe/silverstripe-widgets/issues/17)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Widgets no longer work on SS3.1 [\#40](https://github.com/silverstripe/silverstripe-widgets/issues/40)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- what about 3.0? [\#64](https://github.com/silverstripe/silverstripe-widgets/issues/64)
|
||||
- Change $Content template variable? [\#60](https://github.com/silverstripe/silverstripe-widgets/issues/60)
|
||||
- On `SiteTree` duplicate `WidgetArea`s are linked to both original and duplicate object [\#58](https://github.com/silverstripe/silverstripe-widgets/issues/58)
|
||||
- 'Not Found' error on 3.1 means Widgets cannot be added [\#56](https://github.com/silverstripe/silverstripe-widgets/issues/56)
|
||||
- BUG: Whitespace at the beginning of WidgetContentControllerExtension [\#53](https://github.com/silverstripe/silverstripe-widgets/issues/53)
|
||||
- \[User Deprecated\] Object::get\_static is deprecated. Replaced by Config\#get. Called from Widget-\>CMSTitle [\#42](https://github.com/silverstripe/silverstripe-widgets/issues/42)
|
||||
- New widget shown on top, then moved at bottom after page save [\#41](https://github.com/silverstripe/silverstripe-widgets/issues/41)
|
||||
- widget causes page ajax to not terminate correct [\#35](https://github.com/silverstripe/silverstripe-widgets/issues/35)
|
||||
- Widget wont render in Frontend [\#33](https://github.com/silverstripe/silverstripe-widgets/issues/33)
|
||||
- Widget module not installing [\#32](https://github.com/silverstripe/silverstripe-widgets/issues/32)
|
||||
- wrong filename for WigetContentControllerExtension.php [\#30](https://github.com/silverstripe/silverstripe-widgets/issues/30)
|
||||
- DataObject error with Blog and Widgets modules [\#27](https://github.com/silverstripe/silverstripe-widgets/issues/27)
|
||||
- Widgets area [\#23](https://github.com/silverstripe/silverstripe-widgets/issues/23)
|
||||
- Translations [\#10](https://github.com/silverstripe/silverstripe-widgets/issues/10)
|
||||
- widget management in the cms is broken [\#9](https://github.com/silverstripe/silverstripe-widgets/issues/9)
|
||||
- Missing Resources [\#2](https://github.com/silverstripe/silverstripe-widgets/issues/2)
|
||||
- ArchiveWidget is not translatable [\#1](https://github.com/silverstripe/silverstripe-widgets/issues/1)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Proposed fix for issue \#25 [\#73](https://github.com/silverstripe/silverstripe-widgets/pull/73) ([UndefinedOffset](https://github.com/UndefinedOffset))
|
||||
- FIX Setting `$only\_available\_in` to `private` visibility [\#70](https://github.com/silverstripe/silverstripe-widgets/pull/70) ([dhensby](https://github.com/dhensby))
|
||||
- Use Config to get Widget::$only\_available\_in [\#69](https://github.com/silverstripe/silverstripe-widgets/pull/69) ([ryanwachtl](https://github.com/ryanwachtl))
|
||||
- NEW Adding support for Translatable [\#68](https://github.com/silverstripe/silverstripe-widgets/pull/68) ([tomspeak](https://github.com/tomspeak))
|
||||
- Fixed locale for AJAX requests on addWidget. [\#67](https://github.com/silverstripe/silverstripe-widgets/pull/67) ([Sixfifty](https://github.com/Sixfifty))
|
||||
- BUG Fixed issue with nested InheritSideBar not inheriting beyond the first level [\#66](https://github.com/silverstripe/silverstripe-widgets/pull/66) ([tractorcow](https://github.com/tractorcow))
|
||||
- Change of yml name [\#65](https://github.com/silverstripe/silverstripe-widgets/pull/65) ([Leapfrognz](https://github.com/Leapfrognz))
|
||||
- Added instructions for releasing a widget [\#63](https://github.com/silverstripe/silverstripe-widgets/pull/63) ([jedateach](https://github.com/jedateach))
|
||||
- Added forTemplate function on Widget [\#62](https://github.com/silverstripe/silverstripe-widgets/pull/62) ([jedateach](https://github.com/jedateach))
|
||||
- Added summary fields [\#61](https://github.com/silverstripe/silverstripe-widgets/pull/61) ([jedateach](https://github.com/jedateach))
|
||||
- FIX \#58 on page duplicate [\#59](https://github.com/silverstripe/silverstripe-widgets/pull/59) ([MarkMhn](https://github.com/MarkMhn))
|
||||
- Finnish language [\#57](https://github.com/silverstripe/silverstripe-widgets/pull/57) ([mediaclinic](https://github.com/mediaclinic))
|
||||
- Update WidgetPageExtension.php [\#55](https://github.com/silverstripe/silverstripe-widgets/pull/55) ([MattyBalaam](https://github.com/MattyBalaam))
|
||||
- Removed whitespace, fixes \#53 [\#54](https://github.com/silverstripe/silverstripe-widgets/pull/54) ([christopherdarling](https://github.com/christopherdarling))
|
||||
- FIX: WidgetControllerTest failing due to 3.1 API. [\#52](https://github.com/silverstripe/silverstripe-widgets/pull/52) ([wilr](https://github.com/wilr))
|
||||
- Updated Widget module to show hide Widgets in specified WidgetArea [\#51](https://github.com/silverstripe/silverstripe-widgets/pull/51) ([ghost](https://github.com/ghost))
|
||||
- BUGFIX: solved bug adding a new widget to the top of the widget area, th... [\#46](https://github.com/silverstripe/silverstripe-widgets/pull/46) ([g4b0](https://github.com/g4b0))
|
||||
- Bugfix: check array before shifting [\#45](https://github.com/silverstripe/silverstripe-widgets/pull/45) ([g4b0](https://github.com/g4b0))
|
||||
- MINOR: Added Dutch language [\#38](https://github.com/silverstripe/silverstripe-widgets/pull/38) ([ARNHOE](https://github.com/ARNHOE))
|
||||
- ENHANCEMENT added missing translations, I do not speak swedish and added... [\#34](https://github.com/silverstripe/silverstripe-widgets/pull/34) ([rlehmann](https://github.com/rlehmann))
|
||||
- ENHANCEMENT: Adapt the Widgets Editor CMS UI to fit SS 3.0 [\#31](https://github.com/silverstripe/silverstripe-widgets/pull/31) ([ryanwachtl](https://github.com/ryanwachtl))
|
||||
- german translation [\#28](https://github.com/silverstripe/silverstripe-widgets/pull/28) ([ivoba](https://github.com/ivoba))
|
||||
- Coding conventions [\#19](https://github.com/silverstripe/silverstripe-widgets/pull/19) ([stojg](https://github.com/stojg))
|
||||
- Bug : Removed errors from Composer.json [\#15](https://github.com/silverstripe/silverstripe-widgets/pull/15) ([vikas-srivastava](https://github.com/vikas-srivastava))
|
||||
- ENHANCEMENT: Added Swedish translation file [\#14](https://github.com/silverstripe/silverstripe-widgets/pull/14) ([forsdahl](https://github.com/forsdahl))
|
||||
- MINOR: Added a lang folder with default English translation and manifest... [\#13](https://github.com/silverstripe/silverstripe-widgets/pull/13) ([forsdahl](https://github.com/forsdahl))
|
||||
- ENHANCEMENT: Made title, cmstitle and description on widgets translatable [\#12](https://github.com/silverstripe/silverstripe-widgets/pull/12) ([forsdahl](https://github.com/forsdahl))
|
||||
- New : Added composer.json [\#11](https://github.com/silverstripe/silverstripe-widgets/pull/11) ([vikas-srivastava](https://github.com/vikas-srivastava))
|
||||
- BUG: set current locale when fetching editable widget segment [\#8](https://github.com/silverstripe/silverstripe-widgets/pull/8) ([forsdahl](https://github.com/forsdahl))
|
||||
- BUG: Enable chosen in WidgetAreaEditor fields. [\#7](https://github.com/silverstripe/silverstripe-widgets/pull/7) ([forsdahl](https://github.com/forsdahl))
|
||||
- Issue \#4: Adds missing requirements for WidgetAreaEditor [\#6](https://github.com/silverstripe/silverstripe-widgets/pull/6) ([dancras](https://github.com/dancras))
|
||||
- Missing Resources [\#3](https://github.com/silverstripe/silverstripe-widgets/pull/3) ([nedmas](https://github.com/nedmas))
|
||||
|
||||
|
||||
|
||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
|
@ -0,0 +1 @@
|
|||
When having discussions about this module in issues or pull request please adhere to the [SilverStripe Community Code of Conduct](https://docs.silverstripe.org/en/contributing/code_of_conduct).
|
|
@ -1,65 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Add this to ContentController to enable widgets
|
||||
*
|
||||
* @package widgets
|
||||
*/
|
||||
class WidgetContentControllerExtension extends Extension {
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_actions = array(
|
||||
'handleWidget'
|
||||
);
|
||||
|
||||
/**
|
||||
* Handles widgets attached to a page through one or more {@link WidgetArea}
|
||||
* elements.
|
||||
*
|
||||
* Iterated through each $has_one relation with a {@link WidgetArea} and
|
||||
* looks for connected widgets by their database identifier.
|
||||
*
|
||||
* Assumes URLs in the following format: <URLSegment>/widget/<Widget-ID>.
|
||||
*
|
||||
* @return RequestHandler
|
||||
*/
|
||||
public function handleWidget() {
|
||||
$SQL_id = $this->owner->getRequest()->param('ID');
|
||||
if(!$SQL_id) return false;
|
||||
|
||||
// find WidgetArea relations
|
||||
$widgetAreaRelations = array();
|
||||
$hasOnes = $this->owner->data()->has_one();
|
||||
|
||||
if(!$hasOnes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($hasOnes as $hasOneName => $hasOneClass) {
|
||||
if($hasOneClass == 'WidgetArea' || is_subclass_of($hasOneClass, 'WidgetArea')) {
|
||||
$widgetAreaRelations[] = $hasOneName;
|
||||
}
|
||||
}
|
||||
|
||||
// find widget
|
||||
$widget = null;
|
||||
|
||||
foreach($widgetAreaRelations as $widgetAreaRelation) {
|
||||
if($widget) {
|
||||
break;
|
||||
}
|
||||
|
||||
$widget = $this->owner->data()->$widgetAreaRelation()->Widgets(
|
||||
sprintf('"Widget"."ID" = %d', $SQL_id)
|
||||
)->First();
|
||||
}
|
||||
|
||||
if(!$widget) {
|
||||
user_error('No widget found', E_USER_ERROR);
|
||||
}
|
||||
|
||||
return $widget->getController();
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Optional controller for every widget which has its own logic, e.g. in forms.
|
||||
*
|
||||
* It always handles a single widget, usually passed in as a database
|
||||
* identifier through the controller URL. Needs to be constructed as a nested
|
||||
* controller within a {@link ContentController}.
|
||||
*
|
||||
* ## Forms
|
||||
* You can add forms like in any other SilverStripe controller. If you need
|
||||
* access to the widget from within a form, you can use
|
||||
* `$this->controller->getWidget()` inside the form logic.
|
||||
*
|
||||
* Note: Widget controllers currently only work on {@link Page} objects,
|
||||
* because the logic is implemented in {@link ContentController->handleWidget()}.
|
||||
* Copy this logic and the URL rules to enable it for other controllers.
|
||||
*
|
||||
* @package widgets
|
||||
*/
|
||||
class WidgetController extends Controller {
|
||||
|
||||
/**
|
||||
* @var Widget
|
||||
*/
|
||||
protected $widget;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_actions = array(
|
||||
'editablesegment'
|
||||
);
|
||||
|
||||
/**
|
||||
* @param Widget $widget
|
||||
*/
|
||||
public function __construct($widget = null) {
|
||||
if($widget) {
|
||||
$this->widget = $widget;
|
||||
$this->failover = $widget;
|
||||
}
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action
|
||||
* @return string
|
||||
*/
|
||||
public function Link($action = null) {
|
||||
$id = ($this->widget) ? $this->widget->ID : null;
|
||||
$segment = Controller::join_links('widget', $id, $action);
|
||||
|
||||
if($page = Director::get_current_page()) {
|
||||
return $page->Link($segment);
|
||||
}
|
||||
|
||||
return Controller::curr()->Link($segment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Widget
|
||||
*/
|
||||
public function getWidget() {
|
||||
return $this->widget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloaded from {@link Widget->Content()} to allow for controller / form
|
||||
* linking.
|
||||
*
|
||||
* @return string HTML
|
||||
*/
|
||||
public function Content() {
|
||||
return $this->renderWith(array_reverse(ClassInfo::ancestry($this->widget->class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloaded from {@link Widget->WidgetHolder()} to allow for controller/
|
||||
* form linking.
|
||||
*
|
||||
* @return string HTML
|
||||
*/
|
||||
public function WidgetHolder() {
|
||||
return $this->renderWith("WidgetHolder");
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the `WidgetEditor.ss` template and {@link Widget->editablesegment()}
|
||||
* to render a administrator-view of the widget. It is assumed that this
|
||||
* view contains form elements which are submitted and saved through
|
||||
* {@link WidgetAreaEditor} within the CMS interface.
|
||||
*
|
||||
* @return string HTML
|
||||
*/
|
||||
public function editablesegment() {
|
||||
$className = $this->urlParams['ID'];
|
||||
if (class_exists('Translatable') && Member::currentUserID()) {
|
||||
// set current locale based on logged in user's locale
|
||||
$locale = Member::currentUser()->Locale;
|
||||
i18n::set_locale($locale);
|
||||
}
|
||||
if(class_exists($className) && is_subclass_of($className, 'Widget')) {
|
||||
$obj = new $className();
|
||||
return $obj->EditableSegment();
|
||||
} else {
|
||||
user_error("Bad widget class: $className", E_USER_WARNING);
|
||||
return "Bad widget class name given";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use WidgetController
|
||||
* @package widgets
|
||||
*/
|
||||
class Widget_Controller extends WidgetController {
|
||||
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Adds a single {@link WidgetArea} called "SideBar" to {@link Page} classes.
|
||||
* Adjust your templates to render the resulting
|
||||
* {@link WidgetArea} as required, through the $SideBarView placeholder.
|
||||
*
|
||||
* This extension is just an example on how to use the widgets functionality,
|
||||
* feel free to create your own relationships, naming conventions, etc.
|
||||
* without using this class.
|
||||
*/
|
||||
class WidgetPageExtension extends DataExtension {
|
||||
|
||||
private static $db = array(
|
||||
'InheritSideBar' => 'Boolean',
|
||||
);
|
||||
|
||||
private static $defaults = array(
|
||||
'InheritSideBar' => true
|
||||
);
|
||||
|
||||
private static $has_one = array(
|
||||
'SideBar' => 'WidgetArea'
|
||||
);
|
||||
|
||||
public function updateCMSFields(FieldList $fields) {
|
||||
$fields->addFieldToTab(
|
||||
"Root.Widgets",
|
||||
new CheckboxField("InheritSideBar", 'Inherit Sidebar From Parent')
|
||||
);
|
||||
$fields->addFieldToTab(
|
||||
"Root.Widgets",
|
||||
new WidgetAreaEditor("SideBar")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WidgetArea
|
||||
*/
|
||||
public function SideBarView() {
|
||||
if(
|
||||
$this->owner->InheritSideBar
|
||||
&& ($parent = $this->owner->getParent())
|
||||
&& $parent->hasMethod('SideBarView')
|
||||
) {
|
||||
return $parent->SideBarView();
|
||||
} elseif($this->owner->SideBar()->exists()){
|
||||
return $this->owner->SideBar();
|
||||
}
|
||||
}
|
||||
|
||||
public function onBeforeDuplicate($duplicatePage) {
|
||||
if($this->owner->hasField('SideBarID')) {
|
||||
$sideBar = $this->owner->getComponent('SideBar');
|
||||
$duplicateWidgetArea = $sideBar->duplicate();
|
||||
|
||||
foreach($sideBar->Items() as $originalWidget) {
|
||||
$widget = $originalWidget->duplicate(false);
|
||||
$widget->ParentID = $duplicateWidgetArea->ID;
|
||||
$widget->write();
|
||||
}
|
||||
|
||||
$duplicatePage->SideBarID = $duplicateWidgetArea->ID;
|
||||
|
||||
}
|
||||
|
||||
return $duplicatePage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Support Translatable so that we don't link WidgetAreas across translations
|
||||
*/
|
||||
public function onTranslatableCreate() {
|
||||
//reset the sidebar ID
|
||||
$this->owner->SideBarID = 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Special field type for selecting and configuring widgets on a page.
|
||||
*
|
||||
* @package widgets
|
||||
*/
|
||||
class WidgetAreaEditor extends FormField {
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $widgetClasses
|
||||
* @param int $maxWidgets
|
||||
*/
|
||||
public function __construct($name, $widgetClasses = array('Widget'), $maxWidgets = 0) {
|
||||
$this->MaxWidgets = $maxWidgets;
|
||||
$this->widgetClasses = $widgetClasses;
|
||||
|
||||
parent::__construct($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
*
|
||||
* @return string - HTML
|
||||
*/
|
||||
public function FieldHolder($properties = array()) {
|
||||
Requirements::css('widgets/css/WidgetAreaEditor.css');
|
||||
Requirements::javascript('widgets/javascript/WidgetAreaEditor.js');
|
||||
|
||||
return $this->renderWith("WidgetAreaEditor");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function AvailableWidgets() {
|
||||
$widgets= new ArrayList();
|
||||
|
||||
foreach($this->widgetClasses as $widgetClass) {
|
||||
$classes = ClassInfo::subclassesFor($widgetClass);
|
||||
|
||||
if (isset($classes['Widget'])) {
|
||||
unset($classes['Widget']);
|
||||
}
|
||||
else if (isset($classes[0]) && $classes[0] == 'Widget') {
|
||||
unset($classes[0]);
|
||||
}
|
||||
|
||||
foreach($classes as $class) {
|
||||
|
||||
$available = Config::inst()->get($class, 'only_available_in');
|
||||
|
||||
if (!empty($available) && is_array($available)) {
|
||||
if(in_array($this->Name, $available)) {
|
||||
$widgets->push(singleton($class));
|
||||
}
|
||||
}else {
|
||||
$widgets->push(singleton($class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasManyList
|
||||
*/
|
||||
public function UsedWidgets() {
|
||||
// Call class_exists() to load Widget.php earlier and avoid a segfault
|
||||
class_exists('Widget');
|
||||
|
||||
$relationName = $this->name;
|
||||
$widgets = $this->form->getRecord()->getComponent($relationName)->Items();
|
||||
|
||||
return $widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function IdxField() {
|
||||
return $this->id() . 'ID';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function Value() {
|
||||
$relationName = $this->name;
|
||||
|
||||
return $this->form->getRecord()->getComponent($relationName)->ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DataObjectInterface $record
|
||||
*/
|
||||
public function saveInto(DataObjectInterface $record) {
|
||||
$name = $this->name;
|
||||
$idName = $name . "ID";
|
||||
|
||||
$widgetarea = $record->getComponent($name);
|
||||
$widgetarea->write();
|
||||
|
||||
$record->$idName = $widgetarea->ID;
|
||||
|
||||
$widgets = $widgetarea->Items();
|
||||
|
||||
// store the field IDs and delete the missing fields
|
||||
// alternatively, we could delete all the fields and re add them
|
||||
$missingWidgets = array();
|
||||
|
||||
if($widgets) {
|
||||
foreach($widgets as $existingWidget) {
|
||||
$missingWidgets[$existingWidget->ID] = $existingWidget;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($_REQUEST['Widget'])) {
|
||||
foreach(array_keys($_REQUEST['Widget']) as $widgetAreaName) {
|
||||
if ($widgetAreaName !== $this->name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach(array_keys($_REQUEST['Widget'][$widgetAreaName]) as $newWidgetID) {
|
||||
$newWidgetData = $_REQUEST['Widget'][$widgetAreaName][$newWidgetID];
|
||||
|
||||
// Sometimes the id is "new-1" or similar, ensure this doesn't get into the query
|
||||
if(!is_numeric($newWidgetID)) {
|
||||
$newWidgetID = 0;
|
||||
}
|
||||
|
||||
// \"ParentID\" = '0' is for the new page
|
||||
$widget = DataObject::get_one(
|
||||
'Widget',
|
||||
"(\"ParentID\" = '{$record->$name()->ID}' OR ".
|
||||
"\"ParentID\" = '0') AND \"Widget\".\"ID\" = '$newWidgetID'"
|
||||
);
|
||||
|
||||
// check if we are updating an existing widget
|
||||
if($widget && isset($missingWidgets[$widget->ID])) {
|
||||
unset($missingWidgets[$widget->ID]);
|
||||
}
|
||||
|
||||
// create a new object
|
||||
if(!$widget && !empty($newWidgetData['Type']) && class_exists($newWidgetData['Type'])) {
|
||||
$widget = new $newWidgetData['Type']();
|
||||
$widget->ID = 0;
|
||||
$widget->ParentID = $record->$name()->ID;
|
||||
|
||||
if(!is_subclass_of($widget, 'Widget')) {
|
||||
$widget = null;
|
||||
}
|
||||
}
|
||||
|
||||
if($widget) {
|
||||
if($widget->ParentID == 0) {
|
||||
$widget->ParentID = $record->$name()->ID;
|
||||
}
|
||||
|
||||
$widget->populateFromPostData($newWidgetData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove the fields not saved
|
||||
if($missingWidgets) {
|
||||
foreach($missingWidgets as $removedWidget) {
|
||||
if(isset($removedWidget) && is_numeric($removedWidget->ID)) {
|
||||
$removedWidget->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,269 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Widgets let CMS authors drag and drop small pieces of functionality into
|
||||
* defined areas of their websites.
|
||||
*
|
||||
* You can use forms in widgets by implementing a {@link WidgetController}.
|
||||
*
|
||||
* See {@link Widget_Controller} for more information.
|
||||
*
|
||||
* @package widgets
|
||||
*/
|
||||
class Widget extends DataObject {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
"Sort" => "Int",
|
||||
"Enabled" => "Boolean"
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $defaults = array(
|
||||
'Enabled' => true
|
||||
);
|
||||
|
||||
private static $only_available_in = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
"Parent" => "WidgetArea",
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_many = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $many_many = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $belongs_many_many = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $default_sort = "\"Sort\"";
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $title = "Widget Title";
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $cmsTitle = "Name of this widget";
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $description = "Description of what this widget does.";
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $summary_fields = array(
|
||||
'CMSTitle' => 'Title'
|
||||
);
|
||||
|
||||
/**
|
||||
* @var WidgetController
|
||||
*/
|
||||
protected $controller;
|
||||
|
||||
/**
|
||||
* Note: Overloaded in {@link WidgetController}.
|
||||
*
|
||||
* @return string HTML
|
||||
*/
|
||||
public function WidgetHolder() {
|
||||
return $this->renderWith("WidgetHolder");
|
||||
}
|
||||
|
||||
/**
|
||||
* Default way to render widget in templates.
|
||||
* @return string HTML
|
||||
*/
|
||||
public function forTemplate($holder = true){
|
||||
if($holder){
|
||||
return $this->WidgetHolder();
|
||||
}
|
||||
return $this->Content();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the widget content in a custom template with the same name as the
|
||||
* current class. This should be the main point of output customization.
|
||||
*
|
||||
* Invoked from within WidgetHolder.ss, which contains the "framing" around
|
||||
* the custom content, like a title.
|
||||
*
|
||||
* Note: Overloaded in {@link WidgetController}.
|
||||
*
|
||||
* @return string HTML
|
||||
*/
|
||||
public function Content() {
|
||||
return $this->renderWith(array_reverse(ClassInfo::ancestry($this->class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function Title() {
|
||||
return _t($this->class.'.TITLE', $this->config()->title);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function CMSTitle() {
|
||||
return _t($this->class.'.CMSTITLE', $this->config()->cmsTitle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function Description() {
|
||||
return _t($this->class.'.DESCRIPTION', $this->config()->description);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string - HTML
|
||||
*/
|
||||
public function DescriptionSegment() {
|
||||
return $this->renderWith('WidgetDescription');
|
||||
}
|
||||
|
||||
/**
|
||||
* @see WidgetController::editablesegment()
|
||||
*
|
||||
* @return string - HTML
|
||||
*/
|
||||
public function EditableSegment() {
|
||||
return $this->renderWith('WidgetEditor');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FieldList
|
||||
*/
|
||||
public function getCMSFields() {
|
||||
$fields = parent::getCMSFields();
|
||||
$fields->removeByName('ParentID');
|
||||
$fields->removeByName('Sort');
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FieldList
|
||||
*/
|
||||
public function CMSEditor() {
|
||||
$fields = $this->getCMSFields();
|
||||
$outputFields = new FieldList();
|
||||
|
||||
foreach($fields as $field) {
|
||||
$name = $field->getName();
|
||||
$value = $this->getField($name);
|
||||
if ($value) {
|
||||
$field->setValue($value);
|
||||
}
|
||||
$name = preg_replace("/([A-Za-z0-9\-_]+)/", "Widget[" . $this->ID . "][\\1]", $name);
|
||||
$field->setName($name);
|
||||
$outputFields->push($field);
|
||||
}
|
||||
|
||||
return $outputFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function ClassName() {
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function Name() {
|
||||
return "Widget[".$this->ID."]";
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*
|
||||
* @return WidgetController
|
||||
*/
|
||||
public function getController() {
|
||||
if($this->controller) {
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
foreach(array_reverse(ClassInfo::ancestry($this->class)) as $widgetClass) {
|
||||
$controllerClass = "{$widgetClass}_Controller";
|
||||
|
||||
if(class_exists($controllerClass)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$controllerClass = "{$widgetClass}Controller";
|
||||
|
||||
if(class_exists($controllerClass)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!class_exists($controllerClass)) {
|
||||
throw new Exception("Could not find controller class for $this->classname");
|
||||
}
|
||||
|
||||
$this->controller = Injector::inst()->create($controllerClass, $this);
|
||||
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function populateFromPostData($data) {
|
||||
$fields = $this->getCMSFields();
|
||||
foreach($data as $name => $value) {
|
||||
if($name != "Type") {
|
||||
if ($field = $fields->dataFieldByName($name)) {
|
||||
$field->setValue($value);
|
||||
$field->saveInto($this);
|
||||
}
|
||||
else {
|
||||
$this->setField($name, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Look for checkbox fields not present in the data
|
||||
foreach($fields as $field) {
|
||||
if($field instanceof CheckboxField && !array_key_exists($field->getName(), $data)) {
|
||||
$field->setValue(false);
|
||||
$field->saveInto($this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->write();
|
||||
|
||||
// The field must be written to ensure a unique ID.
|
||||
$this->Name = $this->class.$this->ID;
|
||||
$this->write();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Represents a set of widgets shown on a page.
|
||||
*
|
||||
* @package widgets
|
||||
*/
|
||||
class WidgetArea extends DataObject {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_many = array(
|
||||
"Widgets" => "Widget"
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $template = __CLASS__;
|
||||
|
||||
/**
|
||||
* Used in template instead of {@link Widgets()} to wrap each widget in its
|
||||
* controller, making it easier to access and process form logic and
|
||||
* actions stored in {@link WidgetController}.
|
||||
*
|
||||
* @return SS_List - Collection of {@link WidgetController} instances.
|
||||
*/
|
||||
public function WidgetControllers() {
|
||||
$controllers = new ArrayList();
|
||||
|
||||
foreach($this->ItemsToRender() as $widget) {
|
||||
$controller = $widget->getController();
|
||||
|
||||
$controller->init();
|
||||
$controllers->push($controller);
|
||||
}
|
||||
|
||||
return $controllers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasManyList
|
||||
*/
|
||||
public function Items() {
|
||||
return $this->getComponents('Widgets');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasManyList
|
||||
*/
|
||||
public function ItemsToRender() {
|
||||
return $this->getComponents('Widgets', "\"Widget\".\"Enabled\" = 1");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string - HTML
|
||||
*/
|
||||
public function forTemplate() {
|
||||
return $this->renderWith($this->template);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $template
|
||||
*/
|
||||
public function setTemplate($template) {
|
||||
$this->template = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all connected Widgets when this WidgetArea gets deleted
|
||||
*/
|
||||
public function onBeforeDelete() {
|
||||
parent::onBeforeDelete();
|
||||
foreach($this->Widgets() as $widget) {
|
||||
$widget->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
comment: false
|
|
@ -1,17 +1,41 @@
|
|||
{
|
||||
"name": "silverstripe/widgets",
|
||||
"description": "Widgets are small pieces of functionality such as showing the latest Comments or Flickr Photos. They normally display on the sidebar of your website.",
|
||||
"type": "silverstripe-module",
|
||||
"keywords": ["silverstripe", "widgets", "blog"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ingo Schommer",
|
||||
"email": "ingo@silverstripe.com"
|
||||
}
|
||||
],
|
||||
"require":
|
||||
{
|
||||
"silverstripe/framework": "~3.1",
|
||||
"silverstripe/cms": "~3.1"
|
||||
}
|
||||
}
|
||||
"name": "silverstripe/widgets",
|
||||
"description": "Widgets are small pieces of functionality such as showing the latest Comments or Flickr Photos. They normally display on the sidebar of your website.",
|
||||
"type": "silverstripe-vendormodule",
|
||||
"keywords": [
|
||||
"silverstripe",
|
||||
"widgets",
|
||||
"blog"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ingo Schommer",
|
||||
"email": "ingo@silverstripe.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"silverstripe/vendor-plugin": "^1.0",
|
||||
"silverstripe/framework": "^4.10",
|
||||
"silverstripe/cms": "^4.0",
|
||||
"silverstripe/versioned": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"squizlabs/php_codesniffer": "^3.0"
|
||||
},
|
||||
"extra": {
|
||||
"expose": [
|
||||
"css",
|
||||
"javascript"
|
||||
]
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SilverStripe\\Widgets\\": "src/",
|
||||
"SilverStripe\\Widgets\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"license": "BSD-3-Clause",
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
# Contributing
|
||||
|
||||
Contributions are welcome! Create an issue, explaining a bug or proposal. Submit pull requests if you feel brave. Speak to me on [Twitter](https://twitter.com/assertchris).
|
||||
|
||||
## Releasing a widget
|
||||
|
||||
Follow the [standard procedures defined for releasing a SilverStripe module](https://docs.silverstripe.org/en/4/developer_guides/extending/how_tos/publish_a_module).
|
||||
|
||||
Here is a composer template you can use.
|
||||
|
||||
You need to finish off / change:
|
||||
|
||||
* name (eg: `yourorganisation/silverstripe-widget-carousel`)
|
||||
* description
|
||||
* keywords
|
||||
* license
|
||||
* author
|
||||
* installer-name (eg: `widgets_carousel`)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "",
|
||||
"description": "",
|
||||
"type": "silverstripe-module",
|
||||
"keywords" : ["widget"],
|
||||
"require": {
|
||||
"silverstripe/framework": "3.*",
|
||||
"silverstripe/cms": "3.*"
|
||||
},
|
||||
"license": "BSD-2-Clause",
|
||||
"authors": [
|
||||
{
|
||||
"name": "",
|
||||
"email": ""
|
||||
}
|
||||
],
|
||||
"extra" : {
|
||||
"installer-name": "widgets_"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Yourname\\MyWidget\\": "src/"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
|
@ -1,10 +1,10 @@
|
|||
@charset "UTF-8";
|
||||
.WidgetAreaEditor p { margin-bottom: 15px; }
|
||||
|
||||
.Widget { padding: 0 7px 7px 7px; margin: 16px 0 16px 0; color: #1f1f1f; background-color: #b0bec7; background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2IwYmVjNyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzkyYTViMiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=="); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b0bec7), color-stop(100%, #92a5b2)); background-image: -webkit-linear-gradient(#b0bec7, #92a5b2); background-image: -moz-linear-gradient(#b0bec7, #92a5b2); background-image: -o-linear-gradient(#b0bec7, #92a5b2); background-image: linear-gradient(#b0bec7, #92a5b2); border-top: 1px solid #c2cdd4; border-bottom: 1px solid #748d9d; -webkit-border-radius: 3px; -moz-border-radius: 3px; -ms-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px; }
|
||||
.Widget h3 { margin: 0; padding: 0; line-height: 40px; text-shadow: #bfcad2 1px 1px 0; -webkit-transition-property: background-image; -moz-transition-property: background-image; -o-transition-property: background-image; transition-property: background-image; -webkit-transition-duration: 0.2s; -moz-transition-duration: 0.2s; -o-transition-duration: 0.2s; transition-duration: 0.2s; -webkit-transition-timing-function: ease-out; -moz-transition-timing-function: ease-out; -o-transition-timing-function: ease-out; transition-timing-function: ease-out; }
|
||||
.Widget .widgetDescription { background: #eceff1; padding: 5px 5px 1px 5px; margin: 0; -webkit-border-radius: 3px; -moz-border-radius: 3px; -ms-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px; }
|
||||
.Widget .widgetFields { background: #eceff1; padding: 5px; margin: 7px 0 0 0; -webkit-border-radius: 3px; -moz-border-radius: 3px; -ms-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px; }
|
||||
.Widget { padding: 0 1.25rem .75rem; margin: 16px 0 16px 0; color: #4F5861; background-color: #F1F3F6; border: 1px solid #ced5e1; -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; }
|
||||
.Widget h3 { color: #43536d; margin: 0; padding: 4px 0; line-height: 40px; -moz-transition-property: background-image; -o-transition-property: background-image; -webkit-transition-property: background-image; transition-property: background-image; -moz-transition-duration: 0.2s; -o-transition-duration: 0.2s; -webkit-transition-duration: 0.2s; transition-duration: 0.2s; -moz-transition-timing-function: ease-out; -o-transition-timing-function: ease-out; -webkit-transition-timing-function: ease-out; transition-timing-function: ease-out; }
|
||||
.Widget .widgetDescription { padding: 0 0 .5rem; margin: 0; -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; }
|
||||
.Widget .widgetDescription p { margin: 0; }
|
||||
.Widget .widgetFields { background: #F7F8FA; border-top: 1px solid #dbe0e9; border-bottom: 1px solid #dbe0e9; padding: .75rem 1.25rem; margin: .75rem -1.25rem; }
|
||||
|
||||
.availableWidgetsHolder { width: 38%; float: left; }
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# Developer Tools
|
||||
|
||||
Get the dependencies by running `npm install`.
|
||||
|
||||
After making changes to SCSS files, run the build script `npm run build`. This will compile everything for you and output minified CSS files to the `css` directory.
|
|
@ -0,0 +1,209 @@
|
|||
# Getting Started
|
||||
|
||||
The easiest way to install is by using [Composer](https://getcomposer.org):
|
||||
|
||||
```sh
|
||||
$ composer require silverstripe/widgets
|
||||
```
|
||||
|
||||
You'll also need to run `dev/build`.
|
||||
|
||||
### Installation
|
||||
|
||||
Install the module through [composer](http://getcomposer.org):
|
||||
|
||||
```sh
|
||||
$ composer require silverstripe/widgets
|
||||
```
|
||||
|
||||
Widgets are essentially database relations to other models, mostly page types.
|
||||
By default, they're not added to any of your own models. The easiest and most common
|
||||
way to get started would be to create a single collection of widgets under the
|
||||
name "SideBar" on your `Page` class. This is handled by an extension which you
|
||||
can enable through your `config.yml`:
|
||||
|
||||
```yaml
|
||||
Page:
|
||||
extensions:
|
||||
- SilverStripe\Widgets\Extensions\WidgetPageExtension
|
||||
```
|
||||
|
||||
Run a `dev/build`, and adjust your templates to include the resulting sidebar view.
|
||||
The placeholder is called `$SideBarView`, and loops through all widgets assigned
|
||||
to the current page.
|
||||
|
||||
Alternatively, you can add one or more widget collections to your own page types.
|
||||
Here's an example on how to just add widgets to a `MyPage` type, and call it
|
||||
`MyWidgetArea` instead. Please ensure you add the correct namespaces for your module.
|
||||
|
||||
### Installing a widget
|
||||
|
||||
By following the "Packaging" rules below, widgets are easily installed. This example uses the Blog module which by default has widgets already enabled.
|
||||
|
||||
* Install the [blog module](http://www.silverstripe.org/blog-module/).
|
||||
* Download the widget and unzip to the main folder of your SilverStripe website, e.g. to `/widget_<widget-name>/`. The folder
|
||||
will contain a few files, which generally won't need editing or reading.
|
||||
* Run `http://my-website.com/dev/build`
|
||||
* Login to the CMS and go to the 'Blog' page. Choose the "widgets" tab and click the new widget to activate it.
|
||||
* Your blog will now have the widget shown
|
||||
|
||||
|
||||
### Adding widgets to other pages
|
||||
|
||||
You have to do a couple things to get a Widget to work on a page.
|
||||
|
||||
* Install the Widgets Module, see above.
|
||||
* Add a WidgetArea field to your Page.
|
||||
* Add a new tab to the CMS with a WidgetAreaEditor field for managing the widgets.
|
||||
e.g.
|
||||
|
||||
**mysite/code/Page.php**
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Widgets\Forms\WidgetAreaEditor;
|
||||
use SilverStripe\Widgets\Model\WidgetArea;
|
||||
|
||||
class Page extends SiteTree
|
||||
{
|
||||
// ...
|
||||
private static $has_one = array(
|
||||
'MyWidgetArea' => WidgetArea::class
|
||||
);
|
||||
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = parent::getCMSFields();
|
||||
$fields->addFieldToTab('Root.Widgets', new WidgetAreaEditor('MyWidgetArea'));
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this case, you need to alter your templates to include the `$MyWidgetArea` placeholder.
|
||||
|
||||
## Writing your own widgets
|
||||
|
||||
To create a Widget you need at least three files - a php file containing the class, a template file of the same name and
|
||||
a config file called *_config.php* (if you dont need any config options for the widget to work then you can make it
|
||||
blank). Each widget should be in its own folder like widgets_widgetName/
|
||||
|
||||
After installing or creating a new widget, **make sure to run db/build?flush=1** at the end of the URL, *before*
|
||||
attempting to use it.
|
||||
|
||||
The class should extend the Widget class, and must specify three config variables:
|
||||
|
||||
* `title`: The title that will appear in the rendered widget (eg Photos). This can be customised by the CMS admin
|
||||
* `cmsTitle`: a more descriptive title that will appear in the cms editor (eg Flickr Photos)
|
||||
* `description`: a short description that will appear in the cms editor (eg This widget shows photos from
|
||||
Flickr). The class may also specify functions to be used in the template like a page type can.
|
||||
|
||||
If a Widget has configurable options, then it can specify a number of database fields to store these options in via the
|
||||
static `$db` array, and also specify a `getCMSFields` function that returns a `FieldList`, much the same way as a page type
|
||||
does.
|
||||
|
||||
An example widget is below:
|
||||
|
||||
**FlickrWidget.php**
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Yourname\MyWidget;
|
||||
|
||||
use FlickrService;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\Forms\NumericField;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\View\ArrayData;
|
||||
use SilverStripe\View\Requirements;
|
||||
|
||||
class FlickrWidget extends Widget
|
||||
{
|
||||
private static $db = array(
|
||||
'User' => 'Varchar',
|
||||
'Photoset' => 'Varchar',
|
||||
'Tags' => 'Varchar',
|
||||
'NumberToShow' => 'Int'
|
||||
);
|
||||
|
||||
private static $defaults = array(
|
||||
'NumberToShow' => 8
|
||||
);
|
||||
|
||||
private static $title = 'Photos';
|
||||
private static $cmsTitle = 'Flickr Photos';
|
||||
private static $description = 'Shows flickr photos.';
|
||||
|
||||
public function Photos()
|
||||
{
|
||||
// You'll need to install these yourself
|
||||
Requirements::javascript(THIRDPARTY_DIR . '/prototype/prototype.js');
|
||||
Requirements::javascript(THIRDPARTY_DIR . '/scriptaculous/effects.js');
|
||||
Requirements::javascript('mashups/javascript/lightbox.js');
|
||||
Requirements::css('mashups/css/lightbox.css');
|
||||
|
||||
$flickr = new FlickrService();
|
||||
if ($this->Photoset == '') {
|
||||
$photos = $flickr->getPhotos($this->Tags, $this->User, $this->NumberToShow, 1);
|
||||
} else {
|
||||
$photos = $flickr->getPhotoSet($this->Photoset, $this->User, $this->NumberToShow, 1);
|
||||
}
|
||||
|
||||
$output = new ArrayList();
|
||||
foreach ($photos->PhotoItems as $photo) {
|
||||
$output->push(
|
||||
new ArrayData(
|
||||
array(
|
||||
'Title' => $photo->title,
|
||||
'Link' => 'http://farm1.static.flickr.com/' . $photo->image_path .'.jpg',
|
||||
'Image' => 'http://farm1.static.flickr.com/' .$photo->image_path. '_s.jpg'
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function getCMSFields()
|
||||
{
|
||||
return new FieldList(
|
||||
new TextField('User', 'User'),
|
||||
new TextField('PhotoSet', 'Photo Set'),
|
||||
new TextField('Tags', 'Tags'),
|
||||
new NumericField('NumberToShow', 'Number to Show')
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**FlickrWidget.ss**
|
||||
|
||||
```
|
||||
<% control Photos %>
|
||||
<a href="$Link" rel="lightbox" title="$Title"><img src="$Image" alt="$Title" /></a>
|
||||
<% end_control %>
|
||||
```
|
||||
|
||||
## Limiting Allowed Widgets for a Pagetype
|
||||
|
||||
You can lock down a particular `WidgetAreaEditor` to only allow adding certain widgets by passing them as a second parameter.
|
||||
|
||||
**GreatPage.php**
|
||||
|
||||
```php
|
||||
$fields->addFieldToTab(
|
||||
'Root.Widgets',
|
||||
new WidgetAreaEditor(
|
||||
'PhenomenalWidgetArea',
|
||||
[
|
||||
'Fully\\Qualified\\ParticularlyEpicWidget',
|
||||
'Yourname\\MyModule\\LessGreatWidget'
|
||||
]
|
||||
)
|
||||
);
|
||||
```
|
|
@ -0,0 +1,24 @@
|
|||
# Introduction
|
||||
[Widgets](http://silverstripe.org/widgets) are small pieces of functionality such as showing the latest comments or Flickr photos. They normally display on
|
||||
the sidebar of your website. To check out a what a [Widget](http://silverstripe.org/widgets) can do watch the
|
||||
[Widget video](http://silverstripe.com/assets/screencasts/SilverStripe-Blog-DragDrop-Widgets.swf) and try out the
|
||||
[demo site](http://demo.silverstripe.org/)
|
||||
[![Build Status](http://img.shields.io/travis/silverstripe/silverstripe-widgets.svg?style=flat-square)](https://travis-ci.org/silverstripe/silverstripe-widgets)
|
||||
[![Code Quality](http://img.shields.io/scrutinizer/g/silverstripe/silverstripe-widgets.svg?style=flat-square)](https://scrutinizer-ci.com/g/silverstripe/silverstripe-widgets)
|
||||
[![Version](http://img.shields.io/packagist/v/silverstripe/widgets.svg?style=flat-square)](https://packagist.org/packages/silverstripe/widgets)
|
||||
[![License](http://img.shields.io/packagist/l/silverstripe/widgets.svg?style=flat-square)](LICENSE.md)
|
||||
|
||||
## Share Links
|
||||
|
||||
The generated share links have a public key and hash. There can be any number of share links per-page, but all share links are unique, and cannot be used to gain access to pages other than the one each link was created for.
|
||||
|
||||
## Sections
|
||||
|
||||
- [Getting Started](getting-started.md)
|
||||
- [Developer Tools](developer-tools.md)
|
||||
|
||||
## Questions
|
||||
|
||||
This module was created by [SilverStripe](https://twitter.com/silverstripe). You can ask questions on Twitter.
|
||||
|
||||
You can report bugs or request features on [GitHub](https://github.com/silverstripe/silverstripe-widgets/issues).
|
|
@ -5,7 +5,7 @@
|
|||
var parentName=$(this).attr('name');
|
||||
this.rewriteWidgetAreaAttributes();
|
||||
|
||||
var availableWidgets=$('#availableWidgets-'+$(this).attr('name')).children().each(function() {
|
||||
var availableWidgets=$('#availableWidgets-'+parentName).children().each(function() {
|
||||
// Don't run on comments, whitespace, etc
|
||||
if($(this)[0].nodeType==1) {
|
||||
// Gotta change their ID's because otherwise we get clashes between two tabs
|
||||
|
@ -20,29 +20,28 @@
|
|||
$(this).find('.usedWidgets').sortable({
|
||||
opacity: 0.6,
|
||||
handle: '.handle',
|
||||
update: function(e, ui) {parentRef.updateWidgets(e, ui)},
|
||||
update: function (e, ui) {
|
||||
parentRef.updateWidgets(e, ui)
|
||||
},
|
||||
placeholder: 'ui-state-highlight',
|
||||
forcePlaceholderSize: true
|
||||
});
|
||||
|
||||
// Figure out maxid, this is used when creating new widgets
|
||||
$(this).data('maxid', 0);
|
||||
|
||||
var usedWidgets = $(this).find('.usedWidgets .Widget');
|
||||
usedWidgets.each(function() {
|
||||
var widget = $(this)[0];
|
||||
if(widget.id) {
|
||||
widgetid = widget.id.match(/Widget\[(.+?)\]\[([0-9]+)\]/i);
|
||||
if(widgetid && parseInt(widgetid[2]) > parseInt(parentRef.data('maxid'))) {
|
||||
parentRef.data('maxid', parseInt(widgetid[2]));
|
||||
}
|
||||
forcePlaceholderSize: true,
|
||||
start: function (e, ui) {
|
||||
htmleditors = $(ui.item).closest('.Widget').find('textarea.htmleditor');
|
||||
$.each(htmleditors, function (k, i) {
|
||||
tinyMCE.execCommand('mceRemoveControl', false, $(i).attr('id'));
|
||||
})
|
||||
},
|
||||
stop: function (e, ui) {
|
||||
htmleditors = $(ui.item).closest('.Widget').find('textarea.htmleditor');
|
||||
$.each(htmleditors, function (k, i) {
|
||||
tinyMCE.execCommand('mceAddControl', true, $(i).attr('id'));
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Ensure correct sort values are written when page is saved
|
||||
// TODO Adjust to new event listeners
|
||||
$('.cms-container').bind('submitform', this.beforeSave);
|
||||
$('.cms-container').bind('submitform', function(e) {parentRef.beforeSave(e)});
|
||||
},
|
||||
|
||||
rewriteWidgetAreaAttributes: function() {
|
||||
|
@ -58,17 +57,13 @@
|
|||
if (!widget.rewritten && (widget.id || widget.name)) {
|
||||
if (widget.id && widget.id.indexOf('Widget[') === 0) {
|
||||
var newValue = widget.id.replace(/Widget\[/, 'Widget['+name+'][');
|
||||
//console.log('Renaming '+widget.tagName+' ID '+widget.id+' to '+newValue);
|
||||
widget.id = newValue;
|
||||
}
|
||||
if (widget.name && widget.name.indexOf('Widget[') === 0) {
|
||||
var newValue = widget.name.replace(/Widget\[/, 'Widget['+name+'][');
|
||||
//console.log('Renaming '+widget.tagName+' Name '+widget.name+' to '+newValue);
|
||||
widget.name=newValue;
|
||||
}
|
||||
widget.rewritten='yes';
|
||||
}else {
|
||||
//console.log('Skipping '+(widget.id ? widget.id : (widget.name ? widget.name : 'unknown '+widget.tagName)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -130,13 +125,8 @@
|
|||
},
|
||||
|
||||
insertWidgetEditor: function(response) {
|
||||
var usedWidgets = $('#usedWidgets-'+$(this).attr('name')).children();
|
||||
|
||||
// Give the widget a unique id
|
||||
var newID=parseInt($(this).data('maxid'))+1;
|
||||
$(this).data('maxid', newID);
|
||||
|
||||
var widgetContent = response.replace(/Widget\[0\]/gi, "Widget[new-" + (newID) + "]");
|
||||
var newID = $(response).find('.formid').val();
|
||||
var widgetContent = response.replace(/Widget\[0\]/gi, "Widget[" + (newID) + "]");
|
||||
$('#usedWidgets-'+$(this).attr('name')).append(widgetContent);
|
||||
|
||||
this.rewriteWidgetAreaAttributes();
|
||||
|
@ -144,7 +134,7 @@
|
|||
|
||||
sortWidgets: function() {
|
||||
// Order the sort by the order the widgets are in the list
|
||||
$('#usedWidgets-'+$(this).attr('name')).children().each(function() {
|
||||
$('#usedWidgets-'+$(this).attr('name')).children().each(function(i) {
|
||||
var div = $(this)[0];
|
||||
|
||||
if(div.nodeName != '#comment') {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
ar:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'التطبيقات المصغرة المتاحة'
|
||||
AVAILWIDGETS: 'اضغط على عنوان تطبيق مصغر بالأسفل لاستخدامه بهذه الصفحة.'
|
||||
INUSE: 'التطبيقات المصغرة المستخدمة حاليا'
|
||||
NOAVAIL: 'لا توجد حاليا أية تطبيقات مصغرة متاحة.'
|
||||
TOSORT: 'لترتيب التطبيقات المصغرة المستخدمة حاليا فى هذه الصفحة, اسحبها لأعلى و لأسفل.'
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: 'تطبيقات مصغرة'
|
||||
SINGULARNAME: 'تطبيق مصغر'
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 'مساحات التطبيقات المصغرة'
|
||||
SINGULARNAME: 'مساحة التطبيقات المصغرة'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'اضغط لإضافة هذا التطبيق المصغر'
|
||||
WidgetEditor_ss:
|
||||
DELETE: مسح
|
27
lang/de.yml
27
lang/de.yml
|
@ -1,14 +1,17 @@
|
|||
de:
|
||||
WidgetAreaEditor_ss:
|
||||
AVAILABLE: "Vorhandene Widgets"
|
||||
AVAILWIDGETS: "Klicke den Widget Titel, um es zu benutzen."
|
||||
NOAVAIL: "Es sind derzeit keine Widgets verfügbar."
|
||||
INUSE: "Benutzte Widgets"
|
||||
TOSORT: "Um die Widgets auf dieser Seite zu sortieren, ziehe sie nach oben oder unten."
|
||||
WidgetEditor_ss:
|
||||
DELETE: "Löschen"
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Vorhandene Widgets'
|
||||
AVAILWIDGETS: 'Klicke den Widget Titel, um es zu benutzen.'
|
||||
INUSE: 'Benutzte Widgets'
|
||||
NOAVAIL: 'Es sind derzeit keine Widgets verfügbar.'
|
||||
TOSORT: 'Um die Widgets auf dieser Seite zu sortieren, ziehe sie nach oben oder unten.'
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: Widgets
|
||||
SINGULARNAME: Widget
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: Widgetbereiche
|
||||
SINGULARNAME: Widgetbereich
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: "Klicke um dieses Widget hinzuzufügen"
|
||||
WidgetArea:
|
||||
PLURALNAME: "Widgetbereiche"
|
||||
SINGULARNAME: "Widgetbereich"
|
||||
CLICKTOADDWIDGET: 'Klicke um dieses Widget hinzuzufügen'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Löschen
|
||||
|
|
28
lang/en.yml
28
lang/en.yml
|
@ -1,17 +1,25 @@
|
|||
en:
|
||||
WidgetAreaEditor_ss:
|
||||
SilverStripe\Widgets\Extensions\WidgetPageExtension:
|
||||
INHERITSIDEBAR: 'Inherit Sidebar From Parent'
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Available Widgets'
|
||||
AVAILWIDGETS: 'Click a widget title below to use it on this page.'
|
||||
NOAVAIL: 'There are currently no widgets available.'
|
||||
INUSE: 'Widgets currently used'
|
||||
NOAVAIL: 'There are currently no widgets available.'
|
||||
TOSORT: 'To sort currently used widgets on this page, drag them up and down.'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Delete
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: Widgets
|
||||
PLURALS:
|
||||
one: 'A Widget'
|
||||
other: '{count} Widgets'
|
||||
SINGULARNAME: Widget
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 'Widget Areas'
|
||||
PLURALS:
|
||||
one: 'A Widget Area'
|
||||
other: '{count} Widget Areas'
|
||||
SINGULARNAME: 'Widget Area'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'Click to add this widget'
|
||||
WidgetArea:
|
||||
PLURALNAME: 'Widget Areas'
|
||||
SINGULARNAME: 'Widget Area'
|
||||
Widget:
|
||||
PLURALNAME: Widgets
|
||||
SINGULARNAME: Widget
|
||||
WidgetEditor_ss:
|
||||
DELETE: Delete
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
eo:
|
||||
SilverStripe\Widgets\Extensions\WidgetPageExtension:
|
||||
INHERITSIDEBAR: 'Heredi flankpanelon el patro'
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Disponeblaj fenestraĵoj'
|
||||
AVAILWIDGETS: 'Alklaku sube titolon de fenestraĵo por uzi ĝin en ĉi tiu paĝo.'
|
||||
INUSE: 'Fenestraĵoj aktuale uzataj'
|
||||
NOAVAIL: 'Aktuale mankas disponeblaj fenestraĵoj.'
|
||||
TOSORT: 'Por ordigi aktuale uzatajn fenestraĵojn en ĉi tiu paĝo, ŝovu ilin supren kaj malsupren.'
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: Fenestraĵoj
|
||||
PLURALS:
|
||||
one: 'Unu fenestraĵo'
|
||||
other: '{count} fenestraĵoj'
|
||||
SINGULARNAME: Fenestraĵo
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 'Fenestraĵaj zonoj'
|
||||
PLURALS:
|
||||
one: 'Unu fenestraĵa zono'
|
||||
other: '{count} fenestraĵaj zonoj'
|
||||
SINGULARNAME: 'Fenestraĵa zono'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'Alklaku por aldoni la fenestraĵon'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Forigi
|
|
@ -0,0 +1,17 @@
|
|||
fa_IR:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'ویجت های موجود'
|
||||
AVAILWIDGETS: 'بر روی عنوان یک ویجت در پایین کلیک کنید تا آن را در این صفحه بکار بگیرید.'
|
||||
INUSE: 'ویجت هایی که در حال حاضر استفاده می شوند'
|
||||
NOAVAIL: 'در حال حاضر هیچ ویجتی موجود نیست.'
|
||||
TOSORT: 'برای مرتب سازی ویجت های این صفحه، آنها را بالا و پایین بکشید.'
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: 'ویجت ها'
|
||||
SINGULARNAME: ویجت
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 'محیط ویجت ها'
|
||||
SINGULARNAME: 'محیط ویجت'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'برای افزودن این ویجت کلیک کنید'
|
||||
WidgetEditor_ss:
|
||||
DELETE: حذف
|
29
lang/fi.yml
29
lang/fi.yml
|
@ -1,14 +1,17 @@
|
|||
fi:
|
||||
WidgetAreaEditor.ss:
|
||||
AVAILABLE: "Tarjolla olevat vimpaimet"
|
||||
AVAILWIDGETS: "Klikkaa vimpaimen nimeä ottaaksesi se käyttöön tällä sivulla."
|
||||
NOAVAIL: "Ei vimpaimia käytettävissä."
|
||||
INUSE: "Käytössä olevat vimpaimet"
|
||||
TOSORT: "Järjestääksesi käytössä olevat vimpamet, raahaa ne haluamaasi järjestykseen"
|
||||
WidgetEditor.ss:
|
||||
DELETE: "Poista"
|
||||
WidgetDescription.ss:
|
||||
CLICKTOADDWIDGET: "Lisää vimpain klikkaamalla"
|
||||
WidgetArea:
|
||||
PLURALNAME: "Vimpain alueet"
|
||||
SINGULARNAME: "Vimpain alue"
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Käytettävissä olevat vimpaimet'
|
||||
AVAILWIDGETS: 'Napsauta vimpaimen otsikkoa alla ottaaksesi se käyttöön tällä sivulla.'
|
||||
INUSE: 'Käytössä olevat vimpaimet'
|
||||
NOAVAIL: 'Tällä hetkellä ei vimpaimia tarjolla.'
|
||||
TOSORT: 'Järjestääksesi käytössä olevat vimpaimet, raahaa ja pudota ne paikoilleen.'
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: Vimpaimet
|
||||
SINGULARNAME: Vimpain
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 'Vimpain alueet'
|
||||
SINGULARNAME: 'Vimpain alue'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'Napsauta lisätäksesi tämä vimpain'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Poista
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
fi_FI:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Käytettävissä olevat vimpaimet'
|
||||
AVAILWIDGETS: 'Napsauta vimpaimen otsikkoa alla ottaaksesi se käyttöön tällä sivulla.'
|
||||
INUSE: 'Käytössä olevat vimpaimet'
|
||||
NOAVAIL: 'Tällä hetkellä ei vimpaimia tarjolla.'
|
||||
TOSORT: 'Järjestääksesi käytössä olevat vimpaimet, raahaa ja pudota ne paikoilleen.'
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: Vimpaimet
|
||||
SINGULARNAME: Vimpain
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 'Vimpain alueet'
|
||||
SINGULARNAME: 'Vimpain alue'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'Napsauta lisätäksesi tämä vimpain'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Poista
|
|
@ -0,0 +1,17 @@
|
|||
hr:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Dostupni widgeti'
|
||||
AVAILWIDGETS: 'Kliknite na naziv widgeta ispod za korištenje na ovoj stranici'
|
||||
INUSE: 'Trenutno korišteni widgeti'
|
||||
NOAVAIL: 'Trenutno nema dostupnih widgeta.'
|
||||
TOSORT: 'Za sortiranje trenutno korištenih widgeta na ovoj stranici, rasporedite ih povlačeći gore i dolje.'
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: Widgeti
|
||||
SINGULARNAME: Widget
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 'Zone widgeta'
|
||||
SINGULARNAME: 'Zona widgeta'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'Klikni za dodavanje ovog widgeta'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Obriši
|
|
@ -0,0 +1,5 @@
|
|||
id_ID:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Widget Tersedia'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Hapus
|
|
@ -0,0 +1,17 @@
|
|||
it:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Widget Disponibili'
|
||||
AVAILWIDGETS: 'Clickare sul titolo di un widget per usarlo in questa pagina'
|
||||
INUSE: 'Widget attualmente in uso'
|
||||
NOAVAIL: 'Attualmente non ci sono widget disponibili'
|
||||
TOSORT: 'Per riordinare i widget usati in questa pagina, trascinarli in alto o in basso.'
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: Widget
|
||||
SINGULARNAME: Widget
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 'Aree Widget'
|
||||
SINGULARNAME: 'Area Widget'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'Clickare per aggiungere questo widget'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Elimina
|
|
@ -0,0 +1,17 @@
|
|||
it_IT:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Widgets disponibili'
|
||||
AVAILWIDGETS: 'Clicca sul titolo di un widget qui sotto per usarlo in questa pagina'
|
||||
INUSE: 'Widgets attualmente utilizzati'
|
||||
NOAVAIL: 'Non ci sono widgets attualmente disponibili.'
|
||||
TOSORT: 'Per ordinare i widgets utilizzati in questa pagina, trascinali in alto o in basso.'
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: Widgets
|
||||
SINGULARNAME: Widget
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 'Aree Widget'
|
||||
SINGULARNAME: 'Area Widget'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'Clicca per aggiungere questo widget'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Cancella
|
27
lang/nl.yml
27
lang/nl.yml
|
@ -1,14 +1,17 @@
|
|||
nl:
|
||||
WidgetAreaEditor_ss:
|
||||
AVAILABLE: "Beschikbare Widgets"
|
||||
AVAILWIDGETS: "Klik op een widget titel hieronder, om de widget te gebruiken op deze pagina."
|
||||
NOAVAIL: "Er zijn geen widgets beschikbaar."
|
||||
INUSE: "Huidige gebruikte Widgets"
|
||||
TOSORT: "Om uw widgets te sorteren op deze pagina, rangschik met drag & drop."
|
||||
WidgetEditor_ss:
|
||||
DELETE: "Verwijderen"
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Beschikbare Widgets'
|
||||
AVAILWIDGETS: 'Klik op een widget titel hieronder, om de widget te gebruiken op deze pagina.'
|
||||
INUSE: 'Huidige gebruikte Widgets'
|
||||
NOAVAIL: 'Er zijn geen widgets beschikbaar.'
|
||||
TOSORT: 'Om uw widgets te sorteren op deze pagina, rangschik met drag & drop.'
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: Widgets
|
||||
SINGULARNAME: Widget
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 'Widget plaatsen'
|
||||
SINGULARNAME: 'Widget plaats'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: "Klik om deze widget toe te voegen"
|
||||
WidgetArea:
|
||||
PLURALNAME: "Widget plaatsen"
|
||||
SINGULARNAME: "Widget plaats"
|
||||
CLICKTOADDWIDGET: 'Klik om deze widget toe te voegen'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Verwijderen
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
pl_PL:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Dostępne widżety'
|
||||
AVAILWIDGETS: 'Kliknij w tytuł widżetu, aby używać go na tej stronie.'
|
||||
INUSE: 'Obecnie stosowane widżety'
|
||||
NOAVAIL: 'Nie ma żadnych dostępnych widżetów'
|
||||
TOSORT: 'Aby posortować używane widżety na tej stronie, przeciągnij je w górę i w dół.'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'Kliknij aby dodać ten widżet'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Usuń
|
|
@ -0,0 +1,11 @@
|
|||
ro_RO:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Widget-uri disponibile'
|
||||
AVAILWIDGETS: 'Faceți clic pe un titlu widget de mai jos pentru a-l folosi pe aceasta pagina.'
|
||||
INUSE: 'Widget-uri utilizate în prezent'
|
||||
NOAVAIL: 'Momentan nu exista widget-uri disponibile.'
|
||||
TOSORT: 'Pentru a sorta widget-uri utilizate în prezent pe această pagină, trageți-le în sus și în jos.'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'Click pentru a adăuga acest widget'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Ştergeţi
|
|
@ -0,0 +1,17 @@
|
|||
ru:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Доступные виджеты'
|
||||
AVAILWIDGETS: 'Щелкните заголовок виджета ниже, чтобы использовать его на этой странице.'
|
||||
INUSE: 'Используемые виджеты'
|
||||
NOAVAIL: 'В данный момент доступных виджетов нет.'
|
||||
TOSORT: 'Чтобы расположить используемые на странице виджеты в нужном порядке, перетащите их вверх или вниз.'
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: Виджеты
|
||||
SINGULARNAME: Виджет
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 'Области виджетов'
|
||||
SINGULARNAME: 'Область виджета'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'Щелкните, чтобы добавить этот виджет'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Удалить
|
|
@ -0,0 +1,17 @@
|
|||
sk:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Dostupné widgety'
|
||||
AVAILWIDGETS: 'Kliknite na názov widgetu pre jeho použitie na tejto stránke.'
|
||||
INUSE: 'Momentálne používané widgety.'
|
||||
NOAVAIL: 'Momentálne nie sú dostupné žiadne widgety.'
|
||||
TOSORT: 'Pre zotriedenie momentálne používaných widgetov na tejto stránke, potiahnite ich hore alebo dole.'
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: Widgety
|
||||
SINGULARNAME: Widget
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 'Oblasti widgetu'
|
||||
SINGULARNAME: 'Oblasť widgetu'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'Kliknite pre pridanie tohto widgetu'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Vymazať
|
|
@ -0,0 +1,17 @@
|
|||
sl:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Vtičniki, ki so na voljo'
|
||||
AVAILWIDGETS: 'Spodaj izberite vtičnik, ki ga želite uporabiti na strani.'
|
||||
INUSE: 'Vtičniki v uporabi'
|
||||
NOAVAIL: 'Na voljo ni nobenega vtičnika.'
|
||||
TOSORT: 'Za razvrstitev vtičnikov na strani jih povlecite na ustrezno mesto. '
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: Vtičniki
|
||||
SINGULARNAME: Vtičnik
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 'Prostori za vtičnike'
|
||||
SINGULARNAME: 'Prostor za vtičnike'
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 'Kliknite na ta vtičnik, če ga želite dodati'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Odstrani
|
27
lang/sv.yml
27
lang/sv.yml
|
@ -1,14 +1,17 @@
|
|||
sv:
|
||||
WidgetAreaEditor_ss:
|
||||
AVAILABLE: "Tillgängliga widgets"
|
||||
AVAILWIDGETS: "Klicka på en widget nedan för att använda den på sidan."
|
||||
NOAVAIL: "Inga widgets tillängliga."
|
||||
INUSE: "Använda widgets"
|
||||
TOSORT: "För att sortera widgetar på denna sida, dra dem uppåt eller nedåt."
|
||||
WidgetEditor_ss:
|
||||
DELETE: "Radera"
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 'Tillgängliga widgets'
|
||||
AVAILWIDGETS: 'Klicka på en widget nedan för att använda den på sidan.'
|
||||
INUSE: 'Använda widgets'
|
||||
NOAVAIL: 'Inga widgets tillängliga.'
|
||||
TOSORT: 'För att sortera widgetar på denna sida, dra dem uppåt eller nedåt.'
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: Widgets
|
||||
SINGULARNAME: Widget
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: Widgetområden
|
||||
SINGULARNAME: Widgetområde
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: "Klicka för att lägga till denna widget"
|
||||
WidgetArea:
|
||||
PLURALNAME: "Widget Areas"
|
||||
SINGULARNAME: "Widget Area"
|
||||
CLICKTOADDWIDGET: 'Klicka för att lägga till denna widget'
|
||||
WidgetEditor_ss:
|
||||
DELETE: Radera
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
zh:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 可用的小工具
|
||||
AVAILWIDGETS: 点击下方的小工具名称即可在此页上使用。
|
||||
INUSE: 当前使用的小工具
|
||||
NOAVAIL: 目前没有可用小工具。
|
||||
TOSORT: 要对本页当前使用的小工具进行排序,请像上下拖拽。
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: 小工具
|
||||
SINGULARNAME: 小工具
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 小工具区域
|
||||
SINGULARNAME: 小工具区域
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 点击添加该小工具
|
||||
WidgetEditor_ss:
|
||||
DELETE: 删除
|
|
@ -0,0 +1,17 @@
|
|||
zh_CN:
|
||||
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
|
||||
AVAILABLE: 有效插件
|
||||
AVAILWIDGETS: 点击一个插件标题应用在当前页面
|
||||
INUSE: 当前已使用插件
|
||||
NOAVAIL: 当前无有效插件
|
||||
TOSORT: 请上下拖动当前页面的对应插件进行排序
|
||||
SilverStripe\Widgets\Model\Widget:
|
||||
PLURALNAME: 插件
|
||||
SINGULARNAME: 插件
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
PLURALNAME: 插件区域
|
||||
SINGULARNAME: 插件区域
|
||||
WidgetDescription_ss:
|
||||
CLICKTOADDWIDGET: 点击增加该插件
|
||||
WidgetEditor_ss:
|
||||
DELETE: 删除
|
|
@ -0,0 +1,12 @@
|
|||
Copyright (c) 2017, SilverStripe Limited
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ruleset name="SilverStripe">
|
||||
<description>CodeSniffer ruleset for SilverStripe coding conventions.</description>
|
||||
|
||||
<file>src</file>
|
||||
<file>tests</file>
|
||||
|
||||
<rule ref="PSR2">
|
||||
<!-- Current exclusions -->
|
||||
<exclude name="PSR1.Methods.CamelCapsMethodName"/>
|
||||
</rule>
|
||||
</ruleset>
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit bootstrap="vendor/silverstripe/cms/tests/bootstrap.php" colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="Default">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src/</directory>
|
||||
<exclude>
|
||||
<directory suffix=".php">tests/</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
|
@ -3,7 +3,7 @@
|
|||
// root widget area editor container
|
||||
.WidgetAreaEditor {
|
||||
h2 {
|
||||
|
||||
|
||||
}
|
||||
p {
|
||||
margin-bottom: 15px;
|
||||
|
@ -12,40 +12,36 @@
|
|||
|
||||
// base styles for a widget
|
||||
.Widget {
|
||||
padding: 0 7px 7px 7px;
|
||||
padding: 0 1.25rem .75rem;
|
||||
margin: 16px 0 16px 0;
|
||||
color: #1f1f1f;
|
||||
background-color: #b0bec7;
|
||||
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2IwYmVjNyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzkyYTViMiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA==');
|
||||
background-size: 100%;
|
||||
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b0bec7), color-stop(100%, #92a5b2));
|
||||
background-image: -webkit-linear-gradient(#b0bec7, #92a5b2);
|
||||
background-image: -moz-linear-gradient(#b0bec7, #92a5b2);
|
||||
background-image: -o-linear-gradient(#b0bec7, #92a5b2);
|
||||
background-image: linear-gradient(#b0bec7, #92a5b2);
|
||||
border-top: 1px solid #c2cdd4;
|
||||
border-bottom: 1px solid #748d9d;
|
||||
color: #4F5861;
|
||||
background-color: #F1F3F6;
|
||||
border: 1px solid #ced5e1;
|
||||
@include border-radius(3px);
|
||||
h3 {
|
||||
color: #43536d;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding: 4px 0;
|
||||
line-height: 40px;
|
||||
text-shadow: #bfcad2 1px 1px 0;
|
||||
@include transition-property(background-image);
|
||||
@include transition-duration(0.2s);
|
||||
@include transition-timing-function(ease-out);
|
||||
}
|
||||
.widgetDescription {
|
||||
background: #eceff1;
|
||||
padding: 5px 5px 1px 5px;
|
||||
padding: 0 0 .5rem;
|
||||
margin: 0;
|
||||
@include border-radius(3px);
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
.widgetFields {
|
||||
background: #eceff1;
|
||||
padding: 5px;
|
||||
margin: 7px 0 0 0;
|
||||
@include border-radius(3px);
|
||||
background: #F7F8FA;
|
||||
border-top: 1px solid #dbe0e9;
|
||||
border-bottom: 1px solid #dbe0e9;
|
||||
padding: .75rem 1.25rem;
|
||||
margin: .75rem -1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,7 +85,7 @@
|
|||
}
|
||||
|
||||
.noWidgets {
|
||||
|
||||
|
||||
}
|
||||
|
||||
// one column layout when space is limited
|
||||
|
@ -103,4 +99,4 @@
|
|||
width: 100%;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\Widgets\Controllers;
|
||||
|
||||
use SilverStripe\Core\Extension;
|
||||
use SilverStripe\Widgets\Model\WidgetArea;
|
||||
|
||||
/**
|
||||
* Add this to ContentController to enable widgets
|
||||
*
|
||||
* @package widgets
|
||||
*/
|
||||
class WidgetContentControllerExtension extends Extension
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_actions = array(
|
||||
'handleWidget'
|
||||
);
|
||||
|
||||
/**
|
||||
* Handles widgets attached to a page through one or more {@link WidgetArea}
|
||||
* elements.
|
||||
*
|
||||
* Iterated through each $has_one relation with a {@link WidgetArea} and
|
||||
* looks for connected widgets by their database identifier.
|
||||
*
|
||||
* Assumes URLs in the following format: <URLSegment>/widget/<Widget-ID>.
|
||||
*
|
||||
* @return RequestHandler
|
||||
*/
|
||||
public function handleWidget()
|
||||
{
|
||||
$SQL_id = $this->owner->getRequest()->param('ID');
|
||||
if (!$SQL_id) {
|
||||
return false;
|
||||
}
|
||||
/** @var SiteTree $widgetOwner */
|
||||
$widgetOwner = $this->owner->data();
|
||||
while ($widgetOwner->InheritSideBar && $widgetOwner->Parent()->exists()) {
|
||||
$widgetOwner = $widgetOwner->Parent();
|
||||
}
|
||||
|
||||
// find WidgetArea relations
|
||||
$widgetAreaRelations = array();
|
||||
$hasOnes = $widgetOwner->hasOne();
|
||||
|
||||
if (!$hasOnes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($hasOnes as $hasOneName => $hasOneClass) {
|
||||
if ($hasOneClass == WidgetArea::class || is_subclass_of($hasOneClass, WidgetArea::class)) {
|
||||
$widgetAreaRelations[] = $hasOneName;
|
||||
}
|
||||
}
|
||||
|
||||
// find widget
|
||||
$widget = null;
|
||||
|
||||
foreach ($widgetAreaRelations as $widgetAreaRelation) {
|
||||
if ($widget) {
|
||||
break;
|
||||
}
|
||||
|
||||
$widget = $widgetOwner->$widgetAreaRelation()->Widgets()
|
||||
->filter('ID', $SQL_id)
|
||||
->First();
|
||||
}
|
||||
|
||||
if (!$widget) {
|
||||
user_error('No widget found', E_USER_ERROR);
|
||||
}
|
||||
|
||||
return $widget->getController();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\Widgets\Extensions;
|
||||
|
||||
use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\Widgets\Forms\WidgetAreaEditor;
|
||||
use SilverStripe\Widgets\Model\WidgetArea;
|
||||
|
||||
/**
|
||||
* Adds a single {@link WidgetArea} called "SideBar" to {@link Page} classes.
|
||||
* Adjust your templates to render the resulting
|
||||
* {@link WidgetArea} as required, through the $SideBarView placeholder.
|
||||
*
|
||||
* This extension is just an example on how to use the widgets functionality,
|
||||
* feel free to create your own relationships, naming conventions, etc.
|
||||
* without using this class.
|
||||
*/
|
||||
class WidgetPageExtension extends DataExtension
|
||||
{
|
||||
private static $db = [
|
||||
'InheritSideBar' => 'Boolean',
|
||||
];
|
||||
|
||||
private static $defaults = [
|
||||
'InheritSideBar' => true
|
||||
];
|
||||
|
||||
private static $has_one = [
|
||||
'SideBar' => WidgetArea::class,
|
||||
];
|
||||
|
||||
private static $owns = [
|
||||
'SideBar',
|
||||
];
|
||||
|
||||
private static $cascade_deletes = [
|
||||
'SideBar',
|
||||
];
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
$fields->addFieldToTab(
|
||||
"Root.Widgets",
|
||||
new CheckboxField("InheritSideBar", _t(__CLASS__ . '.INHERITSIDEBAR', 'Inherit Sidebar From Parent'))
|
||||
);
|
||||
$fields->addFieldToTab(
|
||||
"Root.Widgets",
|
||||
new WidgetAreaEditor("SideBar")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WidgetArea
|
||||
*/
|
||||
public function SideBarView()
|
||||
{
|
||||
if ($this->owner->InheritSideBar
|
||||
&& ($parent = $this->owner->getParent())
|
||||
&& $parent->hasMethod('SideBarView')
|
||||
) {
|
||||
return $parent->SideBarView();
|
||||
} elseif ($this->owner->SideBar()->exists()) {
|
||||
return $this->owner->SideBar();
|
||||
}
|
||||
}
|
||||
|
||||
public function onBeforeDuplicate($duplicatePage)
|
||||
{
|
||||
if ($this->owner->hasField('SideBarID')) {
|
||||
$sideBar = $this->owner->getComponent('SideBar');
|
||||
$duplicateWidgetArea = $sideBar->duplicate();
|
||||
|
||||
foreach ($sideBar->Items() as $originalWidget) {
|
||||
$widget = $originalWidget->duplicate(false);
|
||||
$widget->ParentID = $duplicateWidgetArea->ID;
|
||||
$widget->write();
|
||||
}
|
||||
|
||||
$duplicatePage->SideBarID = $duplicateWidgetArea->ID;
|
||||
}
|
||||
|
||||
return $duplicatePage;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\Widgets\Forms;
|
||||
|
||||
use Exception;
|
||||
use SilverStripe\Core\ClassInfo;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Forms\FormField;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\DataObjectInterface;
|
||||
use SilverStripe\View\Requirements;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
/**
|
||||
* Special field type for selecting and configuring widgets on a page.
|
||||
*
|
||||
* @package widgets
|
||||
*/
|
||||
class WidgetAreaEditor extends FormField
|
||||
{
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $widgetClasses
|
||||
* @param int $maxWidgets
|
||||
*/
|
||||
public function __construct($name, $widgetClasses = array(Widget::class), $maxWidgets = 0)
|
||||
{
|
||||
$this->MaxWidgets = $maxWidgets;
|
||||
$this->widgetClasses = $widgetClasses;
|
||||
|
||||
parent::__construct($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
*
|
||||
* @return string - HTML
|
||||
*/
|
||||
public function FieldHolder($properties = array())
|
||||
{
|
||||
Requirements::css('silverstripe/widgets:css/WidgetAreaEditor.css');
|
||||
Requirements::javascript('silverstripe/widgets:javascript/WidgetAreaEditor.js');
|
||||
|
||||
return $this->renderWith(WidgetAreaEditor::class);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function AvailableWidgets()
|
||||
{
|
||||
$widgets= new ArrayList();
|
||||
|
||||
foreach ($this->widgetClasses as $widgetClass) {
|
||||
$classes = ClassInfo::subclassesFor($widgetClass);
|
||||
|
||||
if (isset($classes[strtolower(Widget::class)])) {
|
||||
unset($classes[strtolower(Widget::class)]);
|
||||
} elseif (isset($classes[0]) && $classes[0] == Widget::class) {
|
||||
unset($classes[0]);
|
||||
}
|
||||
|
||||
foreach ($classes as $class) {
|
||||
$available = Config::inst()->get($class, 'only_available_in');
|
||||
|
||||
if (!empty($available) && is_array($available)) {
|
||||
if (in_array($this->Name, $available ?? [])) {
|
||||
$widgets->push(singleton($class));
|
||||
}
|
||||
} else {
|
||||
$widgets->push(singleton($class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasManyList
|
||||
*/
|
||||
public function UsedWidgets()
|
||||
{
|
||||
// Call class_exists() to load Widget.php earlier and avoid a segfault
|
||||
class_exists(Widget::class);
|
||||
|
||||
$relationName = $this->name;
|
||||
$widgets = $this->form->getRecord()->getComponent($relationName)->Items();
|
||||
|
||||
return $widgets;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function IdxField()
|
||||
{
|
||||
return $this->id() . 'ID';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function Value()
|
||||
{
|
||||
$relationName = $this->name;
|
||||
|
||||
return $this->form->getRecord()->getComponent($relationName)->ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DataObjectInterface $record
|
||||
* @throws Exception if no form could be retrieved
|
||||
*/
|
||||
public function saveInto(DataObjectInterface $record)
|
||||
{
|
||||
$name = $this->name;
|
||||
$idName = $name . "ID";
|
||||
|
||||
$widgetarea = $record->getComponent($name);
|
||||
$widgetarea->write();
|
||||
|
||||
$record->$idName = $widgetarea->ID;
|
||||
|
||||
$widgets = $widgetarea->Items();
|
||||
|
||||
// store the field IDs and delete the missing fields
|
||||
// alternatively, we could delete all the fields and re add them
|
||||
$missingWidgets = array();
|
||||
|
||||
if ($widgets) {
|
||||
foreach ($widgets as $existingWidget) {
|
||||
$missingWidgets[$existingWidget->ID] = $existingWidget;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->getForm()) {
|
||||
throw new Exception("no form");
|
||||
}
|
||||
|
||||
$widgetData = $this->getForm()->getController()->getRequest()->requestVar('Widget');
|
||||
if ($widgetData && isset($widgetData[$this->getName()])) {
|
||||
$widgetAreaData = $widgetData[$this->getName()];
|
||||
|
||||
foreach ($widgetAreaData as $newWidgetID => $newWidgetData) {
|
||||
// Sometimes the id is "new-1" or similar, ensure this doesn't get into the query
|
||||
if (!is_numeric($newWidgetID)) {
|
||||
$newWidgetID = 0;
|
||||
}
|
||||
|
||||
$widget = null;
|
||||
if ($newWidgetID) {
|
||||
// \"ParentID\" = '0' is for the new page
|
||||
$widget = Widget::get()
|
||||
->filter('ParentID', array(0, $record->$name()->ID))
|
||||
->byID($newWidgetID);
|
||||
|
||||
// check if we are updating an existing widget
|
||||
if ($widget && isset($missingWidgets[$widget->ID])) {
|
||||
unset($missingWidgets[$widget->ID]);
|
||||
}
|
||||
}
|
||||
|
||||
// unsantise the class name
|
||||
if (empty($newWidgetData['Type'])) {
|
||||
$newWidgetData['Type'] = '';
|
||||
}
|
||||
$newWidgetData['Type'] = str_replace('_', '\\', $newWidgetData['Type'] ?? '');
|
||||
|
||||
// create a new object
|
||||
if (!$widget
|
||||
&& !empty($newWidgetData['Type'])
|
||||
&& class_exists($newWidgetData['Type'] ?? '')
|
||||
&& is_subclass_of($newWidgetData['Type'], Widget::class)
|
||||
) {
|
||||
$widget = Injector::inst()->create($newWidgetData['Type']);
|
||||
$widget->ID = 0;
|
||||
$widget->ParentID = $record->$name()->ID;
|
||||
}
|
||||
|
||||
if ($widget) {
|
||||
if ($widget->ParentID == 0) {
|
||||
$widget->ParentID = $record->$name()->ID;
|
||||
}
|
||||
$widget->populateFromPostData($newWidgetData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove the fields not saved
|
||||
if ($missingWidgets) {
|
||||
foreach ($missingWidgets as $removedWidget) {
|
||||
if (isset($removedWidget) && is_numeric($removedWidget->ID)) {
|
||||
$removedWidget->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\Widgets\Model;
|
||||
|
||||
use Exception;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Core\ClassInfo;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\HiddenField;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\FieldType\DBHTMLText;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
use SilverStripe\View\SSViewer;
|
||||
|
||||
/**
|
||||
* Widgets let CMS authors drag and drop small pieces of functionality into
|
||||
* defined areas of their websites.
|
||||
*
|
||||
* You can use forms in widgets by implementing a {@link WidgetController}.
|
||||
*
|
||||
* See {@link WidgetController} for more information.
|
||||
*
|
||||
* @package widgets
|
||||
*/
|
||||
class Widget extends DataObject
|
||||
{
|
||||
private static $db = [
|
||||
"Title" => "Varchar(255)",
|
||||
"Sort" => "Int",
|
||||
"Enabled" => "Boolean",
|
||||
];
|
||||
|
||||
private static $defaults = [
|
||||
'Enabled' => true,
|
||||
];
|
||||
|
||||
private static $casting = [
|
||||
'CMSTitle' => 'Text',
|
||||
'Description' => 'Text',
|
||||
];
|
||||
|
||||
private static $only_available_in = [];
|
||||
|
||||
private static $has_one = [
|
||||
"Parent" => WidgetArea::class,
|
||||
];
|
||||
|
||||
private static $default_sort = "\"Sort\"";
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $cmsTitle = "Name of this widget";
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $description = "Description of what this widget does.";
|
||||
|
||||
private static $summary_fields = [
|
||||
'CMSTitle' => 'Title'
|
||||
];
|
||||
|
||||
private static $table_name = 'Widget';
|
||||
|
||||
private static $extensions = [
|
||||
Versioned::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var WidgetController
|
||||
*/
|
||||
protected $controller;
|
||||
|
||||
public function populateDefaults()
|
||||
{
|
||||
parent::populateDefaults();
|
||||
$this->setField('Title', $this->getTitle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: Overloaded in {@link WidgetController}.
|
||||
*
|
||||
* @return string HTML
|
||||
*/
|
||||
public function WidgetHolder()
|
||||
{
|
||||
return $this->renderWith("WidgetHolder");
|
||||
}
|
||||
|
||||
/**
|
||||
* Default way to render widget in templates.
|
||||
* @return string HTML
|
||||
*/
|
||||
public function forTemplate($holder = true)
|
||||
{
|
||||
if ($holder) {
|
||||
return $this->WidgetHolder();
|
||||
}
|
||||
|
||||
return $this->Content();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the widget content in a custom template with the same name as the
|
||||
* current class. This should be the main point of output customization.
|
||||
*
|
||||
* Invoked from within WidgetHolder.ss, which contains the "framing" around
|
||||
* the custom content, like a title.
|
||||
*
|
||||
* Note: Overloaded in {@link WidgetController}.
|
||||
*
|
||||
* @return string HTML
|
||||
*/
|
||||
public function Content()
|
||||
{
|
||||
return $this->renderWith(SSViewer::get_templates_by_class(static::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCMSTitle()
|
||||
{
|
||||
return _t(__CLASS__ . '.CMSTITLE', $this->config()->get('cmsTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return _t(__CLASS__ . '.DESCRIPTION', $this->config()->get('description'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string - HTML
|
||||
*/
|
||||
public function DescriptionSegment()
|
||||
{
|
||||
return $this->renderWith('WidgetDescription');
|
||||
}
|
||||
|
||||
/**
|
||||
* @see WidgetController::editablesegment()
|
||||
*
|
||||
* @return string - HTML
|
||||
*/
|
||||
public function EditableSegment()
|
||||
{
|
||||
return $this->renderWith('WidgetEditor');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FieldList
|
||||
*/
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = new FieldList(
|
||||
new TextField('Title', $this->fieldLabel('Title'), null, 255),
|
||||
new CheckboxField('Enabled', $this->fieldLabel('Enabled'))
|
||||
);
|
||||
$this->extend('updateCMSFields', $fields);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FieldList
|
||||
*/
|
||||
public function CMSEditor()
|
||||
{
|
||||
$fields = $this->getCMSFields();
|
||||
$outputFields = new FieldList();
|
||||
|
||||
$this->FormID = $this->ID ?: uniqid();
|
||||
$outputFields->push(
|
||||
HiddenField::create(
|
||||
'Widget[' . $this->FormID . '][FormID]',
|
||||
'FormID',
|
||||
$this->FormID
|
||||
)->addExtraClass('formid')
|
||||
);
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$name = $field->getName();
|
||||
$value = $this->getField($name);
|
||||
if ($value) {
|
||||
$field->setValue($value);
|
||||
}
|
||||
$namefiltered = preg_replace("/([A-Za-z0-9\-_]+)/", "Widget[" . $this->FormID . "][\\1]", $name ?? '');
|
||||
|
||||
$field->setName($namefiltered);
|
||||
$outputFields->push($field);
|
||||
}
|
||||
|
||||
return $outputFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* A fully qualified class name is returned with underscores instead of backslashes so it is HTML safe. Dashes
|
||||
* can't be used as they're handled in the Javascript for other purposes.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function ClassName()
|
||||
{
|
||||
return str_replace('\\', '_', get_class($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function Name()
|
||||
{
|
||||
return "Widget[" . $this->ID . "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception If the widget controller's class name couldn't be found
|
||||
*
|
||||
* @return WidgetController
|
||||
*/
|
||||
public function getController()
|
||||
{
|
||||
if ($this->controller) {
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
foreach (array_reverse(ClassInfo::ancestry(get_class($this)) ?? []) as $widgetClass) {
|
||||
$controllerClass = "{$widgetClass}Controller";
|
||||
if (class_exists($controllerClass ?? '')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists($controllerClass ?? '')) {
|
||||
throw new Exception('Could not find controller class for ' . static::class);
|
||||
}
|
||||
|
||||
$this->controller = Injector::inst()->create($controllerClass, $this);
|
||||
if (Injector::inst()->has(HTTPRequest::class)) {
|
||||
$this->controller->setRequest(Injector::inst()->get(HTTPRequest::class));
|
||||
}
|
||||
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*/
|
||||
public function populateFromPostData($data)
|
||||
{
|
||||
$fields = $this->getCMSFields();
|
||||
foreach ($data as $name => $value) {
|
||||
if ($name != "Type") {
|
||||
if ($field = $fields->dataFieldByName($name)) {
|
||||
$field->setValue($value);
|
||||
$field->saveInto($this);
|
||||
} else {
|
||||
$this->setField($name, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Look for checkbox fields not present in the data
|
||||
foreach ($fields as $field) {
|
||||
if ($field instanceof CheckboxField && !array_key_exists($field->getName(), $data ?? [])) {
|
||||
$field->setValue(false);
|
||||
$field->saveInto($this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->write();
|
||||
|
||||
// The field must be written to ensure a unique ID.
|
||||
$this->Name = get_class($this) . $this->ID;
|
||||
$this->write();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\Widgets\Model;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\HasManyList;
|
||||
use SilverStripe\ORM\SS_List;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
|
||||
/**
|
||||
* Represents a set of widgets shown on a page.
|
||||
*/
|
||||
class WidgetArea extends DataObject
|
||||
{
|
||||
private static $has_many = [
|
||||
"Widgets" => Widget::class
|
||||
];
|
||||
|
||||
private static $owns = [
|
||||
'Widgets',
|
||||
];
|
||||
|
||||
private static $cascade_deletes = [
|
||||
'Widgets',
|
||||
];
|
||||
|
||||
private static $extensions = [
|
||||
Versioned::class,
|
||||
];
|
||||
|
||||
private static $table_name = 'WidgetArea';
|
||||
|
||||
public $template = __CLASS__;
|
||||
|
||||
/**
|
||||
* Used in template instead of {@link Widgets()} to wrap each widget in its
|
||||
* controller, making it easier to access and process form logic and
|
||||
* actions stored in {@link WidgetController}.
|
||||
*
|
||||
* @return SS_List - Collection of {@link WidgetController} instances.
|
||||
*/
|
||||
public function WidgetControllers()
|
||||
{
|
||||
$controllers = new ArrayList();
|
||||
$items = $this->ItemsToRender();
|
||||
if (!is_null($items)) {
|
||||
foreach ($items as $widget) {
|
||||
/** @var Widget $widget */
|
||||
|
||||
/** @var Controller $controller */
|
||||
$controller = $widget->getController();
|
||||
|
||||
$controller->doInit();
|
||||
$controllers->push($controller);
|
||||
}
|
||||
}
|
||||
return $controllers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasManyList
|
||||
*/
|
||||
public function Items()
|
||||
{
|
||||
return $this->Widgets();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasManyList
|
||||
*/
|
||||
public function ItemsToRender()
|
||||
{
|
||||
return $this->Items()->filter('Enabled', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string - HTML
|
||||
*/
|
||||
public function forTemplate()
|
||||
{
|
||||
return $this->renderWith($this->template);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $template
|
||||
*/
|
||||
public function setTemplate($template)
|
||||
{
|
||||
$this->template = $template;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\Widgets\Model;
|
||||
|
||||
use SilverStripe\Admin\LeftAndMain;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Core\ClassInfo;
|
||||
|
||||
/**
|
||||
* Optional controller for every widget which has its own logic, e.g. in forms.
|
||||
*
|
||||
* It always handles a single widget, usually passed in as a database
|
||||
* identifier through the controller URL. Needs to be constructed as a nested
|
||||
* controller within a {@link ContentController}.
|
||||
*
|
||||
* ## Forms
|
||||
* You can add forms like in any other SilverStripe controller. If you need
|
||||
* access to the widget from within a form, you can use
|
||||
* `$this->controller->getWidget()` inside the form logic.
|
||||
*
|
||||
* Note: Widget controllers currently only work on {@link Page} objects,
|
||||
* because the logic is implemented in {@link ContentController->handleWidget()}.
|
||||
* Copy this logic and the URL rules to enable it for other controllers.
|
||||
*
|
||||
* @package widgets
|
||||
*/
|
||||
class WidgetController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var Widget
|
||||
*/
|
||||
protected $widget;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_actions = array(
|
||||
'editablesegment'
|
||||
);
|
||||
|
||||
/**
|
||||
* @param Widget $widget
|
||||
*/
|
||||
public function __construct($widget = null)
|
||||
{
|
||||
if ($widget) {
|
||||
$this->widget = $widget;
|
||||
$this->failover = $widget;
|
||||
}
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action
|
||||
* @return string
|
||||
*/
|
||||
public function Link($action = null)
|
||||
{
|
||||
$id = ($this->widget) ? $this->widget->ID : null;
|
||||
$segment = Controller::join_links('widget', $id, $action);
|
||||
|
||||
$page = Director::get_current_page();
|
||||
if ($page && !($page instanceof WidgetController)) {
|
||||
return $page->Link($segment);
|
||||
}
|
||||
|
||||
if ($controller = $this->getParentController()) {
|
||||
return $controller->Link($segment);
|
||||
}
|
||||
|
||||
return $segment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycles up the controller stack until it finds a non-widget controller
|
||||
* This is needed becauseController::currreturns the widget controller,
|
||||
* which means anyLinkfunction turns into endless loop.
|
||||
*
|
||||
* @return Controller
|
||||
*/
|
||||
public function getParentController()
|
||||
{
|
||||
foreach (Controller::$controller_stack as $controller) {
|
||||
if (!($controller instanceof WidgetController)) {
|
||||
return $controller;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Widget
|
||||
*/
|
||||
public function getWidget()
|
||||
{
|
||||
return $this->widget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloaded from {@link Widget->Content()} to allow for controller / form
|
||||
* linking.
|
||||
*
|
||||
* @return string HTML
|
||||
*/
|
||||
public function Content()
|
||||
{
|
||||
return $this->renderWith(array_reverse(ClassInfo::ancestry(get_class($this->widget)) ?? []));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloaded from {@link Widget->WidgetHolder()} to allow for controller/
|
||||
* form linking.
|
||||
*
|
||||
* @return string HTML
|
||||
*/
|
||||
public function WidgetHolder()
|
||||
{
|
||||
return $this->renderWith("WidgetHolder");
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the `WidgetEditor.ss` template and {@link Widget->editablesegment()}
|
||||
* to render a administrator-view of the widget. It is assumed that this
|
||||
* view contains form elements which are submitted and saved through
|
||||
* {@link WidgetAreaEditor} within the CMS interface.
|
||||
*
|
||||
* @return string HTML
|
||||
*/
|
||||
public function editablesegment()
|
||||
{
|
||||
// use left and main to set the html config
|
||||
$leftandmain = LeftAndMain::create();
|
||||
$leftandmain->setRequest($this->getRequest());
|
||||
$leftandmain->doInit();
|
||||
|
||||
// Decode if fully qualified - @see Widget::ClassName
|
||||
$className = str_replace('_', '\\', $this->urlParams['ID'] ?? '');
|
||||
if (class_exists($className ?? '') && is_subclass_of($className, Widget::class)) {
|
||||
$obj = new $className();
|
||||
return $obj->EditableSegment();
|
||||
} else {
|
||||
user_error("Bad widget class: $className", E_USER_WARNING);
|
||||
return "Bad widget class name given";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<div class="WidgetAreaEditor field" id="WidgetAreaEditor-$Name" name="$Name"<% if $MaxWidgets %> maxwidgets="$MaxWidgets"<% end_if %>>
|
||||
<input type="hidden" id="$Name" name="$IdxField" value="$Value" />
|
||||
<div class="availableWidgetsHolder">
|
||||
<h2><%t SilverStripe\\Widgets\\Forms\\WidgetAreaEditor_ss.AVAILABLE 'Available Widgets' %></h2>
|
||||
<p class="message"><%t SilverStripe\\Widgets\\Forms\\WidgetAreaEditor_ss.AVAILWIDGETS 'Click a widget title below to use it on this page.' %></p>
|
||||
<div class="availableWidgets" id="availableWidgets-$Name">
|
||||
<% if $AvailableWidgets %>
|
||||
<% loop $AvailableWidgets %>
|
||||
$DescriptionSegment
|
||||
<% end_loop %>
|
||||
<% else %>
|
||||
<div class="NoWidgets" id="NoWidgets-$Name">
|
||||
<p><%t SilverStripe\\Widgets\\Forms\\WidgetAreaEditor_ss.NOAVAIL 'There are currently no widgets available.' %></p>
|
||||
</div>
|
||||
<% end_if %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="usedWidgetsHolder">
|
||||
<h2><%t SilverStripe\\Widgets\\Forms\\WidgetAreaEditor_ss.INUSE 'Widgets currently used' %></h2>
|
||||
<p class="message"><%t SilverStripe\\Widgets\\Forms\\WidgetAreaEditor_ss.TOSORT 'To sort currently used widgets on this page, drag them up and down.' %></p>
|
||||
|
||||
<div class="usedWidgets" id="usedWidgets-$Name">
|
||||
<% if $UsedWidgets %>
|
||||
<% loop $UsedWidgets %>
|
||||
$EditableSegment
|
||||
<% end_loop %>
|
||||
<% else %>
|
||||
<div class="NoWidgets" id="NoWidgets-$Name"></div>
|
||||
<% end_if %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,3 @@
|
|||
<% loop $WidgetControllers %>
|
||||
$WidgetHolder
|
||||
<% end_loop %>
|
|
@ -1,3 +0,0 @@
|
|||
<% loop WidgetControllers %>
|
||||
$WidgetHolder
|
||||
<% end_loop %>
|
|
@ -1,32 +0,0 @@
|
|||
<div class="WidgetAreaEditor field" id="WidgetAreaEditor-$Name" name="$Name"<% if MaxWidgets %> maxwidgets="$MaxWidgets"<% end_if %>>
|
||||
<input type="hidden" id="$Name" name="$IdxField" value="$Value" />
|
||||
<div class="availableWidgetsHolder">
|
||||
<h2><% _t('WidgetAreaEditor_ss.AVAILABLE', 'Available Widgets') %></h2>
|
||||
<p class="message"><% _t('WidgetAreaEditor_ss.AVAILWIDGETS', 'Click a widget title below to use it on this page.') %></p>
|
||||
<div class="availableWidgets" id="availableWidgets-$Name">
|
||||
<% if AvailableWidgets %>
|
||||
<% loop AvailableWidgets %>
|
||||
$DescriptionSegment
|
||||
<% end_loop %>
|
||||
<% else %>
|
||||
<div class="NoWidgets" id="NoWidgets-$Name">
|
||||
<p><% _t('WidgetAreaEditor_ss.NOAVAIL', 'There are currently no widgets available.') %></p>
|
||||
</div>
|
||||
<% end_if %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="usedWidgetsHolder">
|
||||
<h2><% _t('WidgetAreaEditor_ss.INUSE', 'Widgets currently used') %></h2>
|
||||
<p class="message"><% _t('WidgetAreaEditor_ss.TOSORT', 'To sort currently used widgets on this page, drag them up and down.') %></p>
|
||||
|
||||
<div class="usedWidgets" id="usedWidgets-$Name">
|
||||
<% if UsedWidgets %>
|
||||
<% loop UsedWidgets %>
|
||||
$EditableSegment
|
||||
<% end_loop %>
|
||||
<% else %>
|
||||
<div class="NoWidgets" id="NoWidgets-$Name"></div>
|
||||
<% end_if %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,6 +1,6 @@
|
|||
<div class="Widget" id="$ClassName">
|
||||
<h3 title="<% _t('WidgetDescription_ss.CLICKTOADDWIDGET', 'Click to add this widget') %>">$CMSTitle</h3>
|
||||
<h3 title="<%t WidgetDescription_ss.CLICKTOADDWIDGET 'Click to add this widget' %>">$CMSTitle</h3>
|
||||
<div class="widgetDescription">
|
||||
<p>$Description</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,5 +10,5 @@
|
|||
<% end_if %>
|
||||
<input type="hidden" name="$Name[Type]" value="$ClassName" />
|
||||
<input type="hidden" name="$Name[Sort]" value="$Sort" />
|
||||
<p class="deleteWidget"><span class="widgetDelete ss-ui-button"><% _t('WidgetEditor_ss.DELETE', 'Delete') %></span></p>
|
||||
</div>
|
||||
<p class="deleteWidget"><span class="widgetDelete btn btn-danger"><%t WidgetEditor_ss.DELETE 'Delete' %></span></p>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="WidgetHolder $ClassName<% if FirstLast %> $FirstLast<% end_if %>">
|
||||
<% if Title %><h3>$Title</h3><% end_if %>
|
||||
<div class="WidgetHolder $ClassName<% if $FirstLast %> $FirstLast<% end_if %>">
|
||||
<% if $Title %><h3>$Title</h3><% end_if %>
|
||||
$Content
|
||||
</div>
|
||||
|
|
|
@ -1,471 +1,503 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\Widgets\Tests;
|
||||
|
||||
use SilverStripe\CMS\Controllers\ContentController;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Widgets\Extensions\WidgetPageExtension;
|
||||
use SilverStripe\Widgets\Forms\WidgetAreaEditor;
|
||||
use SilverStripe\Widgets\Tests\WidgetAreaEditorTest\FakePage;
|
||||
use SilverStripe\Widgets\Tests\WidgetAreaEditorTest\TestWidget;
|
||||
|
||||
/**
|
||||
* @package cms
|
||||
* @subpackage tests
|
||||
*/
|
||||
class WidgetAreaEditorTest extends SapphireTest {
|
||||
/**
|
||||
* This is the widget you want to use for your unit tests.
|
||||
*/
|
||||
protected $widgetToTest = 'WidgetAreaEditorTest_TestWidget';
|
||||
class WidgetAreaEditorTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
* This is the widget you want to use for your unit tests.
|
||||
*/
|
||||
protected $widgetToTest = TestWidget::class;
|
||||
|
||||
protected $extraDataObjects = array(
|
||||
'WidgetAreaEditorTest_FakePage',
|
||||
'WidgetAreaEditorTest_TestWidget',
|
||||
);
|
||||
|
||||
protected $usesDatabase = true;
|
||||
|
||||
function testFillingOneArea() {
|
||||
$oldRequest = $_REQUEST;
|
||||
|
||||
$_REQUEST = array(
|
||||
'Widget' => array(
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidget',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$page = new WidgetAreaEditorTest_FakePage();
|
||||
protected static $extra_dataobjects = [
|
||||
FakePage::class,
|
||||
TestWidget::class,
|
||||
];
|
||||
|
||||
$editorSide->saveInto($page);
|
||||
$editorBott->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
protected $usesDatabase = true;
|
||||
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 0);
|
||||
|
||||
$_REQUEST = $oldRequest;
|
||||
}
|
||||
protected static $required_extensions = [
|
||||
SiteTree::class => [WidgetPageExtension::class]
|
||||
];
|
||||
|
||||
function testFillingTwoAreas() {
|
||||
$oldRequest = $_REQUEST;
|
||||
|
||||
$_REQUEST = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetSide',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$page = new WidgetAreaEditorTest_FakePage();
|
||||
public function testFillingOneArea()
|
||||
{
|
||||
$data = array(
|
||||
'Widget' => array(
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidget',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$request = new HTTPRequest('get', 'post', array(), $data);
|
||||
|
||||
$editorSide->saveInto($page);
|
||||
$editorBott->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
|
||||
// Make sure they both got saved
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 1);
|
||||
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
$this->assertEquals($sideWidgets[0]->Title(), 'MyTestWidgetSide');
|
||||
$this->assertEquals($bottWidgets[0]->Title(), 'MyTestWidgetBottom');
|
||||
|
||||
$_REQUEST = $oldRequest;
|
||||
}
|
||||
|
||||
function testDeletingOneWidgetFromOneArea() {
|
||||
$oldRequest = $_REQUEST;
|
||||
|
||||
// First get some widgets in there
|
||||
$_REQUEST = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetSide',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$page = new WidgetAreaEditorTest_FakePage();
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$form = new Form(
|
||||
$controller = new ContentController(),
|
||||
Form::class,
|
||||
new FieldList($editorSide, $editorBott),
|
||||
new FieldList()
|
||||
);
|
||||
$controller->setRequest($request);
|
||||
$controller->setRequest($request);
|
||||
$form->setController($controller);
|
||||
|
||||
$editorSide->saveInto($page);
|
||||
$editorBott->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
// Save again (after removing the SideBar's widget)
|
||||
$_REQUEST = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
),
|
||||
'BottomBar' => array(
|
||||
$bottWidgets[0]->ID => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$page = new FakePage();
|
||||
|
||||
$editorSide->saveInto($page);
|
||||
$editorBott->saveInto($page);
|
||||
$form->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($bottWidgets[0]->Title(), 'MyTestWidgetBottom');
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 0);
|
||||
|
||||
$_REQUEST = $oldRequest;
|
||||
}
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 0);
|
||||
}
|
||||
|
||||
function testDeletingAWidgetFromEachArea() {
|
||||
$oldRequest = $_REQUEST;
|
||||
|
||||
// First get some widgets in there
|
||||
$_REQUEST = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetSide',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$page = new WidgetAreaEditorTest_FakePage();
|
||||
public function testFillingTwoAreas()
|
||||
{
|
||||
$data = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetSide',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$request = new HTTPRequest('get', 'post', array(), $data);
|
||||
|
||||
$editorSide->saveInto($page);
|
||||
$editorBott->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
// Save again (after removing the SideBar's widget)
|
||||
$_REQUEST = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
),
|
||||
'BottomBar' => array(
|
||||
)
|
||||
)
|
||||
);
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$form = new Form(
|
||||
$controller = new ContentController(),
|
||||
Form::class,
|
||||
new FieldList($editorSide, $editorBott),
|
||||
new FieldList()
|
||||
);
|
||||
$controller->setRequest($request);
|
||||
$form->setController($controller);
|
||||
$page = new FakePage();
|
||||
|
||||
$editorSide->saveInto($page);
|
||||
$editorBott->saveInto($page);
|
||||
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 0);
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 0);
|
||||
|
||||
$_REQUEST = $oldRequest;
|
||||
}
|
||||
|
||||
function testEditingOneWidget() {
|
||||
$oldRequest = $_REQUEST;
|
||||
|
||||
// First get some widgets in there
|
||||
$_REQUEST = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetSide',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$page = new WidgetAreaEditorTest_FakePage();
|
||||
$form->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
|
||||
$editorSide->saveInto($page);
|
||||
$editorBott->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
// Save again (after removing the SideBar's widget)
|
||||
$_REQUEST = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
$sideWidgets[0]->ID => array(
|
||||
'Title' => 'MyTestWidgetSide-edited',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
$bottWidgets[0]->ID => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Make sure they both got saved
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 1);
|
||||
|
||||
$editorSide->saveInto($page);
|
||||
$editorBott->saveInto($page);
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
$this->assertEquals($sideWidgets[0]->getTitle(), 'MyTestWidgetSide');
|
||||
$this->assertEquals($bottWidgets[0]->getTitle(), 'MyTestWidgetBottom');
|
||||
}
|
||||
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($bottWidgets[0]->Title(), 'MyTestWidgetBottom');
|
||||
$this->assertEquals($sideWidgets[0]->Title(), 'MyTestWidgetSide-edited');
|
||||
|
||||
|
||||
$_REQUEST = $oldRequest;
|
||||
}
|
||||
public function testDeletingOneWidgetFromOneArea()
|
||||
{
|
||||
// First get some widgets in there
|
||||
$data = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetSide',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$request = new HTTPRequest('get', 'post', array(), $data);
|
||||
|
||||
function testEditingAWidgetFromEachArea() {
|
||||
$oldRequest = $_REQUEST;
|
||||
|
||||
// First get some widgets in there
|
||||
$_REQUEST = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetSide',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$page = new WidgetAreaEditorTest_FakePage();
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$form = new Form(
|
||||
$controller = new ContentController(),
|
||||
Form::class,
|
||||
new FieldList($editorSide, $editorBott),
|
||||
new FieldList()
|
||||
);
|
||||
$controller->setRequest($request);
|
||||
$form->setController($controller);
|
||||
$page = new FakePage();
|
||||
|
||||
$editorSide->saveInto($page);
|
||||
$editorBott->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
// Save again (after removing the SideBar's widget)
|
||||
$_REQUEST = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
$sideWidgets[0]->ID => array(
|
||||
'Title' => 'MyTestWidgetSide-edited',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
$bottWidgets[0]->ID => array(
|
||||
'Title' => 'MyTestWidgetBottom-edited',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$form->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
$editorSide->saveInto($page);
|
||||
$editorBott->saveInto($page);
|
||||
// Save again (after removing the SideBar's widget)
|
||||
$data = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
),
|
||||
'BottomBar' => array(
|
||||
$bottWidgets[0]->ID => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$request = new HTTPRequest('get', 'post', array(), $data);
|
||||
$controller->setRequest($request);
|
||||
$form->setController($controller);
|
||||
$form->saveInto($page);
|
||||
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($bottWidgets[0]->Title(), 'MyTestWidgetBottom-edited');
|
||||
$this->assertEquals($sideWidgets[0]->Title(), 'MyTestWidgetSide-edited');
|
||||
|
||||
|
||||
$_REQUEST = $oldRequest;
|
||||
}
|
||||
|
||||
function testEditAWidgetFromOneAreaAndDeleteAWidgetFromAnotherArea() {
|
||||
$oldRequest = $_REQUEST;
|
||||
|
||||
// First get some widgets in there
|
||||
$_REQUEST = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetSide',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$page = new WidgetAreaEditorTest_FakePage();
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
$editorSide->saveInto($page);
|
||||
$editorBott->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
// Save again (after removing the SideBar's widget)
|
||||
$_REQUEST = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
$sideWidgets[0]->ID => array(
|
||||
'Title' => 'MyTestWidgetSide-edited',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($bottWidgets[0]->getTitle(), 'MyTestWidgetBottom');
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 0);
|
||||
}
|
||||
|
||||
$editorSide->saveInto($page);
|
||||
$editorBott->saveInto($page);
|
||||
public function testDeletingAWidgetFromEachArea()
|
||||
{
|
||||
// First get some widgets in there
|
||||
$data = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetSide',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$request = new HTTPRequest('get', 'post', array(), $data);
|
||||
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 0);
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($sideWidgets[0]->Title(), 'MyTestWidgetSide-edited');
|
||||
|
||||
|
||||
$_REQUEST = $oldRequest;
|
||||
}
|
||||
}
|
||||
|
||||
class WidgetAreaEditorTest_FakePage extends Page implements TestOnly {
|
||||
private static $has_one = array(
|
||||
"SideBar" => "WidgetArea",
|
||||
"BottomBar" => "WidgetArea",
|
||||
);
|
||||
}
|
||||
|
||||
class WidgetAreaEditorTest_TestWidget extends Widget implements TestOnly {
|
||||
private static $cmsTitle = "Test widget";
|
||||
private static $title = "Test widget";
|
||||
private static $description = "Test widget";
|
||||
private static $db = array(
|
||||
'Title' => 'Varchar'
|
||||
);
|
||||
public function getCMSFields() {
|
||||
$fields = new FieldList();
|
||||
$fields->push(new TextField('Title'));
|
||||
return $fields;
|
||||
}
|
||||
function Title() {
|
||||
return $this->Title ? $this->Title : self::$title;
|
||||
}
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$form = new Form(
|
||||
$controller = new ContentController(),
|
||||
Form::class,
|
||||
new FieldList($editorSide, $editorBott),
|
||||
new FieldList()
|
||||
);
|
||||
$controller->setRequest($request);
|
||||
$form->setController($controller);
|
||||
$page = new FakePage();
|
||||
|
||||
$form->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
// Save again (after removing the SideBar's widget)
|
||||
$data = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
),
|
||||
'BottomBar' => array(
|
||||
)
|
||||
)
|
||||
);
|
||||
$request = new HTTPRequest('get', 'post', array(), $data);
|
||||
$controller->setRequest($request);
|
||||
$form->setController($controller);
|
||||
$form->saveInto($page);
|
||||
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 0);
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 0);
|
||||
}
|
||||
|
||||
public function testEditingOneWidget()
|
||||
{
|
||||
// First get some widgets in there
|
||||
$data = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetSide',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$request = new HTTPRequest('get', 'post', array(), $data);
|
||||
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$form = new Form(
|
||||
$controller = new ContentController(),
|
||||
Form::class,
|
||||
new FieldList($editorSide, $editorBott),
|
||||
new FieldList()
|
||||
);
|
||||
$controller->setRequest($request);
|
||||
$form->setController($controller);
|
||||
$page = new FakePage();
|
||||
|
||||
$form->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
// Save again (after removing the SideBar's widget)
|
||||
$data = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
$sideWidgets[0]->ID => array(
|
||||
'Title' => 'MyTestWidgetSide-edited',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
$bottWidgets[0]->ID => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$request = new HTTPRequest('get', 'post', array(), $data);
|
||||
$controller->setRequest($request);
|
||||
$form->setController($controller);
|
||||
$form->saveInto($page);
|
||||
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($bottWidgets[0]->getTitle(), 'MyTestWidgetBottom');
|
||||
$this->assertEquals($sideWidgets[0]->getTitle(), 'MyTestWidgetSide-edited');
|
||||
}
|
||||
|
||||
public function testEditingAWidgetFromEachArea()
|
||||
{
|
||||
// First get some widgets in there
|
||||
$data = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetSide',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$request = new HTTPRequest('get', 'post', array(), $data);
|
||||
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$form = new Form(
|
||||
$controller = new ContentController(),
|
||||
Form::class,
|
||||
new FieldList($editorSide, $editorBott),
|
||||
new FieldList()
|
||||
);
|
||||
$controller->setRequest($request);
|
||||
$form->setController($controller);
|
||||
$page = new FakePage();
|
||||
|
||||
$form->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
// Save again (after removing the SideBar's widget)
|
||||
$data = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
$sideWidgets[0]->ID => array(
|
||||
'Title' => 'MyTestWidgetSide-edited',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
$bottWidgets[0]->ID => array(
|
||||
'Title' => 'MyTestWidgetBottom-edited',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$request = new HTTPRequest('get', 'post', array(), $data);
|
||||
$controller->setRequest($request);
|
||||
$form->setController($controller);
|
||||
$form->saveInto($page);
|
||||
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($bottWidgets[0]->getTitle(), 'MyTestWidgetBottom-edited');
|
||||
$this->assertEquals($sideWidgets[0]->getTitle(), 'MyTestWidgetSide-edited');
|
||||
}
|
||||
|
||||
public function testEditAWidgetFromOneAreaAndDeleteAWidgetFromAnotherArea()
|
||||
{
|
||||
// First get some widgets in there
|
||||
$data = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetSide',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
'new-1' => array(
|
||||
'Title' => 'MyTestWidgetBottom',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$request = new HTTPRequest('get', 'post', array(), $data);
|
||||
|
||||
$editorSide = new WidgetAreaEditor('SideBar');
|
||||
$editorBott = new WidgetAreaEditor('BottomBar');
|
||||
$form = new Form(
|
||||
$controller = new ContentController(),
|
||||
Form::class,
|
||||
new FieldList($editorSide, $editorBott),
|
||||
new FieldList()
|
||||
);
|
||||
$controller->setRequest($request);
|
||||
$form->setController($controller);
|
||||
$page = new FakePage();
|
||||
|
||||
$editorSide->saveInto($page);
|
||||
$editorBott->saveInto($page);
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
// Save again (after removing the SideBar's widget)
|
||||
$data = array(
|
||||
'Widget' => array(
|
||||
'SideBar' => array(
|
||||
$sideWidgets[0]->ID => array(
|
||||
'Title' => 'MyTestWidgetSide-edited',
|
||||
'Type' => $this->widgetToTest,
|
||||
'Sort' => 0
|
||||
)
|
||||
),
|
||||
'BottomBar' => array(
|
||||
)
|
||||
)
|
||||
);
|
||||
$request = new HTTPRequest('get', 'post', array(), $data);
|
||||
$controller->setRequest($request);
|
||||
$form->setController($controller);
|
||||
$form->saveInto($page);
|
||||
|
||||
$page->write();
|
||||
$page->flushCache();
|
||||
$page->BottomBar()->flushCache();
|
||||
$page->SideBar()->flushCache();
|
||||
$sideWidgets = $page->SideBar()->Widgets()->toArray();
|
||||
$bottWidgets = $page->BottomBar()->Widgets()->toArray();
|
||||
|
||||
$this->assertEquals($page->BottomBar()->Widgets()->Count(), 0);
|
||||
$this->assertEquals($page->SideBar()->Widgets()->Count(), 1);
|
||||
$this->assertEquals($sideWidgets[0]->getTitle(), 'MyTestWidgetSide-edited');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\Widgets\Tests\WidgetAreaEditorTest;
|
||||
|
||||
use Page;
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\Widgets\Model\WidgetArea;
|
||||
|
||||
class FakePage extends Page implements TestOnly
|
||||
{
|
||||
private static $table_name = 'FakePage';
|
||||
|
||||
private static $has_one = array(
|
||||
"BottomBar" => WidgetArea::class
|
||||
);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\Widgets\Tests\WidgetAreaEditorTest;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
class TestWidget extends Widget implements TestOnly
|
||||
{
|
||||
private static $table_name = 'WidgetAreaEditorTest_TestWidget';
|
||||
private static $cmsTitle = "Test widget";
|
||||
private static $description = "Test widget";
|
||||
}
|
|
@ -1,94 +1,61 @@
|
|||
<?php
|
||||
/**
|
||||
* @package widgets
|
||||
* @subpackage tests
|
||||
*/
|
||||
class WidgetControllerTest extends FunctionalTest {
|
||||
|
||||
protected static $fixture_file = 'WidgetControllerTest.yml';
|
||||
namespace SilverStripe\Widgets\Tests;
|
||||
|
||||
protected $extraDataObjects = array(
|
||||
'WidgetControllerTestPage',
|
||||
'WidgetControllerTest_Widget',
|
||||
);
|
||||
|
||||
function testWidgetFormRendering() {
|
||||
$page = $this->objFromFixture('WidgetControllerTestPage', 'page1');
|
||||
$page->publish('Stage', 'Live');
|
||||
|
||||
$widget = $this->objFromFixture('WidgetControllerTest_Widget', 'widget1');
|
||||
|
||||
$response = $this->get($page->URLSegment);
|
||||
|
||||
$formAction = sprintf('%s/widget/%d/Form', $page->URLSegment, $widget->ID);
|
||||
$this->assertContains(
|
||||
$formAction,
|
||||
$response->getBody(),
|
||||
"Widget forms are rendered through WidgetArea templates"
|
||||
);
|
||||
}
|
||||
|
||||
function testWidgetFormSubmission() {
|
||||
$page = $this->objFromFixture('WidgetControllerTestPage', 'page1');
|
||||
$page->publish('Stage', 'Live');
|
||||
|
||||
$widget = $this->objFromFixture('WidgetControllerTest_Widget', 'widget1');
|
||||
|
||||
$response = $this->get($page->URLSegment);
|
||||
$response = $this->submitForm('Form_Form', null, array('TestValue'=>'Updated'));
|
||||
use SilverStripe\Dev\FunctionalTest;
|
||||
use SilverStripe\Widgets\Tests\WidgetControllerTest\TestPage;
|
||||
use SilverStripe\Widgets\Tests\WidgetControllerTest\TestWidget;
|
||||
|
||||
$this->assertContains(
|
||||
'TestValue: Updated',
|
||||
$response->getBody(),
|
||||
"Form values are submitted to correct widget form"
|
||||
);
|
||||
$this->assertContains(
|
||||
sprintf('Widget ID: %d', $widget->ID),
|
||||
$response->getBody(),
|
||||
"Widget form acts on correct widget, as identified in the URL"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package widgets
|
||||
* @subpackage tests
|
||||
*/
|
||||
class WidgetControllerTest_Widget extends Widget implements TestOnly {
|
||||
private static $db = array(
|
||||
'TestValue' => 'Text'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @package widgets
|
||||
* @subpackage tests
|
||||
*/
|
||||
class WidgetControllerTest_WidgetController extends WidgetController implements TestOnly {
|
||||
|
||||
private static $allowed_actions = array(
|
||||
'Form'
|
||||
);
|
||||
|
||||
function Form() {
|
||||
$widgetform = new Form(
|
||||
$this,
|
||||
'Form',
|
||||
new FieldList(
|
||||
new TextField('TestValue')
|
||||
),
|
||||
new FieldList(
|
||||
new FormAction('doAction')
|
||||
)
|
||||
);
|
||||
|
||||
return $widgetform;
|
||||
}
|
||||
|
||||
function doAction($data, $form) {
|
||||
return sprintf('TestValue: %s\nWidget ID: %d',
|
||||
$data['TestValue'],
|
||||
$this->widget->ID
|
||||
);
|
||||
}
|
||||
class WidgetControllerTest extends FunctionalTest
|
||||
{
|
||||
protected static $fixture_file = 'WidgetControllerTest.yml';
|
||||
|
||||
protected static $extra_dataobjects = [
|
||||
TestPage::class,
|
||||
TestWidget::class,
|
||||
];
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->actWithPermission('ADMIN', function () {
|
||||
$this->objFromFixture(TestPage::class, 'page1')->publishRecursive();
|
||||
});
|
||||
}
|
||||
|
||||
public function testWidgetFormRendering()
|
||||
{
|
||||
$page = $this->objFromFixture(TestPage::class, 'page1');
|
||||
$widget = $this->objFromFixture(TestWidget::class, 'widget1');
|
||||
|
||||
$response = $this->get($page->URLSegment);
|
||||
|
||||
$formAction = sprintf('%s/widget/%d/%s', $page->URLSegment, $widget->ID, 'Form');
|
||||
$this->assertStringContainsString(
|
||||
$formAction,
|
||||
$response->getBody(),
|
||||
"Widget forms are rendered through WidgetArea templates"
|
||||
);
|
||||
}
|
||||
|
||||
public function testWidgetFormSubmission()
|
||||
{
|
||||
$page = $this->objFromFixture(TestPage::class, 'page1');
|
||||
$widget = $this->objFromFixture(TestWidget::class, 'widget1');
|
||||
|
||||
$this->get($page->URLSegment);
|
||||
$response = $this->submitForm('Form_Form', null, array('TestValue' => 'Updated'));
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'TestValue: Updated',
|
||||
$response->getBody(),
|
||||
"Form values are submitted to correct widget form"
|
||||
);
|
||||
$this->assertStringContainsString(
|
||||
sprintf('Widget ID: %d', $widget->ID),
|
||||
$response->getBody(),
|
||||
"Widget form acts on correct widget, as identified in the URL"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
WidgetControllerTest_Widget:
|
||||
widget1:
|
||||
Title: Widget 1
|
||||
WidgetArea:
|
||||
area1:
|
||||
Widgets: =>WidgetControllerTest_Widget.widget1
|
||||
WidgetControllerTestPage:
|
||||
page1:
|
||||
Title: Page1
|
||||
WidgetControllerTestSidebar: =>WidgetArea.area1
|
||||
SilverStripe\Widgets\Tests\WidgetControllerTest\TestWidget:
|
||||
widget1:
|
||||
Title: Widget 1
|
||||
SilverStripe\Widgets\Model\WidgetArea:
|
||||
area1:
|
||||
Widgets: =>SilverStripe\Widgets\Tests\WidgetControllerTest\TestWidget.widget1
|
||||
SilverStripe\Widgets\Tests\WidgetControllerTest\TestPage:
|
||||
page1:
|
||||
Title: Page1
|
||||
WidgetControllerTestSidebar: =>SilverStripe\Widgets\Model\WidgetArea.area1
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\Widgets\Tests\WidgetControllerTest;
|
||||
|
||||
use Page;
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\Widgets\Model\WidgetArea;
|
||||
|
||||
class TestPage extends Page implements TestOnly
|
||||
{
|
||||
private static $table_name = 'TestPage';
|
||||
|
||||
private static $has_one = [
|
||||
'WidgetControllerTestSidebar' => WidgetArea::class,
|
||||
];
|
||||
|
||||
private static $owns = [
|
||||
'WidgetControllerTestSidebar',
|
||||
];
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\Widgets\Tests\WidgetControllerTest;
|
||||
|
||||
use PageController;
|
||||
use ReflectionClass;
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\View\SSViewer;
|
||||
use SilverStripe\Widgets\Tests\WidgetControllerTest\TestPage;
|
||||
|
||||
/**
|
||||
* @package cms
|
||||
* @subpackage tests
|
||||
*/
|
||||
class TestPageController extends PageController implements TestOnly
|
||||
{
|
||||
/**
|
||||
* Template selection doesnt work in test folders, so we add a test theme a template name.
|
||||
*/
|
||||
public function getViewer($action)
|
||||
{
|
||||
SSViewer::add_themes(['silverstripe/widgets:/tests/WidgetControllerTest']);
|
||||
return new SSViewer(TestPage::class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\Widgets\Tests\WidgetControllerTest;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\Widgets\Model\Widget;
|
||||
|
||||
class TestWidget extends Widget implements TestOnly
|
||||
{
|
||||
private static $table_name = 'WidgetControllerTest_TestWidget';
|
||||
|
||||
private static $db = [
|
||||
'TestValue' => 'Text',
|
||||
];
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\Widgets\Tests\WidgetControllerTest;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\FormAction;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\Widgets\Model\WidgetController;
|
||||
|
||||
/**
|
||||
* @package widgets
|
||||
* @subpackage tests
|
||||
*/
|
||||
class TestWidgetController extends WidgetController implements TestOnly
|
||||
{
|
||||
private static $allowed_actions = array(
|
||||
'Form'
|
||||
);
|
||||
|
||||
public function Form()
|
||||
{
|
||||
$widgetform = new Form(
|
||||
$this,
|
||||
__FUNCTION__,
|
||||
new FieldList(
|
||||
new TextField('TestValue')
|
||||
),
|
||||
new FieldList(
|
||||
new FormAction('doAction')
|
||||
)
|
||||
);
|
||||
|
||||
return $widgetform;
|
||||
}
|
||||
|
||||
public function doAction($data, $form)
|
||||
{
|
||||
return sprintf(
|
||||
'TestValue: %s\nWidget ID: %d',
|
||||
$data['TestValue'],
|
||||
$this->widget->ID
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
$WidgetControllerTestSidebar
|
|
@ -0,0 +1 @@
|
|||
$Form
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @package cms
|
||||
* @subpackage tests
|
||||
*/
|
||||
class WidgetControllerTestPage extends Page implements TestOnly {
|
||||
private static $has_one = array(
|
||||
'WidgetControllerTestSidebar' => 'WidgetArea'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @package cms
|
||||
* @subpackage tests
|
||||
*/
|
||||
class WidgetControllerTestPage_Controller extends Page_Controller implements TestOnly {
|
||||
|
||||
/**
|
||||
* Template selection doesnt work in test folders,
|
||||
* so we enforce a template name.
|
||||
*/
|
||||
function getViewer($action) {
|
||||
$templates = array('WidgetControllerTestPage');
|
||||
|
||||
return new SSViewer($templates);
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
$WidgetControllerTestSidebar
|
|
@ -1 +0,0 @@
|
|||
$Form
|
Loading…
Reference in New Issue