Webylon 3.2 API Docs
  • Package
  • Class
  • Tree
  • Deprecated
  • Download
Version: current
  • 3.2
  • 3.1

Packages

  • 1c
    • exchange
      • catalog
  • auth
  • Booking
  • building
    • company
  • cart
    • shipping
    • steppedcheckout
  • Catalog
    • monument
  • cms
    • assets
    • batchaction
    • batchactions
    • bulkloading
    • comments
    • content
    • core
    • export
    • newsletter
    • publishers
    • reports
    • security
    • tasks
  • Dashboard
  • DataObjectManager
  • event
  • faq
  • forms
    • actions
    • core
    • fields-basic
    • fields-dataless
    • fields-datetime
    • fields-files
    • fields-formatted
    • fields-formattedinput
    • fields-relational
    • fields-structural
    • transformations
    • validators
  • googlesitemaps
  • guestbook
  • installer
  • newsletter
  • None
  • photo
    • gallery
  • PHP
  • polls
  • recaptcha
  • sapphire
    • api
    • bulkloading
    • control
    • core
    • cron
    • dev
    • email
    • fields-formattedinput
    • filesystem
    • formatters
    • forms
    • i18n
    • integration
    • misc
    • model
    • parsers
    • search
    • security
    • tasks
    • testing
    • tools
    • validation
    • view
    • widgets
  • seo
    • open
      • graph
  • sfDateTimePlugin
  • spamprotection
  • stealth
    • captha
  • subsites
  • userform
    • pagetypes
  • userforms
  • webylon
  • widgets

