Skip to content

Commit

Permalink
Merge branch '4.3' into 4
Browse files Browse the repository at this point in the history
  • Loading branch information
robbieaverill committed Nov 6, 2018
2 parents 0e492b0 + 3c58ae0 commit 64c2938
Show file tree
Hide file tree
Showing 15 changed files with 171 additions and 20 deletions.
3 changes: 3 additions & 0 deletions .upgrade.yml
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,9 @@ warnings:
'Object':
message: 'Replaced with traits'
url: 'https://docs.silverstripe.org/en/4/changelogs/4.0.0#object-replace'
'SS_Object':
message: 'Replaced with traits'
url: 'https://docs.silverstripe.org/en/4/changelogs/4.0.0#object-replace'
'SS_Log':
message: 'Replaced with a PSR-3 logger'
url: 'https://docs.silverstripe.org/en/4/changelogs/4.0.0#psr3-logging'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,35 @@ coding conventions.

A pattern library is a collection of user interface design elements, this helps developers and designers collaborate and to provide a quick preview of elements as they were intended without the need to build an entire interface to see it.
Components built in React and used by the CMS are actively being added to the pattern library.
The pattern library can be used to preview React components without including them in the SilverStripe CMS.

To access the pattern library, starting from your project root:
### Viewing the latest pattern library

The easiest way to access the pattern library is to view it online. The pattern library for the latest SilverStripe 4 development branch is automatically built and deployed. Note that this may include new components that are not yet available in a stable release.

