Compare commits

...

88 Commits
1.3.2 ... 2

Author SHA1 Message Date
Guy Sartorelli eacb02c297
Merge branch '2.4' into 2 2023-08-30 10:23:04 +12:00
Guy Sartorelli afe1cc5da0
ENH Update translations (#201) 2023-08-21 13:19:54 +12:00
Guy Sartorelli f2b92e8f83
Merge branch '2.4' into 2 2023-04-26 12:46:00 +12:00
Guy Sartorelli 2b53f5095c
MNT Revert erroneous dependency changes (#198) 2023-03-28 17:02:24 +13:00
Maxime Rainville d27f656e97
Merge pull request #197 from creative-commoners/pulls/2/dispatch-ci
MNT Use gha-dispatch-ci
2023-03-23 14:19:31 +13:00
Steve Boyd b615184c54 MNT Use gha-dispatch-ci 2023-03-21 13:42:53 +13:00
Guy Sartorelli a9a20ab1c0
MNT Update development dependencies 2023-03-10 16:32:47 +13:00
Guy Sartorelli 8ad0537473
MNT Update release dependencies 2023-03-10 16:32:44 +13:00
Guy Sartorelli 02d5a17e1f
MNT Update development dependencies 2023-03-10 12:21:29 +13:00
Guy Sartorelli 53e7646700
Merge pull request #196 from creative-commoners/pulls/2/tx-1678080282
ENH Update translations
2023-03-08 10:32:31 +13:00
Steve Boyd 812f3dc282 ENH Update translations 2023-03-06 18:24:42 +13:00
Steve Boyd 9934adecdb Merge branch '2.3' into 2 2022-08-02 19:02:13 +12:00
Steve Boyd 08cdad1290 Merge branch '2.2' into 2.3 2022-08-02 19:02:09 +12:00
Guy Sartorelli 08554e7882
Merge pull request #192 from creative-commoners/pulls/2.2/standardise-modules
MNT Standardise modules
2022-08-02 15:50:59 +12:00
Steve Boyd 57b1d87577 MNT Standardise modules 2022-08-01 16:23:55 +12:00
Steve Boyd 3fd8120b74 Merge branch '2.3' into 2 2022-07-25 11:50:19 +12:00
Steve Boyd d907c77ed6 Merge branch '2.2' into 2.3 2022-07-25 11:50:15 +12:00
Guy Sartorelli 65ef1bbc50
Merge pull request #191 from creative-commoners/pulls/2.2/module-standards
MNT Use GitHub Actions CI
2022-07-15 11:46:04 +12:00
Steve Boyd d16e4770da MNT Use GitHub Actions CI 2022-07-05 19:10:19 +12:00
Steve Boyd 3685869917 Merge branch '2.3' into 2 2022-05-10 22:11:42 +12:00
Steve Boyd 3d89a29628 Update translations 2022-05-04 13:29:41 +12:00
Guy Sartorelli d78326987c
Merge pull request #189 from creative-commoners/pulls/2/php81
ENH PHP 8.1 compatibility
2022-04-26 17:58:46 +12:00
Steve Boyd 51d29b3d5a ENH PHP 8.1 compatibility 2022-04-13 13:54:56 +12:00
Maxime Rainville 5aca443a7c
Merge pull request #187 from creative-commoners/pulls/2/sapphire-test-nine
API phpunit 9 support
2021-11-10 13:19:21 +13:00
Steve Boyd 2b9536e5ae
DEP Use phpunit ^9.5 2021-11-10 10:47:55 +13:00
Steve Boyd a08aa08f4d API phpunit 9 support 2021-10-21 13:14:57 +13:00
Maxime Rainville 6c2b654f19 Merge branch '2.1' into 2 2021-06-02 15:33:46 +12:00
Maxime Rainville 6e163beb44
Merge pull request #186 from creative-commoners/pulls/2.1/revert-translations
FIX Add namespaces back to translation files
2021-06-01 14:25:13 +12:00
Steve Boyd d4ae7ff9ab FIX Add namespaces back to translation files 2021-05-31 10:57:28 +12:00
Steve Boyd 92f90fbea5 Merge branch '2.1' into 2 2021-05-21 14:32:28 +12:00
Maxime Rainville f759a8b56b Update translations 2021-05-17 15:09:54 +12:00
Maxime Rainville 81b59ab4b9 MNT Remove obsolete branch-alias 2021-05-05 11:17:32 +12:00
Steve Boyd 0f37c09531
Update build status badge 2021-01-21 16:43:31 +13:00
Steve Boyd c3fc2cab68 Merge branch '2.0' into 2 2020-11-11 17:47:14 +13:00
Serge Latyntsev 0fda5eec16
Merge pull request #185 from creative-commoners/pulls/2.0/shared-config
MNT Use shared travis config, use sminnee/phpunit
2020-11-10 16:37:35 +13:00
Steve Boyd 1265ce99a6 MNT Use shared travis config, use sminnee/phpunit 2020-11-10 14:08:17 +13:00
Garion Herman 8db05ea5f0 Merge branch '2.0' into 2 2019-11-18 14:39:24 +13:00
Serge Latyntsev 007b6fcdfa
Merge pull request #181 from creative-commoners/pulls/2.0/update-travis-config
Update and expand Travis config
2019-11-18 14:35:17 +13:00
Garion Herman 42af10671e FIX Fetch source so that PHPCS config is available 2019-11-18 14:20:09 +13:00
Garion Herman f396ee7242 Update and expand Travis config 2019-11-18 14:00:27 +13:00
Robbie Averill ade8047461
Merge pull request #177 from wernerkrauss/patch-1
Fix broken template paths in $Content
2018-10-16 15:29:38 +02:00
wernerkrauss 5d2f0570d8
Fix broken template paths in $Content
Use framework logic to get the templates of the current Widget Subclass
2018-10-16 15:12:34 +02:00
Robbie Averill 7d2dd46159
Merge pull request #170 from creative-commoners/pulls/master/add-supported-module-badge
Add supported module badge to readme
2018-06-19 09:24:08 +12:00
Dylan Wagstaff 41c9ecffe6 Add supported module badge to readme 2018-06-15 17:52:55 +12:00
Robbie Averill 35df96f502 Merge branch '2.0' 2018-06-11 12:09:57 +12:00
Robbie Averill 6ce962607f Remove obsolete branch alias 2018-06-11 12:09:26 +12:00
Robbie Averill eb81b3c8d9
Merge pull request #167 from silverstripe/raissanorth-patch-1
Add missing section to README.md
2018-03-26 10:54:06 +13:00
Raissa North 129a650729
Add missing section to README.md
Comparing the README to the Getting Started section of the user docs, it appears that there is a section mission. This has been added back in.
2018-03-26 10:50:16 +13:00
Dylan Wagstaff d17c577609
Merge pull request #166 from spaceship-operators/widget-area-ss4-styling
Update widgetarea styling to be inline with SS4
2018-03-15 15:08:25 +13:00
Scott Hutchinson 294f9b60b5 Update widgetarea styling to be inline with SS4 2018-03-15 14:44:10 +13:00
Robbie Averill 76b8b3f891
Merge pull request #164 from creative-commoners/pulls/2.0/return-null-for-empty-null
API Remove overriding function getTitle()
2018-02-02 11:53:16 +13:00
Raissa North ab8349209a API Remove overriding function getTitle()
In the case that no title is entered in the CMS's title field no title is returned, rather than the CMS widget's title.
2018-02-02 11:13:52 +13:00
Dylan Wagstaff 0a3c280c3d
Merge pull request #163 from creative-commoners/pulls/2.0/ownership-api
API Widgets and WidgetAreas are versioned and are owned by pages
2017-12-22 11:13:55 +13:00
Robbie Averill 8583b894a1 FIX Update tests to ensure TestPage owns its widget area 2017-12-21 11:16:48 +13:00
Robbie Averill 5ca288c9a3 FIX Use full versioning for Widget and WidgetArea to preserve draft state between page publishes 2017-12-21 10:45:55 +13:00
Robbie Averill e1a0469c46 API Widgets and WidgetAreas are versioned and are owned by pages 2017-12-19 16:01:52 +13:00
Robbie Averill a0b1f9b1cf
Merge pull request #162 from creative-commoners/pulls/2.0/update-to-ss4
Check module for SS4 compatibility
2017-12-19 16:01:49 +13:00
Robbie Averill a2f4594597 FIX Update legacy translation tags in templates, run text collector, update other langs 2017-12-19 14:33:16 +13:00
Raissa North de7473e9ea FIX Update translations 2017-12-19 10:28:59 +13:00
Raissa North 58e26969a3 FIX Update documentation and turn into vendor module 2017-12-19 10:25:38 +13:00
Raissa North 285b46fafb FIX Add and optimise namespaces 2017-12-19 10:25:38 +13:00
Raissa North 0916970951 ENHANCEMENT Add codesniffer ruleset 2017-12-19 10:25:38 +13:00
Robbie Averill 5287ebcbe5 Merge branch '1' 2017-12-19 10:21:48 +13:00
Robbie Averill c5511726ef Update branch alias for 1.x-dev 2017-12-19 10:19:48 +13:00
Dylan Wagstaff f8f10e96ff
Merge pull request #161 from creative-commoners/pulls/2.0/fix-namespace-handling
FIX Ensure namespaced widget classes are handled and Widget is not included
2017-12-14 14:48:54 +13:00
Robbie Averill bf9fbe3aa7 FIX Ensure namespaced widget classes are handled and Widget is not included 2017-12-13 23:03:12 +13:00
Robbie Averill 406f925beb
FIX Scrutinizer configuration uses new build system 2017-11-21 13:55:58 +13:00
Robbie Averill 712fb56897
Merge pull request #158 from adrian-stein/master
Add in missing requirements
2017-11-21 13:55:05 +13:00
Adrian Stein e9e30b6489 Add in missing requirements 2017-11-21 11:28:55 +11:00
Damian Mooyman 023eef23c6 Merge pull request #156 from creative-commoners/pulls/2.0/vendorise
NEW Install module to the vendor folder
2017-10-11 11:51:36 +13:00
Robbie Averill fb91acbe3a NEW Install module to the vendor folder 2017-10-11 09:54:52 +13:00
Robbie Averill 3f02a0b295 Merge pull request #155 from creative-commoners/pulls/vendorise-ci
FIX convert CI bootstrap references to new their new locations in vendor
2017-10-05 16:02:05 +13:00
Dylan Wagstaff 18296d00e4 Update template path to allow PageController test to pass 2017-10-05 15:26:52 +13:00
Dylan Wagstaff 36a5840d45 FIX convert CI bootstrap references to new their new locations in vendor 2017-10-05 10:09:49 +13:00
Franco Springveldt 26f1c2bd74 Merge pull request #153 from robbieaverill/pulls/2.0/update-travis
FIX Update Travis configuration to be standalone, add codecov.io
2017-09-14 14:27:49 +12:00
Robbie Averill 9b12b602f5 FIX Use ModuleLoader for requirement resolution and typo in exception in Widget::getController 2017-09-14 14:16:45 +12:00
Robbie Averill 7c85b1b5d9 FIX Update tests for removed core features, use Controller for requests instead of Forms, etc 2017-09-14 11:15:01 +12:00
Robbie Averill ad6a965526 FIX Update Travis configuration to be standalone, add codecov.io 2017-08-25 16:29:09 +12:00
Daniel Hensby 5437f6b2a5 Merge pull request #149 from creative-commoners/pulls/2.0/remove-php5.5
Remove PHP 5.5 from SS4 compat build matrix
2017-06-15 16:58:28 +01:00
Robbie Averill 9aa40a0611 Remove PHP 5.5 from SS4 compat build matrix 2017-06-15 11:47:47 +12:00
Damian Mooyman 1fe4c3db70 Merge pull request #143 from robbieaverill/feature/ss4-compat
Enhancement: SilverStripe 4 compatibility
2017-01-26 17:42:32 +13:00
Robbie Averill 8dae0a6f95 Tabs to spaces in Controller 2017-01-26 08:25:53 +13:00
Robbie Averill ec9421ba65 FIX Add legacy class remapping, escape FQ class names in URLs, docs and add bootstrap to buttons 2017-01-26 08:25:10 +13:00
Robbie Averill e7de0c61bf DOCS Add all build badges to readme 2017-01-26 08:22:02 +13:00
Robbie Averill 8eee397b9a API Implement namespacing, add upgrade mapping. Remove deprecated methods and support for Widget_Controller (underscored) 2017-01-26 08:22:02 +13:00
Robbie Averill defb4bf134 SS4 compat: Update readme, docs, composer configuration and Travis build configuration 2017-01-26 08:12:04 +13:00
Daniel Hensby f3b14df4e4
Merge branch '1.3' 2017-01-25 13:33:02 +00:00
Daniel Hensby 661e7ee48a
Alias master as 2.0.x 2017-01-25 13:18:05 +00:00
72 changed files with 1475 additions and 1061 deletions

1
.gitattributes vendored
View File

@ -4,3 +4,4 @@
/.gitignore export-ignore
/.travis.yml export-ignore
/.scrutinizer.yml export-ignore
/codecov.yml export-ignore

11
.github/workflows/ci.yml vendored Normal file
View File

@ -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

16
.github/workflows/dispatch-ci.yml vendored Normal file
View File

@ -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

17
.github/workflows/keepalive.yml vendored Normal file
View File

@ -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

View File

@ -1,9 +0,0 @@
inherit: true
checks:
php:
code_rating: true
duplication: true
filter:
paths: [code/*, tests/*]

View File

@ -1,31 +0,0 @@
# See https://github.com/silverstripe/silverstripe-travis-support for setup details
sudo: false
language: php
php:
- 5.4
- 5.5
env:
- DB=MYSQL CORE_RELEASE=3.5
matrix:
include:
- php: 5.6
env: DB=MYSQL CORE_RELEASE=3
- php: 5.6
env: DB=PGSQL CORE_RELEASE=3.4
- php: 7.1
env: DB=MYSQL CORE_RELEASE=3.6
before_script:
- composer self-update || true
- git clone git://github.com/silverstripe/silverstripe-travis-support.git ~/travis-support
- php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss
- cd ~/builds/ss
- composer install
script:
- vendor/bin/phpunit widgets/tests

9
.tx/config Normal file
View File

@ -0,0 +1,9 @@
[main]
host = https://www.transifex.com
[o:silverstripe:p:silverstripe-widgets:r:master]
file_filter = lang/<lang>.yml
source_file = lang/en.yml
source_lang = en
type = YML

7
.upgrade.yml Normal file
View File

@ -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

400
README.md
View File

@ -1,6 +1,7 @@
# Widgets Module
[![Build Status](https://secure.travis-ci.org/silverstripe/silverstripe-widgets.png?branch=1.1)](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/)
## Overview
@ -9,8 +10,9 @@ the sidebar of your website.
## Requirements
* SilverStripe 3.2
* 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
@ -26,6 +28,31 @@ 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](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
See the [docs/en](docs/en/introduction.md) folder.
@ -34,20 +61,10 @@ See the [docs/en](docs/en/introduction.md) folder.
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.
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](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
## 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.
@ -55,18 +72,28 @@ e.g.
**mysite/code/Page.php**
class Page extends SiteTree {
...
private static $has_one = array(
"MyWidgetArea" => "WidgetArea",
);
```php
<?php
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab("Root.Widgets", new WidgetAreaEditor("MyWidgetArea"));
return $fields;
}
}
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.
@ -87,78 +114,97 @@ The class should extend the Widget class, and must specify three config variable
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"
);
```php
<?php
namespace Yourname\MyWidget;
private static $defaults = array(
"NumberToShow" => 8
);
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;
private static $title = "Photos";
private static $cmsTitle = "Flickr Photos";
private static $description = "Shows flickr photos.";
class FlickrWidget extends Widget
{
private static $db = array(
'User' => 'Varchar',
'Photoset' => 'Varchar',
'Tags' => 'Varchar',
'NumberToShow' => 'Int'
);
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");
private static $defaults = array(
'NumberToShow' => 8
);
$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);
}
private static $title = 'Photos';
private static $cmsTitle = 'Flickr Photos';
private static $description = 'Shows flickr photos.';
$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 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');
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")
);
}
}
$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.
@ -178,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": [
@ -190,6 +236,11 @@ You need to finish off / change:
],
"extra" : {
"installer-name": "widgets_"
},
"autoload": {
"psr-4": {
"Yourname\\MyWidget\\": "src/"
}
}
}
```
@ -201,57 +252,62 @@ You need to finish off / change:
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.
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.
@ -263,69 +319,97 @@ 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'
);
}
```php
<?php
class MyWidget_Controller extends WidgetController {
public function MyFormName() {
return new Form(
$this,
'MyFormName',
new FieldList(
new TextField('TestValue')
),
new FieldList(
new FormAction('doAction')
)
);
}
namespace Yourname\MyWidget;
public function doAction($data, $form) {
// $this->widget points to the widget
}
}
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
@ -339,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](https://docs.silverstripe.org/en/3.2/developer_guides/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.

View File

@ -0,0 +1 @@
<?php

View File

@ -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'

7
_config/legacy.yml Normal file
View File

@ -0,0 +1,7 @@
---
Name: widgetslegacymapping
---
SilverStripe\ORM\DatabaseAdmin:
classname_value_remapping:
Widget: SilverStripe\Widgets\Model\Widget
WidgetArea: SilverStripe\Widgets\Model\WidgetArea

View File

@ -1,3 +1,6 @@
Director:
---
Name: widgetsroutes
---
SilverStripe\Control\Director:
rules:
'WidgetController//$Action/$ID/$OtherID': 'WidgetController'
'WidgetController//$Action/$ID/$OtherID': 'SilverStripe\Widgets\Model\WidgetController'

View File

@ -1,71 +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;
}
/** @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' || is_subclass_of($hasOneClass, 'WidgetArea')) {
$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();
}
}

View File

@ -1,145 +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);
$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($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() {
// use left and main to set the html config
$leftandmain = LeftAndMain::create();
$leftandmain->init();
$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
codecov.yml Normal file
View File

@ -0,0 +1 @@
comment: false

View File

@ -1,24 +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.2",
"silverstripe/cms": "^3.2"
},
"require-dev": {
"phpunit/PHPUnit": "~3.7@stable"
},
"extra": {
"branch-alias": {
}
},
"license": "BSD-3-Clause"
}
"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
}

View File

@ -4,7 +4,7 @@ Contributions are welcome! Create an issue, explaining a bug or proposal. Submit
## 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.
@ -36,6 +36,11 @@ You need to finish off / change:
],
"extra" : {
"installer-name": "widgets_"
},
"autoload": {
"psr-4": {
"Yourname\\MyWidget\\": "src/"
}
}
}
```
```

View File

@ -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; }

View File

@ -12,7 +12,9 @@ You'll also need to run `dev/build`.
Install the module through [composer](http://getcomposer.org):
composer require silverstripe/widgets
```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
@ -20,10 +22,11 @@ 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
@ -31,7 +34,7 @@ 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
@ -56,18 +59,28 @@ e.g.
**mysite/code/Page.php**
class Page extends SiteTree {
...
private static $has_one = array(
"MyWidgetArea" => "WidgetArea",
);
```php
<?php
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->addFieldToTab("Root.Widgets", new WidgetAreaEditor("MyWidgetArea"));
return $fields;
}
}
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.
@ -88,73 +101,93 @@ The class should extend the Widget class, and must specify three config variable
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"
);
```php
<?php
namespace Yourname\MyWidget;
private static $defaults = array(
"NumberToShow" => 8
);
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;
private static $title = "Photos";
private static $cmsTitle = "Flickr Photos";
private static $description = "Shows flickr photos.";
class FlickrWidget extends Widget
{
private static $db = array(
'User' => 'Varchar',
'Photoset' => 'Varchar',
'Tags' => 'Varchar',
'NumberToShow' => 'Int'
);
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");
private static $defaults = array(
'NumberToShow' => 8
);
$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);
}
private static $title = 'Photos';
private static $cmsTitle = 'Flickr Photos';
private static $description = 'Shows flickr photos.';
$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 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');
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")
);
}
}
$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 %>
```
## Limiting Allowed Widgets for a Pagetype
@ -162,8 +195,15 @@ You can lock down a particular `WidgetAreaEditor` to only allow adding certain w
**GreatPage.php**
:::php
$fields->addFieldToTab(
'Root.Widgets',
new WidgetAreaEditor('PhenomenalWidgetArea', ['ParticularlyEpicWidget', 'LessGreatWidget'])
);
```php
$fields->addFieldToTab(
'Root.Widgets',
new WidgetAreaEditor(
'PhenomenalWidgetArea',
[
'Fully\\Qualified\\ParticularlyEpicWidget',
'Yourname\\MyModule\\LessGreatWidget'
]
)
);
```

View File

@ -5,8 +5,8 @@ the sidebar of your website. To check out a what a [Widget](http://silverstripe.
[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/silverstripe-widgets.svg?style=flat-square)](https://packagist.org/packages/silverstripe/silverstripe-widgets)
[![License](http://img.shields.io/packagist/l/silverstripe/silverstripe-widgets.svg?style=flat-square)](LICENSE.md)
[![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
@ -21,4 +21,4 @@ The generated share links have a public key and hash. There can be any number of
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).
You can report bugs or request features on [GitHub](https://github.com/silverstripe/silverstripe-widgets/issues).

View File

@ -1,16 +1,16 @@
ar:
Widget:
PLURALNAME: 'تطبيقات مصغرة'
SINGULARNAME: 'تطبيق مصغر'
WidgetArea:
PLURALNAME: 'مساحات التطبيقات المصغرة'
SINGULARNAME: 'مساحة التطبيقات المصغرة'
WidgetAreaEditor_ss:
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:

View File

@ -1,16 +1,16 @@
de:
Widget:
PLURALNAME: Widgets
SINGULARNAME: Widget
WidgetArea:
PLURALNAME: Widgetbereiche
SINGULARNAME: Widgetbereich
WidgetAreaEditor_ss:
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'
WidgetEditor_ss:

View File

@ -1,19 +1,25 @@
en:
Widget:
PLURALNAME: Widgets
SINGULARNAME: Widget
WidgetArea:
PLURALNAME: 'Widget Areas'
SINGULARNAME: 'Widget Area'
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.'
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.'
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'
WidgetEditor_ss:
DELETE: Delete
WidgetPageExtension:
INHERITSIDEBAR: 'Inherit Sidebar From Parent'

View File

@ -1,16 +1,24 @@
eo:
Widget:
PLURALNAME: Fenestraĵoj
SINGULARNAME: Fenestraĵo
WidgetArea:
PLURALNAME: 'Fenestraĵaj zonoj'
SINGULARNAME: 'Fenestraĵa zono'
WidgetAreaEditor_ss:
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:

View File

@ -1,16 +1,16 @@
fa_IR:
Widget:
PLURALNAME: 'ویجت ها'
SINGULARNAME: ویجت
WidgetArea:
PLURALNAME: 'محیط ویجت ها'
SINGULARNAME: 'محیط ویجت'
WidgetAreaEditor_ss:
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:

View File

@ -1,16 +1,16 @@
fi:
Widget:
PLURALNAME: Vimpaimet
SINGULARNAME: Vimpain
WidgetArea:
PLURALNAME: 'Vimpain alueet'
SINGULARNAME: 'Vimpain alue'
WidgetAreaEditor_ss:
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:

17
lang/fi_FI.yml Normal file
View File

@ -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

View File

@ -1,16 +1,16 @@
hr:
Widget:
PLURALNAME: Widgeti
SINGULARNAME: Widget
WidgetArea:
PLURALNAME: 'Zone widgeta'
SINGULARNAME: 'Zona widgeta'
WidgetAreaEditor_ss:
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:

5
lang/id_ID.yml Normal file
View File

@ -0,0 +1,5 @@
id_ID:
SilverStripe\Widgets\Forms\WidgetAreaEditor_ss:
AVAILABLE: 'Widget Tersedia'
WidgetEditor_ss:
DELETE: Hapus

17
lang/it.yml Normal file
View File

@ -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

View File

@ -1,16 +1,16 @@
it_IT:
Widget:
PLURALNAME: Widgets
SINGULARNAME: Widget
WidgetArea:
PLURALNAME: 'Aree Widget'
SINGULARNAME: 'Area Widget'
WidgetAreaEditor_ss:
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:

View File

@ -1,10 +1,16 @@
nl:
WidgetAreaEditor_ss:
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'
WidgetEditor_ss:

View File

@ -1,5 +1,5 @@
pl_PL:
WidgetAreaEditor_ss:
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'

View File

@ -1,5 +1,5 @@
ro_RO:
WidgetAreaEditor_ss:
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'

View File

@ -1,16 +1,16 @@
ru:
Widget:
PLURALNAME: Виджеты
SINGULARNAME: Виджет
WidgetArea:
PLURALNAME: 'Области виджетов'
SINGULARNAME: 'Область виджета'
WidgetAreaEditor_ss:
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:

View File

@ -1,16 +1,16 @@
sk:
Widget:
PLURALNAME: Widgety
SINGULARNAME: Widget
WidgetArea:
PLURALNAME: 'Oblasti widgetu'
SINGULARNAME: 'Oblasť widgetu'
WidgetAreaEditor_ss:
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:

17
lang/sl.yml Normal file
View File

@ -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

View File

@ -1,16 +1,16 @@
sv:
Widget:
PLURALNAME: Widgets
SINGULARNAME: Widget
WidgetArea:
PLURALNAME: Widgetområden
SINGULARNAME: Widgetområde
WidgetAreaEditor_ss:
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'
WidgetEditor_ss:

View File

@ -1,16 +1,16 @@
zh:
Widget:
PLURALNAME: 小工具
SINGULARNAME: 小工具
WidgetArea:
PLURALNAME: 小工具区域
SINGULARNAME: 小工具区域
WidgetAreaEditor_ss:
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:

17
lang/zh_CN.yml Normal file
View File

@ -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: 删除

View File

@ -1,4 +1,4 @@
Copyright (c) 2016, SilverStripe Limited
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:

12
phpcs.xml.dist Normal file
View File

@ -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>

16
phpunit.xml.dist Normal file
View File

@ -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>

View File

@ -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;
}
}
}

View File

@ -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();
}
}

View File

@ -1,4 +1,13 @@
<?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
@ -10,23 +19,31 @@
*/
class WidgetPageExtension extends DataExtension
{
private static $db = array(
private static $db = [
'InheritSideBar' => 'Boolean',
);
];
private static $defaults = array(
private static $defaults = [
'InheritSideBar' => true
);
];
private static $has_one = array(
'SideBar' => 'WidgetArea'
);
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('WidgetPageExtension.INHERITSIDEBAR', 'Inherit Sidebar From Parent'))
new CheckboxField("InheritSideBar", _t(__CLASS__ . '.INHERITSIDEBAR', 'Inherit Sidebar From Parent'))
);
$fields->addFieldToTab(
"Root.Widgets",
@ -39,8 +56,7 @@ class WidgetPageExtension extends DataExtension
*/
public function SideBarView()
{
if (
$this->owner->InheritSideBar
if ($this->owner->InheritSideBar
&& ($parent = $this->owner->getParent())
&& $parent->hasMethod('SideBarView')
) {
@ -49,7 +65,7 @@ class WidgetPageExtension extends DataExtension
return $this->owner->SideBar();
}
}
public function onBeforeDuplicate($duplicatePage)
{
if ($this->owner->hasField('SideBarID')) {
@ -67,13 +83,4 @@ class WidgetPageExtension extends DataExtension
return $duplicatePage;
}
/**
* Support Translatable so that we don't link WidgetAreas across translations
*/
public function onTranslatableCreate()
{
//reset the sidebar ID
$this->owner->SideBarID = 0;
}
}

View File

@ -1,5 +1,17 @@
<?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.
*
@ -12,11 +24,11 @@ class WidgetAreaEditor extends FormField
* @param array $widgetClasses
* @param int $maxWidgets
*/
public function __construct($name, $widgetClasses = array('Widget'), $maxWidgets = 0)
public function __construct($name, $widgetClasses = array(Widget::class), $maxWidgets = 0)
{
$this->MaxWidgets = $maxWidgets;
$this->widgetClasses = $widgetClasses;
parent::__construct($name);
}
@ -27,10 +39,10 @@ class WidgetAreaEditor extends FormField
*/
public function FieldHolder($properties = array())
{
Requirements::css('widgets/css/WidgetAreaEditor.css');
Requirements::javascript('widgets/javascript/WidgetAreaEditor.js');
Requirements::css('silverstripe/widgets:css/WidgetAreaEditor.css');
Requirements::javascript('silverstripe/widgets:javascript/WidgetAreaEditor.js');
return $this->renderWith("WidgetAreaEditor");
return $this->renderWith(WidgetAreaEditor::class);
}
/**
@ -44,17 +56,17 @@ class WidgetAreaEditor extends FormField
foreach ($this->widgetClasses as $widgetClass) {
$classes = ClassInfo::subclassesFor($widgetClass);
if (isset($classes['Widget'])) {
unset($classes['Widget']);
} elseif (isset($classes[0]) && $classes[0] == 'Widget') {
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)) {
if (in_array($this->Name, $available ?? [])) {
$widgets->push(singleton($class));
}
} else {
@ -62,7 +74,7 @@ class WidgetAreaEditor extends FormField
}
}
}
return $widgets;
}
@ -72,8 +84,8 @@ class WidgetAreaEditor extends FormField
public function UsedWidgets()
{
// Call class_exists() to load Widget.php earlier and avoid a segfault
class_exists('Widget');
class_exists(Widget::class);
$relationName = $this->name;
$widgets = $this->form->getRecord()->getComponent($relationName)->Items();
@ -101,6 +113,7 @@ class WidgetAreaEditor extends FormField
/**
* @param DataObjectInterface $record
* @throws Exception if no form could be retrieved
*/
public function saveInto(DataObjectInterface $record)
{
@ -109,15 +122,15 @@ class WidgetAreaEditor extends FormField
$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;
@ -128,12 +141,11 @@ class WidgetAreaEditor extends FormField
throw new Exception("no form");
}
$widgetData = $this->getForm()->getRequest()->requestVar('Widget');
$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;
@ -152,11 +164,17 @@ class WidgetAreaEditor extends FormField
}
}
// 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_exists($newWidgetData['Type'] ?? '')
&& is_subclass_of($newWidgetData['Type'], Widget::class)
) {
$widget = Injector::inst()->create($newWidgetData['Type']);
$widget->ID = 0;
@ -167,12 +185,11 @@ class WidgetAreaEditor extends FormField
if ($widget->ParentID == 0) {
$widget->ParentID = $record->$name()->ID;
}
$widget->populateFromPostData($newWidgetData);
}
}
}
// remove the fields not saved
if ($missingWidgets) {
foreach ($missingWidgets as $removedWidget) {

View File

@ -1,60 +1,55 @@
<?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 Widget_Controller} for more information.
* See {@link WidgetController} for more information.
*
* @package widgets
*/
class Widget extends DataObject
{
/**
* @var array
*/
private static $db = array(
private static $db = [
"Title" => "Varchar(255)",
"Sort" => "Int",
"Enabled" => "Boolean",
);
];
/**
* @var array
*/
private static $defaults = array(
private static $defaults = [
'Enabled' => true,
);
];
/**
* @var array
*/
private static $casting = array(
private static $casting = [
'CMSTitle' => 'Text',
'Description' => 'Text',
);
];
private static $only_available_in = array();
private static $only_available_in = [];
/**
* @var array
*/
private static $has_one = array(
"Parent" => "WidgetArea",
);
private static $has_one = [
"Parent" => WidgetArea::class,
];
/**
* @var string
*/
private static $default_sort = "\"Sort\"";
/**
* @var string
*/
private static $title = "Widget Title";
/**
* @var string
*/
@ -65,12 +60,15 @@ class Widget extends DataObject
*/
private static $description = "Description of what this widget does.";
/**
* @var array
*/
private static $summary_fields = array(
private static $summary_fields = [
'CMSTitle' => 'Title'
);
];
private static $table_name = 'Widget';
private static $extensions = [
Versioned::class,
];
/**
* @var WidgetController
@ -119,36 +117,7 @@ class Widget extends DataObject
*/
public function Content()
{
return $this->renderWith(array_reverse(ClassInfo::ancestry($this->class)));
}
/**
* @return string
* @deprecated
*/
public function Title()
{
return $this->getTitle();
}
/**
* Get the frontend title for this widget
*
* @return string
*/
public function getTitle()
{
return $this->getField('Title')
?: _t($this->class . '.TITLE', $this->config()->title);
}
/**
* @return string
* @deprecated
*/
public function CMSTitle()
{
return $this->getCMSTitle();
return $this->renderWith(SSViewer::get_templates_by_class(static::class));
}
/**
@ -156,16 +125,7 @@ class Widget extends DataObject
*/
public function getCMSTitle()
{
return _t($this->class . '.CMSTITLE', $this->config()->cmsTitle);
}
/**
* @return string
* @deprecated
*/
public function Description()
{
return $this->getDescription();
return _t(__CLASS__ . '.CMSTITLE', $this->config()->get('cmsTitle'));
}
/**
@ -173,7 +133,7 @@ class Widget extends DataObject
*/
public function getDescription()
{
return _t($this->class . '.DESCRIPTION', $this->config()->description);
return _t(__CLASS__ . '.DESCRIPTION', $this->config()->get('description'));
}
/**
@ -217,8 +177,13 @@ class Widget extends DataObject
$outputFields = new FieldList();
$this->FormID = $this->ID ?: uniqid();
$outputFields->push(HiddenField::create('Widget[' . $this->FormID . '][FormID]', 'FormID',
$this->FormID)->addExtraClass('formid'));
$outputFields->push(
HiddenField::create(
'Widget[' . $this->FormID . '][FormID]',
'FormID',
$this->FormID
)->addExtraClass('formid')
);
foreach ($fields as $field) {
$name = $field->getName();
@ -226,7 +191,7 @@ class Widget extends DataObject
if ($value) {
$field->setValue($value);
}
$namefiltered = preg_replace("/([A-Za-z0-9\-_]+)/", "Widget[" . $this->FormID . "][\\1]", $name);
$namefiltered = preg_replace("/([A-Za-z0-9\-_]+)/", "Widget[" . $this->FormID . "][\\1]", $name ?? '');
$field->setName($namefiltered);
$outputFields->push($field);
@ -236,11 +201,14 @@ class Widget extends DataObject
}
/**
* 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 $this->class;
return str_replace('\\', '_', get_class($this));
}
/**
@ -252,7 +220,7 @@ class Widget extends DataObject
}
/**
* @throws Exception
* @throws Exception If the widget controller's class name couldn't be found
*
* @return WidgetController
*/
@ -262,25 +230,21 @@ class Widget extends DataObject
return $this->controller;
}
foreach (array_reverse(ClassInfo::ancestry($this->class)) as $widgetClass) {
$controllerClass = "{$widgetClass}_Controller";
if (class_exists($controllerClass)) {
break;
}
foreach (array_reverse(ClassInfo::ancestry(get_class($this)) ?? []) as $widgetClass) {
$controllerClass = "{$widgetClass}Controller";
if (class_exists($controllerClass)) {
if (class_exists($controllerClass ?? '')) {
break;
}
}
if (!class_exists($controllerClass)) {
throw new Exception("Could not find controller class for $this->classname");
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;
}
@ -304,7 +268,7 @@ class Widget extends DataObject
//Look for checkbox fields not present in the data
foreach ($fields as $field) {
if ($field instanceof CheckboxField && !array_key_exists($field->getName(), $data)) {
if ($field instanceof CheckboxField && !array_key_exists($field->getName(), $data ?? [])) {
$field->setValue(false);
$field->saveInto($this);
}
@ -313,7 +277,7 @@ class Widget extends DataObject
$this->write();
// The field must be written to ensure a unique ID.
$this->Name = $this->class . $this->ID;
$this->Name = get_class($this) . $this->ID;
$this->write();
}
}

View File

@ -1,41 +1,58 @@
<?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.
*
* @package widgets
*/
class WidgetArea extends DataObject
{
/**
* @var array
*/
private static $has_many = array(
"Widgets" => "Widget"
);
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__;
/**
*
* @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
* 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)){
if (!is_null($items)) {
foreach ($items as $widget) {
/** @var Widget $widget */
/** @var Controller $controller */
$controller = $widget->getController();
$controller->init();
$controller->doInit();
$controllers->push($controller);
}
}
@ -47,7 +64,7 @@ class WidgetArea extends DataObject
*/
public function Items()
{
return $this->getComponents('Widgets');
return $this->Widgets();
}
/**
@ -55,8 +72,7 @@ class WidgetArea extends DataObject
*/
public function ItemsToRender()
{
return $this->getComponents('Widgets')
->filter("Enabled", 1);
return $this->Items()->filter('Enabled', 1);
}
/**
@ -75,15 +91,4 @@ class WidgetArea extends DataObject
{
$this->template = $template;
}
/**
* Delete all connected Widgets when this WidgetArea gets deleted
*/
public function onBeforeDelete()
{
parent::onBeforeDelete();
foreach ($this->Widgets() as $widget) {
$widget->delete();
}
}
}

View File

@ -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";
}
}
}