Classes

  • AjaxUniqueTextField
  • AutocompleteTextField
  • ConfirmedPasswordField
  • CreditCardField
  • CurrencyField
  • CurrencyField_Disabled
  • CurrencyField_Readonly
  • EmailField
  • HtmlEditorConfig
  • HtmlEditorField
  • HtmlEditorField_Readonly
  • HtmlEditorField_Toolbar
  • NumericField
  • PasswordField
  • PhoneNumberField
  • UniqueRestrictedTextField
  • UniqueTextField
  1 <?php
  2 /**
  3  * A TinyMCE-powered WYSIWYG HTML editor field with image and link insertion and tracking capabilities. Editor fields
  4  * are created from <textarea> tags, which are then converted with JavaScript.
  5  *
  6  * @package forms
  7  * @subpackage fields-formattedinput
  8  */
  9 class HtmlEditorField extends TextareaField {
 10     
 11     /**
 12      * Includes the JavaScript neccesary for this field to work using the {@link Requirements} system.
 13      */
 14     public static function include_js() {
 15         Requirements::javascript(MCE_ROOT . 'tiny_mce_src.js');
 16         Requirements::customScript(HtmlEditorConfig::get_active()->generateJS(), 'htmlEditorConfig');
 17     }
 18     
 19     /**
 20      * @see TextareaField::__construct()
 21      */
 22     public function __construct($name, $title = null, $rows = 30, $cols = 20, $value = '', $form = null) {
 23         parent::__construct($name, $title, $rows, $cols, $value, $form);
 24         
 25         //$this->addExtraClass('typography');
 26         //$this->addExtraClass('htmleditor');
 27         
 28         self::include_js();
 29     }
 30     
 31     /**
 32      * @return string
 33      */
 34     function Field() {
 35         // mark up broken links
 36         $value  = new SS_HTMLValue($this->value);
 37         
 38         if($links = $value->getElementsByTagName('a')) foreach($links as $link) {
 39             $matches = array();
 40             
 41             if(preg_match('/\[sitetree_link id=([0-9]+)\]/i', $link->getAttribute('href'), $matches)) {
 42                 if(!DataObject::get_by_id('SiteTree', $matches[1])) {
 43                     $class = $link->getAttribute('class');
 44                     $link->setAttribute('class', ($class ? "$class ss-broken" : 'ss-broken'));
 45                 }
 46             }
 47         }
 48         
 49         return $this->createTag (
 50             'textarea',
 51             array (
 52                 'class'   => 'htmleditor typography' . $this->extraClass(),
 53                 'rows'    => $this->rows,
 54                 'cols'    => $this->cols,
 55                 'style'   => 'width: 97%; height: ' . ($this->rows * 16) . 'px', // prevents horizontal scrollbars
 56                 'tinymce' => 'true',
 57                 'id'      => $this->id(),
 58                 'name'    => $this->name
 59             ),
 60             htmlentities($value->getContent(), ENT_COMPAT, 'UTF-8')
 61         );
 62     }
 63     
 64     public function saveInto($record) {
 65         if($record->escapeTypeForField($this->name) != 'xml') {
 66             throw new Exception (
 67                 'HtmlEditorField->saveInto(): This field should save into a HTMLText or HTMLVarchar field.'
 68             );
 69         }
 70         
 71         $linkedPages = array();
 72         $linkedFiles = array();
 73         
 74         $htmlValue = new SS_HTMLValue($this->value);
 75         
 76         // Populate link tracking for internal links & links to asset files.
 77         if($links = $htmlValue->getElementsByTagName('a')) foreach($links as $link) {
 78             $href = Director::makeRelative($link->getAttribute('href'));
 79             
 80             if($href) {
 81                 if(preg_match('/\[sitetree_link id=([0-9]+)\]/i', $href, $matches)) {
 82                     $ID = $matches[1];
 83                     
 84                     // clear out any broken link classes
 85                     if($class = $link->getAttribute('class')) {
 86                         $link->setAttribute('class', preg_replace('/(^ss-broken|ss-broken$| ss-broken )/', null, $class));
 87                     }
 88                     
 89                     $linkedPages[] = $ID;
 90                     if(!DataObject::get_by_id('SiteTree', $ID))  $record->HasBrokenLink = true;
 91 
 92                 } else if(substr($href, 0, strlen(ASSETS_DIR) + 1) == ASSETS_DIR.'/') {
 93                     $candidateFile = File::find(Convert::raw2sql(urldecode($href)));
 94                     if($candidateFile) {
 95                         $linkedFiles[] = $candidateFile->ID;
 96                     } else {
 97                         $record->HasBrokenFile = true;
 98                     }
 99                 } else if($href == '' || $href[0] == '/') {
100                     $record->HasBrokenLink = true;
101                 }
102         
103                 // see if the link has the class meaning it should be resampled
104                 if (($class = $link->getAttribute('class')) && strpos($class, 'ssImagePopup') !== false) {
105                     $href = preg_replace('/([^\?]*)\?r=[0-9]+$/i', '$1', $href);
106                     // get the size to resample to
107                     if(!$image = File::find($href)) {
108                         continue;
109                     }
110 
111                     $width  = Image::$asset_popup_size;
112                     $height  = Image::$asset_popup_size;
113                     
114                     // Only resize if the original image is actually large enough
115                     if($width && $height && ($width < $image->getWidth() || $height < $image->getHeight())) {
116                         $link->setAttribute('href', $image->SetRatioSize($width, $height)->getRelativePath());
117                     }
118                 }
119             }
120         }
121                         
122         // Resample images, add default attributes and add to assets tracking.
123         if($images = $htmlValue->getElementsByTagName('img')) foreach($images as $img) {
124             // strip any ?r=n data from the src attribute
125             $img->setAttribute('src', preg_replace('/([^\?]*)\?r=[0-9]+$/i', '$1', $img->getAttribute('src')));
126             if(!$image = File::find($path = urldecode(Director::makeRelative($img->getAttribute('src'))))) {
127                 if(substr($path, 0, strlen(ASSETS_DIR) + 1) == ASSETS_DIR . '/') {
128                     $record->HasBrokenFile = true;
129                 }
130                 
131                 continue;
132             }
133             
134             // Resample the images if the width & height have changed.
135             $width  = $img->getAttribute('width');
136             $height = $img->getAttribute('height');
137             
138             if($image){
139                 if($width && $height && ($width != $image->getWidth() || $height != $image->getHeight())) {
140                     //Make sure that the resized image actually returns an image:
141                     $resized=$image->ResizedImage($width, $height);
142                     if($resized)
143                         $img->setAttribute('src', $resized->getRelativePath());
144                 }
145             }
146             
147             // Add default empty title & alt attributes.
148             if(!$img->getAttribute('alt')) $img->setAttribute('alt', '');
149             if(!$img->getAttribute('title')) $img->setAttribute('title', '');
150             
151             //If the src attribute is not set, then we won't add this to the list:
152             if($img->getAttribute('src')){
153                 // Add to the tracked files.
154                 $linkedFiles[] = $image->ID;
155             }
156         }
157         
158         // Save file & link tracking data.
159         if($record->ID && $record->many_many('LinkTracking') && $tracker = $record->LinkTracking()) {
160             $filter = sprintf('"FieldName" = \'%s\' AND "SiteTreeID" = %d', $this->name, $record->ID);
161             DB::query("DELETE FROM \"$tracker->tableName\" WHERE $filter");
162 
163             if($linkedPages) foreach($linkedPages as $item) {
164                 $SQL_fieldName = Convert::raw2sql($this->name);
165                 DB::query("INSERT INTO \"SiteTree_LinkTracking\" (\"SiteTreeID\",\"ChildID\", \"FieldName\")
166                     VALUES ($record->ID, $item, '$SQL_fieldName')");
167             }
168         }
169         
170         // вместо ImageTracking сохраняем трекинг через DataObjectFileTrackingDecorator
171         if ($record->hasMethod('updateFieldTracking')) {
172             $record->updateFieldTracking($this->name, $linkedFiles);
173         }
174         
175         $record->{$this->name} = $htmlValue->getContent();
176     }
177 
178     /**
179      * @return HtmlEditorField_Readonly
180      */
181     public function performReadonlyTransformation() {
182         $field = new HtmlEditorField_Readonly($this->name, $this->title, $this->value);
183         $field->setForm($this->form);
184         $field->dontEscape = true;
185         return $field;
186     }
187     
188 }
189 
190 /**
191  * Readonly version of an {@link HTMLEditorField}.
192  * @package forms
193  * @subpackage fields-formattedinput
194  */
195 class HtmlEditorField_Readonly extends ReadonlyField {
196     function Field() {
197         $valforInput = $this->value ? Convert::raw2att($this->value) : "";
198         return "<span class=\"readonly typography\" id=\"" . $this->id() . "\">" . ( $this->value && $this->value != '<p></p>' ? $this->value : '<i>(not set)</i>' ) . "</span><input type=\"hidden\" name=\"".$this->name."\" value=\"".$valforInput."\" />";
199     }
200     function Type() {
201         return 'htmleditorfield readonly';
202     }
203 }
204 
205 /**
206  * External toolbar for the HtmlEditorField.
207  * This is used by the CMS
208  * @package forms
209  * @subpackage fields-formattedinput
210  */
211 class HtmlEditorField_Toolbar extends RequestHandler {
212     protected $controller, $name;
213     
214     function __construct($controller, $name) {
215         parent::__construct();
216         Requirements::javascript(SAPPHIRE_DIR . "/thirdparty/behaviour/behaviour.js");
217         Requirements::javascript(SAPPHIRE_DIR . "/javascript/tiny_mce_improvements.js");
218         
219         Requirements::javascript(SAPPHIRE_DIR ."/thirdparty/jquery-form/jquery.form.js");
220         Requirements::javascript(SAPPHIRE_DIR ."/javascript/HtmlEditorField.js");
221         
222         $this->controller = $controller;
223         $this->name = $name;
224     }
225 
226     /**
227      * Searches the SiteTree for display in the dropdown
228      *  
229      * @return callback
230      */ 
231     function siteTreeSearchCallback($sourceObject, $labelField, $search) {
232         $search = Convert::raw2sql($search);
233         return DataObject::get($sourceObject, "\"$labelField\" LIKE '%$search%'");
234     }
235     
236     /**
237      * Фильтрует файлы без Title (служебные) !!! вообще говоря - костыль
238      *  
239      * @return boolean
240      */ 
241     function fileTreeFilterCallback($item) {
242         if (!$item->Title) return false;
243         if (substr($item->FileName, 0, strlen(ASSETS_DIR)) != ASSETS_DIR) return false;
244         return true;
245     }
246     
247     /**
248      * Return a {@link Form} instance allowing a user to
249      * add links in the TinyMCE content editor.
250      *  
251      * @return Form
252      */
253     function LinkForm() {
254         $siteTree = new TreeDropdownField('internal', _t('HtmlEditorField.PAGE', "Page"), 'SiteTree', 'ID', 'MenuTitle', true);
255         // mimic the SiteTree::getMenuTitle(), which is bypassed when the search is performed
256         $siteTree->setSearchFunction(array($this, 'siteTreeSearchCallback'));
257         
258         $fileTree = new TreeDropdownField('file', _t('HtmlEditorField.FILE', 'File'), 'File', 'Filename', 'Title', true);
259         $fileTree->setFilterFunction(array($this, 'fileTreeFilterCallback'));
260 
261         $form = new Form(
262             $this->controller,
263             "{$this->name}/LinkForm", 
264             new FieldSet(
265                 new LiteralField('Heading', '<h2><img src="cms/images/closeicon.gif" alt="' . _t('HtmlEditorField.CLOSE', 'close').'" title="' . _t('HtmlEditorField.CLOSE', 'close') . '" />' . _t('HtmlEditorField.LINK', 'Link') . '</h2>'),
266                 new OptionsetField(
267                     'LinkType',
268                     _t('HtmlEditorField.LINKTO', 'Link to'), 
269                     array(
270                         'internal' => _t('HtmlEditorField.LINKINTERNAL', 'Page on the site'),
271                         'external' => _t('HtmlEditorField.LINKEXTERNAL', 'Another website'),
272                         'anchor' => _t('HtmlEditorField.LINKANCHOR', 'Anchor on this page'),
273                         'email' => _t('HtmlEditorField.LINKEMAIL', 'Email address'),
274                         'file' => _t('HtmlEditorField.LINKFILE', 'Download a file'),            
275                     )
276                 ),
277                 $siteTree,
278                 new TextField('external', _t('HtmlEditorField.URL', 'URL'), 'http://'),
279                 new EmailField('email', _t('HtmlEditorField.EMAIL', 'Email address')),
280                 $fileTree,
281                 new TextField('Anchor', _t('HtmlEditorField.ANCHORVALUE', 'Anchor')),
282                 new TextField('LinkText', _t('HtmlEditorField.LINKTEXT', 'Link text')),
283                 new TextField('Description', _t('HtmlEditorField.LINKDESCR', 'Link description')),
284                 new CheckboxField('TargetBlank', _t('HtmlEditorField.LINKOPENNEWWIN', 'Open link in a new window?')),
285                 new HiddenField('Locale', null, $this->controller->Locale)
286             ),
287             new FieldSet(
288                 new FormAction('insert', _t('HtmlEditorField.BUTTONINSERTLINK', 'Insert link')),
289                 new FormAction('remove', _t('HtmlEditorField.BUTTONREMOVELINK', 'Remove link'))
290             )
291         );
292         
293         $form->loadDataFrom($this);
294         
295         $this->extend('updateLinkForm', $form);
296         
297         return $form;
298     }
299 
300     /**
301      * Return a {@link Form} instance allowing a user to
302      * add images to the TinyMCE content editor.
303      *  
304      * @return Form
305      */
306     function ImageForm() {
307         $fields = new FieldSet(
308             new LiteralField('Heading', '<h2><img src="cms/images/closeicon.gif" alt="' . _t('HtmlEditorField.CLOSE', 'close') . '" title="' . _t('HtmlEditorField.CLOSE', 'close') . '" />' . _t('HtmlEditorField.IMAGE', 'Image') . '</h2>'),
309             new TreeDropdownField('FolderID', _t('HtmlEditorField.FOLDER', 'Folder'), 'Folder'),
310             new CompositeField(new FieldSet(
311                 new LiteralField('ShowUpload', '<p class="showUploadField"><a href="#">'. _t('HtmlEditorField.SHOWUPLOADFORM', 'Upload File') .'</a></p>'),
312                 new FileField("Files[0]" , _t('AssetAdmin.CHOOSEFILE','Choose file: ')),
313                 new LiteralField('Response', '<div id="UploadFormResponse"></div>'),
314                 new HiddenField('UploadMode', 'Upload Mode', 'CMSEditor') // used as a hook for doUpload switching
315             )),
316             new TextField('getimagesSearch', _t('HtmlEditorField.SEARCHFILENAME', 'Search by file name')),
317             new ThumbnailStripField('FolderImages', 'FolderID', 'getimages'),
318             new TextField('AltText', _t('HtmlEditorField.IMAGEALTTEXT', 'Alternative text (alt) - shown if image cannot be displayed'), '', 80),
319             new TextField('ImageTitle', _t('HtmlEditorField.IMAGETITLE', 'Title text (tooltip) - for additional information about the image')),
320             new TextField('CaptionText', _t('HtmlEditorField.CAPTIONTEXT', 'Caption text')),
321             new DropdownField(
322                 'CSSClass',
323                 _t('HtmlEditorField.CSSCLASS', 'Alignment / style'),
324                 array(
325                     'left' => _t('HtmlEditorField.CSSCLASSLEFT', 'On the left, with text wrapping around.'),
326                     'leftAlone' => _t('HtmlEditorField.CSSCLASSLEFTALONE', 'On the left, on its own.'),
327                     'right' => _t('HtmlEditorField.CSSCLASSRIGHT', 'On the right, with text wrapping around.'),
328                     'center' => _t('HtmlEditorField.CSSCLASSCENTER', 'Centered, on its own.'),
329                     '' => _t('HtmlEditorField.CSSCLASSNONE', 'No alignment'),
330                 )
331             ),
332             new FieldGroup(_t('HtmlEditorField.IMAGEDIMENSIONS', 'Dimensions'),
333                 new TextField('Width', _t('HtmlEditorField.IMAGEWIDTHPX', 'Width'), 100),
334                 new TextField('Height', " x " . _t('HtmlEditorField.IMAGEHEIGHTPX', 'Height'), 100)
335             ),
336             // note: using a Literal field here because the checkbox field default structure
337             // doesn't play nice in the toolbar layout, and we're only accessing
338             // via JS
339             new FieldGroup(
340                 new LiteralField('PopupLargeImage',
341                     '<div id="PopupLargeImage" class="field checkbox">
342                         <input type="checkbox" id="PopupLargeImage_Checkbox" />
343                         <label class="right" for="PopupLargeImage_Checkbox">'._t('HtmlEditorField.POPUPLARGE', 'Image can be clicked for larger version').'</label>
344                     </div>
345                     '
346                 )           
347             )
348         );
349         
350         $actions = new FieldSet(
351             new FormAction('insertimage', _t('HtmlEditorField.BUTTONINSERTIMAGE', 'Insert image'))
352         );
353         
354         $form = new Form(
355             $this->controller,
356             "{$this->name}/ImageForm",
357             $fields,
358             $actions
359         );
360         
361         $form->disableSecurityToken();
362         $form->loadDataFrom($this);
363         
364         // Allow other people to extend the fields being added to the imageform 
365         $this->extend('updateImageForm', $form);
366         
367         return $form;
368     }
369 
370     function FlashForm() {
371         $form = new Form(
372             $this->controller,
373             "{$this->name}/FlashForm", 
374             new FieldSet(
375                 new LiteralField('Heading', '<h2><img src="cms/images/closeicon.gif" alt="'._t('HtmlEditorField.CLOSE', 'close').'" title="'._t('HtmlEditorField.CLOSE', 'close').'" />'._t('HtmlEditorField.FLASH', 'Flash').'</h2>'),
376                 new TreeDropdownField("FolderID", _t('HtmlEditorField.FOLDER'), "Folder"),
377                 new TextField('getflashSearch', _t('HtmlEditorField.SEARCHFILENAME', 'Search by file name')),
378                 new ThumbnailStripField("Flash", "FolderID", "getflash"),
379                 new FieldGroup(_t('HtmlEditorField.IMAGEDIMENSIONS', "Dimensions"),
380                     new TextField("Width", _t('HtmlEditorField.IMAGEWIDTHPX', "Width"), 100),
381                     new TextField("Height", "x " . _t('HtmlEditorField.IMAGEHEIGHTPX', "Height"), 100)
382                 )
383             ),
384             new FieldSet(
385                 new FormAction("insertflash", _t('HtmlEditorField.BUTTONINSERTFLASH', 'Insert Flash'))
386             )
387         );
388 
389         $form->disableSecurityToken();
390         $form->loadDataFrom($this);
391         $form->disableSecurityToken();
392         
393         $this->extend('updateFlashForm', $form);
394         
395         return $form;
396     }
397 
398     function getfileinfo($request) {
399         $fileName = $request->requestVar('fileName');
400         if ($file = File::find($fileName)) {
401             if ($file->ClassName == 'Folder') return '';
402             return sprintf(_t('HtmlEditorField.FILE_INFO', '%s file, %s'), strtoupper($file->Extension), $file->Size);
403         }
404         return '';
405     }
406 }
407 
408 
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -
Webylon 3.2 API Docs API documentation generated by ApiGen 2.8.0