[Browse the SilverStripe pattern library online](https://silverstripe.github.io/silverstripe-admin).

### Running the pattern library

If you're developing a new React component, running the pattern library locally is a good way to interact with it.

The pattern library is built from the `silverstripe/admin` module, but it also requires `silverstripe/asset-admin`, `silversrtipe/cms` and `silverstripe/campaign-admin`.

To run the pattern library locally, you'll need a SilverStripe project based on `silverstripe/recipe-cms` and `yarn` installed locally. The pattern library requires the JS source files so you'll need to use the `--prefer-source` flag when installing your dependencies with Composer.

```bash
composer install --prefer-source
(cd vendor/silverstripe/asset-admin && yarn install)
(cd vendor/silverstripe/campaign-admin && yarn install)
(cd vendor/silverstripe/cms && yarn install)
cd vendor/silverstripe/admin && yarn install && yarn pattern-lib
```
cd vendor/silverstripe/admin && yarn pattern-lib
```

Then browse to `http://localhost:6006/`
The pattern library will be available at [http://localhost:6006](http://localhost:6006). The JS source files will be watched, so every time you make a change to a JavaScript file, the pattern library will automatically update itself.

If you want to build a static version of the pattern library, you can replace `yarn pattern-lib` with `yarn build-storybook`. This will output the pattern library files to a `storybook-static` folder.

The SilverStripe pattern library is built using the [StoryBook JS library](https://storybook.js.org/). You can read the StoryBook documentation to learn about more advanced features and customisation options.

## The Admin URL

Expand Down
27 changes: 27 additions & 0 deletions docs/en/04_Changelogs/4.3.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Take care with `stageChildren()` overrides. `Hierarchy::numChildren() ` results will only make use of `stageChildren()` customisations that are applied to the base class and don't include record-specific behaviour.
- New React-based search UI for the CMS, Asset-Admin, GridFields and ModelAdmins.
- A new `GridFieldLazyLoader` component can be added to `GridField`. This will delay the fetching of data until the user access the container Tab of the GridField.
- `SilverStripe\VersionedAdmin\Controllers\CMSPageHistoryViewerController` is now the default CMS history controller and `SilverStripe\CMS\Controllers\CMSPageHistoryController` has been deprecated.

## Upgrading {#upgrading}

Expand All @@ -25,6 +26,12 @@ To enable the legacy search API on a `GridFieldFilterHeader`, you can either:
* set the `useLegacyFilterHeader` property to `true`,
* or pass `true` to the first argument of its constructor.

To force the legacy search API on all instances of `GridFieldFilterHeader`, you can set it in your [configuration file](../../configuration):
```yml
SilverStripe\Forms\GridField\GridFieldFilterHeader:
force_legacy: true
```
```php
public function getCMSFields()
{
Expand All @@ -41,3 +48,23 @@ public function getCMSFields()
}

```

### Keep using the legacy `CMSPageHistoryController`

To keep using the old CMS history controller for every page type, add the following entry to your YML config.

```yml
SilverStripe\Core\Injector\Injector:
SilverStripe\CMS\Controllers\CMSPageHistoryController:
class: SilverStripe\CMS\Controllers\CMSPageHistoryController
```
If you want to use both CMS history controllers in different contexts, you can implement your own _Factory_ class.
```yml
SilverStripe\Core\Injector\Injector:
SilverStripe\CMS\Controllers\CMSPageHistoryController:
factory:
App\MySite\MyCustomControllerFactory
```
[Implementing a _Factory_ with the Injector](/developer_guides/extending/injector/#factories)
3 changes: 2 additions & 1 deletion src/Core/Convert.php
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ public static function upperCamelToLowerCamel($str)

/**
* Turn a memory string, such as 512M into an actual number of bytes.
* Preserves integer values like "1024" or "-1"
*
* @param string $memString A memory limit string, such as "64M"
* @return float
Expand All @@ -573,7 +574,7 @@ public static function memstring2bytes($memString)
// Remove non-unit characters from the size
$unit = preg_replace('/[^bkmgtpezy]/i', '', $memString);
// Remove non-numeric characters from the size
$size = preg_replace('/[^0-9\.]/', '', $memString);
$size = preg_replace('/[^0-9\.\-]/', '', $memString);

if ($unit) {
// Find the position of the unit in the ordered string which is the power
Expand Down
9 changes: 9 additions & 0 deletions src/Core/Injector/Injector.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Environment;
use SilverStripe\Dev\Deprecation;
use SilverStripe\ORM\DataObject;

/**
* A simple injection manager that manages creating objects and injecting
Expand Down Expand Up @@ -581,6 +582,14 @@ protected function instantiate($spec, $id = null, $type = null)
$constructorParams = $spec['constructor'];
}

// If we're dealing with a DataObject singleton without specific constructor params, pass through Singleton
// flag as second argument
if ((!$type || $type !== self::PROTOTYPE)
&& empty($constructorParams)
&& is_subclass_of($class, DataObject::class)) {
$constructorParams = array(null, true);
}

$factory = isset($spec['factory']) ? $this->get($spec['factory']) : $this->getObjectCreator();
$object = $factory->create($class, $constructorParams);

Expand Down
25 changes: 24 additions & 1 deletion src/Forms/GridField/GridField.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,13 @@ class GridField extends FormField
protected $readonlyComponents = [
GridField_ActionMenu::class,
GridFieldConfig_RecordViewer::class,
GridFieldButtonRow::class,
GridFieldDataColumns::class,
GridFieldDetailForm::class,
GridFieldLazyLoader::class,
GridFieldPageCount::class,
GridFieldPaginator::class,
GridFieldFilterHeader::class,
GridFieldSortableHeader::class,
GridFieldToolbarHeader::class,
GridFieldViewButton::class,
Expand Down Expand Up @@ -241,16 +243,22 @@ public function performReadonlyTransformation()
{
$copy = clone $this;
$copy->setReadonly(true);
$copyConfig = $copy->getConfig();

// get the whitelist for allowable readonly components
$allowedComponents = $this->getReadonlyComponents();
foreach ($this->getConfig()->getComponents() as $component) {
// if a component doesn't exist, remove it from the readonly version.
if (!in_array(get_class($component), $allowedComponents)) {
$copy->getConfig()->removeComponent($component);
$copyConfig->removeComponent($component);
}
}

// As the edit button may have been removed, add a view button if it doesn't have one
if (!$copyConfig->getComponentByType(GridFieldViewButton::class)) {
$copyConfig->addComponent(new GridFieldViewButton);
}

return $copy;
}

Expand Down Expand Up @@ -290,6 +298,18 @@ public function setConfig(GridFieldConfig $config)
return $this;
}

/**
* @param bool $readonly
*
* @return $this
*/
public function setReadonly($readonly)
{
parent::setReadonly($readonly);
$this->getState()->Readonly = $readonly;
return $this;
}

/**
* @return ArrayList
*/
Expand Down Expand Up @@ -1009,6 +1029,9 @@ public function gridFieldAlterAction($data, $form, HTTPRequest $request)
}

if ($request->getHeader('X-Pjax') === 'CurrentField') {
if ($this->getState()->Readonly === true) {
$this->performDisabledTransformation();
}
return $this->FieldHolder();
}

Expand Down
34 changes: 22 additions & 12 deletions src/Forms/GridField/GridFieldFilterHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use SilverStripe\Admin\LeftAndMain;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Convert;
use SilverStripe\Dev\Deprecation;
use SilverStripe\Forms\FieldGroup;
Expand Down Expand Up @@ -43,6 +44,16 @@ class GridFieldFilterHeader implements GridField_URLHandler, GridField_HTMLProvi
*/
public $useLegacyFilterHeader = false;

/**
* Forces all filter components to revert to displaying the legacy
* table header style rather than the react driven search box
*
* @deprecated 4.3.0:5.0.0 Will be removed in 5.0
* @config
* @var bool
*/
private static $force_legacy = false;

/**
* @var \SilverStripe\ORM\Search\SearchContext
*/
Expand Down Expand Up @@ -76,7 +87,7 @@ public function getURLHandlers($gridField)
}

/**
* @param bool $useLegacy
* @param bool $useLegacy This will be removed in 5.0
* @param callable|null $updateSearchContext This will be removed in 5.0
* @param callable|null $updateSearchForm This will be removed in 5.0
*/
Expand All @@ -85,7 +96,7 @@ public function __construct(
callable $updateSearchContext = null,
callable $updateSearchForm = null
) {
$this->useLegacyFilterHeader = $useLegacy;
$this->useLegacyFilterHeader = Config::inst()->get(self::class, 'force_legacy') || $useLegacy;
$this->updateSearchContextCallback = $updateSearchContext;
$this->updateSearchFormCallback = $updateSearchForm;
}
Expand Down Expand Up @@ -154,7 +165,7 @@ public function getActions($gridField)
* If the GridField has a filterable datalist, return an array of actions
*
* @param GridField $gridField
* @return array
* @return void
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data)
{
Expand All @@ -163,14 +174,13 @@ public function handleAction(GridField $gridField, $actionName, $arguments, $dat
}

$state = $gridField->State->GridFieldFilterHeader;
$state->Columns = null;
if ($actionName === 'filter') {
if (isset($data['filter'][$gridField->getName()])) {
foreach ($data['filter'][$gridField->getName()] as $key => $filter) {
$state->Columns->$key = $filter;
}
}
} elseif ($actionName === 'reset') {
$state->Columns = null;
}
}

Expand All @@ -193,12 +203,10 @@ public function getManipulatedData(GridField $gridField, SS_List $dataList)

$filterArguments = $columns->toArray();
$dataListClone = clone($dataList);
foreach ($filterArguments as $columnName => $value) {
if ($dataList->canFilterBy($columnName) && $value) {
$dataListClone = $dataListClone->filter($columnName . ':PartialMatch', $value);
}
}
return $dataListClone;
$results = $this->getSearchContext($gridField)
->getQuery($filterArguments, false, false, $dataListClone);

return $results;
}