View File

@ -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>

View File

@ -0,0 +1,3 @@
<% loop $WidgetControllers %>
$WidgetHolder
<% end_loop %>

View File

@ -1,3 +0,0 @@
<% loop WidgetControllers %>
$WidgetHolder
<% end_loop %>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -1,4 +1,18 @@
<?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
@ -8,21 +22,19 @@ class WidgetAreaEditorTest extends SapphireTest
/**
* This is the widget you want to use for your unit tests.
*/
protected $widgetToTest = 'WidgetAreaEditorTest_TestWidget';
protected $widgetToTest = TestWidget::class;
protected static $extra_dataobjects = [
FakePage::class,
TestWidget::class,
];
protected $extraDataObjects = array(
'WidgetAreaEditorTest_FakePage',
'WidgetAreaEditorTest_TestWidget',
);
protected $usesDatabase = true;
protected $requiredExtensions = array(
"SiteTree" => array(
"WidgetPageExtension"
)
);
protected static $required_extensions = [
SiteTree::class => [WidgetPageExtension::class]
];
public function testFillingOneArea()
{
$data = array(
@ -36,14 +48,21 @@ class WidgetAreaEditorTest extends SapphireTest
)
)
);
$request = new SS_HTTPRequest('get', 'post', array(), $data);
$request = new HTTPRequest('get', 'post', array(), $data);
$editorSide = new WidgetAreaEditor('SideBar');
$editorBott = new WidgetAreaEditor('BottomBar');
$form = new Form(new ContentController(), 'Form', new FieldList($editorSide, $editorBott), new FieldList());
$form->setRequest($request);
$page = new WidgetAreaEditorTest_FakePage();
$form = new Form(
$controller = new ContentController(),
Form::class,
new FieldList($editorSide, $editorBott),
new FieldList()
);
$controller->setRequest($request);
$controller->setRequest($request);
$form->setController($controller);
$page = new FakePage();
$form->saveInto($page);
$page->write();
@ -75,30 +94,36 @@ class WidgetAreaEditorTest extends SapphireTest
)
)
);
$request = new SS_HTTPRequest('get', 'post', array(), $data);
$request = new HTTPRequest('get', 'post', array(), $data);
$editorSide = new WidgetAreaEditor('SideBar');
$editorBott = new WidgetAreaEditor('BottomBar');
$form = new Form(new ContentController(), 'Form', new FieldList($editorSide, $editorBott), new FieldList());
$form->setRequest($request);
$page = new WidgetAreaEditorTest_FakePage();
$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();
// 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');
$this->assertEquals($sideWidgets[0]->getTitle(), 'MyTestWidgetSide');
$this->assertEquals($bottWidgets[0]->getTitle(), 'MyTestWidgetBottom');
}
public function testDeletingOneWidgetFromOneArea()
{
// First get some widgets in there
@ -120,13 +145,19 @@ class WidgetAreaEditorTest extends SapphireTest
)
)
);
$request = new SS_HTTPRequest('get', 'post', array(), $data);
$request = new HTTPRequest('get', 'post', array(), $data);
$editorSide = new WidgetAreaEditor('SideBar');
$editorBott = new WidgetAreaEditor('BottomBar');
$form = new Form(new ContentController(), 'Form', new FieldList($editorSide, $editorBott), new FieldList());
$form->setRequest($request);
$page = new WidgetAreaEditorTest_FakePage();
$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();
@ -135,7 +166,7 @@ class WidgetAreaEditorTest extends SapphireTest
$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(
@ -150,8 +181,9 @@ class WidgetAreaEditorTest extends SapphireTest
)
)
);
$request = new SS_HTTPRequest('get', 'post', array(), $data);
$form->setRequest($request);
$request = new HTTPRequest('get', 'post', array(), $data);
$controller->setRequest($request);
$form->setController($controller);
$form->saveInto($page);
$page->write();
@ -160,9 +192,9 @@ class WidgetAreaEditorTest extends SapphireTest
$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($bottWidgets[0]->getTitle(), 'MyTestWidgetBottom');
$this->assertEquals($page->SideBar()->Widgets()->Count(), 0);
}
@ -187,13 +219,19 @@ class WidgetAreaEditorTest extends SapphireTest
)
)
);
$request = new SS_HTTPRequest('get', 'post', array(), $data);
$request = new HTTPRequest('get', 'post', array(), $data);
$editorSide = new WidgetAreaEditor('SideBar');
$editorBott = new WidgetAreaEditor('BottomBar');
$form = new Form(new ContentController(), 'Form', new FieldList($editorSide, $editorBott), new FieldList());
$form->setRequest($request);
$page = new WidgetAreaEditorTest_FakePage();
$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();
@ -202,7 +240,7 @@ class WidgetAreaEditorTest extends SapphireTest
$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(
@ -212,21 +250,22 @@ class WidgetAreaEditorTest extends SapphireTest
)
)
);
$request = new SS_HTTPRequest('get', 'post', array(), $data);
$form->setRequest($request);
$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
@ -248,13 +287,19 @@ class WidgetAreaEditorTest extends SapphireTest
)
)
);
$request = new SS_HTTPRequest('get', 'post', array(), $data);
$request = new HTTPRequest('get', 'post', array(), $data);
$editorSide = new WidgetAreaEditor('SideBar');
$editorBott = new WidgetAreaEditor('BottomBar');
$form = new Form(new ContentController(), 'Form', new FieldList($editorSide, $editorBott), new FieldList());
$form->setRequest($request);
$page = new WidgetAreaEditorTest_FakePage();
$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();
@ -263,7 +308,7 @@ class WidgetAreaEditorTest extends SapphireTest
$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(
@ -283,8 +328,9 @@ class WidgetAreaEditorTest extends SapphireTest
)
)
);
$request = new SS_HTTPRequest('get', 'post', array(), $data);
$form->setRequest($request);
$request = new HTTPRequest('get', 'post', array(), $data);
$controller->setRequest($request);
$form->setController($controller);
$form->saveInto($page);
$page->write();
@ -293,11 +339,11 @@ class WidgetAreaEditorTest extends SapphireTest
$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');
$this->assertEquals($bottWidgets[0]->getTitle(), 'MyTestWidgetBottom');
$this->assertEquals($sideWidgets[0]->getTitle(), 'MyTestWidgetSide-edited');
}
public function testEditingAWidgetFromEachArea()
@ -321,13 +367,19 @@ class WidgetAreaEditorTest extends SapphireTest
)
)
);
$request = new SS_HTTPRequest('get', 'post', array(), $data);
$request = new HTTPRequest('get', 'post', array(), $data);
$editorSide = new WidgetAreaEditor('SideBar');
$editorBott = new WidgetAreaEditor('BottomBar');
$form = new Form(new ContentController(), 'Form', new FieldList($editorSide, $editorBott), new FieldList());
$form->setRequest($request);
$page = new WidgetAreaEditorTest_FakePage();
$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();
@ -336,7 +388,7 @@ class WidgetAreaEditorTest extends SapphireTest
$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(
@ -356,8 +408,9 @@ class WidgetAreaEditorTest extends SapphireTest
)
)
);
$request = new SS_HTTPRequest('get', 'post', array(), $data);
$form->setRequest($request);
$request = new HTTPRequest('get', 'post', array(), $data);
$controller->setRequest($request);
$form->setController($controller);
$form->saveInto($page);
$page->write();
@ -366,13 +419,13 @@ class WidgetAreaEditorTest extends SapphireTest
$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');
$this->assertEquals($bottWidgets[0]->getTitle(), 'MyTestWidgetBottom-edited');
$this->assertEquals($sideWidgets[0]->getTitle(), 'MyTestWidgetSide-edited');
}
public function testEditAWidgetFromOneAreaAndDeleteAWidgetFromAnotherArea()
{
// First get some widgets in there
@ -394,13 +447,19 @@ class WidgetAreaEditorTest extends SapphireTest
)
)
);
$request = new SS_HTTPRequest('get', 'post', array(), $data);
$request = new HTTPRequest('get', 'post', array(), $data);
$editorSide = new WidgetAreaEditor('SideBar');
$editorBott = new WidgetAreaEditor('BottomBar');
$form = new Form(new ContentController(), 'Form', new FieldList($editorSide, $editorBott), new FieldList());
$form->setRequest($request);
$page = new WidgetAreaEditorTest_FakePage();
$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);
@ -410,7 +469,7 @@ class WidgetAreaEditorTest extends SapphireTest
$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(
@ -425,8 +484,9 @@ class WidgetAreaEditorTest extends SapphireTest
)
)
);
$request = new SS_HTTPRequest('get', 'post', array(), $data);
$form->setRequest($request);
$request = new HTTPRequest('get', 'post', array(), $data);
$controller->setRequest($request);
$form->setController($controller);
$form->saveInto($page);
$page->write();
@ -435,23 +495,9 @@ class WidgetAreaEditorTest extends SapphireTest
$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');
$this->assertEquals($sideWidgets[0]->getTitle(), 'MyTestWidgetSide-edited');
}
}
class WidgetAreaEditorTest_FakePage extends Page implements TestOnly
{
private static $has_one = array(
"BottomBar" => "WidgetArea",
);
}
class WidgetAreaEditorTest_TestWidget extends Widget implements TestOnly
{
private static $cmsTitle = "Test widget";
private static $title = "Test widget";
private static $description = "Test widget";
}

