Merge pull request #6636 from open-sausages/pulls/4.0/filehandlefield-class

Change to use injector for file upload fields in framework
This commit is contained in:
Damian Mooyman 2017-02-27 12:03:16 +13:00 committed by GitHub
commit eda4aae923
20 changed files with 162 additions and 394 deletions

View File

@ -66,3 +66,9 @@ SilverStripe\Core\Injector\Injector:
SilverStripe\Assets\Image_Backend:
class: SilverStripe\Assets\GDBackend
Image_Backend: '%$SilverStripe\Assets\Image_Backend'
---
Name: coreassetfield
---
SilverStripe\Core\Injector\Injector:
SilverStripe\Forms\FileHandleField:
class: SilverStripe\Forms\FileField

View File

@ -3,40 +3,39 @@
use SilverStripe\Admin\CMSMenu;
use SilverStripe\Forms\HTMLEditor\TinyMCEConfig;
// Default CMS HTMLEditorConfig
TinyMCEConfig::get('cms')->setOptions(array(
'friendly_name' => 'Default CMS',
'priority' => '50',
'friendly_name' => 'Default CMS',
'priority' => '50',
'body_class' => 'typography',
'body_class' => 'typography',
'contextmenu' => "sslink ssmedia inserttable | cell row column deletetable",
'contextmenu' => "sslink ssmedia inserttable | cell row column deletetable",
'use_native_selects' => false,
'valid_elements' => "@[id|class|style|title],a[id|rel|rev|dir|tabindex|accesskey|type|name|href|target|title"
. "|class],-strong/-b[class],-em/-i[class],-strike[class],-u[class],#p[id|dir|class|align|style],-ol[class],"
. "-ul[class],-li[class],br,img[id|dir|longdesc|usemap|class|src|border|alt=|title|width|height|align|data*],"
. "-sub[class],-sup[class],-blockquote[dir|class],-cite[dir|class|id|title],"
. "-table[cellspacing|cellpadding|width|height|class|align|summary|dir|id|style],"
. "-tr[id|dir|class|rowspan|width|height|align|valign|bgcolor|background|bordercolor|style],"
. "tbody[id|class|style],thead[id|class|style],tfoot[id|class|style],"
. "#td[id|dir|class|colspan|rowspan|width|height|align|valign|scope|style],"
. "-th[id|dir|class|colspan|rowspan|width|height|align|valign|scope|style],caption[id|dir|class],"
. "-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align],address[class|align],"
. "-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],"
. "-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|dir|class|align|style],hr[class],"
. "dd[id|class|title|dir],dl[id|class|title|dir],dt[id|class|title|dir]",
'extended_valid_elements' => "img[class|src|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name"
. "|usemap|data*],iframe[src|name|width|height|align|frameborder|marginwidth|marginheight|scrolling],"
. "object[width|height|data|type],param[name|value],map[class|name|id],area[shape|coords|href|target|alt]"
'use_native_selects' => false,
'valid_elements' => "@[id|class|style|title],a[id|rel|rev|dir|tabindex|accesskey|type|name|href|target|title"
. "|class],-strong/-b[class],-em/-i[class],-strike[class],-u[class],#p[id|dir|class|align|style],-ol[class],"
. "-ul[class],-li[class],br,img[id|dir|longdesc|usemap|class|src|border|alt=|title|width|height|align|data*],"
. "-sub[class],-sup[class],-blockquote[dir|class],-cite[dir|class|id|title],"
. "-table[cellspacing|cellpadding|width|height|class|align|summary|dir|id|style],"
. "-tr[id|dir|class|rowspan|width|height|align|valign|bgcolor|background|bordercolor|style],"
. "tbody[id|class|style],thead[id|class|style],tfoot[id|class|style],"
. "#td[id|dir|class|colspan|rowspan|width|height|align|valign|scope|style],"
. "-th[id|dir|class|colspan|rowspan|width|height|align|valign|scope|style],caption[id|dir|class],"
. "-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align],address[class|align],"
. "-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],"
. "-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|dir|class|align|style],hr[class],"
. "dd[id|class|title|dir],dl[id|class|title|dir],dt[id|class|title|dir]",
'extended_valid_elements' => "img[class|src|alt|title|hspace|vspace|width|height|align|onmouseover|onmouseout|name"
. "|usemap|data*],iframe[src|name|width|height|align|frameborder|marginwidth|marginheight|scrolling],"
. "object[width|height|data|type],param[name|value],map[class|name|id],area[shape|coords|href|target|alt]"
));
TinyMCEConfig::get('cms')
->enablePlugins(array(
'contextmenu' => null,
'image' => null,
'ssbuttons' => FRAMEWORK_ADMIN_DIR . '/client/dist/js/TinyMCE_SSPlugin.js'
));
->enablePlugins(array(
'contextmenu' => null,
'image' => null,
'sslink' => FRAMEWORK_ADMIN_DIR . '/client/dist/js/TinyMCE_sslink.js'
));
CMSMenu::remove_menu_class('SilverStripe\\Admin\\CMSProfileController');

