tags, which are then converted with JavaScript. * * @package forms * @subpackage fields-formattedinput */ class HtmlEditorField extends TextareaField { /** * Use TinyMCE's GZIP compressor * * @config * @var bool */ private static $use_gzip = true; /** * @config * @var bool Should we check the valid_elements (& extended_valid_elements) rules from HtmlEditorConfig server side? */ private static $sanitise_server_side = false; /** * Number of rows * * @var int */ protected $rows = 30; /** * @deprecated since version 4.0 */ public static function include_js() { Deprecation::notice('4.0', 'Use HtmlEditorConfig::require_js() instead'); HtmlEditorConfig::require_js(); } protected $editorConfig = null; /** * Creates a new HTMLEditorField. * @see TextareaField::__construct() * * @param string $name The internal field name, passed to forms. * @param string $title The human-readable field label. * @param mixed $value The value of the field. * @param string $config HTMLEditorConfig identifier to be used. Default to the active one. */ public function __construct($name, $title = null, $value = '', $config = null) { parent::__construct($name, $title, $value); $this->editorConfig = $config ? $config : HtmlEditorConfig::get_active_identifier(); } public function getAttributes() { return array_merge( parent::getAttributes(), array( 'tinymce' => 'true', 'style' => 'width: 97%; height: ' . ($this->rows * 16) . 'px', // prevents horizontal scrollbars 'value' => null, 'data-config' => $this->editorConfig ) ); } public function saveInto(DataObjectInterface $record) { if($record->hasField($this->name) && $record->escapeTypeForField($this->name) != 'xml') { throw new Exception ( 'HtmlEditorField->saveInto(): This field should save into a HTMLText or HTMLVarchar field.' ); } $htmlValue = Injector::inst()->create('HTMLValue', $this->value); // Sanitise if requested if($this->config()->sanitise_server_side) { $santiser = Injector::inst()->create('HtmlEditorSanitiser', HtmlEditorConfig::get_active()); $santiser->sanitise($htmlValue); } // Resample images and add default attributes if($images = $htmlValue->getElementsByTagName('img')) foreach($images as $img) { // strip any ?r=n data from the src attribute $img->setAttribute('src', preg_replace('/([^\?]*)\?r=[0-9]+$/i', '$1', $img->getAttribute('src'))); // Resample the images if the width & height have changed. $fileID = $img->getAttribute('data-fileid'); if($fileID && ($image = File::get()->byID($fileID))) { $width = (int)$img->getAttribute('width'); $height = (int)$img->getAttribute('height'); if($width && $height && ($width != $image->getWidth() || $height != $image->getHeight())) { //Make sure that the resized image actually returns an image: $resized = $image->ResizedImage($width, $height); if($resized) { $img->setAttribute('src', $resized->getURL()); } } } // Add default empty title & alt attributes. if(!$img->getAttribute('alt')) $img->setAttribute('alt', ''); if(!$img->getAttribute('title')) $img->setAttribute('title', ''); // Use this extension point to manipulate images inserted using TinyMCE, e.g. add a CSS class, change default title // $image is the image, $img is the DOM model $this->extend('processImage', $image, $img); } // optionally manipulate the HTML after a TinyMCE edit and prior to a save $this->extend('processHTML', $htmlValue); // Store into record $record->{$this->name} = $htmlValue->getContent(); } /** * @return HtmlEditorField_Readonly */ public function performReadonlyTransformation() { $field = $this->castedCopy('HtmlEditorField_Readonly'); $field->dontEscape = true; return $field; } public function performDisabledTransformation() { return $this->performReadonlyTransformation(); } } /** * Readonly version of an {@link HTMLEditorField}. * @package forms * @subpackage fields-formattedinput */ class HtmlEditorField_Readonly extends ReadonlyField { public function Field($properties = array()) { $valforInput = $this->value ? Convert::raw2att($this->value) : ""; return "id() . "\">" . ( $this->value && $this->value != '

' ? $this->value : '(not set)' ) . "
name."\" value=\"".$valforInput."\" />"; } public function Type() { return 'htmleditorfield readonly'; } } /** * 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()}. * * @package forms * @subpackage fields-formattedinput */ class HtmlEditorField_Toolbar extends RequestHandler { private static $allowed_actions = array( 'LinkForm', 'MediaForm', 'viewfile', 'getanchors' ); /** * @var string */ protected $templateViewFile = 'HtmlEditorField_viewfile'; protected $controller, $name; public function __construct($controller, $name) { parent::__construct(); Requirements::javascript(FRAMEWORK_DIR . "/thirdparty/jquery/jquery.js"); Requirements::javascript(THIRDPARTY_DIR . '/jquery-ui/jquery-ui.js'); Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js'); Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/ssui.core.js'); HtmlEditorConfig::require_js(); Requirements::javascript(FRAMEWORK_DIR ."/javascript/HtmlEditorField.js"); Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css'); $this->controller = $controller; $this->name = $name; } public function forTemplate() { return sprintf( '
', Controller::join_links($this->controller->Link(), $this->name, 'LinkForm', 'forTemplate'), Controller::join_links($this->controller->Link(), $this->name, 'MediaForm', 'forTemplate') ); } /** * Searches the SiteTree for display in the dropdown * * @return callback */ 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. * * @return Form */ public function LinkForm() { $siteTree = TreeDropdownField::create('internal', _t('HtmlEditorField.PAGE', "Page"), 'SiteTree', 'ID', 'MenuTitle', true); // mimic the SiteTree::getMenuTitle(), which is bypassed when the search is performed $siteTree->setSearchFunction(array($this, 'siteTreeSearchCallback')); $numericLabelTmpl = '%d' . '%s'; $form = new Form( $this->controller, "{$this->name}/LinkForm", new FieldList( $headerWrap = new CompositeField( new LiteralField( 'Heading', sprintf('

%s

', _t('HtmlEditorField.LINK', 'Insert Link')) ) ), $contentComposite = new CompositeField( OptionsetField::create( 'LinkType', sprintf($numericLabelTmpl, '1', _t('HtmlEditorField.LINKTO', 'Link to')), array( 'internal' => _t('HtmlEditorField.LINKINTERNAL', 'Page on the site'), 'external' => _t('HtmlEditorField.LINKEXTERNAL', 'Another website'), 'anchor' => _t('HtmlEditorField.LINKANCHOR', 'Anchor on this page'), 'email' => _t('HtmlEditorField.LINKEMAIL', 'Email address'), 'file' => _t('HtmlEditorField.LINKFILE', 'Download a file'), ), 'internal' ), LiteralField::create('Step2', '
' . sprintf($numericLabelTmpl, '2', _t('HtmlEditorField.DETAILS', 'Details')) . '
' ), $siteTree, TextField::create('external', _t('HtmlEditorField.URL', 'URL'), 'http://'), EmailField::create('email', _t('HtmlEditorField.EMAIL', 'Email address')), $fileField = UploadField::create('file', _t('HtmlEditorField.FILE', 'File')), TextField::create('Anchor', _t('HtmlEditorField.ANCHORVALUE', 'Anchor')), TextField::create('Subject', _t('HtmlEditorField.SUBJECT', 'Email subject')), TextField::create('Description', _t('HtmlEditorField.LINKDESCR', 'Link description')), CheckboxField::create('TargetBlank', _t('HtmlEditorField.LINKOPENNEWWIN', 'Open link in a new window?')), HiddenField::create('Locale', null, $this->controller->Locale) ) ), new FieldList() ); $headerWrap->addExtraClass('CompositeField composite cms-content-header nolabel '); $contentComposite->addExtraClass('ss-insert-link content'); $fileField->setAllowedMaxFileNumber(1); $form->unsetValidator(); $form->loadDataFrom($this); $form->addExtraClass('htmleditorfield-form htmleditorfield-linkform cms-mediaform-content'); $this->extend('updateLinkForm', $form); return $form; } /** * 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; } /** * Return a {@link Form} instance allowing a user to * add images and flash objects to the TinyMCE content editor. * * @return Form */ public function MediaForm() { // TODO Handle through GridState within field - currently this state set too late to be useful here (during // request handling) $parentID = $this->getAttachParentID(); $fileFieldConfig = GridFieldConfig::create()->addComponents( new GridFieldFilterHeader(), new GridFieldSortableHeader(), new GridFieldDataColumns(), new GridFieldPaginator(7), // TODO Shouldn't allow delete here, its too confusing with a "remove from editor view" action. // Remove once we can fit the search button in the last actual title column new GridFieldDeleteAction(), new GridFieldDetailForm() ); $fileField = GridField::create('Files', false, null, $fileFieldConfig); $fileField->setList($this->getFiles($parentID)); $fileField->setAttribute('data-selectable', true); $fileField->setAttribute('data-multiselect', true); $columns = $fileField->getConfig()->getComponentByType('GridFieldDataColumns'); $columns->setDisplayFields(array( 'StripThumbnail' => false, 'Title' => _t('File.Title'), 'Created' => singleton('File')->fieldLabel('Created'), )); $columns->setFieldCasting(array( 'Created' => 'SS_Datetime->Nice' )); $fromCMS = new CompositeField( $select = TreeDropdownField::create('ParentID', "", 'Folder') ->addExtraClass('noborder') ->setValue($parentID), $fileField ); $fromCMS->addExtraClass('content ss-uploadfield htmleditorfield-from-cms'); $select->addExtraClass('content-select'); $fromWeb = new CompositeField( $remoteURL = new TextField('RemoteURL', 'http://'), new LiteralField('addURLImage', '') ); $remoteURL->addExtraClass('remoteurl'); $fromWeb->addExtraClass('content ss-uploadfield htmleditorfield-from-web'); Requirements::css(FRAMEWORK_DIR . '/css/AssetUploadField.css'); $computerUploadField = Object::create('UploadField', 'AssetUploadField', ''); $computerUploadField->setConfig('previewMaxWidth', 40); $computerUploadField->setConfig('previewMaxHeight', 30); $computerUploadField->addExtraClass('ss-assetuploadfield htmleditorfield-from-computer'); $computerUploadField->removeExtraClass('ss-uploadfield'); $computerUploadField->setTemplate('HtmlEditorField_UploadField'); $computerUploadField->setFolderName(Config::inst()->get('Upload', 'uploads_folder')); $allFields = new CompositeField( $computerUploadField, $fromWeb, $fromCMS, $editComposite = new CompositeField( new LiteralField('contentEdit', '
') ) ); $allFields->addExtraClass('ss-insert-media'); $headings = new CompositeField( new LiteralField( 'Heading', sprintf('

%s

', _t('HtmlEditorField.INSERTMEDIA', 'Insert media from')). sprintf('

%s

', _t('HtmlEditorField.UpdateMEDIA', 'Update media')) ) ); $headings->addExtraClass('cms-content-header'); $editComposite->addExtraClass('ss-assetuploadfield'); $fields = new FieldList( $headings, $allFields ); $form = new Form( $this->controller, "{$this->name}/MediaForm", $fields, new FieldList() ); $form->unsetValidator(); $form->disableSecurityToken(); $form->loadDataFrom($this); $form->addExtraClass('htmleditorfield-form htmleditorfield-mediaform cms-dialog-content'); // TODO Re-enable once we remove $.metadata dependency which currently breaks the JS due to $.ui.widget // $form->setAttribute('data-urlViewfile', $this->controller->Link($this->name)); // Allow other people to extend the fields being added to the imageform $this->extend('updateMediaForm', $form); return $form; } /** * View of a single file, either on the filesystem or on the web. * * @param SS_HTTPRequest $request * @return string */ public function viewfile($request) { // TODO Would be cleaner to consistently pass URL for both local and remote files, // but GridField doesn't allow for this kind of metadata customization at the moment. $file = null; if($url = $request->getVar('FileURL')) { // URLS should be used for remote resources (not local assets) $url = Director::absoluteURL($url); } elseif($id = $request->getVar('ID')) { // Use local dataobject $file = DataObject::get_by_id('File', $id); if(!$file) { throw new InvalidArgumentException("File could not be found"); } $url = $file->getURL(); if(!$url) { return $this->httpError(404, 'File not found'); } } else { throw new LogicException('Need either "ID" or "FileURL" parameter to identify the file'); } // Instanciate file wrapper and get fields based on its type // Check if appCategory is an image and exists on the local system, otherwise use oEmbed to refference a // remote image $fileCategory = File::get_app_category(File::get_file_extension($url)); 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 new InvalidArgumentException( "Oembed is only compatible with remote files" ); } // Other files should fallback to oembed $fileWrapper = new HtmlEditorField_Embed($url, $file); break; } // Render fields and return $fields = $this->getFieldsForFile($url, $fileWrapper); return $fileWrapper->customise(array( 'Fields' => $fields, ))->renderWith($this->templateViewFile); } /** * Find all anchors available on the given page. * * @return array */ public function getanchors() { $id = (int)$this->getRequest()->getVar('PageID'); $anchors = array(); if (($page = Page::get()->byID($id)) && !empty($page)) { if (!$page->canView()) { throw new SS_HTTPResponse_Exception( _t( 'HtmlEditorField.ANCHORSCANNOTACCESSPAGE', 'You are not permitted to access the content of the target page.' ), 403 ); } // Similar to the regex found in HtmlEditorField.js / getAnchors method. if (preg_match_all("/name=\"([^\"]+?)\"|name='([^']+?)'/im", $page->Content, $matches)) { $anchors = $matches[1]; } } else { throw new SS_HTTPResponse_Exception( _t('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; } } /** * 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 * @package forms * @subpackage fields-formattedinput */ 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('HtmlEditorField.CAPTIONTEXT', 'Caption text')), DropdownField::create( 'CSSClass', _t('HtmlEditorField.CSSCLASS', 'Alignment / style'), array( 'leftAlone' => _t('HtmlEditorField.CSSCLASSLEFTALONE', 'On the left, on its own.'), 'center' => _t('HtmlEditorField.CSSCLASSCENTER', 'Centered, on its own.'), 'left' => _t('HtmlEditorField.CSSCLASSLEFT', 'On the left, with text wrapping around.'), 'right' => _t('HtmlEditorField.CSSCLASSRIGHT', 'On the right, with text wrapping around.') ) ), FieldGroup::create(_t('HtmlEditorField.IMAGEDIMENSIONS', 'Dimensions'), TextField::create( 'Width', _t('HtmlEditorField.IMAGEWIDTHPX', 'Width'), $this->getInsertWidth() )->setMaxLength(5), TextField::create( 'Height', " x " . _t('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('AssetTableField.TYPE','File type'), $this->getFileType()), ReadonlyField::create( 'ClickableURL', _t('AssetTableField.URL','URL'), $this->getExternalLink() )->setDontEscape(true) ); // Get file size if($this->getSize()) { $fields->insertAfter( 'FileType', ReadonlyField::create("Size", _t('AssetTableField.SIZE','File size'), $this->getSize()) ); } // Get modified details of local record if($this->getFile()) { $fields->push(new DateField_Disabled( "Created", _t('AssetTableField.CREATED', 'First uploaded'), $this->getFile()->Created )); $fields->push(new DateField_Disabled( "LastEdited", _t('AssetTableField.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; } } /** * 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( "%s\n", $thumbnailURL, $fileName ); } /** * HTML Content for external link * * @return string */ public function getExternalLink() { $title = $this->file ? $this->file->getTitle() : $this->getName(); return sprintf( '%1$s', 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 = $this->config()->media_preview_width; $height = $this->config()->media_preview_height; return $this->file->ThumbnailURL($width, $height); } } /** * 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 $this->config()->insert_height; } /** * Get width of this item * * @return type */ public function getWidth() { if($this->file) { $width = $this->file->getWidth(); if($width) { return $width; } } return $this->config()->insert_width; } /** * Provide an initial width for inserted media, restricted based on $embed_width * * @return int */ public function getInsertWidth() { $width = $this->getWidth(); $maxWidth = $this->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 = $this->config()->insert_width; return ($width <= $maxWidth) ? $height : round($height*($maxWidth/$width)); } } /** * Encapsulation of an oembed tag, linking to an external media source. * * @see Oembed * @package forms * @subpackage fields-formattedinput */ class HtmlEditorField_Embed extends HtmlEditorField_File { private static $casting = array( 'Type' => 'Varchar', 'Info' => 'Varchar' ); /** * Oembed result * * @var Oembed_Result */ protected $oembed; public function __construct($url, File $file = null) { parent::__construct($url, $file); $this->oembed = Oembed::get_oembed_from_url($url); if(!$this->oembed) { $controller = Controller::curr(); $response = $controller->getResponse(); $response->addHeader('X-Status', rawurlencode(_t( '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 SS_HTTPResponse_Exception($response); } } /** * Get file-edit fields for this filed * * @return FieldList */ public function getFields() { $fields = parent::getFields(); if($this->Type === 'photo') { $fields->insertBefore('CaptionText', new TextField( 'AltText', _t('HtmlEditorField.IMAGEALTTEXT', 'Alternative text (alt) - shown if image can\'t be displayed'), $this->Title, 80 )); $fields->insertBefore('CaptionText', new TextField( 'Title', _t('HtmlEditorField.IMAGETITLE', 'Title text (tooltip) - for additional information about the image') )); } return $fields; } /** * Get width of this oembed * * @return int */ public function getWidth() { return $this->oembed->Width ?: 100; } /** * Get height of this oembed * * @return int */ public function getHeight() { return $this->oembed->Height ?: 100; } public function getPreviewURL() { // Use thumbnail url if(!empty($this->oembed->thumbnail_url)) { return $this->oembed->thumbnail_url; } // Use direct image type if($this->getType() == 'photo' && !empty($this->Oembed->url)) { return $this->Oembed->url; } // Default media return FRAMEWORK_DIR . '/images/default_media.png'; } public function getName() { if(isset($this->oembed->title)) { return $this->oembed->title; } else { return parent::getName(); } } /** * Get OEmbed type * * @return string */ public function getType() { return $this->oembed->type; } public function getFileType() { return $this->getType() ?: parent::getFileType(); } /** * @return Oembed_Result */ public function getOembed() { return $this->oembed; } public function appCategory() { return 'embed'; } /** * Info for this oembed * * @return string */ public function getInfo() { return $this->oembed->info; } } /** * Encapsulation of an image tag, linking to an image either internal or external to the site. * * @package forms * @subpackage fields-formattedinput */ 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('HtmlEditorField.IMAGEALT', 'Alternative text (alt)'), $this->Title, 80 )->setDescription( _t('HtmlEditorField.IMAGEALTTEXTDESC', 'Shown to screen readers or if image can\'t be displayed') ) ); // Tooltip $fields->insertAfter( 'AltText', TextField::create( 'Title', _t('HtmlEditorField.IMAGETITLETEXT', 'Title text (tooltip)') )->setDescription( _t('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('AssetTableField.WIDTH','Width'), $width ) ); $fields->insertAfter( 'OriginalWidth', ReadonlyField::create( "OriginalHeight", _t('AssetTableField.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; } } } /** * 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; } } } 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); } parent::getSize(); } /** * Provide an initial width for inserted image, restricted based on $embed_width * * @return int */ public function getInsertWidth() { $width = $this->getWidth(); $maxWidth = $this->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 = $this->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; } } /** * Generate flash file embed */ class HtmlEditorField_Flash extends HtmlEditorField_File { public function getFields() { $fields = parent::getFields(); $fields->removeByName('CaptionText', true); return $fields; } }