View File

@ -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
);
}

View File

@ -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";
}

View File

@ -1,99 +1,61 @@
<?php
/**
* @package widgets
* @subpackage tests
*/
namespace SilverStripe\Widgets\Tests;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Widgets\Tests\WidgetControllerTest\TestPage;
use SilverStripe\Widgets\Tests\WidgetControllerTest\TestWidget;
class WidgetControllerTest extends FunctionalTest
{
protected static $fixture_file = 'WidgetControllerTest.yml';
protected $extraDataObjects = array(
'WidgetControllerTestPage',
'WidgetControllerTest_Widget',
);
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('WidgetControllerTestPage', 'page1');
$page->publish('Stage', 'Live');
$widget = $this->objFromFixture('WidgetControllerTest_Widget', 'widget1');
$page = $this->objFromFixture(TestPage::class, 'page1');
$widget = $this->objFromFixture(TestWidget::class, 'widget1');
$response = $this->get($page->URLSegment);
$formAction = sprintf('%s/widget/%d/Form', $page->URLSegment, $widget->ID);
$this->assertContains(
$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('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'));
$page = $this->objFromFixture(TestPage::class, 'page1');
$widget = $this->objFromFixture(TestWidget::class, 'widget1');
$this->assertContains(
$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->assertContains(
$this->assertStringContainsString(
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'
);
public function Form()
{
$widgetform = new Form(
$this,
'Form',
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
);
}
}

View File

@ -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

View File

@ -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',
];
}

View File

@ -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);
}
}

View File

@ -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',
];
}

View File

@ -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
);
}
}

View File

@ -0,0 +1 @@
$WidgetControllerTestSidebar

View File

@ -1,29 +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.
*/
public function getViewer($action)
{
$templates = array('WidgetControllerTestPage');
return new SSViewer($templates);
}
}

View File

@ -1 +0,0 @@
$WidgetControllerTestSidebar

View File

@ -1 +0,0 @@
$Form