mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
FEATURE: Allow adding media to the CMS using oEmbed
This commit is contained in:
parent
b325fea709
commit
bb29ff3611
@ -32,6 +32,7 @@ Object::useCustomClass('Datetime', 'SS_Datetime', true);
|
|||||||
define('MCE_ROOT', FRAMEWORK_DIR . '/thirdparty/tinymce/');
|
define('MCE_ROOT', FRAMEWORK_DIR . '/thirdparty/tinymce/');
|
||||||
|
|
||||||
ShortcodeParser::get('default')->register('file_link', array('File', 'link_shortcode_handler'));
|
ShortcodeParser::get('default')->register('file_link', array('File', 'link_shortcode_handler'));
|
||||||
|
ShortcodeParser::get('default')->register('embed', array('Oembed', 'handle_shortcode'));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The secret key that needs to be sent along with pings to /Email_BounceHandler
|
* The secret key that needs to be sent along with pings to /Email_BounceHandler
|
||||||
|
22
_config/Oembed.yml
Normal file
22
_config/Oembed.yml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
name: Oembed
|
||||||
|
---
|
||||||
|
Oembed:
|
||||||
|
providers:
|
||||||
|
'http://*.youtube.com/watch*':
|
||||||
|
'http://www.youtube.com/oembed/'
|
||||||
|
'http://*.flickr.com/*':
|
||||||
|
'http://www.flickr.com/services/oembed/'
|
||||||
|
'http://*.viddler.com/*':
|
||||||
|
'http://lab.viddler.com/services/oembed/'
|
||||||
|
'http://*.revision3.com/*':
|
||||||
|
'http://revision3.com/api/oembed/'
|
||||||
|
'http://*.hulu.com/watch/*':
|
||||||
|
'http://www.hulu.com/api/oembed.json'
|
||||||
|
'http://*.vimeo.com/*':
|
||||||
|
'http://www.vimeo.com/api/oembed.json'
|
||||||
|
'https://twitter.com/*':
|
||||||
|
'https://api.twitter.com/1/statuses/oembed.json'
|
||||||
|
'http://twitter.com/*':
|
||||||
|
'https://api.twitter.com/1/statuses/oembed.json'
|
||||||
|
autodiscover:
|
||||||
|
true
|
@ -20,7 +20,7 @@ HtmlEditorConfig::get('cms')->enablePlugins('media', 'fullscreen');
|
|||||||
HtmlEditorConfig::get('cms')->enablePlugins(array('ssbuttons' => sprintf('../../../%s/tinymce_ssbuttons/editor_plugin_src.js', THIRDPARTY_DIR)));
|
HtmlEditorConfig::get('cms')->enablePlugins(array('ssbuttons' => sprintf('../../../%s/tinymce_ssbuttons/editor_plugin_src.js', THIRDPARTY_DIR)));
|
||||||
|
|
||||||
HtmlEditorConfig::get('cms')->insertButtonsBefore('formatselect', 'styleselect');
|
HtmlEditorConfig::get('cms')->insertButtonsBefore('formatselect', 'styleselect');
|
||||||
HtmlEditorConfig::get('cms')->addButtonsToLine(2, 'ssimage', 'ssflash', 'sslink', 'unlink', 'anchor', 'separator','code', 'fullscreen', 'separator');
|
HtmlEditorConfig::get('cms')->addButtonsToLine(2, 'ssmedia', 'ssflash', 'sslink', 'unlink', 'anchor', 'separator','code', 'fullscreen', 'separator');
|
||||||
|
|
||||||
HtmlEditorConfig::get('cms')->removeButtons('tablecontrols');
|
HtmlEditorConfig::get('cms')->removeButtons('tablecontrols');
|
||||||
HtmlEditorConfig::get('cms')->addButtonsToLine(3, 'tablecontrols');
|
HtmlEditorConfig::get('cms')->addButtonsToLine(3, 'tablecontrols');
|
||||||
|
@ -111,8 +111,8 @@ so this is considered advanced usage of the field.
|
|||||||
:::php
|
:::php
|
||||||
// File: mysite/_config.php
|
// File: mysite/_config.php
|
||||||
HtmlEditorConfig::get('cms')->disablePlugins('ssbuttons');
|
HtmlEditorConfig::get('cms')->disablePlugins('ssbuttons');
|
||||||
HtmlEditorConfig::get('cms')->removeButtons('sslink', 'ssimage');
|
HtmlEditorConfig::get('cms')->removeButtons('sslink', 'ssmedia');
|
||||||
HtmlEditorConfig::get('cms')->addButtonsToLine(2, 'link', 'image');
|
HtmlEditorConfig::get('cms')->addButtonsToLine(2, 'link', 'media');
|
||||||
|
|
||||||
### Developing a wrapper to use a different WYSIWYG editors with HTMLEditorField
|
### Developing a wrapper to use a different WYSIWYG editors with HTMLEditorField
|
||||||
|
|
||||||
@ -126,4 +126,4 @@ or start your own configuration.
|
|||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
* [Howto: Extend the CMS Interface](../howto/extend-cms-interface)
|
* [Howto: Extend the CMS Interface](../howto/extend-cms-interface)
|
||||||
|
@ -391,6 +391,13 @@ class HtmlEditorField_Toolbar extends RequestHandler {
|
|||||||
$fromCMS->addExtraClass('content');
|
$fromCMS->addExtraClass('content');
|
||||||
$selectComposite->addExtraClass('content-select');
|
$selectComposite->addExtraClass('content-select');
|
||||||
|
|
||||||
|
$fromWeb = new CompositeField(
|
||||||
|
new LiteralField('headerURL', '<h4 class="field header-url">' . sprintf($numericLabelTmpl, '1', _t('HtmlEditorField.ADDURL', 'Add URL')) . '</h4>'),
|
||||||
|
$remoteURL = new TextField('RemoteURL', ''),
|
||||||
|
new LiteralField('addURLImage', '<img class="field add-url" src="' . CMS_DIR . '/images/add.gif" width="16" height="16" />')
|
||||||
|
);
|
||||||
|
$remoteURL->addExtraClass('remoteurl');
|
||||||
|
|
||||||
Requirements::css(FRAMEWORK_DIR . '/css/AssetUploadField.css');
|
Requirements::css(FRAMEWORK_DIR . '/css/AssetUploadField.css');
|
||||||
$computerUploadField = Object::create('UploadField', 'AssetUploadField', '');
|
$computerUploadField = Object::create('UploadField', 'AssetUploadField', '');
|
||||||
$computerUploadField->setConfig('previewMaxWidth', 40);
|
$computerUploadField->setConfig('previewMaxWidth', 40);
|
||||||
@ -401,11 +408,15 @@ class HtmlEditorField_Toolbar extends RequestHandler {
|
|||||||
$computerUploadField->setFolderName(Upload::$uploads_folder);
|
$computerUploadField->setFolderName(Upload::$uploads_folder);
|
||||||
|
|
||||||
$tabSet = new TabSet(
|
$tabSet = new TabSet(
|
||||||
"MediaFormInsertImageTabs",
|
"MediaFormInsertMediaTabs",
|
||||||
new Tab(
|
new Tab(
|
||||||
_t('HtmlEditorField.FROMCOMPUTER','From your computer'),
|
_t('HtmlEditorField.FROMCOMPUTER','From your computer'),
|
||||||
$computerUploadField
|
$computerUploadField
|
||||||
),
|
),
|
||||||
|
new Tab(
|
||||||
|
_t('HtmlEditorField.FROMWEB', 'From the web'),
|
||||||
|
$fromWeb
|
||||||
|
),
|
||||||
new Tab(
|
new Tab(
|
||||||
_t('HtmlEditorField.FROMCMS','From the CMS'),
|
_t('HtmlEditorField.FROMCMS','From the CMS'),
|
||||||
$fromCMS
|
$fromCMS
|
||||||
@ -423,19 +434,19 @@ class HtmlEditorField_Toolbar extends RequestHandler {
|
|||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
new LiteralField(
|
new LiteralField(
|
||||||
'Heading',
|
'Heading',
|
||||||
sprintf('<h3 class="htmleditorfield-mediaform-heading insert">%s</h3>', _t('HtmlEditorField.INSERTIMAGE', 'Insert Image')).
|
sprintf('<h3 class="htmleditorfield-mediaform-heading insert">%s</h3>', _t('HtmlEditorField.INSERTMEDIA', 'Insert Media')).
|
||||||
sprintf('<h3 class="htmleditorfield-mediaform-heading update">%s</h3>', _t('HtmlEditorField.UpdateIMAGE', 'Update Image'))
|
sprintf('<h3 class="htmleditorfield-mediaform-heading update">%s</h3>', _t('HtmlEditorField.UpdateMEDIA', 'Update Media'))
|
||||||
),
|
),
|
||||||
$allFields
|
$allFields
|
||||||
);
|
);
|
||||||
|
|
||||||
$actions = new FieldList(
|
$actions = new FieldList(
|
||||||
FormAction::create('insertimage', _t('HtmlEditorField.BUTTONINSERT', 'Insert'))
|
FormAction::create('insertmedia', _t('HtmlEditorField.BUTTONINSERT', 'Insert'))
|
||||||
->addExtraClass('ss-ui-action-constructive image-insert')
|
->addExtraClass('ss-ui-action-constructive media-insert')
|
||||||
->setAttribute('data-icon', 'accept')
|
->setAttribute('data-icon', 'accept')
|
||||||
->setUseButtonTag(true),
|
->setUseButtonTag(true),
|
||||||
FormAction::create('insertimage', _t('HtmlEditorField.BUTTONUpdate', 'Update'))
|
FormAction::create('insertmedia', _t('HtmlEditorField.BUTTONUpdate', 'Update'))
|
||||||
->addExtraClass('ss-ui-action-constructive image-update')
|
->addExtraClass('ss-ui-action-constructive media-update')
|
||||||
->setAttribute('data-icon', 'accept')
|
->setAttribute('data-icon', 'accept')
|
||||||
->setUseButtonTag(true)
|
->setUseButtonTag(true)
|
||||||
);
|
);
|
||||||
@ -493,6 +504,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
|
|||||||
// Instanciate file wrapper and get fields based on its type
|
// Instanciate file wrapper and get fields based on its type
|
||||||
if($file && $file->appCategory() == 'image') {
|
if($file && $file->appCategory() == 'image') {
|
||||||
$fileWrapper = new HtmlEditorField_Image($url, $file);
|
$fileWrapper = new HtmlEditorField_Image($url, $file);
|
||||||
|
} elseif(!Director::is_site_url($url)) {
|
||||||
|
$fileWrapper = new HtmlEditorField_Embed($url, $file);
|
||||||
} else {
|
} else {
|
||||||
$fileWrapper = new HtmlEditorField_File($url, $file);
|
$fileWrapper = new HtmlEditorField_File($url, $file);
|
||||||
}
|
}
|
||||||
@ -516,7 +529,9 @@ class HtmlEditorField_Toolbar extends RequestHandler {
|
|||||||
protected function getFieldsForFile($url, $file) {
|
protected function getFieldsForFile($url, $file) {
|
||||||
$fields = $this->extend('getFieldsForFile', $url, $file);
|
$fields = $this->extend('getFieldsForFile', $url, $file);
|
||||||
if(!$fields) {
|
if(!$fields) {
|
||||||
if($file->Extension == 'swf') {
|
if($file instanceof HtmlEditorField_Embed) {
|
||||||
|
$fields = $this->getFieldsForOembed($url, $file);
|
||||||
|
} elseif($file->Extension == 'swf') {
|
||||||
$fields = $this->getFieldsForFlash($url, $file);
|
$fields = $this->getFieldsForFlash($url, $file);
|
||||||
} else {
|
} else {
|
||||||
$fields = $this->getFieldsForImage($url, $file);
|
$fields = $this->getFieldsForImage($url, $file);
|
||||||
@ -529,6 +544,75 @@ class HtmlEditorField_Toolbar extends RequestHandler {
|
|||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return FieldList
|
||||||
|
*/
|
||||||
|
protected function getFieldsForOembed($url, $file) {
|
||||||
|
if(isset($file->Oembed->thumbnail_url)) {
|
||||||
|
$thumbnailURL = $file->Oembed->thumbnail_url;
|
||||||
|
} elseif($file->Type == 'photo') {
|
||||||
|
$thumbnailURL = $file->Oembed->url;
|
||||||
|
} else {
|
||||||
|
$thumbnailURL = $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
$previewField = new LiteralField("ImageFull",
|
||||||
|
"<img id='thumbnailImage' class='thumbnail-preview' src='{$thumbnailURL}?r=" . rand(1,100000) . "' alt='{$file->Name}' />\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
$fields = new FieldList(
|
||||||
|
$filePreview = CompositeField::create(
|
||||||
|
CompositeField::create(
|
||||||
|
$previewField
|
||||||
|
)->setName("FilePreviewImage")->addExtraClass('cms-file-info-preview'),
|
||||||
|
CompositeField::create(
|
||||||
|
CompositeField::create(
|
||||||
|
new ReadonlyField("FileType", _t('AssetTableField.TYPE','File type') . ':', $file->Type),
|
||||||
|
$urlField = new ReadonlyField('ClickableURL', _t('AssetTableField.URL','URL'),
|
||||||
|
sprintf('<a href="%s" target="_blank">%s</a>', $url, $url)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)->setName("FilePreviewData")->addExtraClass('cms-file-info-data')
|
||||||
|
)->setName("FilePreview")->addExtraClass('cms-file-info'),
|
||||||
|
new TextField('CaptionText', _t('HtmlEditorField.CAPTIONTEXT', 'Caption text')),
|
||||||
|
new DropdownField(
|
||||||
|
'CSSClass',
|
||||||
|
_t('HtmlEditorField.CSSCLASS', 'Alignment / style'),
|
||||||
|
array(
|
||||||
|
'left' => _t('HtmlEditorField.CSSCLASSLEFT', 'On the left, with text wrapping around.'),
|
||||||
|
'leftAlone' => _t('HtmlEditorField.CSSCLASSLEFTALONE', 'On the left, on its own.'),
|
||||||
|
'right' => _t('HtmlEditorField.CSSCLASSRIGHT', 'On the right, with text wrapping around.'),
|
||||||
|
'center' => _t('HtmlEditorField.CSSCLASSCENTER', 'Centered, on its own.'),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
$dimensionsField = new FieldGroup(_t('HtmlEditorField.IMAGEDIMENSIONS', 'Dimensions'),
|
||||||
|
$widthField = new TextField('Width', _t('HtmlEditorField.IMAGEWIDTHPX', 'Width'), $file->Width),
|
||||||
|
$heightField = new TextField('Height', " x " . _t('HtmlEditorField.IMAGEHEIGHTPX', 'Height'), $file->Height)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$urlField->dontEscape = true;
|
||||||
|
$dimensionsField->addExtraClass('dimensions');
|
||||||
|
$widthField->setMaxLength(5);
|
||||||
|
$heightField->setMaxLength(5);
|
||||||
|
|
||||||
|
if($file->Type == 'photo') {
|
||||||
|
$filePreview->FieldList()->insertBefore(new TextField(
|
||||||
|
'AltText',
|
||||||
|
_t('HtmlEditorField.IMAGEALTTEXT', 'Alternative text (alt) - shown if image cannot be displayed'),
|
||||||
|
$file->Title,
|
||||||
|
80
|
||||||
|
), 'CaptionText');
|
||||||
|
$filePreview->FieldList()->insertBefore(new TextField(
|
||||||
|
'Title',
|
||||||
|
_t('HtmlEditorField.IMAGETITLE', 'Title text (tooltip) - for additional information about the image')
|
||||||
|
), 'CaptionText');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->extend('updateFieldsForImage', $fields, $url, $file);
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return FieldList
|
* @return FieldList
|
||||||
*/
|
*/
|
||||||
@ -552,8 +636,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
|
|||||||
* @return FieldList
|
* @return FieldList
|
||||||
*/
|
*/
|
||||||
protected function getFieldsForImage($url, $file) {
|
protected function getFieldsForImage($url, $file) {
|
||||||
if($file instanceof Image) {
|
if($file->File instanceof Image) {
|
||||||
$formattedImage = $file->FormattedImage('SetWidth', Image::$asset_preview_width);
|
$formattedImage = $file->File->generateFormattedImage('SetWidth', Image::$asset_preview_width);
|
||||||
$thumbnailURL = $formattedImage ? $formattedImage->URL : $url;
|
$thumbnailURL = $formattedImage ? $formattedImage->URL : $url;
|
||||||
} else {
|
} else {
|
||||||
$thumbnailURL = $url;
|
$thumbnailURL = $url;
|
||||||
@ -719,6 +803,52 @@ class HtmlEditorField_File extends ViewableData {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class HtmlEditorField_Embed extends HtmlEditorField_File {
|
||||||
|
protected $oembed;
|
||||||
|
|
||||||
|
public function __construct($url, $file = null) {
|
||||||
|
parent::__construct($url, $file);
|
||||||
|
$this->oembed = Oembed::get_oembed_from_url($url);
|
||||||
|
if(!$this->oembed) {
|
||||||
|
return Controller::curr()->httpError(404, 'The URL ' . $url . ' could not be turned into a media resource.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWidth() {
|
||||||
|
return $this->oembed->Width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeight() {
|
||||||
|
return $this->oembed->Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPreview() {
|
||||||
|
if(isset($this->oembed->thumbnail_url)) {
|
||||||
|
return sprintf('<img src="%s" />', $this->oembed->thumbnail_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName() {
|
||||||
|
if(isset($this->oembed->title)) {
|
||||||
|
return $this->oembed->title;
|
||||||
|
} else {
|
||||||
|
return parent::getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType() {
|
||||||
|
return $this->oembed->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOembed() {
|
||||||
|
return $this->oembed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function appCategory() {
|
||||||
|
return 'embed';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class HtmlEditorField_Image extends HtmlEditorField_File {
|
class HtmlEditorField_Image extends HtmlEditorField_File {
|
||||||
|
|
||||||
protected $width;
|
protected $width;
|
||||||
|
@ -683,7 +683,7 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
var self = this, ed = this.getEditor(), node = $(ed.getSelectedNode());
|
var self = this, ed = this.getEditor(), node = $(ed.getSelectedNode());
|
||||||
// TODO Depends on managed mime type
|
// TODO Depends on managed mime type
|
||||||
if(node.is('img')) {
|
if(node.is('img')) {
|
||||||
this.showFileView(node.attr('src'), function() {
|
this.showFileView(node.data('url') || node.attr('src'), function() {
|
||||||
$(this).updateFromNode(node);
|
$(this).updateFromNode(node);
|
||||||
self.toggleCloseButton();
|
self.toggleCloseButton();
|
||||||
self.redraw();
|
self.redraw();
|
||||||
@ -711,9 +711,9 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
|
|
||||||
var updateExisting = Boolean(this.find('.ss-htmleditorfield-file').length);
|
var updateExisting = Boolean(this.find('.ss-htmleditorfield-file').length);
|
||||||
this.find('.htmleditorfield-mediaform-heading.insert')[updateExisting ? 'hide' : 'show']();
|
this.find('.htmleditorfield-mediaform-heading.insert')[updateExisting ? 'hide' : 'show']();
|
||||||
this.find('.Actions .image-insert')[updateExisting ? 'hide' : 'show']();
|
this.find('.Actions .media-insert')[updateExisting ? 'hide' : 'show']();
|
||||||
this.find('.htmleditorfield-mediaform-heading.update')[updateExisting ? 'show' : 'hide']();
|
this.find('.htmleditorfield-mediaform-heading.update')[updateExisting ? 'show' : 'hide']();
|
||||||
this.find('.Actions .image-update')[updateExisting ? 'show' : 'hide']();
|
this.find('.Actions .media-update')[updateExisting ? 'show' : 'hide']();
|
||||||
},
|
},
|
||||||
resetFields: function() {
|
resetFields: function() {
|
||||||
var ed = this.getEditor(), node = $(ed.getSelectedNode());
|
var ed = this.getEditor(), node = $(ed.getSelectedNode());
|
||||||
@ -796,6 +796,21 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the second step after adding a URL
|
||||||
|
*/
|
||||||
|
$('form.htmleditorfield-form.htmleditorfield-mediaform img.add-url').entwine({
|
||||||
|
onclick: function(e) {
|
||||||
|
var form = this.closest('form');
|
||||||
|
|
||||||
|
var urlField = this.closest('.CompositeField').find('input.remoteurl');
|
||||||
|
|
||||||
|
form.showFileView(urlField.val());
|
||||||
|
form.redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a single selected file, together with a set of form fields to edit its properties.
|
* Represents a single selected file, together with a set of form fields to edit its properties.
|
||||||
* Overload this based on the media type to determine how the HTML should be created.
|
* Overload this based on the media type to determine how the HTML should be created.
|
||||||
@ -985,6 +1000,60 @@ ss.editorWrappers['default'] = ss.editorWrappers.tinyMCE;
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert an oembed object tag into the content.
|
||||||
|
* Requires the 'media' plugin for serialization of tags into <img> placeholders.
|
||||||
|
*/
|
||||||
|
$('form.htmleditorfield-mediaform .ss-htmleditorfield-file.embed').entwine({
|
||||||
|
getAttributes: function() {
|
||||||
|
var width = this.find(':input[name=Width]').val(),
|
||||||
|
height = this.find(':input[name=Height]').val();
|
||||||
|
return {
|
||||||
|
'src' : this.find('.thumbnail-preview').attr('src'),
|
||||||
|
'width' : width ? parseInt(width, 10) : null,
|
||||||
|
'height' : height ? parseInt(height, 10) : null,
|
||||||
|
'class' : this.find(':input[name=CSSClass]').val()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getExtraData: function() {
|
||||||
|
var width = this.find(':input[name=Width]').val(),
|
||||||
|
height = this.find(':input[name=Height]').val();
|
||||||
|
return {
|
||||||
|
'CaptionText': this.find(':input[name=CaptionText]').val(),
|
||||||
|
'Url': this.find(':input[name=URL]').val(),
|
||||||
|
'thumbnail': this.find('.thumbnail-preview').attr('src'),
|
||||||
|
'width' : width ? parseInt(width, 10) : null,
|
||||||
|
'height' : height ? parseInt(height, 10) : null,
|
||||||
|
'cssclass': this.find(':input[name=CSSClass]').val()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getHTML: function() {
|
||||||
|
var el,
|
||||||
|
attrs = this.getAttributes(),
|
||||||
|
extraData = this.getExtraData(),
|
||||||
|
// imgEl = $('<img id="_ss_tmp_img" />');
|
||||||
|
imgEl = $('<img />').attr(attrs).addClass('ss-htmleditorfield-file embed');
|
||||||
|
|
||||||
|
$.each(extraData, function (key, value) {
|
||||||
|
imgEl.attr('data-' + key, value)
|
||||||
|
});
|
||||||
|
|
||||||
|
if(extraData.CaptionText) {
|
||||||
|
el = $('<div style="width: ' + attrs['width'] + 'px;" class="captionImage ' + attrs['class'] + '"><p class="caption">' + extraData.CaptionText + '</p></div>').prepend(imgEl);
|
||||||
|
} else {
|
||||||
|
el = imgEl;
|
||||||
|
}
|
||||||
|
return $('<div />').append(el).html(); // Little hack to get outerHTML string
|
||||||
|
},
|
||||||
|
updateFromNode: function(node) {
|
||||||
|
this.find(':input[name=Width]').val(node.width());
|
||||||
|
this.find(':input[name=Height]').val(node.height());
|
||||||
|
this.find(':input[name=Title]').val(node.attr('title'));
|
||||||
|
this.find(':input[name=CSSClass]').val(node.data('cssclass'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$('form.htmleditorfield-mediaform .ss-htmleditorfield-file .dimensions :input').entwine({
|
$('form.htmleditorfield-mediaform .ss-htmleditorfield-file .dimensions :input').entwine({
|
||||||
OrigVal: null,
|
OrigVal: null,
|
||||||
onmatch: function () {
|
onmatch: function () {
|
||||||
|
@ -255,7 +255,7 @@ en:
|
|||||||
IMAGEHEIGHTPX: Height
|
IMAGEHEIGHTPX: Height
|
||||||
IMAGETITLE: 'Title text (tooltip) - for additional information about the image'
|
IMAGETITLE: 'Title text (tooltip) - for additional information about the image'
|
||||||
IMAGEWIDTHPX: Width
|
IMAGEWIDTHPX: Width
|
||||||
INSERTIMAGE: 'Insert Image'
|
INSERTMEDIA: 'Insert Media'
|
||||||
LINK: 'Insert Link'
|
LINK: 'Insert Link'
|
||||||
LINKANCHOR: 'Anchor on this page'
|
LINKANCHOR: 'Anchor on this page'
|
||||||
LINKDESCR: 'Link description'
|
LINKDESCR: 'Link description'
|
||||||
|
192
oembed/Oembed.php
Normal file
192
oembed/Oembed.php
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Oembed {
|
||||||
|
public static function get_autodiscover() {
|
||||||
|
return Config::inst()->get('Oembed', 'autodiscover');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_providers() {
|
||||||
|
return Config::inst()->get('Oembed', 'providers');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function match_url($url) {
|
||||||
|
foreach(self::get_providers() as $scheme=>$endpoint) {
|
||||||
|
if(self::match_scheme($url, $scheme)) {
|
||||||
|
return $endpoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function match_scheme($url, $scheme) {
|
||||||
|
$urlInfo = parse_url($url);
|
||||||
|
$schemeInfo = parse_url($scheme);
|
||||||
|
foreach($schemeInfo as $k=>$v) {
|
||||||
|
if(!array_key_exists($k, $urlInfo)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(strpos($v, '*') !== false) {
|
||||||
|
$v = preg_quote($v, '/');
|
||||||
|
$v = str_replace('\*', '.*', $v);
|
||||||
|
if($k == 'host') {
|
||||||
|
$v = str_replace('*\.', '*', $v);
|
||||||
|
}
|
||||||
|
if(!preg_match('/' . $v . '/', $urlInfo[$k])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} elseif(strcasecmp($urlInfo[$k], $v)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function autodiscover_from_url($url) {
|
||||||
|
$service = new RestfulService($url);
|
||||||
|
$body = $service->request();
|
||||||
|
if(!$body || $body->isError()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$body = $body->getBody();
|
||||||
|
|
||||||
|
if(preg_match_all('#<link[^>]+?(?:href=[\'"](.+?)[\'"][^>]+?)?type=["\']application/json\+oembed["\'](?:[^>]+?href=[\'"](.+?)[\'"])?#', $body, $matches, PREG_SET_ORDER)) {
|
||||||
|
$match = $matches[0];
|
||||||
|
if(!empty($match[1])) {
|
||||||
|
return html_entity_decode($match[1]);
|
||||||
|
}
|
||||||
|
if(!empty($match[2])) {
|
||||||
|
return html_entity_decode($match[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_oembed_from_url($url, $type = false, Array $options = array()) {
|
||||||
|
$endpoint = self::match_url($url);
|
||||||
|
$ourl = false;
|
||||||
|
if(!$endpoint) {
|
||||||
|
if(self::get_autodiscover()) {
|
||||||
|
$ourl = self::autodiscover_from_url($url);
|
||||||
|
}
|
||||||
|
} elseif($endpoint === true) {
|
||||||
|
$ourl = self::autodiscover_from_url($url);
|
||||||
|
} else {
|
||||||
|
$ourl = Controller::join_links($endpoint, '?format=json&url=' . rawurlencode($url));
|
||||||
|
}
|
||||||
|
if($ourl) {
|
||||||
|
if($options) {
|
||||||
|
if(isset($options['width']) && !isset($options['maxwidth'])) {
|
||||||
|
$options['maxwidth'] = $options['width'];
|
||||||
|
}
|
||||||
|
if(isset($options['height']) && !isset($options['maxheight'])) {
|
||||||
|
$options['maxheight'] = $options['height'];
|
||||||
|
}
|
||||||
|
$ourl = Controller::join_links($ourl, '?' . http_build_query($options, '', '&'));
|
||||||
|
}
|
||||||
|
return new Oembed_Result($ourl, $url, $type, $options);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function handle_shortcode($arguments, $url, $parser, $shortcode) {
|
||||||
|
if(isset($arguments['type'])) {
|
||||||
|
$type = $arguments['type'];
|
||||||
|
unset($arguments['type']);
|
||||||
|
} else {
|
||||||
|
$type = false;
|
||||||
|
}
|
||||||
|
$oembed = self::get_oembed_from_url($url, $type, $arguments);
|
||||||
|
if($oembed && $oembed->exists()) {
|
||||||
|
return $oembed->forTemplate();
|
||||||
|
} else {
|
||||||
|
return '<a href="' . $url . '">' . $url . '</a>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Oembed_Result extends ViewableData {
|
||||||
|
protected $data = false;
|
||||||
|
protected $origin = false;
|
||||||
|
protected $type = false;
|
||||||
|
protected $url;
|
||||||
|
protected $extraClass;
|
||||||
|
|
||||||
|
public static $casting = array(
|
||||||
|
'html' => 'HTMLText',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function __construct($url, $origin = false, $type = false, Array $options = array()) {
|
||||||
|
$this->url = $url;
|
||||||
|
$this->origin = $origin;
|
||||||
|
$this->type = $type;
|
||||||
|
|
||||||
|
if(isset($options['class'])) {
|
||||||
|
$this->extraClass = $options['class'];
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadData() {
|
||||||
|
if($this->data !== false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$service = new RestfulService($this->url);
|
||||||
|
$body = $service->request();
|
||||||
|
if(!$body || $body->isError()) {
|
||||||
|
$this->data = array();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$body = $body->getBody();
|
||||||
|
$data = json_decode($body, true);
|
||||||
|
if(!$data) {
|
||||||
|
$data = array();
|
||||||
|
}
|
||||||
|
foreach($data as $k=>$v) {
|
||||||
|
unset($data[$k]);
|
||||||
|
$data[strtolower($k)] = $v;
|
||||||
|
}
|
||||||
|
if($this->type && $this->type != $data['type']) {
|
||||||
|
$data = array();
|
||||||
|
}
|
||||||
|
$this->data = $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasField($field) {
|
||||||
|
$this->loadData();
|
||||||
|
return array_key_exists(strtolower($field), $this->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getField($field) {
|
||||||
|
$field = strtolower($field);
|
||||||
|
if($this->hasField($field)) {
|
||||||
|
return $this->data[$field];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function forTemplate() {
|
||||||
|
$this->loadData();
|
||||||
|
switch($this->Type) {
|
||||||
|
case 'video':
|
||||||
|
case 'rich':
|
||||||
|
if($this->extraClass) {
|
||||||
|
return "<div class='$this->extraClass'>$this->HTML</div>";
|
||||||
|
} else {
|
||||||
|
return $this->HTML;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'link':
|
||||||
|
return '<a class="' . $this->extraClass . '" href="' . $this->origin . '">' . $this->Title . '</a>';
|
||||||
|
break;
|
||||||
|
case 'photo':
|
||||||
|
return "<img src='$this->URL' width='$this->Width' height='$this->Height' class='$this->extraClass' />";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exists() {
|
||||||
|
$this->loadData();
|
||||||
|
return count($this->data) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,7 @@
|
|||||||
// Patch callbacks, make them point to window.opener
|
// Patch callbacks, make them point to window.opener
|
||||||
patchCallback(settings, 'urlconverter_callback');
|
patchCallback(settings, 'urlconverter_callback');
|
||||||
patchCallback(settings, 'insertlink_callback');
|
patchCallback(settings, 'insertlink_callback');
|
||||||
patchCallback(settings, 'insertimage_callback');
|
patchCallback(settings, 'insertmedia_callback');
|
||||||
patchCallback(settings, 'setupcontent_callback');
|
patchCallback(settings, 'setupcontent_callback');
|
||||||
patchCallback(settings, 'save_callback');
|
patchCallback(settings, 'save_callback');
|
||||||
patchCallback(settings, 'onchange_callback');
|
patchCallback(settings, 'onchange_callback');
|
||||||
|
@ -29,13 +29,13 @@
|
|||||||
|
|
||||||
init : function(ed, url) {
|
init : function(ed, url) {
|
||||||
ed.addButton('sslink', {title : ed.getLang('tinymce_ssbuttons.insertlink'), cmd : 'sslink', 'class' : 'mce_link'});
|
ed.addButton('sslink', {title : ed.getLang('tinymce_ssbuttons.insertlink'), cmd : 'sslink', 'class' : 'mce_link'});
|
||||||
ed.addButton('ssimage', {title : ed.getLang('tinymce_ssbuttons.insertimage'), cmd : 'ssimage', 'class' : 'mce_image'});
|
ed.addButton('ssmedia', {title : ed.getLang('tinymce_ssbuttons.insertmedia'), cmd : 'ssmedia', 'class' : 'mce_image'});
|
||||||
|
|
||||||
ed.addCommand('sslink', function(ed) {
|
ed.addCommand('sslink', function(ed) {
|
||||||
jQuery('#' + this.id).entwine('ss').openLinkDialog();
|
jQuery('#' + this.id).entwine('ss').openLinkDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
ed.addCommand('ssimage', function(ed) {
|
ed.addCommand('ssmedia', function(ed) {
|
||||||
jQuery('#' + this.id).entwine('ss').openMediaDialog();
|
jQuery('#' + this.id).entwine('ss').openMediaDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -44,6 +44,63 @@
|
|||||||
cm.setDisabled('sslink', co && n.nodeName != 'A');
|
cm.setDisabled('sslink', co && n.nodeName != 'A');
|
||||||
cm.setActive('sslink', n.nodeName == 'A' && !n.name);
|
cm.setActive('sslink', n.nodeName == 'A' && !n.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ed.onSaveContent.add(function(ed, o) {
|
||||||
|
var content = jQuery(o.content);
|
||||||
|
content.find('.ss-htmleditorfield-file.embed').each(function() {
|
||||||
|
var el = jQuery(this);
|
||||||
|
var shortCode = '[embed width=' + el.data('width')
|
||||||
|
+ ' height=' + el.data('height')
|
||||||
|
+ ' class=' + el.data('cssclass')
|
||||||
|
+ ' thumbnail=' + el.data('thumbnail')
|
||||||
|
+ ']' + el.data('url')
|
||||||
|
+ '[/embed]';
|
||||||
|
el.replaceWith(shortCode);
|
||||||
|
});
|
||||||
|
o.content = jQuery('<div />').append(content).html(); // Little hack to get outerHTML string
|
||||||
|
});
|
||||||
|
|
||||||
|
var shortTagRegex = /(.?)\[embed(.*?)\](.+?)\[\/\s*embed\s*\](.?)/gi;
|
||||||
|
ed.onBeforeSetContent.add(function(ed, o) {
|
||||||
|
var matches = null, content = o.content;
|
||||||
|
var prefix, suffix, attributes, attributeString, url;
|
||||||
|
var attrs, attr;
|
||||||
|
var imgEl;
|
||||||
|
while((matches = shortTagRegex.exec(content))) {
|
||||||
|
prefix = matches[1];
|
||||||
|
suffix = matches[4];
|
||||||
|
if(prefix === '[' && suffix === ']') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
attributes = {};
|
||||||
|
attributeString = matches[2].replace(/['"]/g, '').replace(/(^\s+|\s+$)/g, '');
|
||||||
|
attrs = attributeString.split(/\s+/);
|
||||||
|
for(attribute in attrs) {
|
||||||
|
attr = attrs[attribute].split('=');
|
||||||
|
if(attr.length == 1) {
|
||||||
|
attributes[attr[0]] = attr[0];
|
||||||
|
} else {
|
||||||
|
attributes[attr[0]] = attr[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributes.cssclass = attributes.class;
|
||||||
|
url = matches[3];
|
||||||
|
imgEl = jQuery('<img/>').attr({
|
||||||
|
'src': attributes.thumbnail,
|
||||||
|
'width': attributes.width,
|
||||||
|
'height': attributes.height,
|
||||||
|
'class': attributes.cssclass,
|
||||||
|
'data-url': url,
|
||||||
|
}).addClass('ss-htmleditorfield-file embed');
|
||||||
|
|
||||||
|
jQuery.each(attributes, function (key, value) {
|
||||||
|
imgEl.attr('data-' + key, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
content = content.replace(matches[0], prefix + (jQuery('<div/>').append(imgEl).html()) + suffix);
|
||||||
|
}
|
||||||
|
o.content = content;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
4
thirdparty/tinymce_ssbuttons/langs/de.js
vendored
4
thirdparty/tinymce_ssbuttons/langs/de.js
vendored
@ -1,5 +1,5 @@
|
|||||||
tinyMCE.addI18n('de.tinymce_ssbuttons',{
|
tinyMCE.addI18n('de.tinymce_ssbuttons',{
|
||||||
insertlink: 'Link einfügen',
|
insertlink: 'Link einfügen',
|
||||||
insertimage: 'Bild einfügen',
|
insertmedia: 'Bild einfügen',
|
||||||
insertflash: 'Flash Objekt einfügen'
|
insertflash: 'Flash Objekt einfügen'
|
||||||
});
|
});
|
||||||
|
4
thirdparty/tinymce_ssbuttons/langs/en.js
vendored
4
thirdparty/tinymce_ssbuttons/langs/en.js
vendored
@ -1,5 +1,5 @@
|
|||||||
tinyMCE.addI18n('en.tinymce_ssbuttons', {
|
tinyMCE.addI18n('en.tinymce_ssbuttons', {
|
||||||
insertlink: 'Insert Link',
|
insertlink: 'Insert Link',
|
||||||
insertimage: 'Insert Image',
|
insertmedia: 'Insert Media',
|
||||||
insertflash: 'Insert Flash Object'
|
insertflash: 'Insert Flash Object'
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user