View File

@ -1,18 +0,0 @@
webpackJsonp([2],[function(t,e,n){(function(t){"use strict"
!function(){var e={getInfo:function n(){return{longname:"Special buttons for SilverStripe CMS",author:"Sam Minnée",authorurl:"http://www.siverstripe.com/",infourl:"http://www.silverstripe.com/",version:"1.0"
}},init:function i(e){e.addButton("sslink",{icon:"link",title:"Insert Link",cmd:"sslink"}),e.addMenuItem("sslink",{icon:"link",text:"Insert Link",cmd:"sslink"}),e.addButton("ssmedia",{icon:"image",title:"Insert Media",
cmd:"ssmedia"}),e.addMenuItem("ssmedia",{icon:"image",text:"Insert Media",cmd:"ssmedia"}),e.addCommand("sslink",function(e){t("#"+this.id).entwine("ss").openLinkDialog()}),e.addCommand("ssmedia",function(e){
t("#"+this.id).entwine("ss").openMediaDialog()}),e.on("BeforeExecCommand",function(t){var n=t.command,i=t.ui,a=t.value
"mceAdvLink"==n||"mceLink"==n?(t.preventDefault(),e.execCommand("sslink",i,a)):"mceAdvImage"!=n&&"mceImage"!=n||(t.preventDefault(),e.execCommand("ssmedia",i,a))}),e.on("SaveContent",function(e){var n=t(e.content),i=function a(t){
return Object.keys(t).map(function(e){return t[e]?e+'="'+t[e]+'"':null}).filter(function(t){return null!==t}).join(" ")}
n.find(".ss-htmleditorfield-file.embed").each(function(){var e=t(this),n={width:e.attr("width"),"class":e.attr("cssclass"),thumbnail:e.data("thumbnail")},a="[embed "+i(n)+"]"+e.data("url")+"[/embed]"
e.replaceWith(a)}),n.find("img").each(function(){var e=t(this),n={src:e.attr("src"),id:e.data("id"),width:e.attr("width"),height:e.attr("height"),"class":e.attr("class"),title:e.attr("title"),alt:e.attr("alt")
},a="[image "+i(n)+"]"
e.replaceWith(a)}),e.content="",n.each(function(){void 0!==this.outerHTML&&(e.content+=this.outerHTML)})}),e.on("BeforeSetContent",function(e){for(var n,i=e.content,a=function d(t){return t.match(/([^\s\/'"=,]+)\s*=\s*(('([^']+)')|("([^"]+)")|([^\s,\]]+))/g).reduce(function(t,e){
var n=e.match(/^([^\s\/'"=,]+)\s*=\s*(?:(?:'([^']+)')|(?:"([^"]+)")|(?:[^\s,\]]+))$/),i=n[1],a=n[2]||n[3]||n[4]
return t[i]=a,t},{})},s=/\[embed(.*?)\](.+?)\[\/\s*embed\s*\]/gi;n=s.exec(i);){var c=a(n[1]),r
r=t("<img/>").attr({src:c.thumbnail,width:c.width,height:c.height,"class":c["class"],"data-url":n[2]}).addClass("ss-htmleditorfield-file embed"),c.cssclass=c["class"],Object.keys(c).forEach(function(t){
return r.attr("data-"+t,c[t])}),i=i.replace(n[0],t("<div/>").append(r).html())}for(var s=/\[image(.*?)\]/gi;n=s.exec(i);){var c=a(n[1]),r=t("<img/>").attr({src:c.src,width:c.width,height:c.height,"class":c["class"],
alt:c.alt,title:c.title,"data-id":c.id})
i=i.replace(n[0],t("<div/>").append(r).html())}e.content=i})}}
tinymce.PluginManager.add("ssbuttons",function(t){e.init(t)})}()}).call(e,n(1))}])

View File

@ -0,0 +1,5 @@
webpackJsonp([2],[function(n,i){"use strict"
!function(){var n={init:function i(n){n.addButton("sslink",{icon:"link",title:"Insert Link",cmd:"sslink"}),n.addMenuItem("sslink",{icon:"link",text:"Insert Link",cmd:"sslink"}),n.addCommand("sslink",function(){
window.jQuery("#"+n.id).entwine("ss").openLinkDialog()}),n.on("BeforeExecCommand",function(i){var e=i.command,t=i.ui,s=i.value
"mceAdvLink"!==e&&"mceLink"!==e||(i.preventDefault(),n.execCommand("sslink",t,s))})}}
tinymce.PluginManager.add("sslink",function(i){return n.init(i)})}()}])

23
admin/client/dist/js/TinyMCE_ssmedia.js vendored Normal file
View File

@ -0,0 +1,23 @@
webpackJsonp([3],[function(t,e){"use strict"
function i(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}var n=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var i=arguments[e]
for(var n in i)Object.prototype.hasOwnProperty.call(i,n)&&(t[n]=i[n])}return t}
!function(){var t={getInfo:function e(){return{longname:"Media Dialog for SilverStripe CMS",author:"Sam Minnée",authorurl:"http://www.siverstripe.com/",infourl:"http://www.silverstripe.com/",version:"1.1"
}},init:function a(t){t.addButton("ssmedia",{icon:"image",title:"Insert Media",cmd:"ssmedia"}),t.addMenuItem("ssmedia",{icon:"image",text:"Insert Media",cmd:"ssmedia"}),t.addCommand("ssmedia",function(){
window.jQuery("#"+t.id).entwine("ss").openMediaDialog()}),t.on("BeforeExecCommand",function(e){var i=e.command,n=e.ui,a=e.value
"mceAdvImage"!==i&&"mceImage"!==i||(e.preventDefault(),t.execCommand("ssmedia",n,a))}),t.on("SaveContent",function(t){var e=window.jQuery(t.content),i=function n(t){return Object.keys(t).map(function(e){
return t[e]?e+'="'+t[e]+'"':null}).filter(function(t){return null!==t}).join(" ")}
e.find(".ss-htmleditorfield-file.embed").each(function(){var t=window.jQuery(this),e={width:t.attr("width"),"class":t.attr("cssclass"),thumbnail:t.data("thumbnail")},n="[embed "+i(e)+"]"+t.data("url")+"[/embed]"
t.replaceWith(n)}),e.find("img").each(function(){var t=window.jQuery(this),e={src:t.attr("src"),id:t.data("id"),width:t.attr("width"),height:t.attr("height"),"class":t.attr("class"),title:t.attr("title"),
alt:t.attr("alt")},n="[image "+i(e)+"]"
t.replaceWith(n)}),t.content="",e.each(function(){void 0!==this.outerHTML&&(t.content+=this.outerHTML)})}),t.on("BeforeSetContent",function(t){for(var e=null,a=t.content,r=function l(t){return t.match(/([^\s\/'"=,]+)\s*=\s*(('([^']+)')|("([^"]+)")|([^\s,\]]+))/g).reduce(function(t,e){
var a=e.match(/^([^\s\/'"=,]+)\s*=\s*(?:(?:'([^']+)')|(?:"([^"]+)")|(?:[^\s,\]]+))$/),r=a[1],s=a[2]||a[3]||a[4]
return n({},t,i({},r,s))},{})},s=/\[embed(.*?)](.+?)\[\/\s*embed\s*]/gi,c=function m(){var t=r(e[1]),i=window.jQuery("<img/>").attr({src:t.thumbnail,width:t.width,height:t.height,"class":t["class"],"data-url":e[2]
}).addClass("ss-htmleditorfield-file embed")
t.cssclass=t["class"],Object.keys(t).forEach(function(e){return i.attr("data-"+e,t[e])}),a=a.replace(e[0],window.jQuery("<div/>").append(i).html())};e=s.exec(a);)c()
for(var o=/\[image(.*?)]/gi;e=o.exec(a);){var d=r(e[1]),u=window.jQuery("<img/>").attr({src:d.src,width:d.width,height:d.height,"class":d["class"],alt:d.alt,title:d.title,"data-id":d.id})
a=a.replace(e[0],window.jQuery("<div/>").append(u).html())}t.content=a})}}
tinymce.PluginManager.add("ssmedia",function(e){return t.init(e)})}()}])

View File

@ -1,7 +1,7 @@
webpackJsonp([4],[function(e,t,n){"use strict"
n(2),n(3),n(6),n(16),n(18),n(24),n(26),n(28),n(29),n(31),n(34),n(104),n(112),n(116),n(126),n(127),n(128),n(129),n(130),n(131),n(133),n(136),n(138),n(140),n(143),n(146),n(148),n(150),n(152),n(154),n(156),
n(157),n(166),n(167),n(169),n(170),n(171),n(172),n(173),n(174),n(175),n(176),n(177),n(178),n(179),n(180),n(181),n(184),n(186),n(187),n(188),n(189),n(193),n(194),n(195),n(196),n(197),n(194),n(189),n(200),
n(202),n(204),n(205)},,function(e,t){"use strict"
n(157),n(166),n(167),n(169),n(170),n(171),n(172),n(173),n(174),n(175),n(176),n(177),n(178),n(179),n(180),n(181),n(184),n(186),n(187),n(188),n(189),n(193),n(194),n(195),n(196),n(197),n(194),n(200),n(202),
n(204),n(205)},,function(e,t){"use strict"
function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0})
var i=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n]
i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}(),r=function(){function e(){
@ -1811,15 +1811,13 @@ this.setEditor(t),t.init(this.attr("id")),this._super()},onremove:function n(){t
return n.length||(n=e('<div id="insert-media-react__dialog-wrapper" />'),e("body").append(n)),n.setElement(this),void n.open()}var i=function s(e){return e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()
},r=this,a=e("#cms-editor-dialogs").data("url"+i(t)+"form"),o=e(".htmleditorfield-"+t+"dialog")
o.length?(o.getForm().setElement(this),o.html(""),o.addClass("loading"),o.open()):(o=e('<div class="htmleditorfield-dialog htmleditorfield-'+t+'dialog loading">'),e("body").append(o)),e.ajax({url:a,complete:function l(){
o.removeClass("loading")},success:function u(e){o.html(e),o.getForm().setElement(r),o.trigger("ssdialogopen")}})}}),e(".htmleditorfield-dialog").entwine({onadd:function s(){this.is(".ui-dialog-content")||this.ssdialog({
autoOpen:!0,buttons:{insert:{text:_i18n2["default"]._t("HtmlEditorField.INSERT","Insert"),"data-icon":"accept","class":"btn action btn-primary media-insert",click:function t(){e(this).find("form").submit()
}}}}),this._super()},getForm:function l(){return this.find("form")},open:function u(){this.ssdialog("open")},close:function c(){this.ssdialog("close")},toggle:function d(e){this.is(":visible")?this.close():this.open()
},onscroll:function f(){this.animate({scrollTop:this.find("form").height()},500)}}),e("form.htmleditorfield-form").entwine({Selection:null,Bookmark:null,Element:null,setSelection:function p(t){return this._super(e(t))
},onadd:function h(){var e=this.find(":header:first")
if(!a){if("media"===t)throw new Error("Install silverstripe/asset-admin to use media dialog")
throw new Error("Dialog named "+t+" is not available.")}o.length?(o.getForm().setElement(this),o.html(""),o.addClass("loading"),o.open()):(o=e('<div class="htmleditorfield-dialog htmleditorfield-'+t+'dialog loading">'),
e("body").append(o)),e.ajax({url:a,complete:function l(){o.removeClass("loading")},success:function u(e){o.html(e),o.getForm().setElement(r),o.trigger("ssdialogopen")}})}}),e(".htmleditorfield-dialog").entwine({
onadd:function s(){this.is(".ui-dialog-content")||this.ssdialog({autoOpen:!0,buttons:{insert:{text:_i18n2["default"]._t("HtmlEditorField.INSERT","Insert"),"data-icon":"accept","class":"btn action btn-primary media-insert",
click:function t(){e(this).find("form").submit()}}}}),this._super()},getForm:function l(){return this.find("form")},open:function u(){this.ssdialog("open")},close:function c(){this.ssdialog("close")},toggle:function d(e){
this.is(":visible")?this.close():this.open()},onscroll:function f(){this.animate({scrollTop:this.find("form").height()},500)}}),e("form.htmleditorfield-form").entwine({Selection:null,Bookmark:null,Element:null,
setSelection:function p(t){return this._super(e(t))},onadd:function h(){var e=this.find(":header:first")
this.getDialog().attr("title",e.text()),this._super()},onremove:function m(){this.setSelection(null),this.setBookmark(null),this.setElement(null),this._super()},getDialog:function g(){return this.closest(".htmleditorfield-dialog")
},fromDialog:{onssdialogopen:function v(){var e=this.getEditor()
@ -1844,7 +1842,7 @@ i&&(e+="#"+i)
break
case"anchor":e="#"+i
break
case"file":var r=this.find(".ss-uploadfield .ss-uploadfield-item").attr("data-fileid")
case"file":var r=this.find(":input[name=file]").val()
e=r?"[file_link,id="+r+"]":""
break
case"email":e="mailto:"+this.find(":input[name=email]").val(),n&&(e+="?subject="+encodeURIComponent(n)),t=null

View File

@ -10,7 +10,7 @@ return e.e=function a(t,n){if(0===i[t])return n.call(null,e)
if(void 0!==i[t])i[t].push(n)
else{i[t]=[n]
var r=document.getElementsByTagName("head")[0],o=document.createElement("script")
o.type="text/javascript",o.charset="utf-8",o.async=!0,o.src=e.p+""+t+".js/"+({0:"LeftAndMain.Ping",1:"MemberImportForm",2:"TinyMCE_SSPlugin",3:"UploadField_select",4:"bundle",5:"leaktools"}[t]||t)+".js",
o.type="text/javascript",o.charset="utf-8",o.async=!0,o.src=e.p+""+t+".js/"+({0:"LeftAndMain.Ping",1:"MemberImportForm",2:"TinyMCE_sslink",3:"UploadField_select",4:"bundle",5:"leaktools"}[t]||t)+".js",
r.appendChild(o)}},e.m=t,e.c=r,e.p="",e(0)}([function(t,e,n){"use strict"
n(398),n(694),n(695),n(698),n(700),n(702),n(732),n(870),n(979),n(981),n(1114),n(1124),n(1139),n(1314),n(1316),n(1364),n(1370),n(1623),n(1631),n(1634),n(1635),n(1638),n(1641),n(1642),n(1744),n(1755),n(1758),
n(1772),n(1777),n(168),n(198),n(203),n(1778),n(1779),n(1780),n(1781),n(199),n(1782),n(1783),n(1784),n(1785),n(1786),n(1787),n(1788),n(1789)},function(t,e){t.exports=jQuery},,,,function(t,e){t.exports=React

View File

@ -65,7 +65,6 @@ require('../legacy/ToggleCompositeField.js');
require('../legacy/MemberDatetimeOptionsetField.js');
require('../legacy/TreeDropdownField.js');
require('../legacy/DateField.js');
require('../legacy/UploadField.js');
require('../legacy/HtmlEditorField.js');
require('../legacy/TabSet.js');
require('../legacy/GridField.js');

View File

@ -327,6 +327,7 @@ $.entwine('ss', function($) {
},
openDialog: function(type) {
// Note: This requires asset-admin module
if (type === 'media' && window.InsertMediaModal) {
let dialog = $('#insert-media-react__dialog-wrapper');
@ -348,6 +349,13 @@ $.entwine('ss', function($) {
url = $('#cms-editor-dialogs').data('url' + capitalize(type) + 'form'),
dialog = $('.htmleditorfield-' + type + 'dialog');
if (!url) {
if (type === 'media') {
throw new Error("Install silverstripe/asset-admin to use media dialog")
}
throw new Error(`Dialog named ${type} is not available.`);
}
if(dialog.length) {
// Clean existing dialog for reload
dialog.getForm().setElement(this);
@ -598,7 +606,7 @@ $.entwine('ss', function($) {
break;
case 'file':
var fileid = this.find('.ss-uploadfield .ss-uploadfield-item').attr('data-fileid');
var fileid = this.find(':input[name=file]').val();
href = fileid ? '[file_link,id=' + fileid + ']' : '';
break;

View File

@ -1,178 +0,0 @@
(function() {
var ssbuttons = {
/**
* Returns information about the plugin as a name/value array.
* The current keys are longname, author, authorurl, infourl and version.
*
* @returns Name/value array containing information about the plugin.
* @type Array
*/
getInfo : function() {
return {
longname : 'Special buttons for SilverStripe CMS',
author : 'Sam Minnée',
authorurl : 'http://www.siverstripe.com/',
infourl : 'http://www.silverstripe.com/',
version : "1.0"
};
},
init : function(ed) {
ed.addButton('sslink', {
icon : 'link',
title : 'Insert Link',
cmd : 'sslink'
});
ed.addMenuItem('sslink', {
icon : 'link',
text : 'Insert Link',
cmd : 'sslink'
});
ed.addButton('ssmedia', {
icon : 'image',
title : 'Insert Media',
cmd : 'ssmedia'
});
ed.addMenuItem('ssmedia', {
icon : 'image',
text : 'Insert Media',
cmd : 'ssmedia'
});
ed.addCommand('sslink', function(ed) {
// See HtmlEditorField.js
jQuery('#' + this.id).entwine('ss').openLinkDialog();
});
ed.addCommand('ssmedia', function(ed) {
// See HtmlEditorField.js
jQuery('#' + this.id).entwine('ss').openMediaDialog();
});
// Replace the mceAdvLink and mceLink commands with the sslink command, and
// the mceAdvImage and mceImage commands with the ssmedia command
ed.on('BeforeExecCommand', function(e){
var cmd = e.command;
var ui = e.ui;
var val = e.value;
if (cmd == 'mceAdvLink' || cmd == 'mceLink'){
e.preventDefault();
ed.execCommand('sslink', ui, val);
} else if (cmd == 'mceAdvImage' || cmd == 'mceImage'){
e.preventDefault();
ed.execCommand('ssmedia', ui, val);
}
});
ed.on('SaveContent', function(o) {
var content = jQuery(o.content);
var attrsFn = (attrs) => {
return Object.keys(attrs)
.map((name) => attrs[name] ? name + '="' + attrs[name] + '"' : null)
.filter((el) => el !== null)
.join(' ')
};
// Transform [embed] shortcodes
content.find('.ss-htmleditorfield-file.embed').each(function() {
var el = jQuery(this);
var attrs = {
width: el.attr('width'),
class: el.attr('cssclass'),
thumbnail: el.data('thumbnail')
};
var shortCode = '[embed ' + attrsFn(attrs) + ']' + el.data('url') + '[/embed]';
el.replaceWith(shortCode);
});
// Transform [image] shortcodes
content.find('img').each(function() {
var el = jQuery(this);
var attrs = {
// Requires server-side preprocessing of HTML+shortcodes in HTMLValue
src: el.attr('src'),
id: el.data('id'),
width: el.attr('width'),
height: el.attr('height'),
class: el.attr('class'),
// don't save caption, since that's in the containing element
title: el.attr('title'),
alt: el.attr('alt')
};
var shortCode = '[image ' + attrsFn(attrs) + ']';
el.replaceWith(shortCode);
});
// Insert outerHTML in order to retain all nodes incl. <script>
// tags which would've been filtered out with jQuery.html().
// Note that <script> tags might be sanitized separately based on editor config.
o.content = '';
content.each(function() {
if(this.outerHTML !== undefined) {
o.content += this.outerHTML;
}
});
});
ed.on('BeforeSetContent', function(o) {
var matches;
var content = o.content;
var attrFromStrFn = (str) => {
return str
// Split on all attributes, quoted or not
.match(/([^\s\/'"=,]+)\s*=\s*(('([^']+)')|("([^"]+)")|([^\s,\]]+))/g)
.reduce((coll, val) => {
var match = val.match(/^([^\s\/'"=,]+)\s*=\s*(?:(?:'([^']+)')|(?:"([^"]+)")|(?:[^\s,\]]+))$/),
key = match[1],
value = match[2] || match[3] || match[4]; // single, double, or unquoted match
coll[key] = value;
return coll;
}, {});
};
// Transform [embed] tag
var shortTagRegex = /\[embed(.*?)\](.+?)\[\/\s*embed\s*\]/gi;
while((matches = shortTagRegex.exec(content))) {
var attrs = attrFromStrFn(matches[1]);
var el;
el = jQuery('<img/>').attr({
'src': attrs['thumbnail'],
'width': attrs['width'],
'height': attrs['height'],
'class': attrs['class'],
'data-url': matches[2]
}).addClass('ss-htmleditorfield-file embed');
attrs['cssclass'] = attrs['class'];
Object.keys(attrs).forEach((key) => el.attr('data-' + key, attrs[key]));
content = content.replace(matches[0], (jQuery('<div/>').append(el).html()));
}
// Transform [image] tag
var shortTagRegex = /\[image(.*?)\]/gi;
while((matches = shortTagRegex.exec(content))) {
var attrs = attrFromStrFn(matches[1]);
var el = jQuery('<img/>').attr({
'src': attrs['src'],
'width': attrs['width'],
'height': attrs['height'],
'class': attrs['class'],
'alt': attrs['alt'],
'title': attrs['title'],
'data-id': attrs['id']
});
content = content.replace(matches[0], (jQuery('<div/>').append(el).html()));
}
o.content = content;
});
}
};
// Adds the plugin class to the list of available TinyMCE plugins
tinymce.PluginManager.add("ssbuttons", function(editor) {
ssbuttons.init(editor);
});
})();

View File

@ -0,0 +1,44 @@
/* global tinymce */
(() => {
const sslink = {
/**
* Initialise this plugin
*
* @param {Object} ed
*/
init(ed) {
ed.addButton('sslink', {
icon: 'link',
title: 'Insert Link',
cmd: 'sslink',
});
ed.addMenuItem('sslink', {
icon: 'link',
text: 'Insert Link',
cmd: 'sslink',
});
ed.addCommand('sslink', () => {
// See HtmlEditorField.js
window.jQuery(`#${ed.id}`).entwine('ss').openLinkDialog();
});
// Replace the mceAdvLink and mceLink commands with the sslink command, and
// the mceAdvImage and mceImage commands with the ssmedia command
ed.on('BeforeExecCommand', (e) => {
const cmd = e.command;
const ui = e.ui;
const val = e.value;
if (cmd === 'mceAdvLink' || cmd === 'mceLink') {
e.preventDefault();
ed.execCommand('sslink', ui, val);
}
});
},
};
// Adds the plugin class to the list of available TinyMCE plugins
tinymce.PluginManager.add('sslink', (editor) => sslink.init(editor));
})();

View File

@ -154,4 +154,4 @@ fieldset{
background:#ffe9e9;
border:1px solid #c80700;
border-radius:4px;
}
}

View File

@ -588,4 +588,4 @@ body #Footer p{
font-size:18px;
padding:0 2px 2px 0;
font-weight:700;
}
}

View File

@ -46,7 +46,7 @@ use SilverStripe\Core\Object;
* }
* </code>
*/
class FileField extends FormField
class FileField extends FormField implements FileHandleField
{
use UploadReceiver;

View File

@ -0,0 +1,18 @@
<?php
namespace SilverStripe\Forms;
interface FileHandleField
{
public function getAttributes();
public function getFolderName();
public function setAllowedExtensions($rules);
public function getAllowedExtensions();
public function setAllowedFileCategories($category);
public function setFolderName($folderName);
}

View File

@ -4,7 +4,6 @@ namespace SilverStripe\Forms\HTMLEditor;
use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Core\Object;
/**
* A PHP version of TinyMCE's configuration, to allow various parameters to be configured on a site or section basis
@ -57,7 +56,7 @@ abstract class HTMLEditorConfig
* instance - do not call 'new'
*
* @param string $identifier The identifier for the config set. If omitted, the active config is returned.
* @return HTMLEditorConfig The configuration object.
* @return static The configuration object.
* This will be created if it does not yet exist for that identifier
*/
public static function get($identifier = null)

View File

@ -44,7 +44,6 @@ class HTMLEditorField_Toolbar extends RequestHandler
private static $allowed_actions = array(
'LinkForm',
'MediaForm',
'viewfile',
'getanchors'
);
@ -78,9 +77,8 @@ class HTMLEditorField_Toolbar extends RequestHandler
public function forTemplate()
{
return sprintf(
'<div id="cms-editor-dialogs" data-url-linkform="%s" data-url-mediaform="%s"></div>',
Controller::join_links($this->controller->Link(), $this->name, 'LinkForm', 'forTemplate'),
Controller::join_links($this->controller->Link(), $this->name, 'MediaForm', 'forTemplate')
'<div id="cms-editor-dialogs" data-url-linkform="%s"></div>',
Controller::join_links($this->controller->Link(), $this->name, 'LinkForm', 'forTemplate')
);
}
@ -122,6 +120,7 @@ class HTMLEditorField_Toolbar extends RequestHandler
$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",
@ -159,7 +158,13 @@ class HTMLEditorField_Toolbar extends RequestHandler
$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')),
$fileField = TreeDropdownField::create(
'file',
_t('HTMLEditorField.FILE', 'File'),
File::class,
'ID',
'Name'
),
TextField::create('Anchor', _t('HTMLEditorField.ANCHORVALUE', 'Anchor')),
TextField::create('Subject', _t('HTMLEditorField.SUBJECT', 'Email subject')),
TextField::create('Description', _t('HTMLEditorField.LINKDESCR', 'Link description')),
@ -177,7 +182,6 @@ class HTMLEditorField_Toolbar extends RequestHandler
$headerWrap->addExtraClass('CompositeField composite cms-content-header form-group--no-label ');
$contentComposite->setName('ContentBody');
$contentComposite->addExtraClass('ss-insert-link content');
$fileField->setAllowedMaxFileNumber(1);
$form->unsetValidator();
$form->loadDataFrom($this);
@ -200,146 +204,6 @@ class HTMLEditorField_Toolbar extends RequestHandler
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 GridFieldSortableHeader(),
new GridFieldFilterHeader(),
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);
/** @var GridFieldDataColumns $columns */
$columns = $fileField->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldDataColumns');
$columns->setDisplayFields(array(
'StripThumbnail' => false,
'Title' => _t('File.Title', 'Title'),
'Created' => File::singleton()->fieldLabel('Created'),
));
$columns->setFieldCasting(array(
'Created' => 'DBDatetime->Nice'
));
$fromCMS = new CompositeField(
$select = TreeDropdownField::create('ParentID', "", 'SilverStripe\\Assets\\Folder')
->addExtraClass('noborder')
->setValue($parentID),
$fileField
);
$fromCMS->addExtraClass('content ss-uploadfield htmleditorfield-from-cms');
$select->addExtraClass('content-select');
$URLDescription = _t(
'HTMLEditorField.URLDESCRIPTION',
'Insert videos and images from the web into your page simply by entering the URL of the file. Make sure you have the rights or permissions before sharing media directly from the web.<br /><br />Please note that files are not added to the file store of the CMS but embeds the file from its original location, if for some reason the file is no longer available in its original location it will no longer be viewable on this page.'
);
$fromWeb = new CompositeField(
$description = new LiteralField(
'URLDescription',
'<div class="url-description">' . $URLDescription . '</div>'
),
$remoteURL = new TextField('RemoteURL', 'http://'),
new LiteralField(
'addURLImage',
'<button type="button" class="btn action btn-primary field font-icon-plus add-url">' .
_t('HTMLEditorField.BUTTONADDURL', 'Add url') . '</button>'
)
);
$remoteURL->addExtraClass('remoteurl');
$fromWeb->addExtraClass('content ss-uploadfield htmleditorfield-from-web');
Requirements::css(ltrim(FRAMEWORK_ADMIN_DIR . '/client/dist/styles/AssetUploadField.css', '/'));
$computerUploadField = UploadField::create('AssetUploadField', '');
$computerUploadField->setConfig('previewMaxWidth', 40);
$computerUploadField->setConfig('previewMaxHeight', 30);
$computerUploadField->addExtraClass('toolbar toolbar--content ss-assetuploadfield htmleditorfield-from-computer');
$computerUploadField->removeExtraClass('ss-uploadfield');
$computerUploadField->setTemplate('SilverStripe\\Forms\\HTMLEditorField_UploadField');
$computerUploadField->setFolderName(Upload::config()->get('uploads_folder'));
$defaultPanel = new CompositeField(
$computerUploadField,
$fromCMS
);
$fromWebPanel = new CompositeField(
$fromWeb
);
$defaultPanel->addExtraClass('htmleditorfield-default-panel');
$fromWebPanel->addExtraClass('htmleditorfield-web-panel');
$allFields = new CompositeField(
$defaultPanel,
$fromWebPanel,
$editComposite = new CompositeField(
new LiteralField('contentEdit', '<div class="content-edit ss-uploadfield-files files"></div>')
)
);
$allFields->addExtraClass('ss-insert-media');
$headings = new CompositeField(
new LiteralField(
'Heading',
sprintf(
'<h3 class="htmleditorfield-mediaform-heading insert">%s</h3>',
_t('HTMLEditorField.INSERTMEDIA', 'Insert media from')
) .
sprintf(
'<h3 class="htmleditorfield-mediaform-heading update">%s</h3>',
_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');
// Allow other people to extend the fields being added to the imageform
$this->extend('updateMediaForm', $form);
return $form;
}
/**
* List of allowed schemes (no wildcard, all lower case) or empty to allow all schemes
*

View File

@ -276,7 +276,7 @@ class TinyMCEConfig extends HTMLEditorConfig
2 => array(
'formatselect', '|',
'paste', 'pastetext', '|',
'table', 'ssmedia', 'sslink', 'unlink', '|',
'table', 'sslink', 'unlink', '|',
'code'
),
3 => array()
@ -339,7 +339,7 @@ class TinyMCEConfig extends HTMLEditorConfig
* - relative path - Will be treated as a relative url
* - absolute url - Some url to an external plugin
*
* @param string $plugin,... a string, or several strings, or a single array of strings - The plugins to enable
* @param string|array $plugin,... a string, or several strings, or a single array of strings - The plugins to enable
* @return $this
*/
public function enablePlugins($plugin)

View File

@ -4,8 +4,9 @@ namespace SilverStripe\ORM\FieldType;
use SilverStripe\Assets\File;
use SilverStripe\Assets\Image;
use SilverStripe\Forms\UploadField;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FileHandleField;
use SilverStripe\Forms\NumericField;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;
@ -48,7 +49,7 @@ class DBForeignKey extends DBInt
}
$hasOneSingleton = singleton($hasOneClass);
if ($hasOneSingleton instanceof File) {
$field = new UploadField($relationName, $title);
$field = Injector::inst()->create(FileHandleField::class, $relationName, $title);
if ($hasOneSingleton instanceof Image) {
$field->setAllowedFileCategories('image/supported');
}

View File

@ -45,7 +45,7 @@ const config = [
leaktools: `${PATHS.ADMIN_JS_SRC}/legacy/leaktools.js`,
MemberImportForm: `${PATHS.ADMIN_JS_SRC}/legacy/MemberImportForm.js`,
UploadField_select: `${PATHS.ADMIN_JS_SRC}/legacy/UploadField_select.js`,
TinyMCE_SSPlugin: `${PATHS.ADMIN_JS_SRC}/legacy/TinyMCE_SSPlugin.js`,
TinyMCE_sslink: `${PATHS.ADMIN_JS_SRC}/legacy/TinyMCE_sslink.js`,
},
resolve: {
root: [__dirname, path.resolve(__dirname, PATHS.ADMIN_JS_SRC)],