mirror of
https://github.com/moodle/moodle.git
synced 2025-04-20 07:56:06 +02:00
MDL-82427 core_filters: Coding style tidyup
This commit is contained in:
parent
01f7456a96
commit
8d284e3ce0
@ -39,7 +39,7 @@ use stdClass;
|
||||
* You will then need to edit your moodle/config.php to invoke mathml_filter.php
|
||||
* -------------------------------------------------------------------------
|
||||
*
|
||||
* @package filter
|
||||
* @package filter_algebra
|
||||
* @subpackage algebra
|
||||
* @copyright 2004 Zbigniew Fiedorowicz fiedorow@math.ohio-state.edu
|
||||
* Originally based on code provided by Bruno Vernier bruno@vsbeducation.ca
|
||||
@ -50,39 +50,22 @@ class text_filter extends \core_filters\text_filter {
|
||||
public function filter($text, array $options = []) {
|
||||
global $CFG, $DB;
|
||||
|
||||
/// Do a quick check using stripos to avoid unnecessary wor
|
||||
if (!preg_match('/<algebra/i',$text) && !strstr($text,'@@')) {
|
||||
// Do a quick check using stripos to avoid unnecessary work.
|
||||
if (!preg_match('/<algebra/i', $text) && !strstr($text, '@@')) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
//restrict filtering to forum 130 (Maths Tools on moodle.org)
|
||||
# $scriptname = $_SERVER['SCRIPT_NAME'];
|
||||
# if (!strstr($scriptname,'/forum/')) {
|
||||
# return $text;
|
||||
# }
|
||||
# if (strstr($scriptname,'post.php')) {
|
||||
# $parent = forum_get_post_full($_GET['reply']);
|
||||
# $discussion = $DB->get_record("forum_discussions",array("id"=>$parent->discussion));
|
||||
# } else if (strstr($scriptname,'discuss.php')) {
|
||||
# $discussion = $DB->get_record("forum_discussions",array("id"=>$_GET['d']));
|
||||
# } else {
|
||||
# return $text;
|
||||
# }
|
||||
# if ($discussion->forum != 130) {
|
||||
# return $text;
|
||||
# }
|
||||
|
||||
preg_match_all('/@(@@+)([^@])/',$text,$matches);
|
||||
for ($i=0;$i<count($matches[0]);$i++) {
|
||||
$replacement = str_replace('@','@',$matches[1][$i]).$matches[2][$i];
|
||||
$text = str_replace($matches[0][$i],$replacement,$text);
|
||||
preg_match_all('/@(@@+)([^@])/', $text, $matches);
|
||||
for ($i = 0; $i < count($matches[0]); $i++) {
|
||||
$replacement = str_replace('@', '@', $matches[1][$i]) . $matches[2][$i];
|
||||
$text = str_replace($matches[0][$i], $replacement, $text);
|
||||
}
|
||||
|
||||
// The following regular expression matches TeX expressions delimited by:
|
||||
// <algebra> some algebraic input expression </algebra>
|
||||
// or @@ some algebraic input expression @@
|
||||
|
||||
// or @@ some algebraic input expression @@.
|
||||
preg_match_all('/<algebra>(.+?)<\/algebra>|@@(.+?)@@/is', $text, $matches);
|
||||
for ($i=0; $i<count($matches[0]); $i++) {
|
||||
for ($i = 0; $i < count($matches[0]); $i++) {
|
||||
$algebra = $matches[1][$i] . $matches[2][$i];
|
||||
|
||||
// Look for some common false positives, and skip processing them.
|
||||
@ -95,109 +78,113 @@ class text_filter extends \core_filters\text_filter {
|
||||
continue;
|
||||
}
|
||||
|
||||
$algebra = str_replace('<nolink>','',$algebra);
|
||||
$algebra = str_replace('</nolink>','',$algebra);
|
||||
$algebra = str_replace('<span class="nolink">','',$algebra);
|
||||
$algebra = str_replace('</span>','',$algebra);
|
||||
$algebra = str_replace('<nolink>', '', $algebra);
|
||||
$algebra = str_replace('</nolink>', '', $algebra);
|
||||
$algebra = str_replace('<span class="nolink">', '', $algebra);
|
||||
$algebra = str_replace('</span>', '', $algebra);
|
||||
$align = "middle";
|
||||
if (preg_match('/^align=bottom /',$algebra)) {
|
||||
$align = "text-bottom";
|
||||
$algebra = preg_replace('/^align=bottom /','',$algebra);
|
||||
} else if (preg_match('/^align=top /',$algebra)) {
|
||||
$align = "text-top";
|
||||
$algebra = preg_replace('/^align=top /','',$algebra);
|
||||
if (preg_match('/^align=bottom /', $algebra)) {
|
||||
$align = "text-bottom";
|
||||
$algebra = preg_replace('/^align=bottom /', '', $algebra);
|
||||
} else if (preg_match('/^align=top /', $algebra)) {
|
||||
$align = "text-top";
|
||||
$algebra = preg_replace('/^align=top /', '', $algebra);
|
||||
}
|
||||
$md5 = md5($algebra);
|
||||
$filename = $md5 . ".gif";
|
||||
if (! $texcache = $DB->get_record("cache_filters",array("filter"=>"algebra", "md5key"=>$md5))) {
|
||||
$algebra = str_replace('<','<',$algebra);
|
||||
$algebra = str_replace('>','>',$algebra);
|
||||
$algebra = str_replace('<>','#',$algebra);
|
||||
$algebra = str_replace('<=','%',$algebra);
|
||||
$algebra = str_replace('>=','!',$algebra);
|
||||
$algebra = preg_replace('/([=><%!#] *)-/',"\$1 zeroplace -",$algebra);
|
||||
$algebra = str_replace('delta','zdelta',$algebra);
|
||||
$algebra = str_replace('beta','bita',$algebra);
|
||||
$algebra = str_replace('theta','thita',$algebra);
|
||||
$algebra = str_replace('zeta','zita',$algebra);
|
||||
$algebra = str_replace('eta','xeta',$algebra);
|
||||
$algebra = str_replace('epsilon','zepslon',$algebra);
|
||||
$algebra = str_replace('upsilon','zupslon',$algebra);
|
||||
$algebra = preg_replace('!\r\n?!',' ',$algebra);
|
||||
$algebra = escapeshellarg($algebra);
|
||||
if ( (PHP_OS == "WINNT") || (PHP_OS == "WIN32") || (PHP_OS == "Windows")) {
|
||||
$cmd = "cd $CFG->dirroot\\filter\\algebra & algebra2tex.pl $algebra";
|
||||
} else {
|
||||
$cmd = "cd $CFG->dirroot/filter/algebra; ./algebra2tex.pl $algebra";
|
||||
}
|
||||
$texexp = `$cmd`;
|
||||
if (preg_match('/parsehilight/',$texexp)) {
|
||||
$text = str_replace( $matches[0][$i],"<b>Syntax error:</b> " . $texexp,$text);
|
||||
} else if ($texexp) {
|
||||
$texexp = str_replace('zeroplace','',$texexp);
|
||||
$texexp = str_replace('#','\not= ',$texexp);
|
||||
$texexp = str_replace('%','\leq ',$texexp);
|
||||
$texexp = str_replace('!','\geq ',$texexp);
|
||||
$texexp = str_replace('\left{','{',$texexp);
|
||||
$texexp = str_replace('\right}','}',$texexp);
|
||||
$texexp = str_replace('\fun',' ',$texexp);
|
||||
$texexp = str_replace('infty','\infty',$texexp);
|
||||
$texexp = str_replace('alpha','\alpha',$texexp);
|
||||
$texexp = str_replace('gamma','\gamma',$texexp);
|
||||
$texexp = str_replace('iota','\iota',$texexp);
|
||||
$texexp = str_replace('kappa','\kappa',$texexp);
|
||||
$texexp = str_replace('lambda','\lambda',$texexp);
|
||||
$texexp = str_replace('mu','\mu',$texexp);
|
||||
$texexp = str_replace('nu','\nu',$texexp);
|
||||
$texexp = str_replace('xi','\xi',$texexp);
|
||||
$texexp = str_replace('rho','\rho',$texexp);
|
||||
$texexp = str_replace('sigma','\sigma',$texexp);
|
||||
$texexp = str_replace('tau','\tau',$texexp);
|
||||
$texexp = str_replace('phi','\phi',$texexp);
|
||||
$texexp = str_replace('chi','\chi',$texexp);
|
||||
$texexp = str_replace('psi','\psi',$texexp);
|
||||
$texexp = str_replace('omega','\omega',$texexp);
|
||||
$texexp = str_replace('zdelta','\delta',$texexp);
|
||||
$texexp = str_replace('bita','\beta',$texexp);
|
||||
$texexp = str_replace('thita','\theta',$texexp);
|
||||
$texexp = str_replace('zita','\zeta',$texexp);
|
||||
$texexp = str_replace('xeta','\eta',$texexp);
|
||||
$texexp = str_replace('zepslon','\epsilon',$texexp);
|
||||
$texexp = str_replace('zupslon','\upsilon',$texexp);
|
||||
$texexp = str_replace('\mbox{logten}','\mbox{log}_{10}',$texexp);
|
||||
$texexp = str_replace('\mbox{acos}','\mbox{cos}^{-1}',$texexp);
|
||||
$texexp = str_replace('\mbox{asin}','\mbox{sin}^{-1}',$texexp);
|
||||
$texexp = str_replace('\mbox{atan}','\mbox{tan}^{-1}',$texexp);
|
||||
$texexp = str_replace('\mbox{asec}','\mbox{sec}^{-1}',$texexp);
|
||||
$texexp = str_replace('\mbox{acsc}','\mbox{csc}^{-1}',$texexp);
|
||||
$texexp = str_replace('\mbox{acot}','\mbox{cot}^{-1}',$texexp);
|
||||
$texexp = str_replace('\mbox{acosh}','\mbox{cosh}^{-1}',$texexp);
|
||||
$texexp = str_replace('\mbox{asinh}','\mbox{sinh}^{-1}',$texexp);
|
||||
$texexp = str_replace('\mbox{atanh}','\mbox{tanh}^{-1}',$texexp);
|
||||
$texexp = str_replace('\mbox{asech}','\mbox{sech}^{-1}',$texexp);
|
||||
$texexp = str_replace('\mbox{acsch}','\mbox{csch}^{-1}',$texexp);
|
||||
$texexp = str_replace('\mbox{acoth}','\mbox{coth}^{-1}',$texexp);
|
||||
//$texexp = preg_replace('/\\\frac{(.+?)}{\\\left\((.+?)\\\right\)}/s','\frac{'."\$1}{\$2}",$texexp);
|
||||
$texexp = preg_replace('/\\\sqrt{(.+?),(.+?)}/s','\sqrt['. "\$2]{\$1}",$texexp);
|
||||
$texexp = preg_replace('/\\\mbox{abs}\\\left\((.+?)\\\right\)/s',"|\$1|",$texexp);
|
||||
$texexp = preg_replace('/\\\log\\\left\((.+?),(.+?)\\\right\)/s','\log_{'. "\$2}\\left(\$1\\right)",$texexp);
|
||||
$texexp = preg_replace('/(\\\cos|\\\sin|\\\tan|\\\sec|\\\csc|\\\cot)([h]*)\\\left\((.+?),(.+?)\\\right\)/s',"\$1\$2^{". "\$4}\\left(\$3\\right)",$texexp);
|
||||
$texexp = preg_replace('/\\\int\\\left\((.+?),(.+?),(.+?)\\\right\)/s','\int_'. "{\$2}^{\$3}\$1 ",$texexp);
|
||||
$texexp = preg_replace('/\\\int\\\left\((.+?d[a-z])\\\right\)/s','\int '. "\$1 ",$texexp);
|
||||
$texexp = preg_replace('/\\\lim\\\left\((.+?),(.+?),(.+?)\\\right\)/s','\lim_'. "{\$2\\to \$3}\$1 ",$texexp);
|
||||
// Remove a forbidden keyword.
|
||||
$texexp = str_replace('\mbox', '', $texexp);
|
||||
$texcache = new stdClass();
|
||||
$texcache->filter = 'algebra';
|
||||
$texcache->version = 1;
|
||||
$texcache->md5key = $md5;
|
||||
$texcache->rawtext = $texexp;
|
||||
$texcache->timemodified = time();
|
||||
$DB->insert_record("cache_filters", $texcache, false);
|
||||
$text = str_replace( $matches[0][$i], self::get_image_markup($filename, $texexp, '', '', $align), $text);
|
||||
} else {
|
||||
$text = str_replace( $matches[0][$i],"<b>Undetermined error:</b> " . $matches[0][$i], $text);
|
||||
}
|
||||
|
||||
$md5 = md5($algebra);
|
||||
$filename = $md5 . ".gif";
|
||||
if (! $texcache = $DB->get_record("cache_filters", ["filter" => "algebra", "md5key" => $md5])) {
|
||||
$algebra = str_replace('<', '<', $algebra);
|
||||
$algebra = str_replace('>', '>', $algebra);
|
||||
$algebra = str_replace('<>', '#', $algebra);
|
||||
$algebra = str_replace('<=', '%', $algebra);
|
||||
$algebra = str_replace('>=', '!', $algebra);
|
||||
$algebra = preg_replace('/([=><%!#] *)-/', "\$1 zeroplace -", $algebra);
|
||||
$algebra = str_replace('delta', 'zdelta', $algebra);
|
||||
$algebra = str_replace('beta', 'bita', $algebra);
|
||||
$algebra = str_replace('theta', 'thita', $algebra);
|
||||
$algebra = str_replace('zeta', 'zita', $algebra);
|
||||
$algebra = str_replace('eta', 'xeta', $algebra);
|
||||
$algebra = str_replace('epsilon', 'zepslon', $algebra);
|
||||
$algebra = str_replace('upsilon', 'zupslon', $algebra);
|
||||
$algebra = preg_replace('!\r\n?!', ' ', $algebra);
|
||||
$algebra = escapeshellarg($algebra);
|
||||
if ((PHP_OS == "WINNT") || (PHP_OS == "WIN32") || (PHP_OS == "Windows")) {
|
||||
$cmd = "cd $CFG->dirroot\\filter\\algebra & algebra2tex.pl $algebra";
|
||||
} else {
|
||||
$cmd = "cd $CFG->dirroot/filter/algebra; ./algebra2tex.pl $algebra";
|
||||
}
|
||||
$texexp = `$cmd`;
|
||||
if (preg_match('/parsehilight/', $texexp)) {
|
||||
$text = str_replace($matches[0][$i], "<b>Syntax error:</b> " . $texexp, $text);
|
||||
} else if ($texexp) {
|
||||
$texexp = str_replace('zeroplace', '', $texexp);
|
||||
$texexp = str_replace('#', '\not= ', $texexp);
|
||||
$texexp = str_replace('%', '\leq ', $texexp);
|
||||
$texexp = str_replace('!', '\geq ', $texexp);
|
||||
$texexp = str_replace('\left{', '{', $texexp);
|
||||
$texexp = str_replace('\right}', '}', $texexp);
|
||||
$texexp = str_replace('\fun', ' ', $texexp);
|
||||
$texexp = str_replace('infty', '\infty', $texexp);
|
||||
$texexp = str_replace('alpha', '\alpha', $texexp);
|
||||
$texexp = str_replace('gamma', '\gamma', $texexp);
|
||||
$texexp = str_replace('iota', '\iota', $texexp);
|
||||
$texexp = str_replace('kappa', '\kappa', $texexp);
|
||||
$texexp = str_replace('lambda', '\lambda', $texexp);
|
||||
$texexp = str_replace('mu', '\mu', $texexp);
|
||||
$texexp = str_replace('nu', '\nu', $texexp);
|
||||
$texexp = str_replace('xi', '\xi', $texexp);
|
||||
$texexp = str_replace('rho', '\rho', $texexp);
|
||||
$texexp = str_replace('sigma', '\sigma', $texexp);
|
||||
$texexp = str_replace('tau', '\tau', $texexp);
|
||||
$texexp = str_replace('phi', '\phi', $texexp);
|
||||
$texexp = str_replace('chi', '\chi', $texexp);
|
||||
$texexp = str_replace('psi', '\psi', $texexp);
|
||||
$texexp = str_replace('omega', '\omega', $texexp);
|
||||
$texexp = str_replace('zdelta', '\delta', $texexp);
|
||||
$texexp = str_replace('bita', '\beta', $texexp);
|
||||
$texexp = str_replace('thita', '\theta', $texexp);
|
||||
$texexp = str_replace('zita', '\zeta', $texexp);
|
||||
$texexp = str_replace('xeta', '\eta', $texexp);
|
||||
$texexp = str_replace('zepslon', '\epsilon', $texexp);
|
||||
$texexp = str_replace('zupslon', '\upsilon', $texexp);
|
||||
$texexp = str_replace('\mbox{logten}', '\mbox{log}_{10}', $texexp);
|
||||
$texexp = str_replace('\mbox{acos}', '\mbox{cos}^{-1}', $texexp);
|
||||
$texexp = str_replace('\mbox{asin}', '\mbox{sin}^{-1}', $texexp);
|
||||
$texexp = str_replace('\mbox{atan}', '\mbox{tan}^{-1}', $texexp);
|
||||
$texexp = str_replace('\mbox{asec}', '\mbox{sec}^{-1}', $texexp);
|
||||
$texexp = str_replace('\mbox{acsc}', '\mbox{csc}^{-1}', $texexp);
|
||||
$texexp = str_replace('\mbox{acot}', '\mbox{cot}^{-1}', $texexp);
|
||||
$texexp = str_replace('\mbox{acosh}', '\mbox{cosh}^{-1}', $texexp);
|
||||
$texexp = str_replace('\mbox{asinh}', '\mbox{sinh}^{-1}', $texexp);
|
||||
$texexp = str_replace('\mbox{atanh}', '\mbox{tanh}^{-1}', $texexp);
|
||||
$texexp = str_replace('\mbox{asech}', '\mbox{sech}^{-1}', $texexp);
|
||||
$texexp = str_replace('\mbox{acsch}', '\mbox{csch}^{-1}', $texexp);
|
||||
$texexp = str_replace('\mbox{acoth}', '\mbox{coth}^{-1}', $texexp);
|
||||
$texexp = preg_replace('/\\\sqrt{(.+?),(.+?)}/s', '\sqrt[' . "\$2]{\$1}", $texexp);
|
||||
$texexp = preg_replace('/\\\mbox{abs}\\\left\((.+?)\\\right\)/s', "|\$1|", $texexp);
|
||||
$texexp = preg_replace('/\\\log\\\left\((.+?),(.+?)\\\right\)/s', '\log_{' . "\$2}\\left(\$1\\right)", $texexp);
|
||||
$texexp = preg_replace(
|
||||
'/(\\\cos|\\\sin|\\\tan|\\\sec|\\\csc|\\\cot)([h]*)\\\left\((.+?),(.+?)\\\right\)/s',
|
||||
"\$1\$2^{" . "\$4}\\left(\$3\\right)",
|
||||
$texexp,
|
||||
);
|
||||
$texexp = preg_replace('/\\\int\\\left\((.+?),(.+?),(.+?)\\\right\)/s', '\int_' . "{\$2}^{\$3}\$1 ", $texexp);
|
||||
$texexp = preg_replace('/\\\int\\\left\((.+?d[a-z])\\\right\)/s', '\int ' . "\$1 ", $texexp);
|
||||
$texexp = preg_replace('/\\\lim\\\left\((.+?),(.+?),(.+?)\\\right\)/s', '\lim_' . "{\$2\\to \$3}\$1 ", $texexp);
|
||||
// Remove a forbidden keyword.
|
||||
$texexp = str_replace('\mbox', '', $texexp);
|
||||
$texcache = new stdClass();
|
||||
$texcache->filter = 'algebra';
|
||||
$texcache->version = 1;
|
||||
$texcache->md5key = $md5;
|
||||
$texcache->rawtext = $texexp;
|
||||
$texcache->timemodified = time();
|
||||
$DB->insert_record("cache_filters", $texcache, false);
|
||||
$text = str_replace($matches[0][$i], filter_algebra_image($filename, $texexp, '', '', $align), $text);
|
||||
} else {
|
||||
$text = str_replace($matches[0][$i], "<b>Undetermined error:</b> " . $matches[0][$i], $text);
|
||||
}
|
||||
} else {
|
||||
$text = str_replace($matches[0][$i], self::get_image_markup($filename, $texcache->rawtext), $text);
|
||||
}
|
||||
@ -251,7 +238,8 @@ class text_filter extends \core_filters\text_filter {
|
||||
}
|
||||
$anchorcontents .= "\" $style />";
|
||||
|
||||
if (!file_exists("$CFG->dataroot/filter/algebra/$imagefile") && has_capability('moodle/site:config', context_system::instance())) {
|
||||
$imagefound = file_exists("$CFG->dataroot/filter/algebra/$imagefile");
|
||||
if (!$imagefound && has_capability('moodle/site:config', context_system::instance())) {
|
||||
$link = '/filter/algebra/algebradebug.php';
|
||||
$action = null;
|
||||
} else {
|
||||
|
35
filter/classes/external/get_all_states.php
vendored
35
filter/classes/external/get_all_states.php
vendored
@ -33,7 +33,6 @@ use context;
|
||||
* @since Moodle 4.4
|
||||
*/
|
||||
class get_all_states extends external_api {
|
||||
|
||||
/**
|
||||
* Webservice parameters.
|
||||
*
|
||||
@ -85,24 +84,22 @@ class get_all_states extends external_api {
|
||||
* @return external_single_structure
|
||||
*/
|
||||
public static function execute_returns(): external_single_structure {
|
||||
return new external_single_structure(
|
||||
[
|
||||
'filters' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
[
|
||||
'contextlevel' => new external_value(PARAM_ALPHA, 'The context level where the filters are:
|
||||
(coursecat, course, module).'),
|
||||
'instanceid' => new external_value(PARAM_INT, 'The instance id of item associated with the context.'),
|
||||
'contextid' => new external_value(PARAM_INT, 'The context id.'),
|
||||
'filter' => new external_value(PARAM_PLUGIN, 'Filter plugin name.'),
|
||||
'state' => new external_value(PARAM_INT, 'Filter state: 1 for on, -1 for off, -9999 if disabled.'),
|
||||
'sortorder' => new external_value(PARAM_INT, 'Execution order.'),
|
||||
]
|
||||
return new external_single_structure([
|
||||
'filters' => new external_multiple_structure(
|
||||
new external_single_structure([
|
||||
'contextlevel' => new external_value(
|
||||
PARAM_ALPHA,
|
||||
'The context level where the filters are: (coursecat, course, module).',
|
||||
),
|
||||
'All filters states'
|
||||
),
|
||||
'warnings' => new external_warnings(),
|
||||
]
|
||||
);
|
||||
'instanceid' => new external_value(PARAM_INT, 'The instance id of item associated with the context.'),
|
||||
'contextid' => new external_value(PARAM_INT, 'The context id.'),
|
||||
'filter' => new external_value(PARAM_PLUGIN, 'Filter plugin name.'),
|
||||
'state' => new external_value(PARAM_INT, 'Filter state: 1 for on, -1 for off, -9999 if disabled.'),
|
||||
'sortorder' => new external_value(PARAM_INT, 'Execution order.'),
|
||||
]),
|
||||
'All filters states'
|
||||
),
|
||||
'warnings' => new external_warnings(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -23,9 +23,6 @@
|
||||
*/
|
||||
|
||||
namespace core_filters\external;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/filterlib.php');
|
||||
|
||||
use core_external\external_api;
|
||||
use core_external\external_function_parameters;
|
||||
@ -49,19 +46,18 @@ class get_available_in_context extends external_api {
|
||||
* @since Moodle 3.4
|
||||
*/
|
||||
public static function execute_parameters() {
|
||||
return new external_function_parameters (
|
||||
array(
|
||||
'contexts' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
'contextlevel' => new external_value(PARAM_ALPHA, 'The context level where the filters are:
|
||||
(coursecat, course, module)'),
|
||||
'instanceid' => new external_value(PARAM_INT, 'The instance id of item associated with the context.')
|
||||
)
|
||||
), 'The list of contexts to check.'
|
||||
),
|
||||
)
|
||||
);
|
||||
return new external_function_parameters([
|
||||
'contexts' => new external_multiple_structure(
|
||||
new external_single_structure([
|
||||
'contextlevel' => new external_value(
|
||||
PARAM_ALPHA,
|
||||
'The context level where the filters are: (coursecat, course, module)',
|
||||
),
|
||||
'instanceid' => new external_value(PARAM_INT, 'The instance id of item associated with the context.'),
|
||||
]),
|
||||
'The list of contexts to check.'
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,8 +68,12 @@ class get_available_in_context extends external_api {
|
||||
* @since Moodle 3.4
|
||||
*/
|
||||
public static function execute($contexts) {
|
||||
$params = self::validate_parameters(self::get_available_in_context_parameters(), array('contexts' => $contexts));
|
||||
$filters = $warnings = array();
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->libdir . '/filterlib.php');
|
||||
|
||||
$params = self::validate_parameters(self::execute_parameters(), ['contexts' => $contexts]);
|
||||
$filters = $warnings = [];
|
||||
|
||||
foreach ($params['contexts'] as $contextinfo) {
|
||||
try {
|
||||
@ -81,12 +81,12 @@ class get_available_in_context extends external_api {
|
||||
self::validate_context($context);
|
||||
$contextinfo['contextid'] = $context->id;
|
||||
} catch (Exception $e) {
|
||||
$warnings[] = array(
|
||||
$warnings[] = [
|
||||
'item' => 'context',
|
||||
'itemid' => $contextinfo['instanceid'],
|
||||
'warningcode' => $e->getCode(),
|
||||
'message' => $e->getMessage(),
|
||||
);
|
||||
];
|
||||
continue;
|
||||
}
|
||||
$contextfilters = filter_get_available_in_context($context);
|
||||
@ -96,10 +96,10 @@ class get_available_in_context extends external_api {
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
return [
|
||||
'filters' => $filters,
|
||||
'warnings' => $warnings,
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,24 +109,22 @@ class get_available_in_context extends external_api {
|
||||
* @since Moodle 3.4
|
||||
*/
|
||||
public static function execute_returns() {
|
||||
return new external_single_structure(
|
||||
array(
|
||||
'filters' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
'contextlevel' => new external_value(PARAM_ALPHA, 'The context level where the filters are:
|
||||
(coursecat, course, module).'),
|
||||
'instanceid' => new external_value(PARAM_INT, 'The instance id of item associated with the context.'),
|
||||
'contextid' => new external_value(PARAM_INT, 'The context id.'),
|
||||
'filter' => new external_value(PARAM_PLUGIN, 'Filter plugin name.'),
|
||||
'localstate' => new external_value(PARAM_INT, 'Filter state: 1 for on, -1 for off, 0 if inherit.'),
|
||||
'inheritedstate' => new external_value(PARAM_INT, '1 or 0 to use when localstate is set to inherit.'),
|
||||
)
|
||||
return new external_single_structure([
|
||||
'filters' => new external_multiple_structure(
|
||||
new external_single_structure([
|
||||
'contextlevel' => new external_value(
|
||||
PARAM_ALPHA,
|
||||
'The context level where the filters are: (coursecat, course, module).',
|
||||
),
|
||||
'Available filters'
|
||||
),
|
||||
'warnings' => new external_warnings(),
|
||||
)
|
||||
);
|
||||
'instanceid' => new external_value(PARAM_INT, 'The instance id of item associated with the context.'),
|
||||
'contextid' => new external_value(PARAM_INT, 'The context id.'),
|
||||
'filter' => new external_value(PARAM_PLUGIN, 'Filter plugin name.'),
|
||||
'localstate' => new external_value(PARAM_INT, 'Filter state: 1 for on, -1 for off, 0 if inherit.'),
|
||||
'inheritedstate' => new external_value(PARAM_INT, '1 or 0 to use when localstate is set to inherit.'),
|
||||
]),
|
||||
'Available filters'
|
||||
),
|
||||
'warnings' => new external_warnings(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -36,22 +36,22 @@ class filter_manager {
|
||||
* @var text_filter[][] This list of active filters, by context, for filtering content.
|
||||
* An array contextid => ordered array of filter name => filter objects.
|
||||
*/
|
||||
protected $textfilters = array();
|
||||
protected $textfilters = [];
|
||||
|
||||
/**
|
||||
* @var text_filter[][] This list of active filters, by context, for filtering strings.
|
||||
* An array contextid => ordered array of filter name => filter objects.
|
||||
*/
|
||||
protected $stringfilters = array();
|
||||
protected $stringfilters = [];
|
||||
|
||||
/** @var array Exploded version of $CFG->stringfilters. */
|
||||
protected $stringfilternames = array();
|
||||
protected $stringfilternames = [];
|
||||
|
||||
/** @var filter_manager Holds the singleton instance. */
|
||||
protected static $singletoninstance;
|
||||
|
||||
/**
|
||||
* Constructor. Protected. Use {@link instance()} instead.
|
||||
* Constructor. Protected. Use {@see instance()} instead.
|
||||
*/
|
||||
protected function __construct() {
|
||||
$this->stringfilternames = filter_get_string_filters();
|
||||
@ -65,7 +65,7 @@ class filter_manager {
|
||||
public static function instance() {
|
||||
global $CFG;
|
||||
if (is_null(self::$singletoninstance)) {
|
||||
if (!empty($CFG->perfdebug) and $CFG->perfdebug > 7) {
|
||||
if (!empty($CFG->perfdebug) && $CFG->perfdebug > 7) {
|
||||
self::$singletoninstance = new performance_measuring_filter_manager();
|
||||
} else {
|
||||
self::$singletoninstance = new self();
|
||||
@ -88,9 +88,9 @@ class filter_manager {
|
||||
* Unloads all filters and other cached information
|
||||
*/
|
||||
protected function unload_all_filters() {
|
||||
$this->textfilters = array();
|
||||
$this->stringfilters = array();
|
||||
$this->stringfilternames = array();
|
||||
$this->textfilters = [];
|
||||
$this->stringfilters = [];
|
||||
$this->stringfilternames = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,8 +100,8 @@ class filter_manager {
|
||||
*/
|
||||
protected function load_filters($context) {
|
||||
$filters = filter_get_active_in_context($context);
|
||||
$this->textfilters[$context->id] = array();
|
||||
$this->stringfilters[$context->id] = array();
|
||||
$this->textfilters[$context->id] = [];
|
||||
$this->stringfilters[$context->id] = [];
|
||||
foreach ($filters as $filtername => $localconfig) {
|
||||
$filter = $this->make_filter_object($filtername, $context, $localconfig);
|
||||
if (is_null($filter)) {
|
||||
@ -131,7 +131,7 @@ class filter_manager {
|
||||
return new $filterclass($context, $localconfig);
|
||||
}
|
||||
|
||||
$path = $CFG->dirroot .'/filter/'. $filtername .'/filter.php';
|
||||
$path = $CFG->dirroot . '/filter/' . $filtername . '/filter.php';
|
||||
if (!is_readable($path)) {
|
||||
return null;
|
||||
}
|
||||
@ -148,13 +148,17 @@ class filter_manager {
|
||||
/**
|
||||
* Apply a list of filters to some content.
|
||||
* @param string $text
|
||||
* @param moodle_text_filter[] $filterchain array filter name => filter object.
|
||||
* @param text_filter[] $filterchain array filter name => filter object.
|
||||
* @param array $options options passed to the filters.
|
||||
* @param array $skipfilters of filter names. Any filters that should not be applied to this text.
|
||||
* @param null|array $skipfilters of filter names. Any filters that should not be applied to this text.
|
||||
* @return string $text
|
||||
*/
|
||||
protected function apply_filter_chain($text, $filterchain, array $options = array(),
|
||||
array $skipfilters = null) {
|
||||
protected function apply_filter_chain(
|
||||
$text,
|
||||
$filterchain,
|
||||
array $options = [],
|
||||
?array $skipfilters = null
|
||||
) {
|
||||
if (!isset($options['stage'])) {
|
||||
$filtermethod = 'filter';
|
||||
} else if (in_array($options['stage'], ['pre_format', 'pre_clean', 'post_clean', 'string'], true)) {
|
||||
@ -208,15 +212,19 @@ class filter_manager {
|
||||
* @param string $text The text to filter
|
||||
* @param context $context the context.
|
||||
* @param array $options options passed to the filters
|
||||
* @param array $skipfilters of filter names. Any filters that should not be applied to this text.
|
||||
* @param null|array $skipfilters of filter names. Any filters that should not be applied to this text.
|
||||
* @return string resulting text
|
||||
*/
|
||||
public function filter_text($text, $context, array $options = array(),
|
||||
array $skipfilters = null) {
|
||||
public function filter_text(
|
||||
$text,
|
||||
$context,
|
||||
array $options = [],
|
||||
?array $skipfilters = null
|
||||
) {
|
||||
$text = $this->apply_filter_chain($text, $this->get_text_filters($context), $options, $skipfilters);
|
||||
if (!isset($options['stage']) || $options['stage'] === 'post_clean') {
|
||||
// Remove <nolink> tags for XHTML compatibility after the last filtering stage.
|
||||
$text = str_replace(array('<nolink>', '</nolink>'), '', $text);
|
||||
$text = str_replace(['<nolink>', '</nolink>'], '', $text);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
@ -77,15 +77,18 @@ class filter_object {
|
||||
* list($linkobject->hreftagbegin, $linkobject->hreftagend, $linkobject->replacementphrase) =
|
||||
* call_user_func_array($linkobject->replacementcallback, $linkobject->replacementcallbackdata);
|
||||
* so the return should be an array [$hreftagbegin, $hreftagend, $replacementphrase], the last of which may be null.
|
||||
* @param array $replacementcallbackdata data to be passed to $replacementcallback (optional).
|
||||
* @param null|array $replacementcallbackdata data to be passed to $replacementcallback (optional).
|
||||
*/
|
||||
public function __construct($phrase, $hreftagbegin = '<span class="highlight">',
|
||||
$hreftagend = '</span>',
|
||||
$casesensitive = false,
|
||||
$fullmatch = false,
|
||||
$replacementphrase = null,
|
||||
$replacementcallback = null,
|
||||
array $replacementcallbackdata = null) {
|
||||
public function __construct(
|
||||
$phrase,
|
||||
$hreftagbegin = '<span class="highlight">',
|
||||
$hreftagend = '</span>',
|
||||
$casesensitive = false,
|
||||
$fullmatch = false,
|
||||
$replacementphrase = null,
|
||||
$replacementcallback = null,
|
||||
?array $replacementcallbackdata = null
|
||||
) {
|
||||
|
||||
$this->phrase = $phrase;
|
||||
$this->hreftagbegin = $hreftagbegin;
|
||||
|
@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@ -17,6 +16,7 @@
|
||||
|
||||
namespace core_filters;
|
||||
|
||||
use core\context;
|
||||
use moodleform;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
@ -27,19 +27,24 @@ require_once($CFG->libdir . '/formslib.php');
|
||||
* A Moodle form base class for editing local filter settings.
|
||||
*
|
||||
* @copyright Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @package core_filters
|
||||
*/
|
||||
abstract class local_settings_form extends moodleform {
|
||||
/** @var string The filter to manage */
|
||||
protected $filter;
|
||||
|
||||
/** @var \core\context The context */
|
||||
protected $context;
|
||||
|
||||
public function __construct($submiturl, $filter, $context) {
|
||||
$this->filter = $filter;
|
||||
$this->context = $context;
|
||||
/**
|
||||
* Create an instance of the form.
|
||||
*
|
||||
* @param string $submiturl
|
||||
* @param string $filter
|
||||
* @param context $context
|
||||
*/
|
||||
public function __construct(
|
||||
string $submiturl,
|
||||
/** @var string The filter to manage */
|
||||
protected string $filter,
|
||||
/** @var \core\context The context */
|
||||
protected context $context,
|
||||
) {
|
||||
parent::__construct($submiturl);
|
||||
}
|
||||
|
||||
@ -63,7 +68,7 @@ abstract class local_settings_form extends moodleform {
|
||||
/**
|
||||
* Override this method to add your form controls.
|
||||
*
|
||||
* @param $mform the form we are building. $this->_form, but passed in for convenience.
|
||||
* @param \MoodleQuickForm $mform the form we are building. $this->_form, but passed in for convenience.
|
||||
*/
|
||||
abstract protected function definition_inner($mform);
|
||||
|
||||
|
@ -28,21 +28,25 @@ use core\context;
|
||||
*/
|
||||
class null_filter_manager {
|
||||
/**
|
||||
* As for the equivalent {@link filter_manager} method.
|
||||
* As for the equivalent {@see filter_manager} method.
|
||||
*
|
||||
* @param string $text The text to filter
|
||||
* @param context $context not used.
|
||||
* @param array $options not used
|
||||
* @param array $skipfilters not used
|
||||
* @param null|array $skipfilters not used
|
||||
* @return string resulting text.
|
||||
*/
|
||||
public function filter_text($text, $context, array $options = array(),
|
||||
array $skipfilters = null) {
|
||||
public function filter_text(
|
||||
$text,
|
||||
$context,
|
||||
array $options = [],
|
||||
?array $skipfilters = null
|
||||
) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* As for the equivalent {@link filter_manager} method.
|
||||
* As for the equivalent {@see filter_manager} method.
|
||||
*
|
||||
* @param string $string The text to filter
|
||||
* @param context $context not used.
|
||||
|
@ -25,14 +25,15 @@ namespace core_filters;
|
||||
*/
|
||||
class performance_measuring_filter_manager extends filter_manager {
|
||||
/** @var int number of filter objects created. */
|
||||
protected $filterscreated = 0;
|
||||
protected int $filterscreated = 0;
|
||||
|
||||
/** @var int number of calls to filter_text. */
|
||||
protected $textsfiltered = 0;
|
||||
protected int $textsfiltered = 0;
|
||||
|
||||
/** @var int number of calls to filter_string. */
|
||||
protected $stringsfiltered = 0;
|
||||
protected int $stringsfiltered = 0;
|
||||
|
||||
#[\Override]
|
||||
protected function unload_all_filters() {
|
||||
parent::unload_all_filters();
|
||||
$this->filterscreated = 0;
|
||||
@ -40,40 +41,51 @@ class performance_measuring_filter_manager extends filter_manager {
|
||||
$this->stringsfiltered = 0;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
protected function make_filter_object($filtername, $context, $localconfig) {
|
||||
$this->filterscreated++;
|
||||
return parent::make_filter_object($filtername, $context, $localconfig);
|
||||
}
|
||||
|
||||
public function filter_text($text, $context, array $options = array(),
|
||||
array $skipfilters = null) {
|
||||
#[\Override]
|
||||
public function filter_text(
|
||||
$text,
|
||||
$context,
|
||||
array $options = [],
|
||||
?array $skipfilters = null
|
||||
) {
|
||||
if (!isset($options['stage']) || $options['stage'] === 'post_clean') {
|
||||
$this->textsfiltered++;
|
||||
}
|
||||
return parent::filter_text($text, $context, $options, $skipfilters);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function filter_string($string, $context) {
|
||||
$this->stringsfiltered++;
|
||||
return parent::filter_string($string, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return performance information, in the form required by {@link get_performance_info()}.
|
||||
* Return performance information, in the form required by {@see get_performance_info()}.
|
||||
*
|
||||
* @return array the performance info.
|
||||
*/
|
||||
public function get_performance_summary() {
|
||||
return array(array(
|
||||
'contextswithfilters' => count($this->textfilters),
|
||||
'filterscreated' => $this->filterscreated,
|
||||
'textsfiltered' => $this->textsfiltered,
|
||||
'stringsfiltered' => $this->stringsfiltered,
|
||||
), array(
|
||||
'contextswithfilters' => 'Contexts for which filters were loaded',
|
||||
'filterscreated' => 'Filters created',
|
||||
'textsfiltered' => 'Pieces of content filtered',
|
||||
'stringsfiltered' => 'Strings filtered',
|
||||
));
|
||||
public function get_performance_summary(): array {
|
||||
return [
|
||||
[
|
||||
'contextswithfilters' => count($this->textfilters),
|
||||
'filterscreated' => $this->filterscreated,
|
||||
'textsfiltered' => $this->textsfiltered,
|
||||
'stringsfiltered' => $this->stringsfiltered,
|
||||
],
|
||||
[
|
||||
'contextswithfilters' => 'Contexts for which filters were loaded',
|
||||
'filterscreated' => 'Filters created',
|
||||
'textsfiltered' => 'Pieces of content filtered',
|
||||
'stringsfiltered' => 'Strings filtered',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,26 +14,16 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core_filters\privacy;
|
||||
|
||||
/**
|
||||
* Privacy Subsystem implementation for core_filters.
|
||||
* Privacy Subsystem for core_filters implementing null_provider.
|
||||
*
|
||||
* @package core_filters
|
||||
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_filters\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for core_filters implementing null_provider.
|
||||
*
|
||||
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements \core_privacy\local\metadata\null_provider {
|
||||
|
||||
/**
|
||||
* Get the language string identifier with the component's language
|
||||
* file to explain why this plugin stores no data.
|
||||
|
@ -74,7 +74,7 @@ abstract class text_filter {
|
||||
* @param array $options options passed to the filters
|
||||
* @return string the HTML content after the filtering has been applied.
|
||||
*/
|
||||
abstract public function filter($text, array $options = array());
|
||||
abstract public function filter($text, array $options = []);
|
||||
|
||||
/**
|
||||
* Filter text before changing format to HTML.
|
||||
|
@ -26,36 +26,32 @@ use core_filters\filter_object;
|
||||
*/
|
||||
class text_filter extends \core_filters\text_filter {
|
||||
#[\Override]
|
||||
public function filter($text, array $options = array()) {
|
||||
public function filter($text, array $options = []) {
|
||||
global $CFG, $DB, $USER;
|
||||
|
||||
// Trivial-cache - keyed on $cachedcourseid + $cacheduserid.
|
||||
static $cachedcourseid = null;
|
||||
static $cacheduserid = null;
|
||||
static $coursecontentlist = array();
|
||||
static $sitecontentlist = array();
|
||||
static $coursecontentlist = [];
|
||||
static $sitecontentlist = [];
|
||||
|
||||
static $nothingtodo;
|
||||
|
||||
// Try to get current course.
|
||||
$coursectx = $this->context->get_course_context(false);
|
||||
if (!$coursectx) {
|
||||
// We could be in a course category so no entries for courseid == 0 will be found.
|
||||
$courseid = 0;
|
||||
} else {
|
||||
$courseid = $coursectx->instanceid;
|
||||
}
|
||||
// We could be in a course category so no entries for courseid == 0 will be found.
|
||||
$courseid = $coursectx?->instanceid ?: 0;
|
||||
|
||||
if ($cacheduserid !== $USER->id) {
|
||||
// Invalidate all caches if the user changed.
|
||||
$coursecontentlist = array();
|
||||
$sitecontentlist = array();
|
||||
$coursecontentlist = [];
|
||||
$sitecontentlist = [];
|
||||
$cacheduserid = $USER->id;
|
||||
$cachedcourseid = $courseid;
|
||||
$nothingtodo = false;
|
||||
} else if ($courseid != get_site()->id && $courseid != 0 && $cachedcourseid != $courseid) {
|
||||
// Invalidate course-level caches if the course id changed.
|
||||
$coursecontentlist = array();
|
||||
$coursecontentlist = [];
|
||||
$cachedcourseid = $courseid;
|
||||
$nothingtodo = false;
|
||||
}
|
||||
@ -65,6 +61,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
}
|
||||
|
||||
// If courseid == 0 only site entries will be returned.
|
||||
$site = get_site();
|
||||
if ($courseid == get_site()->id || $courseid == 0) {
|
||||
$contentlist = & $sitecontentlist;
|
||||
} else {
|
||||
@ -73,12 +70,12 @@ class text_filter extends \core_filters\text_filter {
|
||||
|
||||
// Create a list of all the resources to search for. It may be cached already.
|
||||
if (empty($contentlist)) {
|
||||
$coursestosearch = $courseid ? array($courseid) : array(); // Add courseid if found
|
||||
if (get_site()->id != $courseid) { // Add siteid if was not courseid
|
||||
$coursestosearch = $courseid ? [$courseid] : []; // Add courseid if found.
|
||||
if (get_site()->id != $courseid) { // Add siteid if was not courseid.
|
||||
$coursestosearch[] = get_site()->id;
|
||||
}
|
||||
// We look for text field contents only if have autolink enabled (param1)
|
||||
list ($coursesql, $params) = $DB->get_in_or_equal($coursestosearch);
|
||||
// We look for text field contents only if have autolink enabled (param1).
|
||||
[$coursesql, $params] = $DB->get_in_or_equal($coursestosearch);
|
||||
$sql = 'SELECT dc.id AS contentid, dr.id AS recordid, dc.content AS content, d.id AS dataid
|
||||
FROM {data} d
|
||||
JOIN {data_fields} df ON df.dataid = d.id
|
||||
@ -94,7 +91,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
}
|
||||
|
||||
foreach ($contents as $key => $content) {
|
||||
// Trim empty or unlinkable concepts
|
||||
// Trim empty or unlinkable concepts.
|
||||
$currentcontent = trim(strip_tags($content->content));
|
||||
if (empty($currentcontent)) {
|
||||
unset($contents[$key]);
|
||||
@ -103,7 +100,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
$contents[$key]->content = $currentcontent;
|
||||
}
|
||||
|
||||
// Rule out any small integers. See bug 1446
|
||||
// Rule out any small integers. See bug 1446.
|
||||
$currentint = intval($currentcontent);
|
||||
if ($currentint && (strval($currentint) == $currentcontent) && $currentint < 1000) {
|
||||
unset($contents[$key]);
|
||||
@ -118,17 +115,24 @@ class text_filter extends \core_filters\text_filter {
|
||||
usort($contents, [self::class, 'sort_entries_by_length']);
|
||||
|
||||
foreach ($contents as $content) {
|
||||
$href_tag_begin = '<a class="data autolink dataid'.$content->dataid.'" title="'.s($content->content).'" '.
|
||||
'href="'.$CFG->wwwroot.'/mod/data/view.php?d='.$content->dataid.
|
||||
'&rid='.$content->recordid.'">';
|
||||
$contentlist[] = new filter_object($content->content, $href_tag_begin, '</a>', false, true);
|
||||
$hrefopen = '<a class="data autolink dataid' . $content->dataid . '" title="' . s($content->content) . '" ' .
|
||||
'href="' . $CFG->wwwroot . '/mod/data/view.php?d=' . $content->dataid .
|
||||
'&rid=' . $content->recordid . '">';
|
||||
$contentlist[] = new filter_object($content->content, $hrefopen, '</a>', false, true);
|
||||
}
|
||||
|
||||
$contentlist = filter_remove_duplicates($contentlist); // Clean dupes
|
||||
$contentlist = filter_remove_duplicates($contentlist); // Clean dupes.
|
||||
}
|
||||
return filter_phrases($text, $contentlist); // Look for all these links in the text
|
||||
return filter_phrases($text, $contentlist); // Look for all these links in the text.
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to sort array values by content length.
|
||||
*
|
||||
* @param mixed $content0
|
||||
* @param mixed $content1
|
||||
* @return int
|
||||
*/
|
||||
private static function sort_entries_by_length($content0, $content1) {
|
||||
$len0 = strlen($content0->content);
|
||||
$len1 = strlen($content1->content);
|
||||
|
@ -31,22 +31,14 @@ use core_h5p\local\library\autoloader;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class text_filter extends \core_filters\text_filter {
|
||||
/**
|
||||
* @var boolean $loadresizerjs This is whether to request the resize.js script.
|
||||
*/
|
||||
/** @var bool $loadresizerjs This is whether to request the resize.js script */
|
||||
private static $loadresizerjs = true;
|
||||
|
||||
/**
|
||||
* Function filter replaces any h5p-sources.
|
||||
*
|
||||
* @param string $text HTML content to process
|
||||
* @param array $options options passed to the filters
|
||||
* @return string
|
||||
*/
|
||||
public function filter($text, array $options = array()) {
|
||||
#[\Override]
|
||||
public function filter($text, array $options = []) {
|
||||
global $CFG, $USER;
|
||||
|
||||
if (!is_string($text) or empty($text)) {
|
||||
if (!is_string($text) || empty($text)) {
|
||||
// Non string data can not be filtered anyway.
|
||||
return $text;
|
||||
}
|
||||
@ -60,18 +52,18 @@ class text_filter extends \core_filters\text_filter {
|
||||
$allowedsources = get_config('filter_displayh5p', 'allowedsources');
|
||||
$allowedsources = array_filter(array_map('trim', explode("\n", $allowedsources)));
|
||||
|
||||
$localsource = '('.preg_quote($CFG->wwwroot, '~').'/[^ &\#"\'<]*\.h5p([?][^ "\'<]*)?[^ \#"\'<]*)';
|
||||
$localsource = '(' . preg_quote($CFG->wwwroot, '~') . '/[^ &\#"\'<]*\.h5p([?][^ "\'<]*)?[^ \#"\'<]*)';
|
||||
$allowedsources[] = $localsource;
|
||||
|
||||
$params = array(
|
||||
$params = [
|
||||
'tagbegin' => '<iframe src="',
|
||||
'tagend' => '</iframe>'
|
||||
);
|
||||
'tagend' => '</iframe>',
|
||||
];
|
||||
|
||||
$specialchars = ['?', '&'];
|
||||
$escapedspecialchars = ['\?', '&'];
|
||||
$h5pcontents = array();
|
||||
$h5plinks = array();
|
||||
$h5pcontents = [];
|
||||
$h5plinks = [];
|
||||
|
||||
// Check all allowed sources.
|
||||
foreach ($allowedsources as $source) {
|
||||
@ -82,7 +74,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
// only if the user has the proper capabilities.
|
||||
$params['canbeedited'] = (!empty($USER->editing)) && ($source == $localsource);
|
||||
if ($source == $localsource) {
|
||||
$params['tagbegin'] = '<iframe src="'.$CFG->wwwroot.'/h5p/embed.php?url=';
|
||||
$params['tagbegin'] = '<iframe src="' . $CFG->wwwroot . '/h5p/embed.php?url=';
|
||||
$escapechars = $source;
|
||||
$ultimatepattern = $source;
|
||||
} else {
|
||||
@ -100,17 +92,33 @@ class text_filter extends \core_filters\text_filter {
|
||||
continue;
|
||||
}
|
||||
|
||||
$h5pcontenturl = new filter_object($source, null, null, false,
|
||||
false, null, [$this, 'filterobject_prepare_replacement_callback'], $params + ['ish5plink' => false]);
|
||||
$h5pcontenturl = new filter_object(
|
||||
$source,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
[$this, 'filterobject_prepare_replacement_callback'],
|
||||
$params + ['ish5plink' => false]
|
||||
);
|
||||
|
||||
$h5pcontenturl->workregexp = '#'.$ultimatepattern.'#';
|
||||
$h5pcontenturl->workregexp = '#' . $ultimatepattern . '#';
|
||||
$h5pcontents[] = $h5pcontenturl;
|
||||
|
||||
// Regex to find h5p extensions in an <a> tag.
|
||||
$linkregexp = '~<a [^>]*href=["\']('.$escapechars.'[^"\']*)["\'][^>]*>([^<]*)</a>~is';
|
||||
$linkregexp = '~<a [^>]*href=["\'](' . $escapechars . '[^"\']*)["\'][^>]*>([^<]*)</a>~is';
|
||||
|
||||
$h5plinkurl = new filter_object($linkregexp, null, null, false,
|
||||
false, null, [$this, 'filterobject_prepare_replacement_callback'], $params + ['ish5plink' => true]);
|
||||
$h5plinkurl = new filter_object(
|
||||
$linkregexp,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
[$this, 'filterobject_prepare_replacement_callback'],
|
||||
$params + ['ish5plink' => true]
|
||||
);
|
||||
$h5plinkurl->workregexp = $linkregexp;
|
||||
$h5plinks[] = $h5plinkurl;
|
||||
}
|
||||
@ -123,7 +131,8 @@ class text_filter extends \core_filters\text_filter {
|
||||
// Apply filter inside <a> tag href attribute.
|
||||
// We can not use filter_phrase function because it removes all tags and can not be applied in tag attributes.
|
||||
foreach ($h5plinks as $h5plink) {
|
||||
$text = preg_replace_callback($h5plink->workregexp,
|
||||
$text = preg_replace_callback(
|
||||
$h5plink->workregexp,
|
||||
function ($matches) use ($h5plink) {
|
||||
if ($matches[1] == $matches[2]) {
|
||||
filter_prepare_phrase_for_replacement($h5plink);
|
||||
@ -132,7 +141,9 @@ class text_filter extends \core_filters\text_filter {
|
||||
} else {
|
||||
return $matches[0];
|
||||
}
|
||||
}, $text);
|
||||
},
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
// The "Edit" button below each H5P content will be displayed only for users with permissions to edit the content (to
|
||||
@ -140,7 +151,8 @@ class text_filter extends \core_filters\text_filter {
|
||||
// As the H5P URL is required in order to get this information, this action can be done only here(the
|
||||
// prepare_replacement_callback method has only the placeholders).
|
||||
foreach ($h5pcontents as $h5pcontent) {
|
||||
$text = preg_replace_callback($h5pcontent->workregexp,
|
||||
$text = preg_replace_callback(
|
||||
$h5pcontent->workregexp,
|
||||
function ($matches) use ($h5pcontent) {
|
||||
global $USER, $CFG;
|
||||
|
||||
@ -153,7 +165,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
}
|
||||
|
||||
$contenturl = $matches[0];
|
||||
list($file, $h5p) = \core_h5p\api::get_original_content_from_pluginfile_url($contenturl, true, true);
|
||||
[$file, $h5p] = \core_h5p\api::get_original_content_from_pluginfile_url($contenturl, true, true);
|
||||
if ($file) {
|
||||
filter_prepare_phrase_for_replacement($h5pcontent);
|
||||
|
||||
@ -178,7 +190,9 @@ class text_filter extends \core_filters\text_filter {
|
||||
}
|
||||
|
||||
return $matches[0];
|
||||
}, $text);
|
||||
},
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
$result = filter_phrases($text, $h5pcontents, null, null, false, true);
|
||||
@ -187,18 +201,21 @@ class text_filter extends \core_filters\text_filter {
|
||||
// embed.php page is requesting a PARAM_LOCALURL url parameter, so for files/directories use non-alphanumeric
|
||||
// characters, we need to encode the parameter. Fetch url parameter added to embed.php and encode the whole url.
|
||||
$localurl = '#\?url=([^" <]*[\/]+[^" <]*\.h5p)([?][^"]*)?#';
|
||||
$result = preg_replace_callback($localurl,
|
||||
$result = preg_replace_callback(
|
||||
$localurl,
|
||||
function ($matches) {
|
||||
$baseurl = rawurlencode($matches[1]);
|
||||
// Deal with possible parameters in the url link.
|
||||
if (!empty($matches[2])) {
|
||||
$match = explode('?', $matches[2]);
|
||||
if (!empty($match[1])) {
|
||||
$baseurl = $baseurl."&".$match[1];
|
||||
$baseurl = $baseurl . "&" . $match[1];
|
||||
}
|
||||
}
|
||||
return "?url=".$baseurl;
|
||||
}, $result);
|
||||
return "?url=" . $baseurl;
|
||||
},
|
||||
$result
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
@ -214,7 +231,6 @@ class text_filter extends \core_filters\text_filter {
|
||||
* @return array [$hreftagbegin, $hreftagend, $replacementphrase] for filterobject.
|
||||
*/
|
||||
public function filterobject_prepare_replacement_callback($tagbegin, $tagend, $urlmodifier, $canbeedited, $ish5plink) {
|
||||
|
||||
$sourceurl = "$1";
|
||||
if ($urlmodifier !== "") {
|
||||
$sourceurl .= $urlmodifier;
|
||||
|
@ -51,7 +51,6 @@ final class text_filter_test extends \advanced_testcase {
|
||||
'filter_displayh5p'
|
||||
);
|
||||
|
||||
|
||||
$filterplugin = new text_filter(null, []);
|
||||
|
||||
$filteredtext = $filterplugin->filter($text);
|
||||
@ -59,7 +58,7 @@ final class text_filter_test extends \advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides texts to filter for the {@link self::test_filter_urls} method.
|
||||
* Provides texts to filter for the {@see self::test_filter_urls} method.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
@ -103,6 +102,7 @@ final class text_filter_test extends \advanced_testcase {
|
||||
],
|
||||
[
|
||||
"https://generic.wordpress.soton.ac.uk/altc/wp-admin/admin-ajax.php?action=h5p_embed&id=13",
|
||||
// phpcs:ignore moodle.Files.LineLength.TooLong
|
||||
"#<iframe src=\"https://generic.wordpress.soton.ac.uk/altc/wp-admin/admin-ajax.php\?action=h5p_embed\&\;id=13\"[^>]+?>#",
|
||||
],
|
||||
[
|
||||
|
@ -21,32 +21,29 @@ namespace filter_emailprotect;
|
||||
*
|
||||
* This class looks for email addresses in Moodle text and hides them using the Moodle obfuscate_text function.
|
||||
*
|
||||
* @package filter
|
||||
* @package filter_emailprotect
|
||||
* @subpackage emailprotect
|
||||
* @copyright 2004 Mike Churchward
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class text_filter extends \core_filters\text_filter {
|
||||
#[\Override]
|
||||
public function filter($text, array $options = array()) {
|
||||
/// Do a quick check using stripos to avoid unnecessary work
|
||||
public function filter($text, array $options = []) {
|
||||
// Do a quick check using stripos to avoid unnecessary work.
|
||||
if (strpos($text, '@') === false) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
/// There might be an email in here somewhere so continue ...
|
||||
$matches = array();
|
||||
|
||||
/// regular expression to define a standard email string.
|
||||
// Regular expression to define a standard email string.
|
||||
$emailregex = '((?:[\w\.\-])+\@(?:(?:[a-zA-Z\d\-])+\.)+(?:[a-zA-Z\d]{2,4}))';
|
||||
|
||||
/// pattern to find a mailto link with the linked text.
|
||||
$pattern = '|(<a\s+href\s*=\s*[\'"]?mailto:)'.$emailregex.'([\'"]?\s*>)'.'(.*)'.'(</a>)|iU';
|
||||
// Pattern to find a mailto link with the linked text.
|
||||
$pattern = '|(<a\s+href\s*=\s*[\'"]?mailto:)' . $emailregex . '([\'"]?\s*>)' . '(.*)' . '(</a>)|iU';
|
||||
$text = preg_replace_callback($pattern, 'filter_emailprotect_alter_mailto', $text);
|
||||
$text = preg_replace_callback($pattern, [self::class, 'alter_mailto'], $text);
|
||||
|
||||
/// pattern to find any other email address in the text.
|
||||
$pattern = '/(^|\s+|>)'.$emailregex.'($|\s+|\.\s+|\.$|<)/i';
|
||||
// Pattern to find any other email address in the text.
|
||||
$pattern = '/(^|\s+|>)' . $emailregex . '($|\s+|\.\s+|\.$|<)/i';
|
||||
$text = preg_replace_callback($pattern, 'filter_emailprotect_alter_email', $text);
|
||||
$text = preg_replace_callback($pattern, [self::class, 'alter_email'], $text);
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@ -34,7 +33,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
* - dimension 2: theme.
|
||||
* @var array
|
||||
*/
|
||||
protected static $emoticontexts = array();
|
||||
protected static $emoticontexts = [];
|
||||
|
||||
/**
|
||||
* Internal cache used for replacing. Multidimensional array;
|
||||
@ -42,22 +41,14 @@ class text_filter extends \core_filters\text_filter {
|
||||
* - dimension 2: theme.
|
||||
* @var array
|
||||
*/
|
||||
protected static $emoticonimgs = array();
|
||||
|
||||
/**
|
||||
* Apply the filter to the text
|
||||
*
|
||||
* @see \core_filters\filter_manager::apply_filter_chain()
|
||||
* @param string $text to be processed by the text
|
||||
* @param array $options filter options
|
||||
* @return string text after processing
|
||||
*/
|
||||
public function filter($text, array $options = array()) {
|
||||
protected static $emoticonimgs = [];
|
||||
|
||||
#[\Override]
|
||||
public function filter($text, array $options = []) {
|
||||
if (!isset($options['originalformat'])) {
|
||||
// if the format is not specified, we are probably called by {@see format_string()}
|
||||
// If the format is not specified, we are probably called by {@see format_string()}
|
||||
// in that case, it would be dangerous to replace text with the image because it could
|
||||
// be stripped. therefore, we do nothing
|
||||
// be stripped. therefore, we do nothing.
|
||||
return $text;
|
||||
}
|
||||
if (in_array($options['originalformat'], explode(',', get_config('filter_emoticon', 'formats')))) {
|
||||
@ -66,10 +57,6 @@ class text_filter extends \core_filters\text_filter {
|
||||
return $text;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// internal implementation starts here
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Replace emoticons found in the text with their images
|
||||
*
|
||||
@ -82,12 +69,12 @@ class text_filter extends \core_filters\text_filter {
|
||||
$lang = current_language();
|
||||
$theme = $PAGE->theme->name;
|
||||
|
||||
if (!isset(self::$emoticontexts[$lang][$theme]) or !isset(self::$emoticonimgs[$lang][$theme])) {
|
||||
// prepare internal caches
|
||||
if (!isset(self::$emoticontexts[$lang][$theme]) || !isset(self::$emoticonimgs[$lang][$theme])) {
|
||||
// Prepare internal caches.
|
||||
$manager = get_emoticon_manager();
|
||||
$emoticons = $manager->get_emoticons();
|
||||
self::$emoticontexts[$lang][$theme] = array();
|
||||
self::$emoticonimgs[$lang][$theme] = array();
|
||||
self::$emoticontexts[$lang][$theme] = [];
|
||||
self::$emoticonimgs[$lang][$theme] = [];
|
||||
foreach ($emoticons as $emoticon) {
|
||||
self::$emoticontexts[$lang][$theme][] = $emoticon->text;
|
||||
self::$emoticonimgs[$lang][$theme][] = $OUTPUT->render($manager->prepare_renderable_emoticon($emoticon));
|
||||
@ -107,7 +94,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
$exclude = 0;
|
||||
|
||||
// Define the patterns that mark the start of the forbidden zones.
|
||||
$excludepattern = array('/^<script/is', '/^<span[^>]+class="nolink[^"]*"/is', '/^<pre/is');
|
||||
$excludepattern = ['/^<script/is', '/^<span[^>]+class="nolink[^"]*"/is', '/^<pre/is'];
|
||||
|
||||
// Loop through the fragments.
|
||||
foreach ($processing as $fragment) {
|
||||
@ -122,20 +109,26 @@ class text_filter extends \core_filters\text_filter {
|
||||
}
|
||||
if ($exclude > 0) {
|
||||
// If we are ignoring the fragment, then we must check if we may have reached the end of the zone.
|
||||
if (strpos($fragment, '</span') !== false || strpos($fragment, '</script') !== false
|
||||
|| strpos($fragment, '</pre') !== false) {
|
||||
if (
|
||||
strpos($fragment, '</span') !== false || strpos($fragment, '</script') !== false
|
||||
|| strpos($fragment, '</pre') !== false
|
||||
) {
|
||||
$exclude -= 1;
|
||||
// This is needed because of a double increment at the first element.
|
||||
if ($exclude == 1) {
|
||||
$exclude -= 1;
|
||||
}
|
||||
} else if (strpos($fragment, '<span') !== false || strpos($fragment, '<script') !== false
|
||||
|| strpos($fragment, '<pre') !== false) {
|
||||
} else if (
|
||||
strpos($fragment, '<span') !== false || strpos($fragment, '<script') !== false
|
||||
|| strpos($fragment, '<pre') !== false
|
||||
) {
|
||||
// If we find a nested tag we increase the exclusion level.
|
||||
$exclude = $exclude + 1;
|
||||
}
|
||||
} else if (strpos($fragment, '<span') === false ||
|
||||
strpos($fragment, '</span') === false) {
|
||||
} else if (
|
||||
strpos($fragment, '<span') === false ||
|
||||
strpos($fragment, '</span') === false
|
||||
) {
|
||||
// This is the meat of the code - this is run every time.
|
||||
// This code only runs for fragments that are not ignored (including the tags themselves).
|
||||
$fragment = str_replace(self::$emoticontexts[$lang][$theme], self::$emoticonimgs[$lang][$theme], $fragment);
|
||||
|
@ -88,7 +88,7 @@ final class text_filter_test extends \advanced_testcase {
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function filter_emoticon_provider() {
|
||||
public static function filter_emoticon_provider(): array {
|
||||
$grr = '(grr)';
|
||||
return [
|
||||
'FORMAT_MOODLE is not filtered' => [
|
||||
@ -191,11 +191,12 @@ final class text_filter_test extends \advanced_testcase {
|
||||
/**
|
||||
* Get a copy of the filter configured for testing.
|
||||
*
|
||||
* @param array $args
|
||||
* @param array ...$args
|
||||
* @return \filter_emoticon\text_filter
|
||||
*/
|
||||
protected function get_testable_filter_emoticon(...$args): text_filter {
|
||||
return new class extends text_filter {
|
||||
// phpcs:ignore moodle.Commenting.MissingDocblock.MissingTestcaseMethodDescription
|
||||
public function __construct(...$args) {
|
||||
// Reset static emoticon caches.
|
||||
parent::$emoticontexts = [];
|
||||
|
@ -23,6 +23,9 @@ use core\url;
|
||||
use core_filters\filter_object;
|
||||
use stdClass;
|
||||
|
||||
// phpcs:disable moodle.NamingConventions.ValidVariableName.VariableNameLowerCase -- GLOSSARY_EXCLUDEENTRY
|
||||
// phpcs:disable moodle.NamingConventions.ValidVariableName.VariableNameUnderscore -- GLOSSARY_EXCLUDEENTRY
|
||||
|
||||
/**
|
||||
* This filter provides automatic linking to glossary entries, aliases and categories when found inside every Moodle text.
|
||||
*
|
||||
@ -36,6 +39,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
/** @var null|cache_store cache used to store the terms for this course. */
|
||||
protected $cache = null;
|
||||
|
||||
#[\Override]
|
||||
public function setup($page, $context) {
|
||||
if ($page->requires->should_create_one_time_item_now('filter_glossary_autolinker')) {
|
||||
$page->requires->js_call_amd('filter_glossary/autolinker', 'init', []);
|
||||
@ -72,7 +76,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
return $cached->cacheconceptlist;
|
||||
}
|
||||
|
||||
list($glossaries, $allconcepts) = \mod_glossary\local\concept_cache::get_concepts($courseid);
|
||||
[$glossaries, $allconcepts] = \mod_glossary\local\concept_cache::get_concepts($courseid);
|
||||
|
||||
if (!$allconcepts) {
|
||||
$tocache = new stdClass();
|
||||
@ -83,13 +87,20 @@ class text_filter extends \core_filters\text_filter {
|
||||
return [];
|
||||
}
|
||||
|
||||
$conceptlist = array();
|
||||
$conceptlist = [];
|
||||
|
||||
foreach ($allconcepts as $concepts) {
|
||||
foreach ($concepts as $concept) {
|
||||
$conceptlist[] = new filter_object($concept->concept, null, null,
|
||||
$concept->casesensitive, $concept->fullmatch, null,
|
||||
[$this, 'filterobject_prepare_replacement_callback'], [$concept, $glossaries]);
|
||||
$conceptlist[] = new filter_object(
|
||||
$concept->concept,
|
||||
null,
|
||||
null,
|
||||
$concept->casesensitive,
|
||||
$concept->fullmatch,
|
||||
null,
|
||||
[$this, 'filterobject_prepare_replacement_callback'],
|
||||
[$concept, $glossaries]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,30 +131,39 @@ class text_filter extends \core_filters\text_filter {
|
||||
global $CFG;
|
||||
|
||||
if ($concept->category) { // Link to a category.
|
||||
$title = get_string('glossarycategory', 'filter_glossary',
|
||||
['glossary' => $glossaries[$concept->glossaryid], 'category' => $concept->concept]);
|
||||
$link = new url('/mod/glossary/view.php',
|
||||
['g' => $concept->glossaryid, 'mode' => 'cat', 'hook' => $concept->id]);
|
||||
$attributes = array(
|
||||
$title = get_string(
|
||||
'glossarycategory',
|
||||
'filter_glossary',
|
||||
['glossary' => $glossaries[$concept->glossaryid], 'category' => $concept->concept]
|
||||
);
|
||||
$link = new url(
|
||||
'/mod/glossary/view.php',
|
||||
['g' => $concept->glossaryid, 'mode' => 'cat', 'hook' => $concept->id]
|
||||
);
|
||||
$attributes = [
|
||||
'href' => $link,
|
||||
'title' => $title,
|
||||
'class' => 'glossary autolink category glossaryid' . $concept->glossaryid);
|
||||
|
||||
'class' => 'glossary autolink category glossaryid' . $concept->glossaryid, ];
|
||||
} else { // Link to entry or alias.
|
||||
$title = get_string('glossaryconcept', 'filter_glossary',
|
||||
['glossary' => $glossaries[$concept->glossaryid], 'concept' => $concept->concept]);
|
||||
$title = get_string(
|
||||
'glossaryconcept',
|
||||
'filter_glossary',
|
||||
['glossary' => $glossaries[$concept->glossaryid], 'concept' => $concept->concept]
|
||||
);
|
||||
// Hardcoding dictionary format in the URL rather than defaulting
|
||||
// to the current glossary format which may not work in a popup.
|
||||
// for example "entry list" means the popup would only contain
|
||||
// a link that opens another popup.
|
||||
$link = new url('/mod/glossary/showentry.php',
|
||||
['eid' => $concept->id, 'displayformat' => 'dictionary']);
|
||||
$attributes = array(
|
||||
$link = new url(
|
||||
'/mod/glossary/showentry.php',
|
||||
['eid' => $concept->id, 'displayformat' => 'dictionary']
|
||||
);
|
||||
$attributes = [
|
||||
'href' => $link,
|
||||
'title' => str_replace('&', '&', $title), // Undo the s() mangling.
|
||||
'class' => 'glossary autolink concept glossaryid' . $concept->glossaryid,
|
||||
'data-entryid' => $concept->id,
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
// This flag is optionally set by resource_pluginfile()
|
||||
@ -156,7 +176,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function filter($text, array $options = array()) {
|
||||
public function filter($text, array $options = []) {
|
||||
global $GLOSSARY_EXCLUDEENTRY;
|
||||
|
||||
$conceptlist = $this->get_all_concepts();
|
||||
|
@ -26,7 +26,7 @@ use core\url;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class text_filter extends \core_filters\text_filter {
|
||||
/*
|
||||
/**
|
||||
* Perform a mapping of the moodle language code to the equivalent for MathJax.
|
||||
*
|
||||
* @param string $moodlelangcode - The moodle language code - e.g. en_pirate
|
||||
@ -38,7 +38,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
$mathjaxlangcodes = [
|
||||
'ar', 'ast', 'bcc', 'bg', 'br', 'ca', 'cdo', 'ce', 'cs', 'cy', 'da', 'de', 'diq', 'en', 'eo', 'es', 'fa',
|
||||
'fi', 'fr', 'gl', 'he', 'ia', 'it', 'ja', 'kn', 'ko', 'lb', 'lki', 'lt', 'mk', 'nl', 'oc', 'pl', 'pt',
|
||||
'pt-br', 'qqq', 'ru', 'scn', 'sco', 'sk', 'sl', 'sv', 'th', 'tr', 'uk', 'vi', 'zh-hans', 'zh-hant'
|
||||
'pt-br', 'qqq', 'ru', 'scn', 'sco', 'sk', 'sl', 'sv', 'th', 'tr', 'uk', 'vi', 'zh-hans', 'zh-hant',
|
||||
];
|
||||
|
||||
// List of explicit mappings and known exceptions (moodle => mathjax).
|
||||
@ -69,39 +69,29 @@ class text_filter extends \core_filters\text_filter {
|
||||
return 'en';
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the javascript to enable mathjax processing on this page.
|
||||
*
|
||||
* @param moodle_page $page The current page.
|
||||
* @param context $context The current context.
|
||||
*/
|
||||
#[\Override]
|
||||
public function setup($page, $context) {
|
||||
|
||||
if ($page->requires->should_create_one_time_item_now('filter_mathjaxloader-scripts')) {
|
||||
$url = get_config('filter_mathjaxloader', 'httpsurl');
|
||||
$lang = $this->map_language_code(current_language());
|
||||
$url = new url($url, array('delayStartupUntil' => 'configured'));
|
||||
|
||||
$page->requires->js($url);
|
||||
|
||||
$config = get_config('filter_mathjaxloader', 'mathjaxconfig');
|
||||
$wwwroot = new url('/');
|
||||
|
||||
$config = str_replace('{wwwroot}', $wwwroot->out(true), $config);
|
||||
|
||||
$params = array('mathjaxconfig' => $config, 'lang' => $lang);
|
||||
|
||||
$page->requires->js_call_amd('filter_mathjaxloader/loader', 'configure', [$params]);
|
||||
if (!$page->requires->should_create_one_time_item_now('filter_mathjaxloader-scripts')) {
|
||||
return;
|
||||
}
|
||||
$url = get_config('filter_mathjaxloader', 'httpsurl');
|
||||
$lang = $this->map_language_code(current_language());
|
||||
$url = new url($url, ['delayStartupUntil' => 'configured']);
|
||||
|
||||
$page->requires->js($url);
|
||||
|
||||
$config = get_config('filter_mathjaxloader', 'mathjaxconfig');
|
||||
$wwwroot = new url('/');
|
||||
|
||||
$config = str_replace('{wwwroot}', $wwwroot->out(true), $config);
|
||||
|
||||
$params = ['mathjaxconfig' => $config, 'lang' => $lang];
|
||||
|
||||
$page->requires->js_call_amd('filter_mathjaxloader/loader', 'configure', [$params]);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function wraps the filtered text in a span, that mathjaxloader is configured to process.
|
||||
*
|
||||
* @param string $text The text to filter.
|
||||
* @param array $options The filter options.
|
||||
*/
|
||||
public function filter($text, array $options = array()) {
|
||||
#[\Override]
|
||||
public function filter($text, array $options = []) {
|
||||
global $PAGE;
|
||||
|
||||
$legacy = get_config('filter_mathjaxloader', 'texfiltercompatibility');
|
||||
@ -148,7 +138,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
// inside display math, only the outer display math is wrapped in
|
||||
// a span. The span HTML inside a LaTex math environment would break
|
||||
// MathJax. See MDL-61981.
|
||||
list($text, $hasdisplayorinline) = $this->wrap_math_in_nolink($text);
|
||||
[$text, $hasdisplayorinline] = $this->wrap_math_in_nolink($text);
|
||||
}
|
||||
|
||||
if ($hasdisplayorinline || $hasextra) {
|
||||
@ -205,8 +195,10 @@ class text_filter extends \core_filters\text_filter {
|
||||
}
|
||||
} else {
|
||||
// Display math open.
|
||||
if (($text[$i - 1] === '\\' && $text[$i] === ']' && $displaybracket) ||
|
||||
($text[$i - 1] === '$' && $text[$i] === '$' && $displaydollar)) {
|
||||
if (
|
||||
($text[$i - 1] === '\\' && $text[$i] === ']' && $displaybracket) ||
|
||||
($text[$i - 1] === '$' && $text[$i] === '$' && $displaydollar)
|
||||
) {
|
||||
// Display math ends, wrap the span around it.
|
||||
$text = $this->insert_span($text, $displaystart, $i);
|
||||
|
||||
@ -221,7 +213,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
|
||||
++$i;
|
||||
}
|
||||
return array($text, $changesdone);
|
||||
return [$text, $changesdone];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -237,10 +229,12 @@ class text_filter extends \core_filters\text_filter {
|
||||
* the defined substring.
|
||||
*/
|
||||
protected function insert_span($text, $start, $end) {
|
||||
return substr_replace($text,
|
||||
'<span class="nolink">'. substr($text, $start, $end - $start + 1) .'</span>',
|
||||
$start,
|
||||
$end - $start + 1);
|
||||
return substr_replace(
|
||||
$text,
|
||||
'<span class="nolink">' . substr($text, $start, $end - $start + 1) . '</span>',
|
||||
$start,
|
||||
$end - $start + 1
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -252,7 +246,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
* @return string Returns the input string with HTML tags escaped.
|
||||
*/
|
||||
private function escape_html_tag_wrapper(string $text): string {
|
||||
return preg_replace_callback('/\{([^}]+)\}/', function(array $matches): string {
|
||||
return preg_replace_callback('/\{([^}]+)\}/', function (array $matches): string {
|
||||
$search = ['<', '>'];
|
||||
$replace = ['<', '>'];
|
||||
return str_replace($search, $replace, $matches[0]);
|
||||
|
@ -25,14 +25,14 @@ use core\context\system as context_system;
|
||||
* @category test
|
||||
* @copyright 2018 Markku Riekkinen
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @covers \filter_mathjaxloader\text_filter
|
||||
*/
|
||||
final class filtermath_test extends \advanced_testcase {
|
||||
/**
|
||||
* Test the functionality of {@link text_filter::filter()}.
|
||||
* Test the functionality of {@see text_filter::filter()}.
|
||||
*
|
||||
* @param string $inputtext The text given by the user.
|
||||
* @param string $expected The expected output after filtering.
|
||||
*
|
||||
* @dataProvider math_filtering_inputs
|
||||
*/
|
||||
public function test_math_filtering($inputtext, $expected): void {
|
||||
@ -45,53 +45,70 @@ final class filtermath_test extends \advanced_testcase {
|
||||
*
|
||||
* @return array of [inputtext, expectedoutput] tuples.
|
||||
*/
|
||||
public static function math_filtering_inputs() {
|
||||
public static function math_filtering_inputs(): array {
|
||||
// phpcs:disable moodle.Files.LineLength.TooLong
|
||||
return [
|
||||
// One inline formula.
|
||||
['Some inline math \\( y = x^2 \\).',
|
||||
'<span class="filter_mathjaxloader_equation">Some inline math <span class="nolink">\\( y = x^2 \\)</span>.</span>', ],
|
||||
[
|
||||
'Some inline math \\( y = x^2 \\).',
|
||||
'<span class="filter_mathjaxloader_equation">Some inline math <span class="nolink">\\( y = x^2 \\)</span>.</span>',
|
||||
],
|
||||
|
||||
// One inline and one display.
|
||||
['Some inline math \\( y = x^2 \\) and display formula \\[ S = \\sum_{n=1}^{\\infty} 2^n \\]',
|
||||
'<span class="filter_mathjaxloader_equation">Some inline math <span class="nolink">\\( y = x^2 \\)</span> and '
|
||||
. 'display formula <span class="nolink">\\[ S = \\sum_{n=1}^{\\infty} 2^n \\]</span></span>', ],
|
||||
[
|
||||
'Some inline math \\( y = x^2 \\) and display formula \\[ S = \\sum_{n=1}^{\\infty} 2^n \\]',
|
||||
'<span class="filter_mathjaxloader_equation">Some inline math <span class="nolink">\\( y = x^2 \\)</span> and '
|
||||
. 'display formula <span class="nolink">\\[ S = \\sum_{n=1}^{\\infty} 2^n \\]</span></span>',
|
||||
],
|
||||
|
||||
// One display and one inline.
|
||||
['Display formula \\[ S = \\sum_{n=1}^{\\infty} 2^n \\] and some inline math \\( y = x^2 \\).',
|
||||
'<span class="filter_mathjaxloader_equation">Display formula <span class="nolink">\\[ S = \\sum_{n=1}^{\\infty} 2^n \\]</span> and '
|
||||
. 'some inline math <span class="nolink">\\( y = x^2 \\)</span>.</span>', ],
|
||||
[
|
||||
'Display formula \\[ S = \\sum_{n=1}^{\\infty} 2^n \\] and some inline math \\( y = x^2 \\).',
|
||||
'<span class="filter_mathjaxloader_equation">Display formula <span class="nolink">\\[ S = \\sum_{n=1}^{\\infty} 2^n \\]</span> and '
|
||||
. 'some inline math <span class="nolink">\\( y = x^2 \\)</span>.</span>',
|
||||
],
|
||||
|
||||
// One inline and one display (with dollars).
|
||||
['Some inline math \\( y = x^2 \\) and display formula $$ S = \\sum_{n=1}^{\\infty} 2^n $$',
|
||||
'<span class="filter_mathjaxloader_equation">Some inline math <span class="nolink">\\( y = x^2 \\)</span> and '
|
||||
. 'display formula <span class="nolink">$$ S = \\sum_{n=1}^{\\infty} 2^n $$</span></span>', ],
|
||||
[
|
||||
'Some inline math \\( y = x^2 \\) and display formula $$ S = \\sum_{n=1}^{\\infty} 2^n $$',
|
||||
'<span class="filter_mathjaxloader_equation">Some inline math <span class="nolink">\\( y = x^2 \\)</span> and '
|
||||
. 'display formula <span class="nolink">$$ S = \\sum_{n=1}^{\\infty} 2^n $$</span></span>',
|
||||
],
|
||||
|
||||
// One display (with dollars) and one inline.
|
||||
['Display formula $$ S = \\sum_{n=1}^{\\infty} 2^n $$ and some inline math \\( y = x^2 \\).',
|
||||
'<span class="filter_mathjaxloader_equation">Display formula <span class="nolink">$$ S = \\sum_{n=1}^{\\infty} 2^n $$</span> and '
|
||||
. 'some inline math <span class="nolink">\\( y = x^2 \\)</span>.</span>', ],
|
||||
[
|
||||
'Display formula $$ S = \\sum_{n=1}^{\\infty} 2^n $$ and some inline math \\( y = x^2 \\).',
|
||||
'<span class="filter_mathjaxloader_equation">Display formula <span class="nolink">$$ S = \\sum_{n=1}^{\\infty} 2^n $$</span> and '
|
||||
. 'some inline math <span class="nolink">\\( y = x^2 \\)</span>.</span>',
|
||||
],
|
||||
|
||||
// Inline math environment nested inside display environment (using a custom LaTex macro).
|
||||
['\\[ \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} \\] '
|
||||
. 'Text with inline formula using the custom LaTex macro \\( a = \\NullF \\).',
|
||||
'<span class="filter_mathjaxloader_equation"><span class="nolink">'
|
||||
. '\\[ \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} \\]</span> '
|
||||
. 'Text with inline formula using the custom LaTex macro <span class="nolink">\\( a = \\NullF \\)</span>.</span>', ],
|
||||
[
|
||||
'\\[ \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} \\] '
|
||||
. 'Text with inline formula using the custom LaTex macro \\( a = \\NullF \\).',
|
||||
'<span class="filter_mathjaxloader_equation"><span class="nolink">'
|
||||
. '\\[ \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} \\]</span> '
|
||||
. 'Text with inline formula using the custom LaTex macro <span class="nolink">\\( a = \\NullF \\)</span>.</span>',
|
||||
],
|
||||
|
||||
// Nested environments and some more content.
|
||||
['\\[ \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} \\] '
|
||||
. 'Text with inline formula using the custom LaTex macro \\( a = \\NullF \\). Finally, a display formula '
|
||||
. '$$ b = \\NullF $$',
|
||||
'<span class="filter_mathjaxloader_equation"><span class="nolink">'
|
||||
. '\\[ \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} \\]</span> '
|
||||
. 'Text with inline formula using the custom LaTex macro <span class="nolink">\\( a = \\NullF \\)</span>. '
|
||||
. 'Finally, a display formula <span class="nolink">$$ b = \\NullF $$</span></span>', ],
|
||||
[
|
||||
'\\[ \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} \\] '
|
||||
. 'Text with inline formula using the custom LaTex macro \\( a = \\NullF \\). Finally, a display formula '
|
||||
. '$$ b = \\NullF $$',
|
||||
'<span class="filter_mathjaxloader_equation"><span class="nolink">'
|
||||
. '\\[ \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} \\]</span> '
|
||||
. 'Text with inline formula using the custom LaTex macro <span class="nolink">\\( a = \\NullF \\)</span>. '
|
||||
. 'Finally, a display formula <span class="nolink">$$ b = \\NullF $$</span></span>',
|
||||
],
|
||||
|
||||
// Broken math: the delimiters ($$) are not closed.
|
||||
['Writing text and starting display math. $$ k = i^3 \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} '
|
||||
. 'More text and inline math \\( x = \\NullF \\).',
|
||||
'Writing text and starting display math. $$ k = i^3 \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} '
|
||||
. 'More text and inline math \\( x = \\NullF \\).', ],
|
||||
[
|
||||
'Writing text and starting display math. $$ k = i^3 \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} '
|
||||
. 'More text and inline math \\( x = \\NullF \\).',
|
||||
'Writing text and starting display math. $$ k = i^3 \\newcommand{\\False}{\\mathsf{F}} \\newcommand{\\NullF}{\\fbox{\\(\\False\\)}} '
|
||||
. 'More text and inline math \\( x = \\NullF \\).',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -27,11 +27,10 @@ namespace filter_mathjaxloader;
|
||||
*/
|
||||
final class text_filter_test extends \advanced_testcase {
|
||||
/**
|
||||
* Test the functionality of {@link text_filter::map_language_code()}.
|
||||
* Test the functionality of {@see text_filter::map_language_code()}.
|
||||
*
|
||||
* @param string $moodlelangcode the user's current language
|
||||
* @param string $mathjaxlangcode the mathjax language to be used for the moodle language
|
||||
*
|
||||
* @dataProvider map_language_code_expected_mappings
|
||||
*/
|
||||
public function test_map_language_code($moodlelangcode, $mathjaxlangcode): void {
|
||||
@ -44,7 +43,7 @@ final class text_filter_test extends \advanced_testcase {
|
||||
*
|
||||
* @return array of [moodlelangcode, mathjaxcode] tuples
|
||||
*/
|
||||
public static function map_language_code_expected_mappings() {
|
||||
public static function map_language_code_expected_mappings(): array {
|
||||
return [
|
||||
['cz', 'cs'], // Explicit mapping.
|
||||
['cs', 'cs'], // Implicit mapping (exact match).
|
||||
|
@ -28,7 +28,7 @@ use moodle_page;
|
||||
* It is highly recommended to configure servers to be compatible with our slasharguments,
|
||||
* otherwise the "?d=600x400" may not work.
|
||||
*
|
||||
* @package filter
|
||||
* @package filter_mediaplugin
|
||||
* @subpackage mediaplugin
|
||||
* @copyright 2004 onwards Martin Dougiamas {@link http://moodle.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
@ -56,11 +56,11 @@ class text_filter extends \core_filters\text_filter {
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function filter($text, array $options = array()) {
|
||||
public function filter($text, array $options = []) {
|
||||
global $CFG, $PAGE;
|
||||
|
||||
if (!is_string($text) or empty($text)) {
|
||||
// non string data can not be filtered anyway
|
||||
if (!is_string($text) || empty($text)) {
|
||||
// Non string data can not be filtered anyway.
|
||||
return $text;
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
}
|
||||
|
||||
// Check permissions.
|
||||
$this->trusted = !empty($options['noclean']) or !empty($CFG->allowobjectembed);
|
||||
$this->trusted = !empty($options['noclean']) || !empty($CFG->allowobjectembed);
|
||||
|
||||
// Looking for tags.
|
||||
$matches = preg_split('/(<[^>]*>)/i', $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
|
||||
@ -92,14 +92,14 @@ class text_filter extends \core_filters\text_filter {
|
||||
// and build them so that the callback function can check it for
|
||||
// embedded content. Then we rebuild the string.
|
||||
foreach ($matches as $idx => $tag) {
|
||||
if (preg_match('|</'.$tagname.'>|', $tag) && !empty($validtag)) {
|
||||
if (preg_match('|</' . $tagname . '>|', $tag) && !empty($validtag)) {
|
||||
$validtag .= $tag;
|
||||
|
||||
// Given we now have a valid <a> tag to process it's time for
|
||||
// ReDoS protection. Stop processing if a word is too large.
|
||||
if (strlen($validtag) < 4096) {
|
||||
if ($tagname === 'a') {
|
||||
$processed = preg_replace_callback($re, array($this, 'callback'), $validtag);
|
||||
$processed = preg_replace_callback($re, [$this, 'callback'], $validtag);
|
||||
} else {
|
||||
// For audio and video tags we just process them without precheck for embeddable markers.
|
||||
$processed = $this->process_media_tag($validtag);
|
||||
@ -110,8 +110,10 @@ class text_filter extends \core_filters\text_filter {
|
||||
// Wipe it so we can catch any more instances to filter.
|
||||
$validtag = '';
|
||||
$processed = '';
|
||||
} else if (preg_match('/<(a|video|audio)\s[^>]*/', $tag, $tagmatches) && $sizeofmatches > 1 &&
|
||||
(empty($validtag) || $tagname === strtolower($tagmatches[1]))) {
|
||||
} else if (
|
||||
preg_match('/<(a|video|audio)\s[^>]*/', $tag, $tagmatches) && $sizeofmatches > 1 &&
|
||||
(empty($validtag) || $tagname === strtolower($tagmatches[1]))
|
||||
) {
|
||||
// Looking for a starting tag. Ignore tags embedded into each other.
|
||||
$validtag = $tag;
|
||||
$tagname = strtolower($tagmatches[1]);
|
||||
@ -147,7 +149,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
|
||||
// Get name.
|
||||
$name = trim($matches[2]);
|
||||
if (empty($name) or strpos($name, 'http') === 0) {
|
||||
if (empty($name) || strpos($name, 'http') === 0) {
|
||||
$name = ''; // Use default name.
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ $enabledmediaplugins = \core\plugininfo\media::get_enabled_plugins();
|
||||
\core\plugininfo\media::set_enabled_plugins('vimeo,youtube,videojs,html5audio,html5video');
|
||||
|
||||
// Create plugin.
|
||||
$filterplugin = new \filter_mediaplugin\text_filter(null, array());
|
||||
$filterplugin = new \filter_mediaplugin\text_filter(null, []);
|
||||
|
||||
// Note: As this is a developer test page, language strings are not used: all
|
||||
// text is English-only.
|
||||
|
@ -40,30 +40,27 @@ namespace filter_multilang;
|
||||
*/
|
||||
class text_filter extends \core_filters\text_filter {
|
||||
#[\Override]
|
||||
public function filter($text, array $options = array()) {
|
||||
public function filter($text, array $options = []) {
|
||||
global $CFG;
|
||||
|
||||
// [pj] I don't know about you but I find this new implementation funny :P
|
||||
// [skodak] I was laughing while rewriting it ;-)
|
||||
// [nicolasconnault] Should support inverted attributes: <span class="multilang" lang="en"> (Doesn't work curently)
|
||||
// [skodak] it supports it now, though it is slower - any better idea?
|
||||
|
||||
if (empty($text) or is_numeric($text)) {
|
||||
if (empty($text) || is_numeric($text)) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
if (empty($CFG->filter_multilang_force_old) and !empty($CFG->filter_multilang_converted)) {
|
||||
// new syntax
|
||||
if (empty($CFG->filter_multilang_force_old) && !empty($CFG->filter_multilang_converted)) {
|
||||
// New syntax.
|
||||
// phpcs:ignore moodle.Files.LineLength.TooLong
|
||||
$search = '/(<span(\s+lang="[a-zA-Z0-9_-]+"|\s+class="multilang"){2}\s*>.*?<\/span>)(\s*<span(\s+lang="[a-zA-Z0-9_-]+"|\s+class="multilang"){2}\s*>.*?<\/span>)+/is';
|
||||
} else {
|
||||
// old syntax
|
||||
// Old syntax.
|
||||
// phpcs:ignore moodle.Files.LineLength.TooLong
|
||||
$search = '/(<(?:lang|span) lang="[a-zA-Z0-9_-]*".*?>.*?<\/(?:lang|span)>)(\s*<(?:lang|span) lang="[a-zA-Z0-9_-]*".*?>.*?<\/(?:lang|span)>)+/is';
|
||||
}
|
||||
|
||||
$result = preg_replace_callback($search, [$this, 'process_match'], $text);
|
||||
|
||||
if (is_null($result)) {
|
||||
return $text; //error during regex processing (too many nested spans?)
|
||||
return $text; // Error during regex processing (too many nested spans?).
|
||||
} else {
|
||||
return $result;
|
||||
}
|
||||
@ -83,7 +80,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
return $langblock[0];
|
||||
}
|
||||
|
||||
$langlist = array();
|
||||
$langlist = [];
|
||||
foreach ($rawlanglist[1] as $index => $lang) {
|
||||
$lang = str_replace('-', '_', strtolower($lang)); // Normalize languages.
|
||||
$langlist[$lang] = $rawlanglist[2][$index];
|
||||
|
@ -56,7 +56,7 @@ final class get_available_in_context_test extends externallib_advanced_testcase
|
||||
$this->setAdminUser();
|
||||
|
||||
$this->expectException('moodle_exception');
|
||||
get_available_in_context::execute(array(array('contextlevel' => 'system', 'instanceid' => 0)));
|
||||
get_available_in_context::execute([['contextlevel' => 'system', 'instanceid' => 0]]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,7 +76,7 @@ final class get_available_in_context_test extends externallib_advanced_testcase
|
||||
filter_set_global_state($filter, TEXTFILTER_DISABLED);
|
||||
}
|
||||
|
||||
$result = get_available_in_context::execute(array(array('contextlevel' => 'coursecat', 'instanceid' => $category->id)));
|
||||
$result = get_available_in_context::execute([['contextlevel' => 'coursecat', 'instanceid' => $category->id]]);
|
||||
$result = external_api::clean_returnvalue(get_available_in_context::execute_returns(), $result);
|
||||
$this->assertEmpty($result['filters']); // No filters, all disabled.
|
||||
$this->assertEmpty($result['warnings']);
|
||||
@ -86,7 +86,7 @@ final class get_available_in_context_test extends externallib_advanced_testcase
|
||||
$firstfilter = key($allfilters);
|
||||
filter_set_global_state($firstfilter, TEXTFILTER_ON);
|
||||
|
||||
$result = get_available_in_context::execute(array(array('contextlevel' => 'coursecat', 'instanceid' => $category->id)));
|
||||
$result = get_available_in_context::execute([['contextlevel' => 'coursecat', 'instanceid' => $category->id]]);
|
||||
$result = external_api::clean_returnvalue(get_available_in_context::execute_returns(), $result);
|
||||
$this->assertEmpty($result['warnings']);
|
||||
$this->assertEquals($firstfilter, $result['filters'][0]['filter']); // OK, the filter is enabled.
|
||||
@ -95,7 +95,7 @@ final class get_available_in_context_test extends externallib_advanced_testcase
|
||||
|
||||
// Set off the same filter at local context level.
|
||||
filter_set_local_state($firstfilter, \context_coursecat::instance($category->id)->id, TEXTFILTER_OFF);
|
||||
$result = get_available_in_context::execute(array(array('contextlevel' => 'coursecat', 'instanceid' => $category->id)));
|
||||
$result = get_available_in_context::execute([['contextlevel' => 'coursecat', 'instanceid' => $category->id]]);
|
||||
$result = external_api::clean_returnvalue(get_available_in_context::execute_returns(), $result);
|
||||
$this->assertEmpty($result['warnings']);
|
||||
$this->assertEquals($firstfilter, $result['filters'][0]['filter']); // OK, the filter is enabled globally.
|
||||
@ -120,7 +120,7 @@ final class get_available_in_context_test extends externallib_advanced_testcase
|
||||
filter_set_global_state($filter, TEXTFILTER_DISABLED);
|
||||
}
|
||||
|
||||
$result = get_available_in_context::execute(array(array('contextlevel' => 'course', 'instanceid' => $course->id)));
|
||||
$result = get_available_in_context::execute([['contextlevel' => 'course', 'instanceid' => $course->id]]);
|
||||
$result = external_api::clean_returnvalue(get_available_in_context::execute_returns(), $result);
|
||||
$this->assertEmpty($result['filters']); // No filters, all disabled at global level.
|
||||
$this->assertEmpty($result['warnings']);
|
||||
@ -130,7 +130,7 @@ final class get_available_in_context_test extends externallib_advanced_testcase
|
||||
$firstfilter = key($allfilters);
|
||||
filter_set_global_state($firstfilter, TEXTFILTER_ON);
|
||||
|
||||
$result = get_available_in_context::execute(array(array('contextlevel' => 'course', 'instanceid' => $course->id)));
|
||||
$result = get_available_in_context::execute([['contextlevel' => 'course', 'instanceid' => $course->id]]);
|
||||
$result = external_api::clean_returnvalue(get_available_in_context::execute_returns(), $result);
|
||||
$this->assertEmpty($result['warnings']);
|
||||
$this->assertEquals($firstfilter, $result['filters'][0]['filter']); // OK, the filter is enabled.
|
||||
@ -139,7 +139,7 @@ final class get_available_in_context_test extends externallib_advanced_testcase
|
||||
|
||||
// Set off the same filter at local context level.
|
||||
filter_set_local_state($firstfilter, \context_course::instance($course->id)->id, TEXTFILTER_OFF);
|
||||
$result = get_available_in_context::execute(array(array('contextlevel' => 'course', 'instanceid' => $course->id)));
|
||||
$result = get_available_in_context::execute([['contextlevel' => 'course', 'instanceid' => $course->id]]);
|
||||
$result = external_api::clean_returnvalue(get_available_in_context::execute_returns(), $result);
|
||||
$this->assertEmpty($result['warnings']);
|
||||
$this->assertEquals($firstfilter, $result['filters'][0]['filter']); // OK, the filter is enabled globally.
|
||||
@ -158,7 +158,7 @@ final class get_available_in_context_test extends externallib_advanced_testcase
|
||||
|
||||
// Create one activity.
|
||||
$course = self::getDataGenerator()->create_course();
|
||||
$forum = self::getDataGenerator()->create_module('forum', (object) array('course' => $course->id));
|
||||
$forum = self::getDataGenerator()->create_module('forum', (object) ['course' => $course->id]);
|
||||
|
||||
// Get all filters and disable them all globally.
|
||||
$allfilters = filter_get_all_installed();
|
||||
@ -166,7 +166,7 @@ final class get_available_in_context_test extends externallib_advanced_testcase
|
||||
filter_set_global_state($filter, TEXTFILTER_DISABLED);
|
||||
}
|
||||
|
||||
$result = get_available_in_context::execute(array(array('contextlevel' => 'module', 'instanceid' => $forum->cmid)));
|
||||
$result = get_available_in_context::execute([['contextlevel' => 'module', 'instanceid' => $forum->cmid]]);
|
||||
$result = external_api::clean_returnvalue(get_available_in_context::execute_returns(), $result);
|
||||
$this->assertEmpty($result['filters']); // No filters, all disabled at global level.
|
||||
$this->assertEmpty($result['warnings']);
|
||||
@ -176,7 +176,7 @@ final class get_available_in_context_test extends externallib_advanced_testcase
|
||||
$firstfilter = key($allfilters);
|
||||
filter_set_global_state($firstfilter, TEXTFILTER_ON);
|
||||
|
||||
$result = get_available_in_context::execute(array(array('contextlevel' => 'module', 'instanceid' => $forum->cmid)));
|
||||
$result = get_available_in_context::execute([['contextlevel' => 'module', 'instanceid' => $forum->cmid]]);
|
||||
$result = external_api::clean_returnvalue(get_available_in_context::execute_returns(), $result);
|
||||
$this->assertEmpty($result['warnings']);
|
||||
$this->assertEquals($firstfilter, $result['filters'][0]['filter']); // OK, the filter is enabled.
|
||||
@ -185,7 +185,7 @@ final class get_available_in_context_test extends externallib_advanced_testcase
|
||||
|
||||
// Set off the same filter at local context level.
|
||||
filter_set_local_state($firstfilter, \context_module::instance($forum->cmid)->id, TEXTFILTER_OFF);
|
||||
$result = get_available_in_context::execute(array(array('contextlevel' => 'module', 'instanceid' => $forum->cmid)));
|
||||
$result = get_available_in_context::execute([['contextlevel' => 'module', 'instanceid' => $forum->cmid]]);
|
||||
$result = external_api::clean_returnvalue(get_available_in_context::execute_returns(), $result);
|
||||
$this->assertEmpty($result['warnings']);
|
||||
$this->assertEquals($firstfilter, $result['filters'][0]['filter']); // OK, the filter is enabled globally.
|
||||
@ -195,7 +195,7 @@ final class get_available_in_context_test extends externallib_advanced_testcase
|
||||
// Try user without permission, warning expected.
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
$result = get_available_in_context::execute(array(array('contextlevel' => 'module', 'instanceid' => $forum->cmid)));
|
||||
$result = get_available_in_context::execute([['contextlevel' => 'module', 'instanceid' => $forum->cmid]]);
|
||||
$result = external_api::clean_returnvalue(get_available_in_context::execute_returns(), $result);
|
||||
$this->assertNotEmpty($result['warnings']);
|
||||
$this->assertEquals('context', $result['warnings'][0]['item']);
|
||||
|
@ -34,7 +34,7 @@ use stdClass;
|
||||
* Note that there may be patent restrictions on the production of gif images
|
||||
* in Canada and some parts of Western Europe and Japan until July 2004.
|
||||
*
|
||||
* @package filter
|
||||
* @package filter_tex
|
||||
* @subpackage tex
|
||||
* @copyright 2004 Zbigniew Fiedorowicz fiedorow@math.ohio-state.edu
|
||||
* Originally based on code provided by Bruno Vernier bruno@vsbeducation.ca
|
||||
@ -45,73 +45,60 @@ class text_filter extends \core_filters\text_filter {
|
||||
public function filter($text, array $options = []) {
|
||||
global $CFG, $DB;
|
||||
|
||||
/// Do a quick check using stripos to avoid unnecessary work
|
||||
if ((!preg_match('/<tex/i', $text)) &&
|
||||
(strpos($text,'$$') === false) &&
|
||||
(strpos($text,'\\[') === false) &&
|
||||
// Do a quick check using stripos to avoid unnecessary work.
|
||||
if (
|
||||
(!preg_match('/<tex/i', $text)) &&
|
||||
(strpos($text, '$$') === false) &&
|
||||
(strpos($text, '\\[') === false) &&
|
||||
(strpos($text, '\\(') === false) &&
|
||||
(!preg_match('/\[tex/i',$text))) {
|
||||
(!preg_match('/\[tex/i', $text))
|
||||
) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
# //restrict filtering to forum 130 (Maths Tools on moodle.org)
|
||||
# $scriptname = $_SERVER['SCRIPT_NAME'];
|
||||
# if (!strstr($scriptname,'/forum/')) {
|
||||
# return $text;
|
||||
# }
|
||||
# if (strstr($scriptname,'post.php')) {
|
||||
# $parent = forum_get_post_full($_GET['reply']);
|
||||
# $discussion = $DB->get_record("forum_discussions", array("id"=>$parent->discussion));
|
||||
# } else if (strstr($scriptname,'discuss.php')) {
|
||||
# $discussion = $DB->get_record("forum_discussions", array("id"=>$_GET['d']));
|
||||
# } else {
|
||||
# return $text;
|
||||
# }
|
||||
# if ($discussion->forum != 130) {
|
||||
# return $text;
|
||||
# }
|
||||
$text .= ' ';
|
||||
preg_match_all('/\$(\$\$+?)([^\$])/s',$text,$matches);
|
||||
for ($i=0; $i<count($matches[0]); $i++) {
|
||||
$replacement = str_replace('$','$', $matches[1][$i]).$matches[2][$i];
|
||||
preg_match_all('/\$(\$\$+?)([^\$])/s', $text, $matches);
|
||||
for ($i = 0; $i < count($matches[0]); $i++) {
|
||||
$replacement = str_replace('$', '$', $matches[1][$i]) . $matches[2][$i];
|
||||
$text = str_replace($matches[0][$i], $replacement, $text);
|
||||
}
|
||||
|
||||
// The following regular expression matches TeX expressions delimited by:
|
||||
// <tex> TeX expression </tex>
|
||||
// or <tex alt="My alternative text to be used instead of the TeX form"> TeX expression </tex>
|
||||
// or $$ TeX expression $$
|
||||
// or \[ TeX expression \] // original tag of MathType and TeXaide (dlnsk)
|
||||
// or [tex] TeX expression [/tex] // somtime it's more comfortable than <tex> (dlnsk)
|
||||
$rules = array(
|
||||
// or \[ TeX expression \] // original tag of MathType and TeXaide
|
||||
// or [tex] TeX expression [/tex] // somtime it's more comfortable than <tex>.
|
||||
$rules = [
|
||||
'<tex(?:\s+alt=["\'](.*?)["\'])?>(.+?)<\/tex>',
|
||||
'\$\$(.+?)\$\$',
|
||||
'\\\\\[(.+?)\\\\\]',
|
||||
'\\\\\((.+?)\\\\\)',
|
||||
'\\[tex\\](.+?)\\[\/tex\\]'
|
||||
);
|
||||
'\\[tex\\](.+?)\\[\/tex\\]',
|
||||
];
|
||||
$megarule = '/' . implode('|', $rules) . '/is';
|
||||
preg_match_all($megarule, $text, $matches);
|
||||
for ($i=0; $i<count($matches[0]); $i++) {
|
||||
for ($i = 0; $i < count($matches[0]); $i++) {
|
||||
$texexp = '';
|
||||
for ($j = 0; $j < count($rules); $j++) {
|
||||
$texexp .= $matches[$j + 2][$i];
|
||||
}
|
||||
$alt = $matches[1][$i];
|
||||
$texexp = str_replace('<nolink>','',$texexp);
|
||||
$texexp = str_replace('</nolink>','',$texexp);
|
||||
$texexp = str_replace('<span class="nolink">','',$texexp);
|
||||
$texexp = str_replace('</span>','',$texexp);
|
||||
$texexp = preg_replace("/<br[[:space:]]*\/?>/i", '', $texexp); //dlnsk
|
||||
$texexp = str_replace('<nolink>', '', $texexp);
|
||||
$texexp = str_replace('</nolink>', '', $texexp);
|
||||
$texexp = str_replace('<span class="nolink">', '', $texexp);
|
||||
$texexp = str_replace('</span>', '', $texexp);
|
||||
$texexp = preg_replace("/<br[[:space:]]*\/?>/i", '', $texexp);
|
||||
$align = "middle";
|
||||
if (preg_match('/^align=bottom /',$texexp)) {
|
||||
$align = "text-bottom";
|
||||
$texexp = preg_replace('/^align=bottom /','',$texexp);
|
||||
} else if (preg_match('/^align=top /',$texexp)) {
|
||||
$align = "text-top";
|
||||
$texexp = preg_replace('/^align=top /','',$texexp);
|
||||
if (preg_match('/^align=bottom /', $texexp)) {
|
||||
$align = "text-bottom";
|
||||
$texexp = preg_replace('/^align=bottom /', '', $texexp);
|
||||
} else if (preg_match('/^align=top /', $texexp)) {
|
||||
$align = "text-top";
|
||||
$texexp = preg_replace('/^align=top /', '', $texexp);
|
||||
}
|
||||
|
||||
// decode entities encoded by editor, luckily there is very little chance of double decoding
|
||||
// Decode entities encoded by editor, luckily there is very little chance of double decoding.
|
||||
$texexp = html_entity_decode($texexp, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
if ($texexp === '') {
|
||||
@ -122,7 +109,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
$texexp = clean_param($texexp, PARAM_TEXT);
|
||||
|
||||
$md5 = md5($texexp);
|
||||
if (!$DB->record_exists("cache_filters", array("filter"=>"tex", "md5key"=>$md5))) {
|
||||
if (!$DB->record_exists("cache_filters", ["filter" => "tex", "md5key" => $md5])) {
|
||||
$texcache = new stdClass();
|
||||
$texcache->filter = 'tex';
|
||||
$texcache->version = 1;
|
||||
@ -135,8 +122,8 @@ class text_filter extends \core_filters\text_filter {
|
||||
if ($convertformat == 'svg' && !core_useragent::supports_svg()) {
|
||||
$convertformat = 'png';
|
||||
}
|
||||
$filename = $md5.".{$convertformat}";
|
||||
$text = str_replace( $matches[0][$i], self::get_image_markup($filename, $texexp, 0, 0, $align, $alt), $text);
|
||||
$filename = $md5 . ".{$convertformat}";
|
||||
$text = str_replace($matches[0][$i], $this->get_image_markup($filename, $texexp, 0, 0, $align, $alt), $text);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
@ -163,11 +150,11 @@ class text_filter extends \core_filters\text_filter {
|
||||
global $CFG, $OUTPUT;
|
||||
|
||||
if (!$imagefile) {
|
||||
throw new coding_exception('image file argument empty in get_image_markup()');
|
||||
throw new coding_exception('Image file argument empty in get_image_markup()');
|
||||
}
|
||||
|
||||
// Work out any necessary inline style.
|
||||
$rules = array();
|
||||
$rules = [];
|
||||
if ($align !== 'middle') {
|
||||
$rules[] = 'vertical-align:' . $align . ';';
|
||||
}
|
||||
@ -189,7 +176,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
// users (to provide a text equivalent to the equation) while the title
|
||||
// is there as a convenience for sighted users who want to see the TeX
|
||||
// code.
|
||||
$title = 'title="'.s($tex).'"';
|
||||
$title = 'title="' . s($tex) . '"';
|
||||
|
||||
if ($alt === '') {
|
||||
$alt = s($tex);
|
||||
@ -199,21 +186,24 @@ class text_filter extends \core_filters\text_filter {
|
||||
|
||||
// Build the output.
|
||||
$anchorcontents = "<img class=\"texrender\" $title alt=\"$alt\" src=\"";
|
||||
if ($CFG->slasharguments) { // Use this method if possible for better caching
|
||||
if ($CFG->slasharguments) {
|
||||
// Use this method if possible for better client-side caching.
|
||||
$anchorcontents .= "$CFG->wwwroot/filter/tex/pix.php/$imagefile";
|
||||
} else {
|
||||
$anchorcontents .= "$CFG->wwwroot/filter/tex/pix.php?file=$imagefile";
|
||||
}
|
||||
$anchorcontents .= "\" $style/>";
|
||||
|
||||
if (!file_exists("$CFG->dataroot/filter/tex/$imagefile") && has_capability('moodle/site:config', context_system::instance())) {
|
||||
$imagefound = file_exists("$CFG->dataroot/filter/tex/$imagefile");
|
||||
if (!$imagefound && has_capability('moodle/site:config', context_system::instance())) {
|
||||
$link = '/filter/tex/texdebug.php';
|
||||
$action = null;
|
||||
} else {
|
||||
$link = new url('/filter/tex/displaytex.php', array('texexp'=>$tex));
|
||||
$action = new popup_action('click', $link, 'popup', array('width'=>320,'height'=>240));
|
||||
$link = new url('/filter/tex/displaytex.php', ['texexp' => $tex]);
|
||||
$action = new popup_action('click', $link, 'popup', ['width' => 320, 'height' => 240]);
|
||||
}
|
||||
$output = $OUTPUT->action_link($link, $anchorcontents, $action, array('title'=>'TeX')); //TODO: the popups do not work when text caching is enabled!!
|
||||
// TODO: the popups do not work when text caching is enabled.
|
||||
$output = $OUTPUT->action_link($link, $anchorcontents, $action, ['title' => 'TeX']);
|
||||
$output = "<span class=\"MathJax_Preview\">$output</span><script type=\"math/tex\">$tex</script>";
|
||||
|
||||
return $output;
|
||||
|
@ -81,7 +81,7 @@ final class text_filter_test extends \advanced_testcase {
|
||||
['$', '$', false],
|
||||
['(', ')', false],
|
||||
['[', ']', false],
|
||||
['$$', '\\]', false]
|
||||
['$$', '\\]', false],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace filter_tidy;
|
||||
|
||||
/**
|
||||
* HTML tidy text filter.
|
||||
*
|
||||
@ -36,8 +38,8 @@
|
||||
class text_filter extends \core_filters\text_filter {
|
||||
#[\Override]
|
||||
public function filter($text, array $options = []) {
|
||||
// Configuration for tidy. Feel free to tune for your needs, e.g. to allow
|
||||
// proprietary markup.
|
||||
// Configuration for tidy.
|
||||
// See https://api.html-tidy.org/tidy/quickref_5.0.0.html for details.
|
||||
$tidyoptions = [
|
||||
'output-xhtml' => true,
|
||||
'show-body-only' => true,
|
||||
@ -53,12 +55,11 @@ class text_filter extends \core_filters\text_filter {
|
||||
return $text;
|
||||
}
|
||||
|
||||
|
||||
// If enabled: run tidy over the entire string.
|
||||
if (function_exists('tidy_repair_string')) {
|
||||
if (extension_loaded('tidy')) {
|
||||
$currentlocale = \core\locale::get_locale();
|
||||
try {
|
||||
$text = tidy_repair_string($text, $tidyoptions, 'utf8');
|
||||
$text = (new \tidy())->repairString($text, $tidyoptions, 'utf8');
|
||||
} finally {
|
||||
\core\locale::set_locale(LC_ALL, $currentlocale);
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
@ -26,19 +25,19 @@ namespace filter_urltolink;
|
||||
*/
|
||||
class text_filter extends \core_filters\text_filter {
|
||||
/**
|
||||
* @var array global configuration for this filter
|
||||
*
|
||||
* This might be eventually moved into parent class if we found it
|
||||
* useful for other filters, too.
|
||||
*
|
||||
* @var array global configuration for this filter
|
||||
*/
|
||||
protected static $globalconfig;
|
||||
|
||||
#[\Override]
|
||||
public function filter($text, array $options = array()) {
|
||||
public function filter($text, array $options = []) {
|
||||
if (!isset($options['originalformat'])) {
|
||||
// if the format is not specified, we are probably called by {@see format_string()}
|
||||
// If the format is not specified, we are probably called by {@see format_string()}
|
||||
// in that case, it would be dangerous to replace URL with the link because it could
|
||||
// be stripped. therefore, we do nothing
|
||||
// be stripped. therefore, we do nothing.
|
||||
return $text;
|
||||
}
|
||||
if (in_array($options['originalformat'], explode(',', get_config('filter_urltolink', 'formats')))) {
|
||||
@ -47,26 +46,22 @@ class text_filter extends \core_filters\text_filter {
|
||||
return $text;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// internal implementation starts here
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Given some text this function converts any URLs it finds into HTML links
|
||||
*
|
||||
* @param string $text Passed in by reference. The string to be searched for urls.
|
||||
*/
|
||||
protected function convert_urls_into_links(&$text) {
|
||||
//I've added img tags to this list of tags to ignore.
|
||||
//See MDL-21168 for more info. A better way to ignore tags whether or not
|
||||
//they are escaped partially or completely would be desirable. For example:
|
||||
//<a href="blah">
|
||||
//<a href="blah">
|
||||
//<a href="blah">
|
||||
$filterignoretagsopen = array('<a\s[^>]+?>', '<span[^>]+?class="nolink"[^>]*?>');
|
||||
$filterignoretagsclose = array('</a>', '</span>');
|
||||
// I've added img tags to this list of tags to ignore.
|
||||
// See MDL-21168 for more info. A better way to ignore tags whether or not
|
||||
// they are escaped partially or completely would be desirable. For example:
|
||||
// <a href="blah">
|
||||
// <a href="blah">
|
||||
// <a href="blah">.
|
||||
$filterignoretagsopen = ['<a\s[^>]+?>', '<span[^>]+?class="nolink"[^>]*?>'];
|
||||
$filterignoretagsclose = ['</a>', '</span>'];
|
||||
$ignoretags = [];
|
||||
filter_save_ignore_tags($text,$filterignoretagsopen,$filterignoretagsclose,$ignoretags);
|
||||
filter_save_ignore_tags($text, $filterignoretagsopen, $filterignoretagsclose, $ignoretags);
|
||||
|
||||
// Check if we support unicode modifiers in regular expressions. Cache it.
|
||||
// TODO: this check should be a environment requirement in Moodle 2.0, as far as unicode
|
||||
@ -76,10 +71,10 @@ class text_filter extends \core_filters\text_filter {
|
||||
// Unicode check, negative assertion and other bits from Moodle.
|
||||
static $unicoderegexp;
|
||||
if (!isset($unicoderegexp)) {
|
||||
$unicoderegexp = @preg_match('/\pL/u', 'a'); // This will fail silently, returning false,
|
||||
$unicoderegexp = @preg_match('/\pL/u', 'a'); // This will fail silently, returning false.
|
||||
}
|
||||
|
||||
// TODO MDL-21296 - use of unicode modifiers may cause a timeout
|
||||
// TODO MDL-21296 - use of unicode modifiers may cause a timeout.
|
||||
$urlstart = '(?:http(s)?://|(?<!://)(www\.))';
|
||||
$domainsegment = '(?:[\pLl0-9][\pLl0-9-]*[\pLl0-9]|[\pLl0-9])';
|
||||
$numericip = '(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})';
|
||||
@ -98,7 +93,7 @@ class text_filter extends \core_filters\text_filter {
|
||||
if ($unicoderegexp) {
|
||||
$regex = '#' . $regex . '#ui';
|
||||
} else {
|
||||
$regex = '#' . preg_replace(array('\pLl', '\PL'), 'a-z', $regex) . '#i';
|
||||
$regex = '#' . preg_replace(['\pLl', '\PL'], 'a-z', $regex) . '#i';
|
||||
}
|
||||
|
||||
// Locate any HTML tags.
|
||||
@ -135,14 +130,14 @@ class text_filter extends \core_filters\text_filter {
|
||||
$text = implode('', $matches);
|
||||
|
||||
if (!empty($ignoretags)) {
|
||||
$ignoretags = array_reverse($ignoretags); /// Reversed so "progressive" str_replace() will solve some nesting problems.
|
||||
$text = str_replace(array_keys($ignoretags),$ignoretags,$text);
|
||||
$ignoretags = array_reverse($ignoretags); // Reversed so "progressive" str_replace() will solve some nesting problems.
|
||||
$text = str_replace(array_keys($ignoretags), $ignoretags, $text);
|
||||
}
|
||||
|
||||
if (get_config('filter_urltolink', 'embedimages')) {
|
||||
// now try to inject the images, this code was originally in the mediapluing filter
|
||||
// Now try to inject the images, this code was originally in the mediapluing filter
|
||||
// this may be useful only if somebody relies on the fact the links in FORMAT_MOODLE get converted
|
||||
// to URLs which in turn change to real images
|
||||
// to URLs which in turn change to real images.
|
||||
$search = '/<a href="([^"]+\.(jpg|png|gif))" class="_blanktarget">([^>]*)<\/a>/is';
|
||||
$text = preg_replace_callback($search, [self::class, 'get_image_markup'], $text);
|
||||
}
|
||||
@ -153,14 +148,14 @@ class text_filter extends \core_filters\text_filter {
|
||||
*
|
||||
* This plugin is intended for automatic conversion of image URLs when FORMAT_MOODLE used.
|
||||
*
|
||||
* @param $link
|
||||
* @param array $link
|
||||
* @return string
|
||||
*/
|
||||
private function get_image_markup($link) {
|
||||
if ($link[1] !== $link[3]) {
|
||||
// this is not a link created by this filter, because the url does not match the text
|
||||
// This is not a link created by this filter, because the url does not match the text.
|
||||
return $link[0];
|
||||
}
|
||||
return '<img class="filter_urltolink_image" alt="" src="'.$link[1].'" />';
|
||||
return '<img class="filter_urltolink_image" alt="" src="' . $link[1] . '" />';
|
||||
}
|
||||
}
|
||||
|
@ -26,11 +26,17 @@ namespace filter_urltolink;
|
||||
* @covers \filter_urltolink\text_filter
|
||||
*/
|
||||
final class text_filter_test extends \basic_testcase {
|
||||
/**
|
||||
* Data provider for test_convert_urls_into_links.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_convert_urls_into_links_test_cases(): array {
|
||||
// Create a 4095 and 4096 long URLs.
|
||||
$superlong4095 = str_pad('http://www.superlong4095.com?this=something', 4095, 'a');
|
||||
$superlong4096 = str_pad('http://www.superlong4096.com?this=something', 4096, 'a');
|
||||
|
||||
// phpcs:disable moodle.Files.LineLength.MaxExceeded, moodle.Files.LineLength.TooLong
|
||||
$texts = [
|
||||
// Just a url.
|
||||
'http://moodle.org - URL' => '<a href="http://moodle.org" class="_blanktarget">http://moodle.org</a> - URL',
|
||||
@ -70,8 +76,6 @@ final class text_filter_test extends \basic_testcase {
|
||||
'URL: www.cc.org/url_(withpar)_go/?i=2' => 'URL: <a href="http://www.cc.org/url_(withpar)_go/?i=2" class="_blanktarget">www.cc.org/url_(withpar)_go/?i=2</a>',
|
||||
'URL: http://cc.org/url_(with)_(par)_go/?i=2' => 'URL: <a href="http://cc.org/url_(with)_(par)_go/?i=2" class="_blanktarget">http://cc.org/url_(with)_(par)_go/?i=2</a>',
|
||||
'URL: www.cc.org/url_(with)_(par)_go/?i=2' => 'URL: <a href="http://www.cc.org/url_(with)_(par)_go/?i=2" class="_blanktarget">www.cc.org/url_(with)_(par)_go/?i=2</a>',
|
||||
// URL legitimately ending in a bracket. Commented out as part of MDL-22390. See next tests for work-arounds.
|
||||
// 'http://en.wikipedia.org/wiki/Slash_(punctuation)'=>'<a href="http://en.wikipedia.org/wiki/Slash_(punctuation)" class="_blanktarget">http://en.wikipedia.org/wiki/Slash_(punctuation)</a>',
|
||||
'http://en.wikipedia.org/wiki/%28#Parentheses_.28_.29 - URL' => '<a href="http://en.wikipedia.org/wiki/%28#Parentheses_.28_.29" class="_blanktarget">http://en.wikipedia.org/wiki/%28#Parentheses_.28_.29</a> - URL',
|
||||
'http://en.wikipedia.org/wiki/(#Parentheses_.28_.29 - URL' => '<a href="http://en.wikipedia.org/wiki/(#Parentheses_.28_.29" class="_blanktarget">http://en.wikipedia.org/wiki/(#Parentheses_.28_.29</a> - URL',
|
||||
// Escaped brackets in url.
|
||||
@ -134,16 +138,14 @@ final class text_filter_test extends \basic_testcase {
|
||||
'<table style="background-image: url(http://moodle.org/pic.jpg);">' => '<table style="background-image: url(http://moodle.org/pic.jpg);">',
|
||||
'<table style="background-image: url("http://moodle.org/pic.jpg");">' => '<table style="background-image: url("http://moodle.org/pic.jpg");">',
|
||||
'<table style="background-image: url( http://moodle.org/pic.jpg );">' => '<table style="background-image: url( http://moodle.org/pic.jpg );">',
|
||||
// Partially escaped img tag
|
||||
// Partially escaped img tag.
|
||||
'partially escaped img tag <img src="http://moodle.org/logo/logo-240x60.gif" />' => 'partially escaped img tag <img src="http://moodle.org/logo/logo-240x60.gif" />',
|
||||
// Fully escaped img tag. Commented out as part of MDL-21183
|
||||
// Htmlspecialchars('fully escaped img tag <img src="http://moodle.org/logo/logo-240x60.gif" />') => 'fully escaped img tag <img src="http://moodle.org/logo/logo-240x60.gif" />',
|
||||
// Double http with www
|
||||
// Double http with www.
|
||||
'One more link like http://www.moodle.org to test' => 'One more link like <a href="http://www.moodle.org" class="_blanktarget">http://www.moodle.org</a> to test',
|
||||
// Encoded URLs in the path
|
||||
// Encoded URLs in the path.
|
||||
'URL: http://127.0.0.1/one%28parenthesis%29/path?param=value' => 'URL: <a href="http://127.0.0.1/one%28parenthesis%29/path?param=value" class="_blanktarget">http://127.0.0.1/one%28parenthesis%29/path?param=value</a>',
|
||||
'URL: www.localhost.com/one%28parenthesis%29/path?param=value' => 'URL: <a href="http://www.localhost.com/one%28parenthesis%29/path?param=value" class="_blanktarget">www.localhost.com/one%28parenthesis%29/path?param=value</a>',
|
||||
// Encoded URLs in the query
|
||||
// Encoded URLs in the query.
|
||||
'URL: http://127.0.0.1/path/to?param=value_with%28parenthesis%29¶m2=1' => 'URL: <a href="http://127.0.0.1/path/to?param=value_with%28parenthesis%29¶m2=1" class="_blanktarget">http://127.0.0.1/path/to?param=value_with%28parenthesis%29¶m2=1</a>',
|
||||
'URL: www.localhost.com/path/to?param=value_with%28parenthesis%29¶m2=1' => 'URL: <a href="http://www.localhost.com/path/to?param=value_with%28parenthesis%29¶m2=1" class="_blanktarget">www.localhost.com/path/to?param=value_with%28parenthesis%29¶m2=1</a>',
|
||||
// Test URL less than 4096 characters in size is converted to link.
|
||||
@ -173,6 +175,8 @@ final class text_filter_test extends \basic_testcase {
|
||||
'<span class="nolink">URL: http://moodle.org</span>' => '<span class="nolink">URL: http://moodle.org</span>',
|
||||
];
|
||||
|
||||
// phpcs:enable
|
||||
|
||||
$data = [];
|
||||
foreach ($texts as $text => $correctresult) {
|
||||
$data[] = [$text, $correctresult];
|
||||
@ -181,7 +185,11 @@ final class text_filter_test extends \basic_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the convert_urls_into_links method.
|
||||
*
|
||||
* @dataProvider get_convert_urls_into_links_test_cases
|
||||
* @param string $text
|
||||
* @param string $correctresult
|
||||
*/
|
||||
public function test_convert_urls_into_links($text, $correctresult): void {
|
||||
$testablefilter = $this->get_testable_text_filter();
|
||||
@ -193,13 +201,15 @@ final class text_filter_test extends \basic_testcase {
|
||||
/**
|
||||
* Get a copy of the filter configured for testing.
|
||||
*
|
||||
* @param array $args
|
||||
* @param array ...$args
|
||||
* @return \filter_urltolink\text_filter
|
||||
*/
|
||||
protected function get_testable_text_filter(...$args): text_filter {
|
||||
return new class extends text_filter {
|
||||
// phpcs:ignore moodle.Commenting.MissingDocblock.MissingTestcaseMethodDescription
|
||||
public function __construct() {
|
||||
}
|
||||
// phpcs:ignore moodle.Commenting.MissingDocblock.MissingTestcaseMethodDescription, Generic.CodeAnalysis.UselessOverridingMethod.Found
|
||||
public function convert_urls_into_links(&$text) {
|
||||
parent::convert_urls_into_links($text);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user