/**
Expand Down Expand Up @@ -337,9 +345,11 @@ public function getSearchForm(GridField $gridField)
$field->addExtraClass('stacked');
}

$name = $gridField->Title ?: singleton($gridField->getModelClass())->i18n_plural_name();

$this->searchForm = $form = new Form(
$gridField,
"SearchForm",
$name . "SearchForm",
$searchFields,
new FieldList()
);
Expand Down
1 change: 1 addition & 0 deletions src/Forms/GridField/GridField_FormAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public function getAttributes()
// will strip it from the requests
'name' => 'action_gridFieldAlterAction' . '?' . http_build_query($actionData),
'data-url' => $this->gridField->Link(),
'type' => "button",
)
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/Forms/HTMLEditor/HTMLEditorConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public static function get($identifier = null)
// Create new instance if unconfigured
if (!isset(self::$configs[$identifier])) {
self::$configs[$identifier] = static::create();
self::$configs[$identifier]->setOption('editorIdentifier', $identifier);
}
return self::$configs[$identifier];
}
Expand All @@ -98,6 +99,7 @@ public static function set_config($identifier, HTMLEditorConfig $config = null)
{
if ($config) {
self::$configs[$identifier] = $config;
self::$configs[$identifier]->setOption('editorIdentifier', $identifier);
} else {
unset(self::$configs[$identifier]);
}
Expand Down
2 changes: 2 additions & 0 deletions src/Forms/HTMLEditor/TinyMCECombinedGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,13 @@ public function generateContent(TinyMCEConfig $config)

// Register vars for config
$baseDirJS = Convert::raw2js(Director::absoluteBaseURL());
$name = Convert::raw2js($this->checkName($config));
$buffer = [];
$buffer[] = <<<SCRIPT
(function() {
var baseTag = window.document.getElementsByTagName('base');
var baseURL = baseTag.length ? baseTag[0].baseURI : '$baseDirJS';
var editorIdentifier = '$name';
SCRIPT;
$buffer[] = <<<SCRIPT
(function() {
Expand Down
2 changes: 1 addition & 1 deletion src/Forms/SingleLookupField.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public function Value()
return $label;
}

return $value;
return parent::Value();
}

/**
Expand Down
1 change: 1 addition & 0 deletions tests/php/Core/ConvertTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,7 @@ public function testMemString2Bytes($memString, $expected)
public function memString2BytesProvider()
{
return [
['-1', (float)-1],
['2048', (float)(2 * 1024)],
['2k', (float)(2 * 1024)],
['512M', (float)(512 * 1024 * 1024)],
Expand Down
2 changes: 1 addition & 1 deletion tests/php/Forms/HTMLEditor/HTMLEditorConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public function testDisablePluginsByArrayWithPaths()
$this->assertNotContains('plugin1', array_keys($plugins));
$this->assertNotContains('plugin2', array_keys($plugins));
}

public function testRequireJSIncludesAllConfigs()
{
$a = HTMLEditorConfig::get('configA');
Expand Down
7 changes: 7 additions & 0 deletions tests/php/Forms/HTMLEditor/TinyMCEConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@

class TinyMCEConfigTest extends SapphireTest
{

public function testEditorIdentifier()
{
$config = TinyMCEConfig::get('myconfig');
$this->assertEquals('myconfig', $config->getOption('editorIdentifier'));
}

/**
* Ensure that all TinyMCEConfig.tinymce_lang are valid
*/
Expand Down
Loading

0 comments on commit 64c2938

Please sign in to comment.