API Remove legacy HTMLEditor classes

This commit is contained in:
Damian Mooyman 2017-05-25 13:43:13 +12:00
parent 36e3a43bdb
commit e7d87add9f
14 changed files with 25 additions and 1552 deletions

View File

@ -352,7 +352,7 @@ mappings:
HTMLEditorConfig: SilverStripe\Forms\HTMLEditor\HTMLEditorConfig
HTMLEditorField: SilverStripe\Forms\HTMLEditor\HTMLEditorField
HTMLEditorField_Readonly: SilverStripe\Forms\HTMLEditor\HTMLEditorField_Readonly
HTMLEditorField_Toolbar: SilverStripe\Forms\HTMLEditor\HTMLEditorField_Toolbar
HTMLEditorField_Toolbar: SilverStripe\Admin\ModalController\HTMLEditorField_Toolbar
HTMLEditorField_Embed: SilverStripe\Forms\HTMLEditor\HTMLEditorField_Embed
HTMLEditorField_File: SilverStripe\Forms\HTMLEditor\HTMLEditorField_File
HTMLEditorField_Flash: SilverStripe\Forms\HTMLEditor\HTMLEditorField_Flash

View File

@ -261,19 +261,11 @@ of the CMS you have to take care of instantiate yourself:
:::php
// File: mysite/code/MyController.php
class MyObjectController extends Controller {
public function EditorToolbar() {
return HtmlEditorField_Toolbar::create($this, "EditorToolbar");
public function Modals() {
return ModalController::create($this, "Modals");
}
}
:::ss
// File: mysite/templates/MyController.ss
$Form
<% with $EditorToolbar %>
$MediaForm
$LinkForm
<% end_with %>
Note: The dialogs rely on CMS-access, e.g. for uploading and browsing files,
so this is considered advanced usage of the field.

View File

@ -1309,6 +1309,8 @@ After (`mysite/_config/config.yml`):
* `getsubtree()` -> moved to `CMSMain`
* `updatetreenodes()` -> moved to `CMSMain`
* `savetreenodes()` -> moved to `CMSMain`
* `EditorToolbar()` method renamed to `Modals()` and now returns a `ModalController` handler
instance rather than a `HTMLEditorField_Toolbar`
* Some `Director` API have been removed.
* $dev_servers
* $test_servers
@ -1857,6 +1859,12 @@ New `TimeField` methods replace `getConfig()` / `setConfig()`
* `setOption`
* Removed `MemberDatetimeOptionsetField` (no replacement)
* Removed `DateField_View_JQuery` (replaced with native HTML5 support in `DateField`)
* The following HTMLEditorField_* classes have been removed:
* `HTMLEditorField_Toolbar` (replaced With `ModalController` in admin module)
* `HTMLEditorField_Embed` (replaced with `EmbedResource` in asset-admin module)
* `HTMLEditorField_File`
* `HTMLEditorField_Flash`
* `HTMLEditorField_Image`
### <a name="overview-i18n"></a>i18n API

View File

@ -1,167 +0,0 @@
<?php
namespace SilverStripe\Forms\HTMLEditor;
use Embed\Adapters\Adapter;
use SilverStripe\Assets\File;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TextField;
use Embed\Adapters\AdapterInterface;
use Embed\Embed;
/**
* Encapsulation of an embed tag, linking to an external media source.
*
* @see Embed
*/
class HTMLEditorField_Embed extends HTMLEditorField_File
{
private static $casting = array(
'Type' => 'Varchar',
'Info' => 'Varchar'
);
/**
* Embed result
*
* @var Adapter
*/
protected $embed;
public function __construct($url, File $file = null)
{
parent::__construct($url, $file);
$this->embed = Embed::create($url);
if (!$this->embed) {
$controller = Controller::curr();
$response = $controller->getResponse();
$response->addHeader(
'X-Status',
rawurlencode(_t(
'SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.URLNOTANOEMBEDRESOURCE',
"The URL '{url}' could not be turned into a media resource.",
"The given URL is not a valid Oembed resource; the embed element couldn't be created.",
array('url' => $url)
))
);
$response->setStatusCode(404);
throw new HTTPResponse_Exception($response);
}
}
/**
* Get file-edit fields for this filed
*
* @return FieldList
*/
public function getFields()
{
$fields = parent::getFields();
if ($this->getType() === 'photo') {
$fields->insertBefore('CaptionText', new TextField(
'AltText',
_t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.IMAGEALTTEXT', 'Alternative text (alt) - shown if image can\'t be displayed'),
$this->Title,
80
));
$fields->insertBefore('CaptionText', new TextField(
'Title',
_t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.IMAGETITLE', 'Title text (tooltip) - for additional information about the image')
));
}
return $fields;
}
/**
* Get width of this Embed
*
* @return int
*/
public function getWidth()
{
return $this->embed->getWidth() ?: 100;
}
/**
* Get height of this Embed
*
* @return int
*/
public function getHeight()
{
return $this->embed->getHeight() ?: 100;
}
public function getPreviewURL()
{
// Use thumbnail url
if ($this->embed->image) {
return $this->embed->image;
}
// Use direct image type
if ($this->getType() == 'photo' && !empty($this->embed->url)) {
return $this->embed->url;
}
// Default media
return FRAMEWORK_ADMIN_DIR . '/client/dist/images/src/default_media.png';
}
public function getName()
{
if ($this->embed->title) {
return $this->embed->title;
} else {
return parent::getName();
}
}
/**
* Get Embed type
*
* @return string
*/
public function getType()
{
return $this->embed->type;
}
/**
* Get filetype
*
* @return string
*/
public function getFileType()
{
return $this->getType()
?: parent::getFileType();
}
/**
* @return AdapterInterface
*/
public function getEmbed()
{
return $this->embed;
}
public function appCategory()
{
return 'embed';
}
/**
* Info for this Embed
*
* @return string
*/
public function getInfo()
{
return $this->embed->info;
}
}

