From e74055a981bcb0b3667c2c822cea400243ddeaa7 Mon Sep 17 00:00:00 2001 From: Peter Sistrom Date: Fri, 13 Mar 2026 20:24:01 +1100 Subject: [PATCH 1/6] Add client-side oembed renderer --- amd/build/clientrender.min.js | 10 ++++ amd/src/clientrender.js | 94 +++++++++++++++++++++++++++++++++++ classes/db/providerrow.php | 5 ++ classes/forms/provider.php | 13 ++++- classes/provider/provider.php | 8 ++- classes/service/oembed.php | 41 ++++++++++++--- db/install.xml | 1 + db/upgrade.php | 15 ++++++ lang/en/filter_oembed.php | 3 ++ version.php | 4 +- 10 files changed, 183 insertions(+), 11 deletions(-) create mode 100644 amd/build/clientrender.min.js create mode 100644 amd/src/clientrender.js diff --git a/amd/build/clientrender.min.js b/amd/build/clientrender.min.js new file mode 100644 index 0000000..d26913e --- /dev/null +++ b/amd/build/clientrender.min.js @@ -0,0 +1,10 @@ +/** + * Client-side rendering for oembed content. + * + * @module filter_oembed/clientrender + * @copyright 2026 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define("filter_oembed/clientrender",["jquery","core/log"],(function($,Log){return{init:function(){$(".oembed-client-render").each((function(){var $container=$(this);if(!$container.data("processed")){$container.data("processed",!0);var oembedUrl=$container.data("oembed-url"),originalUrl=$container.data("original-url"),params=$container.data("params");if(!oembedUrl)return Log.debug("filter_oembed/clientrender: No oembed URL provided"),void $container.html(''+originalUrl+"");$.ajax({url:oembedUrl,dataType:"json",success:function(data){if(data&&data.html){var embed=data.html;if(params){var paramStr="";for(var key in params)params.hasOwnProperty(key)&&(paramStr+="&"+key+"="+encodeURIComponent(params[key]));embed=embed.replace("?feature=oembed","?feature=oembed"+paramStr)}var aspectRatio=0;if(data.width&&data.height&&(aspectRatio=data.height/data.width),aspectRatio>0){var paddiv='
';$container.html('
'+embed+paddiv+"
")}else $container.html('
'+embed+"
")}else Log.debug("filter_oembed/clientrender: No HTML in oembed response for "+originalUrl),$container.html(''+originalUrl+"")},error:function(jqXHR,textStatus,errorThrown){Log.debug("filter_oembed/clientrender: Error loading oembed content for "+originalUrl+" - "+textStatus+": "+errorThrown),$container.html(''+originalUrl+"")}})}}))}}})); + +//# sourceMappingURL=clientrender.min.js.map \ No newline at end of file diff --git a/amd/src/clientrender.js b/amd/src/clientrender.js new file mode 100644 index 0000000..cdaf701 --- /dev/null +++ b/amd/src/clientrender.js @@ -0,0 +1,94 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Client-side rendering for oembed content. + * + * @module filter_oembed/clientrender + * @copyright 2026 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +define(['jquery', 'core/log'], function($, Log) { + + /** + * Initialize client-side rendering for oembed content. + */ + var init = function() { + $('.oembed-client-render').each(function() { + var $container = $(this); + + if ($container.data('processed')) { + return; + } + $container.data('processed', true); + + var oembedUrl = $container.data('oembed-url'); + var originalUrl = $container.data('original-url'); + var params = $container.data('params'); + + if (!oembedUrl) { + Log.debug('filter_oembed/clientrender: No oembed URL provided'); + $container.html('' + originalUrl + ''); + return; + } + + $.ajax({ + url: oembedUrl, + dataType: 'json', + success: function(data) { + if (data && data.html) { + var embed = data.html; + + if (params) { + var paramStr = ''; + for (var key in params) { + if (params.hasOwnProperty(key)) { + paramStr += '&' + key + '=' + encodeURIComponent(params[key]); + } + } + embed = embed.replace('?feature=oembed', '?feature=oembed' + paramStr); + } + + var aspectRatio = 0; + if (data.width && data.height) { + aspectRatio = data.height / data.width; + } + + if (aspectRatio > 0) { + var padding = aspectRatio * 100; + var paddiv = '
'; + $container.html('
' + embed + paddiv + '
'); + } else { + $container.html('
' + embed + '
'); + } + } else { + Log.debug('filter_oembed/clientrender: No HTML in oembed response for ' + originalUrl); + $container.html('' + originalUrl + ''); + } + }, + error: function(jqXHR, textStatus, errorThrown) { + Log.debug('filter_oembed/clientrender: Error loading oembed content for ' + originalUrl + + ' - ' + textStatus + ': ' + errorThrown); + $container.html('' + originalUrl + ''); + } + }); + }); + }; + + return { + init: init + }; +}); diff --git a/classes/db/providerrow.php b/classes/db/providerrow.php index c10ae31..1bdef31 100644 --- a/classes/db/providerrow.php +++ b/classes/db/providerrow.php @@ -59,6 +59,11 @@ class providerrow extends abstract_dbrow { */ public $enabled; + /** + * @var str render mode + */ + public $rendermode; + /** * @var int time created */ diff --git a/classes/forms/provider.php b/classes/forms/provider.php index b80fb06..fabd999 100644 --- a/classes/forms/provider.php +++ b/classes/forms/provider.php @@ -47,6 +47,7 @@ public function definition() { 'providerurl' => ['required' => true, 'type' => 'text', 'paramtype' => PARAM_URL], 'endpoints' => ['required' => true, 'type' => 'textarea', 'paramtype' => PARAM_TEXT], 'enabled' => ['required' => false, 'type' => 'checkbox', 'paramtype' => PARAM_INT], + 'rendermode' => ['required' => false, 'type' => 'select', 'paramtype' => PARAM_TEXT], 'source' => ['required' => true, 'type' => 'hidden', 'paramtype' => PARAM_TEXT], ]; @@ -66,7 +67,17 @@ public function definition() { } else { $fieldlabel = get_string($fieldname, 'filter_oembed'); } - $el = $mform->addElement($row->type, $fieldname, $fieldlabel); + + if ($fieldname === 'rendermode') { + $options = [ + 'server' => get_string('rendermode_server', 'filter_oembed'), + 'client' => get_string('rendermode_client', 'filter_oembed'), + ]; + $el = $mform->addElement($row->type, $fieldname, $fieldlabel, $options); + } else { + $el = $mform->addElement($row->type, $fieldname, $fieldlabel); + } + if (!empty($commonattributes)) { $el->updateAttributes($commonattributes); } diff --git a/classes/provider/provider.php b/classes/provider/provider.php index 964b4f0..914728c 100644 --- a/classes/provider/provider.php +++ b/classes/provider/provider.php @@ -65,6 +65,11 @@ class provider { */ protected $source = ''; + /** + * @var rendermode + */ + protected $rendermode = 'server'; + /** * @var Class constant descriptio for local. */ @@ -110,6 +115,7 @@ public function __construct($data = null) { } $this->source = isset($data['source']) ? $data['source'] : ''; + $this->rendermode = isset($data['rendermode']) ? $data['rendermode'] : 'server'; } } @@ -248,7 +254,7 @@ protected function endpoints_regex(endpoint $endpoint) { * @throws \coding_exception */ public function __get($name) { - $allowed = ['id', 'enabled', 'providername', 'providerurl', 'endpoints', 'source']; + $allowed = ['id', 'enabled', 'providername', 'providerurl', 'endpoints', 'source', 'rendermode']; if (in_array($name, $allowed)) { return $this->$name; } else { diff --git a/classes/service/oembed.php b/classes/service/oembed.php index 8d6d8cb..5dfb2c0 100755 --- a/classes/service/oembed.php +++ b/classes/service/oembed.php @@ -127,6 +127,8 @@ protected function get_provider_instance($provider) { * @return string */ public function html_output($text) { + global $PAGE; + $lazyload = get_config('filter_oembed', 'lazyload'); $lazyload = $lazyload == 1 || $lazyload === false; $output = ''; @@ -144,14 +146,20 @@ public function html_output($text) { $params = []; parse_str($query, $params); - // If we have a consumer request, we're done searching. Try for a response. - $jsonret = $provider->oembed_response($requesturl); - if (!$jsonret) { - $output = ''; - } else if ($lazyload) { - $output = $this->oembed_getpreloadhtml($jsonret, $params); + // Check if this provider uses client-side rendering. + if ($provider->rendermode === 'client') { + $PAGE->requires->js_call_amd('filter_oembed/clientrender', 'init'); + $output = $this->oembed_getclienthtml($requesturl, $text, $params); } else { - $output = $this->oembed_gethtml($jsonret, $params); + // Server-side rendering. + $jsonret = $provider->oembed_response($requesturl); + if (!$jsonret) { + $output = ''; + } else if ($lazyload) { + $output = $this->oembed_getpreloadhtml($jsonret, $params); + } else { + $output = $this->oembed_gethtml($jsonret, $params); + } } break; // Done, break out of all loops. } @@ -159,6 +167,25 @@ public function html_output($text) { return $output; } + /** + * Get client-side rendering placeholder html. + * + * @param string $requesturl The oembed request URL. + * @param string $originalurl The original URL being embedded. + * @param array $params Additional URL parameters. + * @return string + */ + protected function oembed_getclienthtml($requesturl, $originalurl, $params = []) { + $paramsdata = !empty($params) ? htmlspecialchars(json_encode($params), ENT_QUOTES, 'UTF-8') : ''; + $output = '
' . + '
Loading...
' . + '
'; + return $output; + } + /** * Get oembed html. * diff --git a/db/install.xml b/db/install.xml index 6b07857..eaefc19 100644 --- a/db/install.xml +++ b/db/install.xml @@ -12,6 +12,7 @@ + diff --git a/db/upgrade.php b/db/upgrade.php index 60f258a..cf07aec 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -142,5 +142,20 @@ function xmldb_filter_oembed_upgrade($oldversion) { upgrade_plugin_savepoint(true, 2016070501, 'filter', 'oembed'); } + if ($oldversion < 2026031300) { + + // Define field rendermode to be added to filter_oembed. + $table = new xmldb_table('filter_oembed'); + $field = new xmldb_field('rendermode', XMLDB_TYPE_CHAR, '50', null, null, null, 'server', 'enabled'); + + // Conditionally launch add field rendermode. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Oembed savepoint reached. + upgrade_plugin_savepoint(true, 2026031300, 'filter', 'oembed'); + } + return true; } diff --git a/lang/en/filter_oembed.php b/lang/en/filter_oembed.php index 975b987..c4b8ab1 100644 --- a/lang/en/filter_oembed.php +++ b/lang/en/filter_oembed.php @@ -56,6 +56,9 @@ $string['providersrestrict_desc'] = 'Restrict providers to a list of allowed providers'; $string['providerurl'] = 'Provider URL'; $string['requiredfield'] = 'The field "{$a}" must be completed'; +$string['rendermode'] = 'Render mode'; +$string['rendermode_client'] = 'Client'; +$string['rendermode_server'] = 'Server'; $string['saveasnew'] = 'Save as new local'; $string['savefailed'] = 'Failed to save provider.'; $string['saveok'] = 'Successfully saved provider.'; diff --git a/version.php b/version.php index c2872ef..083e3f5 100644 --- a/version.php +++ b/version.php @@ -26,8 +26,8 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2019112600; +$plugin->version = 2026031300; $plugin->requires = 2019111800; $plugin->component = 'filter_oembed'; $plugin->maturity = MATURITY_STABLE; -$plugin->release = '3.8.0 (Build - 2019112600)'; +$plugin->release = '3.8.0 (Build - 2026031300)'; From b1eb9327d6fe846f319fd1c6bcd928260531ac08 Mon Sep 17 00:00:00 2001 From: Peter Sistrom Date: Fri, 13 Mar 2026 21:06:51 +1100 Subject: [PATCH 2/6] UI tweaks --- amd/build/manageproviders.min.js | 24 +++++++++++++++++++++- amd/src/manageproviders.js | 30 +++++++++++++++++++++++----- classes/forms/provider.php | 12 +++++++++-- classes/service/oembed.php | 2 +- db/upgrade.php | 1 - lang/en/filter_oembed.php | 2 +- templates/managementpage.mustache | 8 +++++--- templates/managementpagerow.mustache | 11 +++++++--- 8 files changed, 73 insertions(+), 17 deletions(-) diff --git a/amd/build/manageproviders.min.js b/amd/build/manageproviders.min.js index 21dd840..75b790f 100644 --- a/amd/build/manageproviders.min.js +++ b/amd/build/manageproviders.min.js @@ -1 +1,23 @@ -define(["jquery","core/notification","core/ajax","core/templates","core/fragment","core/str","filter_oembed/list"],function(a,b,c,d,e,f,g){return{prevEditId:null,reloadRow:function(e,f,g,h){g=g?g:"reload",c.call([{methodname:"filter_oembed_provider_manage",args:{pid:e,action:g},done:function(b){d.render("filter_oembed/managementpagerow",b.providermodel).done(function(b){a(f).replaceWith(b),f=a("#oembed-display-providers_"+e),"function"==typeof h&&h(f)})},fail:function(a){b.exception(a)}}],!0,!0)},reloadProviders:function(e){c.call([{methodname:"filter_oembed_providers",args:{scope:"all"},done:function(b){d.render("filter_oembed/managementpage",b).done(function(b){var c=a(a.parseHTML(b)).html();a("#providermanagement").html(c),"function"==typeof e&&e()})},fail:function(a){b.exception(a)}}],!0,!0)},listenEnableDisable:function(){var b=this;a("#providermanagement").on("click",".oembed-provider-actions .filter-oembed-visibility",function(c){c.preventDefault();var d=a(this).parents("tr")[0],e=a(d).data("pid"),f=!a(d).hasClass("dimmed_text"),g=f?"disable":"enable";b.reloadRow(e,d,g)})},listenDelete:function(){var d=function(d){var e=a(d).data("pid");c.call([{methodname:"filter_oembed_provider_manage",args:{pid:e,action:"delete"},done:function(){a(d).remove()},fail:function(a){b.exception(a)}}],!0,!0)};a("#providermanagement").on("click",".oembed-provider-actions .filter-oembed-delete",function(c){c.preventDefault();var e=a(this).parents("tr")[0],g=a(a(this).parents("td").find(".list-providername")[0]).text();f.get_strings([{key:"deleteprovidertitle",component:"filter_oembed"},{key:"deleteproviderconfirm",component:"filter_oembed",param:g},{key:"ok",component:"core"},{key:"cancel",component:"core"}]).done(function(a){var c=a[0],f=a[1],g=a[2],h=a[3];b.confirm(c,f,g,h,function(){d(e)})})})},listenEdit:function(){var b=this,c=function(b){var c="#oembed-display-providers_"+b;a(c).removeClass("oembed-provider-editing"),a(c+" form").remove(),a(c+" td div.alert").remove()},f=function(b,c,f){var g,h=new RegExp("(?:course-)(\\S)"),i=h.exec(a("body").attr("class")),j=parseInt(i[1]);g=c?{formdata:c,pid:b}:{pid:b},e.loadFragment("filter_oembed","provider",j,g).done(function(c,e){a("#oembed-display-providers_"+b).addClass("oembed-provider-editing"),d.replaceNodeContents(a("#oembed-display-providers_"+b+" .oembed-provider-details"),c,e),"function"==typeof f&&f()})};a("#providermanagement").on("click",".oembed-provider-actions .filter-oembed-edit",function(d){d.preventDefault();var e=a(this).parents("tr")[0],g=a(e).data("pid");null!==b.prevEditId&&(c(b.prevEditId),c(g)),b.prevEditId=g,f(g)}),a("#providermanagement").on("click",".oembed-provider-details form #id_submitbutton",function(d){d.preventDefault();var e=a(this).parents("tr")[0],g=a(e).data("pid"),h=a(this).parents("form")[0],i=a(h).find('input[name="source"]').val();a(h).trigger("save-form-state");var j=a(h).serialize();f(g,j,function(){var d="#oembed-display-providers_"+g+" .oembed-provider-details",f=d+" div.alert-success",h=a(f);if(h.length){var j=h[0].outerHTML;if(c(g),i.indexOf("download::")>-1){var k=d+" .js-oembed-newprovider",l=a(k);l.length&&(g=l.data("newproviderid"))}var m=function(){var b=a("#oembed-display-providers_"+g+" td");a(b).append(j),a(b).find(" div.alert-success").attr("tabindex",-1),a(b).find(" div.alert-success").focus()};i.indexOf("download::")>-1?b.reloadProviders(m):b.reloadRow(g,e,"reload",m)}})}),a("#providermanagement").on("click",".oembed-provider-details form #id_cancel",function(b){b.preventDefault();var d=a(this).parents("tr")[0];c(a(d).data("pid"))})},init:function(){var a={valueNames:["list-providername"]};new g("providermanagement",a),this.listenEnableDisable(),this.listenDelete(),this.listenEdit()}}}); \ No newline at end of file +/** + * This file is part of Moodle - http://moodle.org/ + * + * Moodle is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Moodle is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moodle. If not, see . + * + * @package filter_oembed + * @copyright Guy Thomas / moodlerooms.com 2016 + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define("filter_oembed/manageproviders",["jquery","core/notification","core/ajax","core/templates","core/fragment","core/str","filter_oembed/list"],(function($,notification,ajax,templates,fragment,str,List){return{prevEditId:null,reloadRow:function(pid,row,action,callback){action=action||"reload",ajax.call([{methodname:"filter_oembed_provider_manage",args:{pid:pid,action:action},done:function(response){templates.render("filter_oembed/managementpagerow",response.providermodel).done((function(result){$(row).replaceWith(result),row=$("#oembed-display-providers_"+pid),"function"==typeof callback&&callback(row)}))},fail:function(response){notification.exception(response)}}],!0,!0)},reloadProviders:function(callback){ajax.call([{methodname:"filter_oembed_providers",args:{scope:"all"},done:function(response){templates.render("filter_oembed/managementpage",response).done((function(result){var resultHtml=$($.parseHTML(result)).html();$("#providermanagement").html(resultHtml),"function"==typeof callback&&callback()}))},fail:function(response){notification.exception(response)}}],!0,!0)},listenEnableDisable:function(){var self=this;$("#providermanagement").on("click",".oembed-provider-actions .filter-oembed-visibility",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],pid=$(row).data("pid"),action=!$(row).hasClass("dimmed_text")?"disable":"enable";self.reloadRow(pid,row,action)}))},listenDelete:function(){$("#providermanagement").on("click",".oembed-provider-actions .filter-oembed-delete",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],providerName=$($(this).parents("td").find(".list-providername")[0]).text();str.get_strings([{key:"deleteprovidertitle",component:"filter_oembed"},{key:"deleteproviderconfirm",component:"filter_oembed",param:providerName},{key:"ok",component:"core"},{key:"cancel",component:"core"}]).done((function(strings){var delTitle=strings[0],delConf=strings[1],ok=strings[2],cancel=strings[3];notification.confirm(delTitle,delConf,ok,cancel,(function(){!function(row){var pid=$(row).data("pid");ajax.call([{methodname:"filter_oembed_provider_manage",args:{pid:pid,action:"delete"},done:function(){$(row).remove()},fail:function(response){notification.exception(response)}}],!0,!0)}(row)}))}))}))},listenEdit:function(){var self=this,turnEditingOff=function(provderId){var sel="#oembed-display-providers_"+provderId;$(sel).removeClass("oembed-provider-editing"),$(sel+" form").remove(),$(sel+" td div.alert").remove()},updateProviderForm=function(pid,data,callback){var params,result=new RegExp("(?:course-)(\\S)").exec($("body").attr("class")),contextid=parseInt(result[1]);params=data?{formdata:data,pid:pid}:{pid:pid},fragment.loadFragment("filter_oembed","provider",contextid,params).done((function(html,js){$("#oembed-display-providers_"+pid).addClass("oembed-provider-editing"),templates.replaceNodeContents($("#oembed-display-providers_"+pid+" .oembed-provider-details"),html,js),"function"==typeof callback&&callback()}))};$("#providermanagement").on("click",".oembed-provider-actions .filter-oembed-edit",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],pid=$(row).data("pid");null!==self.prevEditId&&(turnEditingOff(self.prevEditId),turnEditingOff(pid)),self.prevEditId=pid,updateProviderForm(pid)})),$("#providermanagement").on("click",".list-providername",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],pid=$(row).data("pid");null!==self.prevEditId&&(turnEditingOff(self.prevEditId),turnEditingOff(pid)),self.prevEditId=pid,updateProviderForm(pid)})),$("#providermanagement").on("click",".oembed-provider-details form #id_submitbutton",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],pid=$(row).data("pid"),form=$(this).parents("form")[0],source=$(form).find('input[name="source"]').val();$(form).trigger("save-form-state");var data=$(form).serialize();updateProviderForm(pid,data,(function(){var detailsSel="#oembed-display-providers_"+pid+" .oembed-provider-details",successEl=$(detailsSel+" div.alert-success");if(successEl.length){var successHTML=successEl[0].outerHTML;if(turnEditingOff(pid),source.indexOf("download::")>-1){var newProviderEl=$(detailsSel+" .js-oembed-newprovider");newProviderEl.length&&(pid=newProviderEl.data("newproviderid"))}var onReload=function(){$("#providermanagement .alert-success").remove(),$("#providermanagement").prepend(successHTML),$("#providermanagement div.alert-success").attr("tabindex",-1),$("#providermanagement div.alert-success").focus()};source.indexOf("download::")>-1?self.reloadProviders(onReload):self.reloadRow(pid,row,"reload",onReload)}}))})),$("#providermanagement").on("click",".oembed-provider-details form #id_cancel",(function(e){e.preventDefault();var row=$(this).parents("tr")[0];turnEditingOff($(row).data("pid"))}))},init:function(){new List("providermanagement",{valueNames:["list-providername"]}),this.listenEnableDisable(),this.listenDelete(),this.listenEdit()}}})); + +//# sourceMappingURL=manageproviders.min.js.map \ No newline at end of file diff --git a/amd/src/manageproviders.js b/amd/src/manageproviders.js index 700cd00..4d91b24 100644 --- a/amd/src/manageproviders.js +++ b/amd/src/manageproviders.js @@ -206,7 +206,7 @@ define(['jquery', 'core/notification', 'core/ajax', 'core/templates', 'core/frag ); }; - // Listen for click cancel. + // Listen for click on edit button. $('#providermanagement').on('click', '.oembed-provider-actions .filter-oembed-edit', function(e) { e.preventDefault(); @@ -224,6 +224,24 @@ define(['jquery', 'core/notification', 'core/ajax', 'core/templates', 'core/frag updateProviderForm(pid); }); + // Listen for click on provider name. + $('#providermanagement').on('click', '.list-providername', function(e) { + e.preventDefault(); + + var row = $(this).parents('tr')[0]; + var pid = $(row).data('pid'); + + // Remove editing class from current row / previous row and delete form. + if (self.prevEditId !== null) { + turnEditingOff(self.prevEditId); + turnEditingOff(pid); + } + + self.prevEditId = pid; + + updateProviderForm(pid); + }); + // Listen for form click submit. $('#providermanagement').on('click', '.oembed-provider-details form #id_submitbutton', function(e) { e.preventDefault(); @@ -256,10 +274,12 @@ define(['jquery', 'core/notification', 'core/ajax', 'core/templates', 'core/frag * On reloading providers or single row append success HTML. */ var onReload = function() { - var rowcell = $('#oembed-display-providers_' + pid + ' td'); - $(rowcell).append(successHTML); - $(rowcell).find(' div.alert-success').attr('tabindex', -1); - $(rowcell).find(' div.alert-success').focus(); + // Remove any existing success messages. + $('#providermanagement .alert-success').remove(); + // Add success message at the top of the provider management div. + $('#providermanagement').prepend(successHTML); + $('#providermanagement div.alert-success').attr('tabindex', -1); + $('#providermanagement div.alert-success').focus(); }; if (source.indexOf('download::') > -1) { diff --git a/classes/forms/provider.php b/classes/forms/provider.php index fabd999..28dbfce 100644 --- a/classes/forms/provider.php +++ b/classes/forms/provider.php @@ -67,7 +67,7 @@ public function definition() { } else { $fieldlabel = get_string($fieldname, 'filter_oembed'); } - + if ($fieldname === 'rendermode') { $options = [ 'server' => get_string('rendermode_server', 'filter_oembed'), @@ -77,7 +77,15 @@ public function definition() { } else { $el = $mform->addElement($row->type, $fieldname, $fieldlabel); } - + + if ($fieldname === 'providerurl') { + $el->updateAttributes(['size' => '80']); + } + + if ($fieldname === 'endpoints') { + $el->updateAttributes(['style' => 'font-family: monospace; white-space: pre-wrap; width: 100%;', 'rows' => '10', 'cols' => '80']); + } + if (!empty($commonattributes)) { $el->updateAttributes($commonattributes); } diff --git a/classes/service/oembed.php b/classes/service/oembed.php index 5dfb2c0..b257e13 100755 --- a/classes/service/oembed.php +++ b/classes/service/oembed.php @@ -128,7 +128,7 @@ protected function get_provider_instance($provider) { */ public function html_output($text) { global $PAGE; - + $lazyload = get_config('filter_oembed', 'lazyload'); $lazyload = $lazyload == 1 || $lazyload === false; $output = ''; diff --git a/db/upgrade.php b/db/upgrade.php index cf07aec..364fdf8 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -143,7 +143,6 @@ function xmldb_filter_oembed_upgrade($oldversion) { } if ($oldversion < 2026031300) { - // Define field rendermode to be added to filter_oembed. $table = new xmldb_table('filter_oembed'); $field = new xmldb_field('rendermode', XMLDB_TYPE_CHAR, '50', null, null, null, 'server', 'enabled'); diff --git a/lang/en/filter_oembed.php b/lang/en/filter_oembed.php index c4b8ab1..f56c886 100644 --- a/lang/en/filter_oembed.php +++ b/lang/en/filter_oembed.php @@ -55,10 +55,10 @@ $string['providersrestrict'] = 'Restrict providers'; $string['providersrestrict_desc'] = 'Restrict providers to a list of allowed providers'; $string['providerurl'] = 'Provider URL'; -$string['requiredfield'] = 'The field "{$a}" must be completed'; $string['rendermode'] = 'Render mode'; $string['rendermode_client'] = 'Client'; $string['rendermode_server'] = 'Server'; +$string['requiredfield'] = 'The field "{$a}" must be completed'; $string['saveasnew'] = 'Save as new local'; $string['savefailed'] = 'Failed to save provider.'; $string['saveok'] = 'Successfully saved provider.'; diff --git a/templates/managementpage.mustache b/templates/managementpage.mustache index 64c2f53..eda6c75 100644 --- a/templates/managementpage.mustache +++ b/templates/managementpage.mustache @@ -52,11 +52,13 @@ Displayed if x was true. + {{#str}}providerurl, filter_oembed{{/str}} + - + {{#str}}localproviders, filter_oembed{{/str}} @@ -64,7 +66,7 @@ Displayed if x was true. {{> filter_oembed/managementpagerow}} {{/localrows}} - + {{#str}}pluginproviders, filter_oembed{{/str}} @@ -72,7 +74,7 @@ Displayed if x was true. {{> filter_oembed/managementpagerow}} {{/pluginrows}} - + {{#str}}downloadproviders, filter_oembed{{/str}} diff --git a/templates/managementpagerow.mustache b/templates/managementpagerow.mustache index dda248d..3f47cb3 100644 --- a/templates/managementpagerow.mustache +++ b/templates/managementpagerow.mustache @@ -35,9 +35,14 @@ }} - {{providername}} -
-
+ {{providername}} +
+
+ + + {{providerurl}} + +
{{{enableaction}}} {{{editaction}}} {{{deleteaction}}}
From 75995fb1c401a895c6b7e54234343ca6a851eaa7 Mon Sep 17 00:00:00 2001 From: Peter Sistrom Date: Tue, 17 Mar 2026 15:46:37 +1100 Subject: [PATCH 3/6] Add new local provider --- amd/build/manageproviders.min.js | 2 +- amd/src/manageproviders.js | 61 +++++++++++++++++++++++++++++-- classes/service/oembed.php | 19 ++++++++++ lang/en/filter_oembed.php | 1 + lib.php | 53 ++++++++++++++++++--------- templates/managementpage.mustache | 3 ++ 6 files changed, 117 insertions(+), 22 deletions(-) diff --git a/amd/build/manageproviders.min.js b/amd/build/manageproviders.min.js index 75b790f..64f8b48 100644 --- a/amd/build/manageproviders.min.js +++ b/amd/build/manageproviders.min.js @@ -18,6 +18,6 @@ * @copyright Guy Thomas / moodlerooms.com 2016 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -define("filter_oembed/manageproviders",["jquery","core/notification","core/ajax","core/templates","core/fragment","core/str","filter_oembed/list"],(function($,notification,ajax,templates,fragment,str,List){return{prevEditId:null,reloadRow:function(pid,row,action,callback){action=action||"reload",ajax.call([{methodname:"filter_oembed_provider_manage",args:{pid:pid,action:action},done:function(response){templates.render("filter_oembed/managementpagerow",response.providermodel).done((function(result){$(row).replaceWith(result),row=$("#oembed-display-providers_"+pid),"function"==typeof callback&&callback(row)}))},fail:function(response){notification.exception(response)}}],!0,!0)},reloadProviders:function(callback){ajax.call([{methodname:"filter_oembed_providers",args:{scope:"all"},done:function(response){templates.render("filter_oembed/managementpage",response).done((function(result){var resultHtml=$($.parseHTML(result)).html();$("#providermanagement").html(resultHtml),"function"==typeof callback&&callback()}))},fail:function(response){notification.exception(response)}}],!0,!0)},listenEnableDisable:function(){var self=this;$("#providermanagement").on("click",".oembed-provider-actions .filter-oembed-visibility",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],pid=$(row).data("pid"),action=!$(row).hasClass("dimmed_text")?"disable":"enable";self.reloadRow(pid,row,action)}))},listenDelete:function(){$("#providermanagement").on("click",".oembed-provider-actions .filter-oembed-delete",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],providerName=$($(this).parents("td").find(".list-providername")[0]).text();str.get_strings([{key:"deleteprovidertitle",component:"filter_oembed"},{key:"deleteproviderconfirm",component:"filter_oembed",param:providerName},{key:"ok",component:"core"},{key:"cancel",component:"core"}]).done((function(strings){var delTitle=strings[0],delConf=strings[1],ok=strings[2],cancel=strings[3];notification.confirm(delTitle,delConf,ok,cancel,(function(){!function(row){var pid=$(row).data("pid");ajax.call([{methodname:"filter_oembed_provider_manage",args:{pid:pid,action:"delete"},done:function(){$(row).remove()},fail:function(response){notification.exception(response)}}],!0,!0)}(row)}))}))}))},listenEdit:function(){var self=this,turnEditingOff=function(provderId){var sel="#oembed-display-providers_"+provderId;$(sel).removeClass("oembed-provider-editing"),$(sel+" form").remove(),$(sel+" td div.alert").remove()},updateProviderForm=function(pid,data,callback){var params,result=new RegExp("(?:course-)(\\S)").exec($("body").attr("class")),contextid=parseInt(result[1]);params=data?{formdata:data,pid:pid}:{pid:pid},fragment.loadFragment("filter_oembed","provider",contextid,params).done((function(html,js){$("#oembed-display-providers_"+pid).addClass("oembed-provider-editing"),templates.replaceNodeContents($("#oembed-display-providers_"+pid+" .oembed-provider-details"),html,js),"function"==typeof callback&&callback()}))};$("#providermanagement").on("click",".oembed-provider-actions .filter-oembed-edit",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],pid=$(row).data("pid");null!==self.prevEditId&&(turnEditingOff(self.prevEditId),turnEditingOff(pid)),self.prevEditId=pid,updateProviderForm(pid)})),$("#providermanagement").on("click",".list-providername",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],pid=$(row).data("pid");null!==self.prevEditId&&(turnEditingOff(self.prevEditId),turnEditingOff(pid)),self.prevEditId=pid,updateProviderForm(pid)})),$("#providermanagement").on("click",".oembed-provider-details form #id_submitbutton",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],pid=$(row).data("pid"),form=$(this).parents("form")[0],source=$(form).find('input[name="source"]').val();$(form).trigger("save-form-state");var data=$(form).serialize();updateProviderForm(pid,data,(function(){var detailsSel="#oembed-display-providers_"+pid+" .oembed-provider-details",successEl=$(detailsSel+" div.alert-success");if(successEl.length){var successHTML=successEl[0].outerHTML;if(turnEditingOff(pid),source.indexOf("download::")>-1){var newProviderEl=$(detailsSel+" .js-oembed-newprovider");newProviderEl.length&&(pid=newProviderEl.data("newproviderid"))}var onReload=function(){$("#providermanagement .alert-success").remove(),$("#providermanagement").prepend(successHTML),$("#providermanagement div.alert-success").attr("tabindex",-1),$("#providermanagement div.alert-success").focus()};source.indexOf("download::")>-1?self.reloadProviders(onReload):self.reloadRow(pid,row,"reload",onReload)}}))})),$("#providermanagement").on("click",".oembed-provider-details form #id_cancel",(function(e){e.preventDefault();var row=$(this).parents("tr")[0];turnEditingOff($(row).data("pid"))}))},init:function(){new List("providermanagement",{valueNames:["list-providername"]}),this.listenEnableDisable(),this.listenDelete(),this.listenEdit()}}})); +define("filter_oembed/manageproviders",["jquery","core/notification","core/ajax","core/templates","core/fragment","core/str","filter_oembed/list"],(function($,notification,ajax,templates,fragment,str,List){return{prevEditId:null,reloadRow:function(pid,row,action,callback){action=action||"reload",ajax.call([{methodname:"filter_oembed_provider_manage",args:{pid:pid,action:action},done:function(response){templates.render("filter_oembed/managementpagerow",response.providermodel).done((function(result){$(row).replaceWith(result),row=$("#oembed-display-providers_"+pid),"function"==typeof callback&&callback(row)}))},fail:function(response){notification.exception(response)}}],!0,!0)},reloadProviders:function(callback){ajax.call([{methodname:"filter_oembed_providers",args:{scope:"all"},done:function(response){templates.render("filter_oembed/managementpage",response).done((function(result){var resultHtml=$($.parseHTML(result)).html();$("#providermanagement").html(resultHtml),"function"==typeof callback&&callback()}))},fail:function(response){notification.exception(response)}}],!0,!0)},listenEnableDisable:function(){var self=this;$("#providermanagement").on("click",".oembed-provider-actions .filter-oembed-visibility",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],pid=$(row).data("pid"),action=!$(row).hasClass("dimmed_text")?"disable":"enable";self.reloadRow(pid,row,action)}))},listenDelete:function(){$("#providermanagement").on("click",".oembed-provider-actions .filter-oembed-delete",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],providerName=$($(this).parents("td").find(".list-providername")[0]).text();str.get_strings([{key:"deleteprovidertitle",component:"filter_oembed"},{key:"deleteproviderconfirm",component:"filter_oembed",param:providerName},{key:"ok",component:"core"},{key:"cancel",component:"core"}]).done((function(strings){var delTitle=strings[0],delConf=strings[1],ok=strings[2],cancel=strings[3];notification.confirm(delTitle,delConf,ok,cancel,(function(){!function(row){var pid=$(row).data("pid");ajax.call([{methodname:"filter_oembed_provider_manage",args:{pid:pid,action:"delete"},done:function(){$(row).remove()},fail:function(response){notification.exception(response)}}],!0,!0)}(row)}))}))}))},listenEdit:function(){var self=this,turnEditingOff=function(provderId){var sel="#oembed-display-providers_"+provderId;$(sel).removeClass("oembed-provider-editing"),$(sel+" form").remove(),$(sel+" td div.alert").remove()},updateProviderForm=function(pid,data,callback){var params,result=new RegExp("(?:course-)(\\S)").exec($("body").attr("class")),contextid=parseInt(result[1]);params=data?{formdata:data,pid:pid}:{pid:pid},fragment.loadFragment("filter_oembed","provider",contextid,params).done((function(html,js){$("#oembed-display-providers_"+pid).addClass("oembed-provider-editing"),templates.replaceNodeContents($("#oembed-display-providers_"+pid+" .oembed-provider-details"),html,js),"function"==typeof callback&&callback()}))};$("#providermanagement").on("click",".oembed-provider-actions .filter-oembed-edit",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],pid=$(row).data("pid");null!==self.prevEditId&&(turnEditingOff(self.prevEditId),turnEditingOff(pid)),self.prevEditId=pid,updateProviderForm(pid)})),$("#providermanagement").on("click",".list-providername",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],pid=$(row).data("pid");null!==self.prevEditId&&(turnEditingOff(self.prevEditId),turnEditingOff(pid)),self.prevEditId=pid,updateProviderForm(pid)})),$("#providermanagement").on("click",".oembed-provider-details form #id_submitbutton",(function(e){e.preventDefault();var row=$(this).parents("tr")[0],pid=$(row).data("pid"),form=$(this).parents("form")[0],source=$(form).find('input[name="source"]').val();$(form).trigger("save-form-state");var data=$(form).serialize();updateProviderForm(pid,data,(function(){var detailsSel="#oembed-display-providers_"+pid+" .oembed-provider-details",successEl=$(detailsSel+" div.alert-success");if(successEl.length){var successHTML=successEl[0].outerHTML;turnEditingOff(pid);var isNewProvider=!1;if(source.indexOf("download::")>-1||source.indexOf("local::new")>-1){var newProviderEl=$(detailsSel+" .js-oembed-newprovider");newProviderEl.length&&(pid=newProviderEl.data("newproviderid"),isNewProvider=!0)}var onReload=function(){$("#providermanagement .alert-success").remove(),$("#providermanagement").prepend(successHTML),$("#providermanagement div.alert-success").attr("tabindex",-1),$("#providermanagement div.alert-success").focus()};source.indexOf("download::")>-1||isNewProvider?self.reloadProviders(onReload):self.reloadRow(pid,row,"reload",onReload)}}))})),$("#providermanagement").on("click",".oembed-provider-details form #id_cancel",(function(e){e.preventDefault();var row=$(this).parents("tr")[0];turnEditingOff($(row).data("pid"))}))},listenAddProvider:function(){var self=this;$("#oembed-add-provider").on("click",(function(e){if(e.preventDefault(),null!==self.prevEditId){var sel="#oembed-display-providers_"+self.prevEditId;$(sel).removeClass("oembed-provider-editing"),$(sel+" form").remove(),$(sel+" td div.alert").remove()}var newRowId="oembed-display-providers_0",existingNewRow=$("#"+newRowId);existingNewRow.length&&existingNewRow.remove();$("#oembed-local-providers-head-row").after('
'),self.prevEditId=0;var result=new RegExp("(?:course-)(\\S)").exec($("body").attr("class")),contextid=parseInt(result[1]);fragment.loadFragment("filter_oembed","provider",contextid,{pid:0}).done((function(html,js){templates.replaceNodeContents($("#"+newRowId+" .oembed-provider-details"),html,js)}))}))},init:function(){new List("providermanagement",{valueNames:["list-providername"]}),this.listenEnableDisable(),this.listenDelete(),this.listenEdit(),this.listenAddProvider()}}})); //# sourceMappingURL=manageproviders.min.js.map \ No newline at end of file diff --git a/amd/src/manageproviders.js b/amd/src/manageproviders.js index 4d91b24..f979975 100644 --- a/amd/src/manageproviders.js +++ b/amd/src/manageproviders.js @@ -262,11 +262,13 @@ define(['jquery', 'core/notification', 'core/ajax', 'core/templates', 'core/frag turnEditingOff(pid); // Get new provider id and set pid to it so correct row is targeted on reload. - if (source.indexOf('download::') > -1) { + var isNewProvider = false; + if (source.indexOf('download::') > -1 || source.indexOf('local::new') > -1) { var newProviderSel = detailsSel + ' .js-oembed-newprovider'; var newProviderEl = $(newProviderSel); if (newProviderEl.length) { pid = newProviderEl.data('newproviderid'); + isNewProvider = true; } } @@ -282,9 +284,9 @@ define(['jquery', 'core/notification', 'core/ajax', 'core/templates', 'core/frag $('#providermanagement div.alert-success').focus(); }; - if (source.indexOf('download::') > -1) { - // When a downloaded provider is saved, a new one is created as a local provider, so we - // need to reload the full list. + if (source.indexOf('download::') > -1 || isNewProvider) { + // When a downloaded provider is saved or a new provider is created, + // a new one is created as a local provider, so we need to reload the full list. self.reloadProviders(onReload); } else { self.reloadRow(pid, row, 'reload', onReload); @@ -301,6 +303,56 @@ define(['jquery', 'core/notification', 'core/ajax', 'core/templates', 'core/frag }); }, + /** + * Listen for add new provider button. + */ + listenAddProvider: function() { + var self = this; + + $('#oembed-add-provider').on('click', function(e) { + e.preventDefault(); + + // Turn off any existing editing. + if (self.prevEditId !== null) { + var sel = '#oembed-display-providers_' + self.prevEditId; + $(sel).removeClass('oembed-provider-editing'); + $(sel + ' form').remove(); + $(sel + ' td div.alert').remove(); + } + + // Create a temporary row for the new provider form. + var newRowId = 'oembed-display-providers_0'; + var existingNewRow = $('#' + newRowId); + if (existingNewRow.length) { + existingNewRow.remove(); + } + + var newRow = '' + + '' + + '
' + + ''; + + $('#oembed-local-providers-head-row').after(newRow); + + self.prevEditId = 0; + + // Load the form fragment for a new provider. + var rx = new RegExp('(?:course-)(\\S)'); + var result = rx.exec($('body').attr('class')); + var contextid = parseInt(result[1]); + + fragment.loadFragment('filter_oembed', 'provider', contextid, {pid: 0}).done( + function(html, js) { + templates.replaceNodeContents( + $('#' + newRowId + ' .oembed-provider-details'), + html, + js + ); + } + ); + }); + }, + /** * Initialise. */ @@ -314,6 +366,7 @@ define(['jquery', 'core/notification', 'core/ajax', 'core/templates', 'core/frag this.listenEnableDisable(); this.listenDelete(); this.listenEdit(); + this.listenAddProvider(); } }; } diff --git a/classes/service/oembed.php b/classes/service/oembed.php index b257e13..6a997dc 100755 --- a/classes/service/oembed.php +++ b/classes/service/oembed.php @@ -666,6 +666,25 @@ public function update_provider_row($providerdata) { return $DB->update_record('filter_oembed', $providerdata); } + /** + * Create a new local provider. + * @param array|object $providerdata + * @return bool|int + */ + public function create_local_provider($providerdata) { + global $DB; + $providerdata = (array)$providerdata; + $newsource = provider::PROVIDER_SOURCE_LOCAL . strtolower(str_replace(' ', '', $providerdata['providername'])); + if ($DB->record_exists('filter_oembed', ['source' => $newsource])) { + return false; + } + $providerdata['source'] = $newsource; + $providerdata['timecreated'] = time(); + $providerdata['timemodified'] = time(); + unset($providerdata['id']); // Ensure no id is set for new record. + return $DB->insert_record('filter_oembed', $providerdata, true); + } + /** * Copy downloaded provider row to new local row (or update). * @param array|object $providerdata diff --git a/lang/en/filter_oembed.php b/lang/en/filter_oembed.php index f56c886..cee684b 100644 --- a/lang/en/filter_oembed.php +++ b/lang/en/filter_oembed.php @@ -24,6 +24,7 @@ * oEmbed filter ( Mike Churchward, James McQuillan, Vinayak (Vin) Bhalerao, Josh Gavant and Rob Dolin) */ +$string['addnewlocalprovider'] = 'Add new local provider'; $string['atag'] = 'Filter on < a > tags'; $string['cachedef_embeddata'] = 'oEmbed Cache'; $string['cachelifespan'] = 'Cache lifespan'; diff --git a/lib.php b/lib.php index 9ce2948..5645b95 100644 --- a/lib.php +++ b/lib.php @@ -41,6 +41,7 @@ function filter_oembed_output_fragment_provider($args) { $data = null; $ajaxdata = null; + $isnew = false; if (!empty($args['formdata'])) { $data = []; parse_str($args['formdata'], $data); @@ -50,6 +51,17 @@ function filter_oembed_output_fragment_provider($args) { } else { if (!isset($args['pid'])) { throw new coding_exception('missing "pid" param'); + } else if ($args['pid'] === 'new' || $args['pid'] === 0 || $args['pid'] === '0') { + // Creating a new provider. + $isnew = true; + $data = [ + 'providername' => '', + 'providerurl' => '', + 'endpoints' => '', + 'enabled' => 1, + 'rendermode' => 'server', + 'source' => 'local::new', + ]; } else { $data = $oembed->get_provider_row($args['pid']); if (!$data) { @@ -80,29 +92,36 @@ function filter_oembed_output_fragment_provider($args) { $msg = ''; if (!empty($ajaxdata)) { if ($form->is_validated()) { - // If editing a downloaded provider, create a new local one and disable the download one. - $sourcetype = \filter_oembed\provider\provider::source_type($ajaxdata['source']); - if ($sourcetype == \filter_oembed\provider\provider::PROVIDER_SOURCE_DOWNLOAD) { - $newpid = $oembed->copy_provider_to_local($ajaxdata); + // Check if this is a new provider being created. + if ($ajaxdata['source'] === 'local::new' || empty($ajaxdata['id'])) { + $newpid = $oembed->create_local_provider($ajaxdata); if ($newpid) { - $msg = $output->notification( - get_string('copytolocal', 'filter_oembed', $ajaxdata['providername']), - 'notifysuccess' - ); - // Return an empty div with the new provider id in it so we can target it later with the message in $msg. + $msg = $output->notification(get_string('saveok', 'filter_oembed'), 'notifysuccess'); return '
' . $msg; } else { - $msg = $output->notification( - get_string('nocopytolocal', 'filter_oembed', $ajaxdata['providername']), - 'notifyproblem' - ); + $msg = $output->notification(get_string('savefailed', 'filter_oembed'), 'notifyproblem'); } } else { - $success = $oembed->update_provider_row($ajaxdata); - if ($success) { - $msg = $output->notification(get_string('saveok', 'filter_oembed'), 'notifysuccess'); + // If editing a downloaded provider, create a new local one and disable the download one. + $sourcetype = \filter_oembed\provider\provider::source_type($ajaxdata['source']); + if ($sourcetype == \filter_oembed\provider\provider::PROVIDER_SOURCE_DOWNLOAD) { + $newpid = $oembed->copy_provider_to_local($ajaxdata); + if ($newpid) { + $msg = $output->notification(get_string('copytolocal', 'filter_oembed', $ajaxdata['providername']), + 'notifysuccess'); + // Return an empty div with the new provider id in it so we can target it later with the message in $msg. + return '
'.$msg; + } else { + $msg = $output->notification(get_string('nocopytolocal', 'filter_oembed', $ajaxdata['providername']), + 'notifyproblem'); + } } else { - $msg = $output->notification(get_string('savefailed', 'filter_oembed'), 'notifyproblem'); + $success = $oembed->update_provider_row($ajaxdata); + if ($success) { + $msg = $output->notification(get_string('saveok', 'filter_oembed'), 'notifysuccess'); + } else { + $msg = $output->notification(get_string('savefailed', 'filter_oembed'), 'notifyproblem'); + } } } } diff --git a/templates/managementpage.mustache b/templates/managementpage.mustache index eda6c75..091acba 100644 --- a/templates/managementpage.mustache +++ b/templates/managementpage.mustache @@ -60,6 +60,9 @@ Displayed if x was true. {{#str}}localproviders, filter_oembed{{/str}} + {{#localrows}} From b5db150c675d85b3264e89200b9d6dc30cc7d42d Mon Sep 17 00:00:00 2001 From: Peter Sistrom Date: Wed, 18 Mar 2026 11:17:31 +1100 Subject: [PATCH 4/6] Remove package.json --- package.json | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 package.json diff --git a/package.json b/package.json deleted file mode 100644 index 491b689..0000000 --- a/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "description": "Grunt tasks for sass.", - "private": true, - "devDependencies": { - "grunt": "~0.4.1", - "grunt-autoprefixer": "^3.0.3", - "grunt-contrib-csslint": "^0.5.0", - "grunt-contrib-jshint": "^0.12.0", - "grunt-sass": "^1.2.1", - "grunt-cssbeautifier": "^0.1.2", - "grunt-contrib-watch": "^0.6.1", - "grunt-exec": "~0.4.2", - "grunt-load-gruntfile" : "^0.0.2" - } -} From 7291a87e02139c6448bf32cfd92904d7f6ee01e6 Mon Sep 17 00:00:00 2001 From: Peter Sistrom Date: Wed, 18 Mar 2026 11:38:36 +1100 Subject: [PATCH 5/6] CI fixes --- classes/forms/provider.php | 6 +++++- lib.php | 14 +++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/classes/forms/provider.php b/classes/forms/provider.php index 28dbfce..bf218a8 100644 --- a/classes/forms/provider.php +++ b/classes/forms/provider.php @@ -83,7 +83,11 @@ public function definition() { } if ($fieldname === 'endpoints') { - $el->updateAttributes(['style' => 'font-family: monospace; white-space: pre-wrap; width: 100%;', 'rows' => '10', 'cols' => '80']); + $el->updateAttributes([ + 'style' => 'font-family: monospace; white-space: pre-wrap; width: 100%;', + 'rows' => '10', + 'cols' => '80', + ]); } if (!empty($commonattributes)) { diff --git a/lib.php b/lib.php index 5645b95..a9657c9 100644 --- a/lib.php +++ b/lib.php @@ -107,13 +107,17 @@ function filter_oembed_output_fragment_provider($args) { if ($sourcetype == \filter_oembed\provider\provider::PROVIDER_SOURCE_DOWNLOAD) { $newpid = $oembed->copy_provider_to_local($ajaxdata); if ($newpid) { - $msg = $output->notification(get_string('copytolocal', 'filter_oembed', $ajaxdata['providername']), - 'notifysuccess'); + $msg = $output->notification( + get_string('copytolocal', 'filter_oembed', $ajaxdata['providername']), + 'notifysuccess' + ); // Return an empty div with the new provider id in it so we can target it later with the message in $msg. - return '
'.$msg; + return '
' . $msg; } else { - $msg = $output->notification(get_string('nocopytolocal', 'filter_oembed', $ajaxdata['providername']), - 'notifyproblem'); + $msg = $output->notification( + get_string('nocopytolocal', 'filter_oembed', $ajaxdata['providername']), + 'notifyproblem' + ); } } else { $success = $oembed->update_provider_row($ajaxdata); From 64e62c8f4298777bf78d8ac662652337078c2ca5 Mon Sep 17 00:00:00 2001 From: Brendan Heywood Date: Tue, 24 Mar 2026 13:18:39 +1100 Subject: [PATCH 6/6] Tidy up mustache --- templates/managementpage.mustache | 44 ++++++++++++++++--------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/templates/managementpage.mustache b/templates/managementpage.mustache index 091acba..1bab8d8 100644 --- a/templates/managementpage.mustache +++ b/templates/managementpage.mustache @@ -49,41 +49,43 @@ Displayed if x was true. {{/x}}
- + - - - + + + - - + + - {{#localrows}} - {{> filter_oembed/managementpagerow}} - {{/localrows}} + {{#localrows}} + {{> filter_oembed/managementpagerow}} + {{/localrows}} - {{#pluginrows}} - {{> filter_oembed/managementpagerow}} - {{/pluginrows}} + {{#pluginrows}} + {{> filter_oembed/managementpagerow}} + {{/pluginrows}} - {{#downloadrows}} - {{> filter_oembed/managementpagerow}} - {{/downloadrows}} - + {{#downloadrows}} + {{> filter_oembed/managementpagerow}} + {{/downloadrows}} +
{{#str}}providerurl, filter_oembed{{/str}} + + {{#str}}providerurl, filter_oembed{{/str}}
- {{#str}}localproviders, filter_oembed{{/str}} - + {{#str}}localproviders, filter_oembed{{/str}} +
- {{#str}}pluginproviders, filter_oembed{{/str}} + {{#str}}pluginproviders, filter_oembed{{/str}}
{{#str}}downloadproviders, filter_oembed{{/str}}