diff --git a/admin/settings/security.php b/admin/settings/security.php
index b2b264c5c2e..b72333c2bb3 100644
--- a/admin/settings/security.php
+++ b/admin/settings/security.php
@@ -72,6 +72,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
$temp->add(new admin_setting_configcheckbox('disableuserimages', get_string('disableuserimages', 'admin'), get_string('configdisableuserimages', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('emailchangeconfirmation', get_string('emailchangeconfirmation', 'admin'), get_string('configemailchangeconfirmation', 'admin'), 1));
$temp->add(new admin_setting_configselect('rememberusername', get_string('rememberusername','admin'), get_string('rememberusername_desc','admin'), 2, array(1=>get_string('yes'), 0=>get_string('no'), 2=>get_string('optional'))));
+ $temp->add(new admin_setting_configcheckbox('strictformsrequired', get_string('strictformsrequired', 'admin'), get_string('configstrictformsrequired', 'admin'), 0));
$ADMIN->add('security', $temp);
diff --git a/lang/en/admin.php b/lang/en/admin.php
index ca08403d1f5..bf5b8b05a92 100644
--- a/lang/en/admin.php
+++ b/lang/en/admin.php
@@ -333,6 +333,7 @@ $string['configstatsmaxruntime3'] = 'Specify the maximum time allowed to calcula
$string['configstatsruntimedays'] = 'Specify the maximum number of days processed in each stats execution. When stats are up-to-date, only one day will be processed, so adjust this value depending of your server load, reducing it if shorter cron executions are needed.';
$string['configstatsruntimestart'] = 'What time should the cronjob that does the stats processing start? Please specify different times if there are multiple Moodles on one physical server.';
$string['configstatsuserthreshold'] = 'This setting specifies the minimum number of enrolled users for a course to be included in statistics calculations.';
+$string['configstrictformsrequired'] = 'If enabled, users are prevented from entering a space or line break only in required fields in forms.';
$string['configstripalltitletags'] = 'Uncheck this setting to allow HTML tags in activity and resource names.';
$string['configsupportemail'] = 'This email address will be published to users of this site as the one to email when they need general help (for example, when new users create their own accounts). If this email is left blank then no such helpful email address is supplied.';
$string['configsupportname'] = 'This is the name of a person or other entity offering general help via the support email or web address.';
@@ -961,6 +962,7 @@ $string['stickyblockscourseview'] = 'Course page';
$string['stickyblocksduplicatenotice'] = 'If any block you add here is already present in a particular page, it will result in a duplicate.
Only the pinned block will be non-editable, the duplicate will still be editable.';
$string['stickyblocksmymoodle'] = 'My Moodle';
$string['stickyblockspagetype'] = 'Page type to configure';
+$string['strictformsrequired'] = 'Strict validation of required fields';
$string['stripalltitletags'] = 'Remove HTML tags from all activity names';
$string['supportcontact'] = 'Support contact';
$string['supportemail'] = 'Support email';
diff --git a/lib/formslib.php b/lib/formslib.php
index fbc6a50bc2c..1787b0f2855 100644
--- a/lib/formslib.php
+++ b/lib/formslib.php
@@ -43,6 +43,7 @@ defined('MOODLE_INTERNAL') || die();
require_once 'HTML/QuickForm.php';
require_once 'HTML/QuickForm/DHTMLRulesTableless.php';
require_once 'HTML/QuickForm/Renderer/Tableless.php';
+require_once 'HTML/QuickForm/Rule.php';
require_once $CFG->libdir.'/filelib.php';
@@ -2344,6 +2345,57 @@ class MoodleQuickForm_Renderer extends HTML_QuickForm_Renderer_Tableless{
}
}
+/**
+ * Required elements validation
+ * This class overrides QuickForm validation since it allowed space or empty tag as a value
+ */
+class MoodleQuickForm_Rule_Required extends HTML_QuickForm_Rule {
+ /**
+ * Checks if an element is not empty.
+ * This is a server-side validation, it works for both text fields and editor fields
+ *
+ * @param string $value Value to check
+ * @param mixed $options Not used yet
+ * @return boolean true if value is not empty
+ */
+ function validate($value, $options = null) {
+ global $CFG;
+ if (is_array($value) && array_key_exists('text', $value)) {
+ $value = $value['text'];
+ }
+ $stripvalues = array(
+ '#?(?!img|canvas|hr).*?>#im', // all tags except img, canvas and hr
+ '#(\xc2|\xa0|\s| )#', //any whitespaces actually
+ );
+ if (!empty($CFG->strictformsrequired)) {
+ $value = preg_replace($stripvalues, '', (string)$value);
+ }
+ if ((string)$value == '') {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * This function returns Javascript code used to build client-side validation.
+ * It checks if an element is not empty.
+ * Note, that QuickForm does not know how to work with editor text field and builds not correct
+ * JS code for validation. If client check is enabled for editor field it will not be validated
+ * on client side no matter what this function returns.
+ *
+ * @param mixed $options Not used yet
+ * @return array
+ */
+ function getValidationScript($options = null) {
+ global $CFG;
+ if (!empty($CFG->strictformsrequired)) {
+ return array('', "{jsVar}.replace(/^\s+$/g, '') == ''");
+ } else {
+ return array('', "{jsVar} == ''");
+ }
+ }
+}
+
/**
* @global object $GLOBALS['_HTML_QuickForm_default_renderer']
* @name $_HTML_QuickForm_default_renderer
@@ -2387,3 +2439,5 @@ MoodleQuickForm::registerElementType('text', "$CFG->libdir/form/text.php", 'Mood
MoodleQuickForm::registerElementType('textarea', "$CFG->libdir/form/textarea.php", 'MoodleQuickForm_textarea');
MoodleQuickForm::registerElementType('url', "$CFG->libdir/form/url.php", 'MoodleQuickForm_url');
MoodleQuickForm::registerElementType('warning', "$CFG->libdir/form/warning.php", 'MoodleQuickForm_warning');
+
+MoodleQuickForm::registerRule('required', null, 'MoodleQuickForm_Rule_Required', "$CFG->libdir/formslib.php");
diff --git a/lib/pear/HTML/QuickForm.php b/lib/pear/HTML/QuickForm.php
index fe6c3f6de9e..e9e4a7e37d1 100644
--- a/lib/pear/HTML/QuickForm.php
+++ b/lib/pear/HTML/QuickForm.php
@@ -1502,13 +1502,6 @@ class HTML_QuickForm extends HTML_Common {
$values[] = $this->getSubmitValue($elName);
}
$result = $registry->validate($rule['type'], $values, $rule['format'], true);
- } else if ($rule['type'] === 'required' and $this->getElement($target)->_type === 'editor') {
- //horrible horrible hack
- if (!isset($submitValue['text']) or $submitValue['text'] === '') {
- $result = false;
- } else {
- $result = true;
- }
} elseif (is_array($submitValue) && !isset($rule['howmany'])) {
$result = $registry->validate($rule['type'], $submitValue, $rule['format'], true);
} else {
diff --git a/lib/pear/README_MOODLE.txt b/lib/pear/README_MOODLE.txt
index 8fa59f44828..00eeff53349 100644
--- a/lib/pear/README_MOODLE.txt
+++ b/lib/pear/README_MOODLE.txt
@@ -22,8 +22,6 @@ and documented for Moodle at:
4/ MDL-20876 - replaced deprecated split() with explode() or str_split() where appropriate
-5/ hardcoded editor element required rule validation
-
Such modifications should be carefully each time the Excel PEAR package is updated
to a new release within Moodle.
diff --git a/lib/simpletest/testformslib.php b/lib/simpletest/testformslib.php
new file mode 100644
index 00000000000..03308956cf8
--- /dev/null
+++ b/lib/simpletest/testformslib.php
@@ -0,0 +1,119 @@
+.
+
+
+/**
+ * Unit tests for /lib/formslib.php.
+ *
+ * @package file
+ * @copyright 2011 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+if (!defined('MOODLE_INTERNAL')) {
+ die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page
+}
+require_once($CFG->libdir . '/formslib.php');
+
+class formslib_test extends UnitTestCase {
+
+ public function test_require_rule() {
+ global $CFG;
+
+ $strictformsrequired = false;
+ if (!empty($CFG->strictformsrequired)) {
+ $strictformsrequired = $CFG->strictformsrequired;
+ }
+
+ $rule = new MoodleQuickForm_Rule_Required();
+
+ // First run the tests with strictformsrequired off
+ $CFG->strictformsrequired = false;
+ // Passes
+ $this->assertTrue($rule->validate('Something'));
+ $this->assertTrue($rule->validate("Something\nmore"));
+ $this->assertTrue($rule->validate("\nmore"));
+ $this->assertTrue($rule->validate(" more "));
+ $this->assertTrue($rule->validate("0"));
+ $this->assertTrue($rule->validate(0));
+ $this->assertTrue($rule->validate(true));
+ $this->assertTrue($rule->validate(' '));
+ $this->assertTrue($rule->validate(' '));
+ $this->assertTrue($rule->validate("\t"));
+ $this->assertTrue($rule->validate("\n"));
+ $this->assertTrue($rule->validate("\r"));
+ $this->assertTrue($rule->validate("\r\n"));
+ $this->assertTrue($rule->validate(" \t \n \r "));
+ $this->assertTrue($rule->validate('
')); + $this->assertTrue($rule->validate('
x
')); + $this->assertTrue($rule->validate('
'));
+ $this->assertTrue($rule->validate('
'));
+ $this->assertTrue($rule->validate('
'));
+ $this->assertTrue($rule->validate('x
')); + $this->assertTrue($rule->validate('
'));
+ $this->assertTrue($rule->validate('
'));
+ $this->assertTrue($rule->validate('
'));
+ $this->assertTrue($rule->validate('')); + $this->assertFalse($rule->validate('