View File

@ -1,402 +0,0 @@
<?php
namespace SilverStripe\Forms\HTMLEditor;
use SilverStripe\Assets\File;
use SilverStripe\Control\Controller;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\CompositeField;
use SilverStripe\Forms\DateField_Disabled;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldGroup;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\HiddenField;
use SilverStripe\Forms\HTMLReadonlyField;
use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\ReadonlyField;
use SilverStripe\Forms\TextField;
use SilverStripe\View\ViewableData;
/**
* Encapsulation of a file which can either be a remote URL
* or a {@link File} on the local filesystem, exhibiting common properties
* such as file name or the URL.
*
* @todo Remove once core has support for remote files
*/
abstract class HTMLEditorField_File extends ViewableData
{
/**
* Default insertion width for Images and Media
*
* @config
* @var int
*/
private static $insert_width = 600;
/**
* Default insert height for images and media
*
* @config
* @var int
*/
private static $insert_height = 360;
/**
* Max width for insert-media preview.
*
* Matches CSS rule for .cms-file-info-preview
*
* @var int
*/
private static $media_preview_width = 176;
/**
* Max height for insert-media preview.
*
* Matches CSS rule for .cms-file-info-preview
*
* @var int
*/
private static $media_preview_height = 128;
private static $casting = array(
'URL' => 'Varchar',
'Name' => 'Varchar'
);
/**
* Absolute URL to asset
*
* @var string
*/
protected $url;
/**
* File dataobject (if available)
*
* @var File
*/
protected $file;
/**
* @param string $url
* @param File $file
*/
public function __construct($url, File $file = null)
{
$this->url = $url;
$this->file = $file;
$this->failover = $file;
parent::__construct();
}
/**
* @return FieldList
*/
public function getFields()
{
$fields = new FieldList(
CompositeField::create(
CompositeField::create(LiteralField::create("ImageFull", $this->getPreview()))
->setName("FilePreviewImage")
->addExtraClass('cms-file-info-preview'),
CompositeField::create($this->getDetailFields())
->setName("FilePreviewData")
->addExtraClass('cms-file-info-data')
)
->setName("FilePreview")
->addExtraClass('cms-file-info'),
TextField::create('CaptionText', _t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.CAPTIONTEXT', 'Caption text')),
DropdownField::create(
'CSSClass',
_t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.CSSCLASS', 'Alignment / style'),
array(
'leftAlone' => _t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.CSSCLASSLEFTALONE', 'On the left, on its own.'),
'center' => _t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.CSSCLASSCENTER', 'Centered, on its own.'),
'left' => _t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.CSSCLASSLEFT', 'On the left, with text wrapping around.'),
'right' => _t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.CSSCLASSRIGHT', 'On the right, with text wrapping around.')
),
HtmlEditorField::config()->uninherited('media_alignment')
),
FieldGroup::create(
_t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.IMAGEDIMENSIONS', 'Dimensions'),
TextField::create(
'Width',
_t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.IMAGEWIDTHPX', 'Width'),
$this->getInsertWidth()
)->setMaxLength(5),
TextField::create(
'Height',
" x " . _t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.IMAGEHEIGHTPX', 'Height'),
$this->getInsertHeight()
)->setMaxLength(5)
)->addExtraClass('dimensions last'),
HiddenField::create('URL', false, $this->getURL()),
HiddenField::create('FileID', false, $this->getFileID())
);
return $fields;
}
/**
* Get list of fields for previewing this records details
*
* @return FieldList
*/
protected function getDetailFields()
{
$fields = new FieldList(
ReadonlyField::create("FileType", _t(__CLASS__.'.TYPE', 'File type'), $this->getFileType()),
HTMLReadonlyField::create(
'ClickableURL',
_t(__CLASS__.'.URL', 'URL'),
$this->getExternalLink()
)
);
// Get file size
if ($this->getSize()) {
$fields->insertAfter(
'FileType',
ReadonlyField::create("Size", _t(__CLASS__.'.SIZE', 'File size'), $this->getSize())
);
}
// Get modified details of local record
if ($this->getFile()) {
$fields->push(new DateField_Disabled(
"Created",
_t(__CLASS__.'.CREATED', 'First uploaded'),
$this->getFile()->Created
));
$fields->push(new DateField_Disabled(
"LastEdited",
_t(__CLASS__.'.LASTEDIT', 'Last changed'),
$this->getFile()->LastEdited
));
}
return $fields;
}
/**
* Get file DataObject
*
* Might not be set (for remote files)
*
* @return File
*/
public function getFile()
{
return $this->file;
}
/**
* Get file ID
*
* @return int
*/
public function getFileID()
{
if ($file = $this->getFile()) {
return $file->ID;
}
return null;
}
/**
* Get absolute URL
*
* @return string
*/
public function getURL()
{
return $this->url;
}
/**
* Get basename
*
* @return string
*/
public function getName()
{
return $this->file
? $this->file->Name
: preg_replace('/\?.*/', '', basename($this->url));
}
/**
* Get descriptive file type
*
* @return string
*/
public function getFileType()
{
return File::get_file_type($this->getName());
}
/**
* Get file size (if known) as string
*
* @return string|false String value, or false if doesn't exist
*/
public function getSize()
{
if ($this->file) {
return $this->file->getSize();
}
return false;
}
/**
* HTML content for preview
*
* @return string HTML
*/
public function getPreview()
{
$preview = $this->extend('getPreview');
if ($preview) {
return $preview;
}
// Generate tag from preview
$thumbnailURL = Convert::raw2att(
Controller::join_links($this->getPreviewURL(), "?r=" . rand(1, 100000))
);
$fileName = Convert::raw2att($this->Name);
return sprintf(
"<img id='thumbnailImage' class='thumbnail-preview' src='%s' alt='%s' />\n",
$thumbnailURL,
$fileName
);
}
/**
* HTML Content for external link
*
* @return string
*/
public function getExternalLink()
{
$title = $this->file
? $this->file->getTitle()
: $this->getName();
return sprintf(
'<a href="%1$s" title="%2$s" target="_blank" rel="external" class="file-url">%1$s</a>',
Convert::raw2att($this->url),
Convert::raw2att($title)
);
}
/**
* Generate thumbnail url
*
* @return string
*/
public function getPreviewURL()
{
// Get preview from file
if ($this->file) {
return $this->getFilePreviewURL();
}
// Generate default icon html
return File::get_icon_for_extension($this->getExtension());
}
/**
* Generate thumbnail URL from file dataobject (if available)
*
* @return string
*/
protected function getFilePreviewURL()
{
// Get preview from file
if ($this->file) {
$width = HTMLEditorField_File::config()->media_preview_width;
$height = HTMLEditorField_File::config()->media_preview_height;
return $this->file->ThumbnailURL($width, $height);
}
return null;
}
/**
* Get file extension
*
* @return string
*/
public function getExtension()
{
$extension = File::get_file_extension($this->getName());
return strtolower($extension);
}
/**
* Category name
*
* @return string
*/
public function appCategory()
{
if ($this->file) {
return $this->file->appCategory();
} else {
return File::get_app_category($this->getExtension());
}
}
/**
* Get height of this item
*/
public function getHeight()
{
if ($this->file) {
$height = $this->file->getHeight();
if ($height) {
return $height;
}
}
return HTMLEditorField_File::config()->insert_height;
}
/**
* Get width of this item
*
* @return int
*/
public function getWidth()
{
if ($this->file) {
$width = $this->file->getWidth();
if ($width) {
return $width;
}
}
return HTMLEditorField_File::config()->insert_width;
}
/**
* Provide an initial width for inserted media, restricted based on $embed_width
*
* @return int
*/
public function getInsertWidth()
{
$width = $this->getWidth();
$maxWidth = HTMLEditorField_File::config()->insert_width;
return ($width <= $maxWidth) ? $width : $maxWidth;
}
/**
* Provide an initial height for inserted media, scaled proportionally to the initial width
*
* @return int
*/
public function getInsertHeight()
{
$width = $this->getWidth();
$height = $this->getHeight();
$maxWidth = HTMLEditorField_File::config()->insert_width;
return ($width <= $maxWidth) ? $height : round($height * ($maxWidth / $width));
}
}

View File

@ -1,17 +0,0 @@
<?php
namespace SilverStripe\Forms\HTMLEditor;
/**
* Generate flash file embed
*/
class HTMLEditorField_Flash extends HTMLEditorField_File
{
public function getFields()
{
$fields = parent::getFields();
$fields->removeByName('CaptionText', true);
return $fields;
}
}

View File

@ -1,213 +0,0 @@
<?php
namespace SilverStripe\Forms\HTMLEditor;
use SilverStripe\Assets\File;
use SilverStripe\Forms\ReadonlyField;
use SilverStripe\Forms\TextField;
/**
* Encapsulation of an image tag, linking to an image either internal or external to the site.
*/
class HTMLEditorField_Image extends HTMLEditorField_File
{
/**
* @var int
*/
protected $width;
/**
* @var int
*/
protected $height;
/**
* File size details
*
* @var string
*/
protected $size;
public function __construct($url, File $file = null)
{
parent::__construct($url, $file);
if ($file) {
return;
}
// Get size of remote file
$size = @filesize($url);
if ($size) {
$this->size = $size;
}
// Get dimensions of remote file
$info = @getimagesize($url);
if ($info) {
$this->width = $info[0];
$this->height = $info[1];
}
}
public function getFields()
{
$fields = parent::getFields();
// Alt text
$fields->insertBefore(
'CaptionText',
TextField::create(
'AltText',
_t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.IMAGEALT', 'Alternative text (alt)'),
$this->Title,
80
)->setDescription(
_t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.IMAGEALTTEXTDESC', 'Shown to screen readers or if image can\'t be displayed')
)
);
// Tooltip
$fields->insertAfter(
'AltText',
TextField::create(
'Title',
_t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.IMAGETITLETEXT', 'Title text (tooltip)')
)->setDescription(
_t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.IMAGETITLETEXTDESC', 'For additional information about the image')
)
);
return $fields;
}
protected function getDetailFields()
{
$fields = parent::getDetailFields();
$width = $this->getOriginalWidth();
$height = $this->getOriginalHeight();
// Show dimensions of original
if ($width && $height) {
$fields->insertAfter(
'ClickableURL',
ReadonlyField::create(
"OriginalWidth",
_t(__CLASS__.'.WIDTH', 'Width'),
$width
)
);
$fields->insertAfter(
'OriginalWidth',
ReadonlyField::create(
"OriginalHeight",
_t(__CLASS__.'.HEIGHT', 'Height'),
$height
)
);
}
return $fields;
}
/**
* Get width of original, if known
*
* @return int
*/
public function getOriginalWidth()
{
if ($this->width) {
return $this->width;
}
if ($this->file) {
$width = $this->file->getWidth();
if ($width) {
return $width;
}
}
return null;
}
/**
* Get height of original, if known
*
* @return int
*/
public function getOriginalHeight()
{
if ($this->height) {
return $this->height;
}
if ($this->file) {
$height = $this->file->getHeight();
if ($height) {
return $height;
}
}
return null;
}
public function getWidth()
{
if ($this->width) {
return $this->width;
}
return parent::getWidth();
}
public function getHeight()
{
if ($this->height) {
return $this->height;
}
return parent::getHeight();
}
public function getSize()
{
if ($this->size) {
return File::format_size($this->size);
}
return parent::getSize();
}
/**
* Provide an initial width for inserted image, restricted based on $embed_width
*
* @return int
*/
public function getInsertWidth()
{
$width = $this->getWidth();
$maxWidth = HTMLEditorField_Image::config()->insert_width;
return $width <= $maxWidth
? $width
: $maxWidth;
}
/**
* Provide an initial height for inserted image, scaled proportionally to the initial width
*
* @return int
*/
public function getInsertHeight()
{
$width = $this->getWidth();
$height = $this->getHeight();
$maxWidth = HTMLEditorField_Image::config()->insert_width;
return ($width <= $maxWidth) ? $height : round($height * ($maxWidth / $width));
}
public function getPreviewURL()
{
// Get preview from file
if ($this->file) {
return $this->getFilePreviewURL();
}
// Embed image directly
return $this->url;
}
}

View File

@ -1,495 +0,0 @@
<?php
namespace SilverStripe\Forms\HTMLEditor;
use SilverStripe\Admin\Forms\EditorExternalLinkFormFactory;
use SilverStripe\Admin\Forms\EditorEmailLinkFormFactory;
use SilverStripe\Assets\File;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
use SilverStripe\Control\RequestHandler;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\Forms\CompositeField;
use SilverStripe\Forms\EmailField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\HiddenField;
use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\OptionsetField;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\TreeDropdownField;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\View\SSViewer;
/**
* Toolbar shared by all instances of {@link HTMLEditorField}, to avoid too much markup duplication.
* Needs to be inserted manually into the template in order to function - see {@link LeftAndMain->EditorToolbar()}.
*/
class HTMLEditorField_Toolbar extends RequestHandler
{
private static $allowed_actions = array(
'LinkForm',
'EditorExternalLink',
'EditorEmailLink',
'viewfile',
'getanchors'
);
/**
* @return string
*/
public function getTemplateViewFile()
{
return SSViewer::get_templates_by_class(static::class, '_viewfile', __CLASS__);
}
/**
* @var Controller
*/
protected $controller;
/**
* @var string
*/
protected $name;
public function __construct($controller, $name)
{
parent::__construct();
$this->controller = $controller;
$this->name = $name;
}
public function forTemplate()
{
return sprintf(
'<div id="cms-editor-dialogs" data-url-linkform="%s"></div>',
Controller::join_links($this->controller->Link(), $this->name, 'LinkForm', 'forTemplate')
);
}
/**
* Searches the SiteTree for display in the dropdown
*
* @param string $sourceObject
* @param string $labelField
* @param string $search
* @return DataList
*/
public function siteTreeSearchCallback($sourceObject, $labelField, $search)
{
return DataObject::get($sourceObject)->filterAny(array(
'MenuTitle:PartialMatch' => $search,
'Title:PartialMatch' => $search
));
}
/**
* Return a {@link Form} instance allowing a user to
* add links in the TinyMCE content editor.
*
* @skipUpgrade
* @return Form
*/
public function LinkForm()
{
$siteTree = TreeDropdownField::create(
'internal',
_t(__CLASS__.'.PAGE', "Page"),
SiteTree::class,
'ID',
'MenuTitle',
true
);
// mimic the SiteTree::getMenuTitle(), which is bypassed when the search is performed
$siteTree->setSearchFunction(array($this, 'siteTreeSearchCallback'));
$numericLabelTmpl = '<span class="step-label"><span class="flyout">Step %d.</span>'
. '<span class="title">%s</span></span>';
$form = new Form(
$this->controller,
"{$this->name}/LinkForm",
new FieldList(
$headerWrap = new CompositeField(
new LiteralField(
'Heading',
sprintf(
'<h3 class="htmleditorfield-linkform-heading insert">%s</h3>',
_t(__CLASS__.'.LINK', 'Insert Link')
)
)
),
$contentComposite = new CompositeField(
OptionsetField::create(
'LinkType',
DBField::create_field(
'HTMLFragment',
sprintf($numericLabelTmpl, '1', _t(__CLASS__.'.LINKTO', 'Link type'))
),
array(
'internal' => _t(__CLASS__.'.LINKINTERNAL', 'Link to a page on this site'),
'external' => _t(__CLASS__.'.LINKEXTERNAL', 'Link to another website'),
'anchor' => _t(__CLASS__.'.LINKANCHOR', 'Link to an anchor on this page'),
'email' => _t(__CLASS__.'.LINKEMAIL', 'Link to an email address'),
'file' => _t(__CLASS__.'.LINKFILE', 'Link to download a file'),
),
'internal'
),
LiteralField::create(
'Step2',
'<div class="step2">'
. sprintf($numericLabelTmpl, '2', _t(__CLASS__.'.LINKDETAILS', 'Link details')) . '</div>'
),
$siteTree,
TextField::create('external', _t(__CLASS__.'.URL', 'URL'), 'http://'),
EmailField::create('email', _t(__CLASS__.'.EMAIL', 'Email address')),
$fileField = TreeDropdownField::create(
'file',
_t(__CLASS__.'.FILE', 'File'),
File::class,
'ID',
'Name'
),
TextField::create('Anchor', _t(__CLASS__.'.ANCHORVALUE', 'Anchor')),
TextField::create('Subject', _t(__CLASS__.'.SUBJECT', 'Email subject')),
TextField::create('Description', _t(__CLASS__.'.LINKDESCR', 'Link description')),
CheckboxField::create(
'TargetBlank',
_t(__CLASS__.'.LINKOPENNEWWIN', 'Open link in a new window?')
),
HiddenField::create('Locale', null, $this->controller->Locale)
)
),
new FieldList()
);
$headerWrap->setName('HeaderWrap');
$headerWrap->addExtraClass('CompositeField composite cms-content-header form-group--no-label ');
$contentComposite->setName('ContentBody');
$contentComposite->addExtraClass('ss-insert-link content');
$form->unsetValidator();
$form->loadDataFrom($this);
$form->addExtraClass('htmleditorfield-form htmleditorfield-linkform cms-linkform-content');
$this->extend('updateLinkForm', $form);
return $form;
}
/**
* Builds and returns the external link form
*
* @return null|Form
*/
public function EditorExternalLink($id = null)
{
/** @var EditorExternalLinkFormFactory $factory */
$factory = Injector::inst()->get(EditorExternalLinkFormFactory::class);
if ($factory) {
return $factory->getForm($this->controller, "{$this->name}/EditorExternalLink");
}
return null;
}
/**
* Builds and returns the external link form
*
* @return null|Form
*/
public function EditorEmailLink($id = null)
{
/** @var EditorEmailLinkFormFactory $factory */
$factory = Injector::inst()->get(EditorEmailLinkFormFactory::class);
if ($factory) {
return $factory->getForm($this->controller, "{$this->name}/EditorEmailLink");
}
return null;
}
/**
* Get the folder ID to filter files by for the "from cms" tab
*
* @return int
*/
protected function getAttachParentID()
{
$parentID = $this->controller->getRequest()->requestVar('ParentID');
$this->extend('updateAttachParentID', $parentID);
return $parentID;
}
/**
* List of allowed schemes (no wildcard, all lower case) or empty to allow all schemes
*
* @config
* @var array
*/
private static $fileurl_scheme_whitelist = array('http', 'https');
/**
* List of allowed domains (no wildcard, all lower case) or empty to allow all domains
*
* @config
* @var array
*/
private static $fileurl_domain_whitelist = array();
/**
* Find local File dataobject given ID
*
* @param int $id
* @return array
*/
protected function viewfile_getLocalFileByID($id)
{
/** @var File $file */
$file = DataObject::get_by_id(File::class, $id);
if ($file && $file->canView()) {
return array($file, $file->getURL());
}
return [null, null];
}
/**
* Get remote File given url
*
* @param string $fileUrl Absolute URL
* @return array
* @throws HTTPResponse_Exception
*/
protected function viewfile_getRemoteFileByURL($fileUrl)
{
if (!Director::is_absolute_url($fileUrl)) {
throw $this->getErrorFor(_t(
"SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField_Toolbar.ERROR_ABSOLUTE",
"Only absolute urls can be embedded"
));
}
$scheme = strtolower(parse_url($fileUrl, PHP_URL_SCHEME));
$allowed_schemes = self::config()->get('fileurl_scheme_whitelist');
if (!$scheme || ($allowed_schemes && !in_array($scheme, $allowed_schemes))) {
throw $this->getErrorFor(_t(
"SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField_Toolbar.ERROR_SCHEME",
"This file scheme is not included in the whitelist"
));
}
$domain = strtolower(parse_url($fileUrl, PHP_URL_HOST));
$allowed_domains = self::config()->get('fileurl_domain_whitelist');
if (!$domain || ($allowed_domains && !in_array($domain, $allowed_domains))) {
throw $this->getErrorFor(_t(
"SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField_Toolbar.ERROR_HOSTNAME",
"This file hostname is not included in the whitelist"
));
}
return [null, $fileUrl];
}
/**
* Prepare error for the front end
*
* @param string $message
* @param int $code
* @return HTTPResponse_Exception
*/
protected function getErrorFor($message, $code = 400)
{
$exception = new HTTPResponse_Exception($message, $code);
$exception->getResponse()->addHeader('X-Status', $message);
return $exception;
}
/**
* View of a single file, either on the filesystem or on the web.
*
* @throws HTTPResponse_Exception
* @param HTTPRequest $request
* @return string
*/
public function viewfile($request)
{
$file = null;
$url = null;
// Get file and url by request method
if ($fileUrl = $request->getVar('FileURL')) {
// Get remote url
list($file, $url) = $this->viewfile_getRemoteFileByURL($fileUrl);
} elseif ($id = $request->getVar('ID')) {
// Or we could have been passed an ID directly
list($file, $url) = $this->viewfile_getLocalFileByID($id);
} else {
// Or we could have been passed nothing, in which case panic
throw $this->getErrorFor(_t(
"SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField_Toolbar.ERROR_ID",
'Need either "ID" or "FileURL" parameter to identify the file'
));
}
// Validate file exists
if (!$url) {
throw $this->getErrorFor(_t(
"SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField_Toolbar.ERROR_NOTFOUND",
'Unable to find file to view'
));
}
// Instantiate file wrapper and get fields based on its type
// Check if appCategory is an image and exists on the local system, otherwise use Embed to reference a
// remote image
$fileCategory = $this->getFileCategory($url, $file);
switch ($fileCategory) {
case 'image':
case 'image/supported':
$fileWrapper = new HTMLEditorField_Image($url, $file);
break;
case 'flash':
$fileWrapper = new HTMLEditorField_Flash($url, $file);
break;
default:
// Only remote files can be linked via o-embed
// {@see HTMLEditorField_Toolbar::getAllowedExtensions())
if ($file) {
throw $this->getErrorFor(_t(
"SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField_Toolbar.ERROR_OEMBED_REMOTE",
"Embed is only compatible with remote files"
));
}
// Other files should fallback to embed
$fileWrapper = new HTMLEditorField_Embed($url, $file);
break;
}
// Render fields and return
$fields = $this->getFieldsForFile($url, $fileWrapper);
return $fileWrapper->customise(array(
'Fields' => $fields,
))->renderWith($this->getTemplateViewFile());
}
/**
* Guess file category from either a file or url
*
* @param string $url
* @param File $file
* @return string
*/
protected function getFileCategory($url, $file)
{
if ($file) {
return $file->appCategory();
}
if ($url) {
return File::get_app_category(File::get_file_extension($url));
}
return null;
}
/**
* Find all anchors available on the given page.
*
* @return array
* @throws HTTPResponse_Exception
*/
public function getanchors()
{
$id = (int)$this->getRequest()->getVar('PageID');
$anchors = array();
if (($page = SiteTree::get()->byID($id)) && !empty($page)) {
if (!$page->canView()) {
throw new HTTPResponse_Exception(
_t(
'SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.ANCHORSCANNOTACCESSPAGE',
'You are not permitted to access the content of the target page.'
),
403
);
}
// Parse the shortcodes so [img id=x] doesn't end up as anchor x
$htmlValue = $page->obj('Content')->forTemplate();
// Similar to the regex found in HTMLEditorField.js / getAnchors method.
if (preg_match_all(
"/\\s+(name|id)\\s*=\\s*([\"'])([^\\2\\s>]*?)\\2|\\s+(name|id)\\s*=\\s*([^\"']+)[\\s +>]/im",
$htmlValue,
$matches
)) {
$anchors = array_values(array_unique(array_filter(
array_merge($matches[3], $matches[5])
)));
}
} else {
throw new HTTPResponse_Exception(
_t('SilverStripe\\Forms\\HTMLEditor\\HTMLEditorField.ANCHORSPAGENOTFOUND', 'Target page not found.'),
404
);
}
return json_encode($anchors);
}
/**
* Similar to {@link File->getCMSFields()}, but only returns fields
* for manipulating the instance of the file as inserted into the HTML content,
* not the "master record" in the database - hence there's no form or saving logic.
*
* @param string $url Abolute URL to asset
* @param HTMLEditorField_File $file Asset wrapper
* @return FieldList
*/
protected function getFieldsForFile($url, HTMLEditorField_File $file)
{
$fields = $this->extend('getFieldsForFile', $url, $file);
if (!$fields) {
$fields = $file->getFields();
$file->extend('updateFields', $fields);
}
$this->extend('updateFieldsForFile', $fields, $url, $file);
return $fields;
}
/**
* Gets files filtered by a given parent with the allowed extensions
*
* @param int $parentID
* @return DataList
*/
protected function getFiles($parentID = null)
{
$exts = $this->getAllowedExtensions();
$dotExts = array_map(function ($ext) {
return ".{$ext}";
}, $exts);
$files = File::get()->filter('Name:EndsWith', $dotExts);
// Limit by folder (if required)
if ($parentID) {
$files = $files->filter('ParentID', $parentID);
}
return $files;
}
/**
* @return array All extensions which can be handled by the different views.
*/
protected function getAllowedExtensions()
{
$exts = array('jpg', 'gif', 'png', 'swf', 'jpeg');
$this->extend('updateAllowedExtensions', $exts);
return $exts;
}
}

View File

@ -4,6 +4,7 @@ namespace SilverStripe\Forms\Schema;
use InvalidArgumentException;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Forms\CompositeField;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\FormField;
@ -16,6 +17,8 @@ use SilverStripe\ORM\ValidationResult;
*/
class FormSchema
{
use Injectable;
/**
* Request the schema part
*/

View File

@ -2,40 +2,27 @@
namespace SilverStripe\Forms\Tests\HTMLEditor;
use Page;
use SilverStripe\Assets\File;
use SilverStripe\Assets\FileNameFilter;
use SilverStripe\Assets\Filesystem;
use SilverStripe\Assets\Folder;
use SilverStripe\Assets\Image;
use SilverStripe\Assets\Tests\Storage\AssetStoreTest\TestAssetStore;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\CSSContentParser;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
use SilverStripe\Forms\HTMLEditor\HTMLEditorField_Toolbar;
use SilverStripe\Forms\HTMLEditor\HTMLEditorField_Image;
use SilverStripe\Forms\HTMLReadonlyField;
use SilverStripe\Forms\Tests\HTMLEditor\HTMLEditorFieldTest\DummyMediaFormFieldExtension;
use SilverStripe\Forms\Tests\HTMLEditor\HTMLEditorFieldTest\TestObject;
use SilverStripe\ORM\FieldType\DBHTMLText;
use SilverStripe\Forms\HTMLEditor\TinyMCEConfig;
class HTMLEditorFieldTest extends FunctionalTest
{
protected static $fixture_file = 'HTMLEditorFieldTest.yml';
protected static $use_draft_site = true;
protected static $required_extensions= array(
HTMLEditorField_Toolbar::class => array(
DummyMediaFormFieldExtension::class,
),
);
protected static $extra_dataobjects = [
TestObject::class,
];
@ -48,16 +35,16 @@ class HTMLEditorFieldTest extends FunctionalTest
TestAssetStore::activate('HTMLEditorFieldTest');
// Set the File Name Filter replacements so files have the expected names
Config::inst()->update(
Config::modify()->set(
FileNameFilter::class,
'default_replacements',
array(
'/\s/' => '-', // remove whitespace
'/_/' => '-', // underscores to dashes
'/[^A-Za-z0-9+.\-]+/' => '', // remove non-ASCII chars, only allow alphanumeric plus dash and dot
'/[\-]{2,}/' => '-', // remove duplicate dashes
'/^[\.\-_]+/' => '', // Remove all leading dots, dashes or underscores
)
[
'/\s/' => '-', // remove whitespace
'/_/' => '-', // underscores to dashes
'/[^A-Za-z0-9+.\-]+/' => '', // remove non-ASCII chars, only allow alphanumeric plus dash and dot
'/[\-]{2,}/' => '-', // remove duplicate dashes
'/^[\.\-_]+/' => '', // Remove all leading dots, dashes or underscores
]
);
// Create a test files for each of the fixture references
@ -175,73 +162,6 @@ class HTMLEditorFieldTest extends FunctionalTest
);
}
public function testGetAnchors()
{
if (!class_exists('Page')) {
$this->markTestSkipped();
}
$linkedPage = new Page();
$linkedPage->Title = 'Dummy';
$linkedPage->write();
$html = <<<EOS
<div name="foo"></div>
<div name='bar'></div>
<div id="baz"></div>
[sitetree_link id="{$linkedPage->ID}"]
<div id='bam'></div>
<div id = "baz"></div>
<div id = ""></div>
<div id="some'id"></div>
<div id=bar></div>
EOS
;
$expected = array(
'foo',
'bar',
'baz',
'bam',
"some&#039;id",
);
$page = new Page();
$page->Title = 'Test';
$page->Content = $html;
$page->write();
$this->useDraftSite(true);
$request = new HTTPRequest(
'GET',
'/',
array(
'PageID' => $page->ID,
)
);
$toolBar = new HTMLEditorField_Toolbar(new Controller(), 'test');
$toolBar->setRequest($request);
$results = json_decode($toolBar->getanchors(), true);
$this->assertEquals($expected, $results);
}
public function testHTMLEditorFieldFileLocal()
{
$file = new HTMLEditorField_Image('http://domain.com/folder/my_image.jpg?foo=bar');
$this->assertEquals('http://domain.com/folder/my_image.jpg?foo=bar', $file->URL);
$this->assertEquals('my_image.jpg', $file->Name);
$this->assertEquals('jpg', $file->Extension);
// TODO Can't easily test remote file dimensions
}
public function testHTMLEditorFieldFileRemote()
{
$fileFixture = new File(array('Name' => 'my_local_image.jpg', 'Filename' => 'folder/my_local_image.jpg'));
$file = new HTMLEditorField_Image('http://localdomain.com/folder/my_local_image.jpg', $fileFixture);
$this->assertEquals('http://localdomain.com/folder/my_local_image.jpg', $file->URL);
$this->assertEquals('my_local_image.jpg', $file->Name);
$this->assertEquals('jpg', $file->Extension);
}
public function testReadonlyField()
{
$editor = new HTMLEditorField('Content');
@ -252,13 +172,9 @@ EOS
$fileID
)
);
/**
* @var HTMLReadonlyField $readonly
*/
/** @var HTMLReadonlyField $readonly */
$readonly = $editor->performReadonlyTransformation();
/**
* @var DBHTMLText $readonlyContent
*/
/** @var DBHTMLText $readonlyContent */
$readonlyContent = $readonly->Field();
$this->assertEquals(
@ -276,9 +192,7 @@ EOS
// Test with include input tag
$readonly = $editor->performReadonlyTransformation()
->setIncludeHiddenField(true);
/**
* @var DBHTMLText $readonlyContent
*/
/** @var DBHTMLText $readonlyContent */
$readonlyContent = $readonly->Field();
$this->assertEquals(
<<<EOS

View File

@ -1,22 +0,0 @@
<?php
namespace SilverStripe\Forms\Tests\HTMLEditor\HTMLEditorFieldTest;
use SilverStripe\Core\Extension;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\Form;
class DummyMediaFormFieldExtension extends Extension implements TestOnly
{
public static $fields = null;
public static $update_called = false;
/**
* @param Form $form
*/
public function updateImageForm($form)
{
self::$update_called = true;
self::$fields = $form->Fields();
}
}

View File

@ -1,92 +0,0 @@
<?php
namespace SilverStripe\Forms\Tests\HTMLEditor;
use SilverStripe;
use SilverStripe\Assets\File;
use SilverStripe\Assets\Image;
use SilverStripe\Assets\Tests\Storage\AssetStoreTest\TestAssetStore;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\HTMLEditor\HTMLEditorField_Toolbar;
use SilverStripe\Forms\Tests\HTMLEditor\HTMLEditorFieldToolbarTest\Toolbar;
class HTMLEditorFieldToolbarTest extends SapphireTest
{
protected static $fixture_file = 'HTMLEditorFieldToolbarTest.yml';
/**
* @return Toolbar
*/
protected function getToolbar()
{
return new Toolbar(null, '/');
}
protected function setUp()
{
parent::setUp();
HTMLEditorField_Toolbar::config()->set('fileurl_scheme_whitelist', array('http'));
HTMLEditorField_Toolbar::config()->set('fileurl_domain_whitelist', array('example.com'));
// Filesystem mock
TestAssetStore::activate(__CLASS__);
// Load up files
/** @var File $file1 */
$file1 = $this->objFromFixture(File::class, 'example_file');
$file1->setFromString(str_repeat('x', 1000), $file1->Name);
$file1->write();
/** @var Image $image1 */
$image1 = $this->objFromFixture(Image::class, 'example_image');
$image1->setFromLocalFile(
__DIR__ . '/HTMLEditorFieldTest/images/example.jpg',
'folder/subfolder/example.jpg'
);
$image1->write();
}
public function testValidLocalReference()
{
/** @var File $exampleFile */
$exampleFile = $this->objFromFixture(File::class, 'example_file');
$expectedUrl = $exampleFile->AbsoluteLink();
HTMLEditorField_Toolbar::config()->set(
'fileurl_domain_whitelist',
[
'example.com',
strtolower(parse_url($expectedUrl, PHP_URL_HOST))
]
);
list(, $url) = $this->getToolbar()->viewfile_getRemoteFileByURL($exampleFile->AbsoluteLink());
$this->assertEquals($expectedUrl, $url);
}
public function testValidScheme()
{
list(, $url) = $this->getToolbar()->viewfile_getRemoteFileByURL('http://example.com/test.pdf');
$this->assertEquals($url, 'http://example.com/test.pdf');
}
public function testInvalidScheme()
{
$this->setExpectedException(HTTPResponse_Exception::class);
$this->getToolbar()->viewfile_getRemoteFileByURL('nosuchscheme://example.com/test.pdf');
}
public function testValidDomain()
{
list(, $url) = $this->getToolbar()->viewfile_getRemoteFileByURL('http://example.com/test.pdf');
$this->assertEquals($url, 'http://example.com/test.pdf');
}
public function testInvalidDomain()
{
$this->setExpectedException(HTTPResponse_Exception::class);
$this->getToolbar()->viewfile_getRemoteFileByURL('http://evil.com/test.pdf');
}
}

View File

@ -1,18 +0,0 @@
SilverStripe\Assets\Folder:
folder1:
Name: folder
Title: folder
folder2:
Name: subfolder
Title: subfolder
Parent: =>SilverStripe\Assets\Folder.folder1
SilverStripe\Assets\File:
example_file:
Name: example.pdf
Parent: =>SilverStripe\Assets\Folder.folder2
SilverStripe\Assets\Image:
example_image:
Name: HTMLEditorFieldTest_example.jpg
Parent: =>SilverStripe\Assets\Folder.folder2

View File

@ -1,18 +0,0 @@
<?php
namespace SilverStripe\Forms\Tests\HTMLEditor\HTMLEditorFieldToolbarTest;
use SilverStripe\Forms\HTMLEditor\HTMLEditorField_Toolbar;
class Toolbar extends HTMLEditorField_Toolbar
{
public function viewfile_getLocalFileByID($id)
{
return parent::viewfile_getLocalFileByID($id);
}
public function viewfile_getRemoteFileByURL($fileUrl)
{
return parent::viewfile_getRemoteFileByURL($fileUrl);
}
}