// systems, but let's allow room for expansion.
core_php_time_limit::raise(300);
-$filter = optional_param('filter', '', PARAM_ALPHANUMEXT);
+$filter = optional_param('filter', '', PARAM_NOTAGS);
$type = optional_param('type', false, PARAM_ALPHAEXT);
$component = optional_param('component', '', PARAM_ALPHAEXT);
// The loaded steps depends on the component specified.
behat_config_manager::update_config_file($component, false);
- // The Moodle\BehatExtension\HelpPrinter\MoodleDefinitionsPrinter will parse this search format.
+ // The Moodle\BehatExtension\Definition\Printer\ConsoleDefinitionInformationPrinter will parse this search format.
if ($type) {
$filter .= '&&' . $type;
}
if ($filter) {
- $filteroption = ' -d "' . $filter . '"';
+ $filteroption = ' -d ' . escapeshellarg($filter);
} else {
$filteroption = ' -di';
}
global $CFG;
require_once($CFG->libdir . '/behat/classes/behat_selectors.php');
- $html = $this->generic_info();
-
- // Form.
- ob_start();
- $form->display();
- $html .= ob_get_contents();
- ob_end_clean();
+ $html = $this->output->header();
+ $html .= $this->output->heading(get_string('pluginname', 'tool_behat'));
+ $html .= $form->render();
if (empty($stepsdefinitions)) {
$stepsdefinitions = get_string('nostepsdefinitions', 'tool_behat');
*/
public function render_error($msg) {
- $html = $this->generic_info();
+ $html = $this->output->header();
+ $html .= $this->output->heading(get_string('pluginname', 'tool_behat'));
+ $html .= $this->generic_info();
$a = new stdClass();
$a->errormsg = $msg;
*
* @return string
*/
- protected function generic_info() {
-
- $title = get_string('pluginname', 'tool_behat');
-
- // Header.
- $html = $this->output->header();
- $html .= $this->output->heading($title);
+ public function generic_info() {
// Info.
$installurl = behat_command::DOCS_URL;
);
// List of steps.
- $html .= $this->output->box_start();
- $html .= html_writer::tag('h3', get_string('infoheading', 'tool_behat'));
+ $html = $this->output->box_start();
$html .= html_writer::tag('div', get_string('aim', 'tool_behat'));
$html .= html_writer::start_tag('div');
$html .= html_writer::start_tag('ul');
* @return void
*/
public function definition() {
+ global $PAGE;
$mform = $this->_form;
+ $output = $PAGE->get_renderer('tool_behat');
+
+ $mform->addElement('header', 'info', get_string('infoheading', 'tool_behat'));
+ $mform->setExpanded('info', false);
+ $mform->addElement('html', $output->generic_info());
$mform->addElement('header', 'filters', get_string('stepsdefinitionsfilters', 'tool_behat'));
-.steps-definitions {
- border-style: solid;
- border-width: 1px;
- border-color: #bbb;
- padding: 5px;
- margin: auto;
- width: 50%;
+#page-admin-tool-behat-index .steps-definitions {
+ margin: 1rem auto;
}
-.steps-definitions .step {
- margin: 10px 0 10px 0;
+#page-admin-tool-behat-index .steps-definitions .step {
+ margin: 1rem 0 0 0;
+ border: 1px solid #eee;
+ padding: 1rem;
}
-.steps-definitions .stepdescription {
- color: #bf8c12;
+#page-admin-tool-behat-index .steps-definitions .stepdescription {
+ font-style: italic;
}
-.steps-definitions .steptype {
+#page-admin-tool-behat-index .steps-definitions .stepcontent {
+ margin: 1rem 0;
+}
+
+#page-admin-tool-behat-index .steps-definitions .steptype {
color: #1467a6;
- margin-right: 5px;
+ margin-right: 1ex;
+}
+
+#page-admin-tool-behat-index .steps-definitions .stepapipath {
+ font-family: monospace;
+ font-size: smaller;
}
-.steps-definitions .stepregex {
+#page-admin-tool-behat-index .steps-definitions .stepregex {
color: #060;
}
Given I set the field "Contains" to "homepage"
When I press "Filter"
Then I should see "Opens Moodle homepage."
+
+ @javascript
+ Scenario: Filtering by the multiple words pattern
+ Given I set the field "Contains" to "should exist"
+ When I press "Filter"
+ Then I should not see "There aren't steps definitions matching this filter"
+ And I should see "Checks the provided element and selector type exists in the current page."
+ And I should see "Checks that an element and selector type exists in another element and selector type on the current page."
<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Bulk user actions
+ *
+ * @package core
+ * @copyright Moodle
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
require_once('../../config.php');
require_once($CFG->libdir.'/adminlib.php');
if (!isset($SESSION->bulk_users)) {
$SESSION->bulk_users = array();
}
-// create the user filter form
+// Create the user filter form.
$ufiltering = new user_filtering();
-// array of bulk operations
-// create the bulk operations form
-$action_form = new user_bulk_action_form();
-if ($data = $action_form->get_data()) {
- // check if an action should be performed and do so
- switch ($data->action) {
- case 1: redirect($CFG->wwwroot.'/'.$CFG->admin.'/user/user_bulk_confirm.php');
- case 2: redirect($CFG->wwwroot.'/'.$CFG->admin.'/user/user_bulk_message.php');
- case 3: redirect($CFG->wwwroot.'/'.$CFG->admin.'/user/user_bulk_delete.php');
- case 4: redirect($CFG->wwwroot.'/'.$CFG->admin.'/user/user_bulk_display.php');
- case 5: redirect($CFG->wwwroot.'/'.$CFG->admin.'/user/user_bulk_download.php');
- case 7: redirect($CFG->wwwroot.'/'.$CFG->admin.'/user/user_bulk_forcepasswordchange.php');
- case 8: redirect($CFG->wwwroot.'/'.$CFG->admin.'/user/user_bulk_cohortadd.php');
+// Create the bulk operations form.
+$actionform = new user_bulk_action_form();
+if ($data = $actionform->get_data()) {
+ // Check if an action should be performed and do so.
+ $bulkactions = $actionform->get_actions();
+ if (array_key_exists($data->action, $bulkactions)) {
+ redirect($bulkactions[$data->action]->url);
}
+
}
-$user_bulk_form = new user_bulk_form(null, get_selection_data($ufiltering));
+$userbulkform = new user_bulk_form(null, get_selection_data($ufiltering));
-if ($data = $user_bulk_form->get_data()) {
+if ($data = $userbulkform->get_data()) {
if (!empty($data->addall)) {
add_selection_all($ufiltering);
if (in_array(0, $data->ausers)) {
add_selection_all($ufiltering);
} else {
- foreach($data->ausers as $userid) {
+ foreach ($data->ausers as $userid) {
if ($userid == -1) {
continue;
}
}
} else if (!empty($data->removeall)) {
- $SESSION->bulk_users= array();
+ $SESSION->bulk_users = array();
} else if (!empty($data->removesel)) {
if (!empty($data->susers)) {
if (in_array(0, $data->susers)) {
- $SESSION->bulk_users= array();
+ $SESSION->bulk_users = array();
} else {
- foreach($data->susers as $userid) {
+ foreach ($data->susers as $userid) {
if ($userid == -1) {
continue;
}
}
}
- // reset the form selections
+ // Reset the form selections.
unset($_POST);
- $user_bulk_form = new user_bulk_form(null, get_selection_data($ufiltering));
+ $userbulkform = new user_bulk_form(null, get_selection_data($ufiltering));
}
-// do output
echo $OUTPUT->header();
$ufiltering->display_add();
$ufiltering->display_active();
-$user_bulk_form->display();
+$userbulkform->display();
-$action_form->display();
+$actionform->display();
echo $OUTPUT->footer();
<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Bulk user action forms
+ *
+ * @package core
+ * @copyright Moodle
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
require_once($CFG->libdir.'/datalib.php');
+/**
+ * Bulk user action form
+ *
+ * @copyright Moodle
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
class user_bulk_action_form extends moodleform {
- function definition() {
- global $CFG;
- $mform =& $this->_form;
+ /**
+ * Returns an array of action_link's of all bulk actions available for this user.
+ *
+ * @return array of action_link objects
+ */
+ public function get_actions(): array {
+
+ global $CFG;
$syscontext = context_system::instance();
- $actions = array(0=>get_string('choose').'...');
+ $actions = [];
if (has_capability('moodle/user:update', $syscontext)) {
- $actions[1] = get_string('confirm');
+ $actions['confirm'] = new action_link(
+ new moodle_url('/admin/user/user_bulk_confirm.php'),
+ get_string('confirm'));
}
if (has_capability('moodle/site:readallmessages', $syscontext) && !empty($CFG->messaging)) {
- $actions[2] = get_string('messageselectadd');
+ $actions['message'] = new action_link(
+ new moodle_url('/admin/user/user_bulk_message.php'),
+ get_string('messageselectadd'));
}
if (has_capability('moodle/user:delete', $syscontext)) {
- $actions[3] = get_string('delete');
+ $actions['delete'] = new action_link(
+ new moodle_url('/admin/user/user_bulk_delete.php'),
+ get_string('delete'));
}
- $actions[4] = get_string('displayonpage');
+ $actions['displayonpage'] = new action_link(
+ new moodle_url('/admin/user/user_bulk_display.php'),
+ get_string('displayonpage'));
+
if (has_capability('moodle/user:update', $syscontext)) {
- $actions[5] = get_string('download', 'admin');
+ $actions['download'] = new action_link(
+ new moodle_url('/admin/user/user_bulk_download.php'),
+ get_string('download', 'admin'));
}
+
if (has_capability('moodle/user:update', $syscontext)) {
- $actions[7] = get_string('forcepasswordchange');
+ $actions['forcepasswordchange'] = new action_link(
+ new moodle_url('/admin/user/user_bulk_forcepasswordchange.php'),
+ get_string('forcepasswordchange'));
}
if (has_capability('moodle/cohort:assign', $syscontext)) {
- $actions[8] = get_string('bulkadd', 'core_cohort');
+ $actions['addtocohort'] = new action_link(
+ new moodle_url('/admin/user/user_bulk_cohortadd.php'),
+ get_string('bulkadd', 'core_cohort'));
+ }
+
+ // Any plugin can append actions to this list by implementing a callback
+ // <component>_bulk_user_actions() which returns an array of action_link.
+ // Each new action's key should have a frankenstyle prefix to avoid clashes.
+ // See MDL-38511 for more details.
+ $moreactions = get_plugins_with_function('bulk_user_actions', 'lib.php');
+ foreach ($moreactions as $plugintype => $plugins) {
+ foreach ($plugins as $pluginfunction) {
+ $actions += $pluginfunction();
+ }
+ }
+
+ return $actions;
+
+ }
+
+ /**
+ * Form definition
+ */
+ public function definition() {
+ global $CFG;
+
+ $mform =& $this->_form;
+
+ $actions = [0 => get_string('choose') . '...'];
+ $bulkactions = $this->get_actions();
+ foreach ($bulkactions as $key => $action) {
+ $actions[$key] = $action->text;
}
$objs = array();
$objs[] =& $mform->createElement('select', 'action', null, $actions);
}
}
+/**
+ * Bulk user form
+ *
+ * @copyright Moodle
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
class user_bulk_form extends moodleform {
- function definition() {
+
+ /**
+ * Form definition
+ */
+ public function definition() {
$mform =& $this->_form;
$acount =& $this->_customdata['acount'];
$objs[1] =& $mform->createElement('select', 'susers', get_string('selected', 'bulkusers'), $schoices, 'size="15"');
$objs[1]->setMultiple(true);
-
$grp =& $mform->addElement('group', 'usersgrp', get_string('users', 'bulkusers'), $objs, ' ', false);
$mform->addHelpButton('usersgrp', 'users', 'bulkusers');
$DB->delete_records(template_cohort::TABLE, array('cohortid' => $cohort->id));
}
+ /**
+ * Action to perform when a user is deleted.
+ *
+ * @param int $userid The user id.
+ */
+ public static function hook_user_deleted($userid) {
+ global $DB;
+
+ $usercompetencies = $DB->get_records(user_competency::TABLE, ['userid' => $userid], '', 'id');
+ foreach ($usercompetencies as $usercomp) {
+ $DB->delete_records(evidence::TABLE, ['usercompetencyid' => $usercomp->id]);
+ }
+
+ $DB->delete_records(user_competency::TABLE, ['userid' => $userid]);
+ $DB->delete_records(user_competency_course::TABLE, ['userid' => $userid]);
+ $DB->delete_records(user_competency_plan::TABLE, ['userid' => $userid]);
+
+ // Delete any associated files.
+ $fs = get_file_storage();
+ $context = context_user::instance($userid);
+ $userevidences = $DB->get_records(user_evidence::TABLE, ['userid' => $userid], '', 'id');
+ foreach ($userevidences as $userevidence) {
+ $DB->delete_records(user_evidence_competency::TABLE, ['userevidenceid' => $userevidence->id]);
+ $DB->delete_records(user_evidence::TABLE, ['id' => $userevidence->id]);
+ $fs->delete_area_files($context->id, 'core_competency', 'userevidence', $userevidence->id);
+ }
+
+ $userplans = $DB->get_records(plan::TABLE, ['userid' => $userid], '', 'id');
+ foreach ($userplans as $userplan) {
+ $DB->delete_records(plan_competency::TABLE, ['planid' => $userplan->id]);
+ $DB->delete_records(plan::TABLE, ['id' => $userplan->id]);
+ }
+ }
+
/**
* Manually grade a user competency.
*
$this->assertEquals(1, \core_competency\template_cohort::count_records(array('templateid' => $t1->get('id'))));
$this->assertEquals(0, \core_competency\template_cohort::count_records(array('templateid' => $t2->get('id'))));
}
+
+ public function test_hook_user_deleted() {
+ $this->resetAfterTest();
+ $dg = $this->getDataGenerator();
+ $ccg = $dg->get_plugin_generator('core_competency');
+
+ $u1 = $dg->create_user();
+
+ $framework = $ccg->create_framework();
+ $comp1 = $ccg->create_competency(['competencyframeworkid' => $framework->get('id')]);
+ $comp2 = $ccg->create_competency(['competencyframeworkid' => $framework->get('id')]);
+
+ $c1 = $dg->create_course();
+ $cc1a = $ccg->create_course_competency(['competencyid' => $comp1->get('id'), 'courseid' => $c1->id]);
+ $cc1b = $ccg->create_course_competency(['competencyid' => $comp2->get('id'), 'courseid' => $c1->id]);
+ $assign1a = $dg->create_module('assign', ['course' => $c1]);
+ $assign1b = $dg->create_module('assign', ['course' => $c1]);
+ $cmc1a = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign1a->cmid]);
+ $cmc1b = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign1b->cmid]);
+ $ucc1a = $ccg->create_user_competency_course(['competencyid' => $comp1->get('id'), 'courseid' => $c1->id,
+ 'userid' => $u1->id]);
+ $ucc1b = $ccg->create_user_competency_course(['competencyid' => $comp2->get('id'), 'courseid' => $c1->id,
+ 'userid' => $u1->id]);
+
+ $c2 = $dg->create_course();
+ $cc2a = $ccg->create_course_competency(['competencyid' => $comp1->get('id'), 'courseid' => $c2->id]);
+ $cc2b = $ccg->create_course_competency(['competencyid' => $comp2->get('id'), 'courseid' => $c2->id]);
+ $assign2a = $dg->create_module('assign', ['course' => $c2]);
+ $assign2b = $dg->create_module('assign', ['course' => $c2]);
+ $cmc2a = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign2a->cmid]);
+ $cmc2b = $ccg->create_course_module_competency(['competencyid' => $comp1->get('id'), 'cmid' => $assign2b->cmid]);
+ $ucc2a = $ccg->create_user_competency_course(['competencyid' => $comp1->get('id'), 'courseid' => $c2->id,
+ 'userid' => $u1->id]);
+ $ucc2b = $ccg->create_user_competency_course(['competencyid' => $comp2->get('id'), 'courseid' => $c2->id,
+ 'userid' => $u1->id]);
+
+ reset_course_userdata((object) ['id' => $c1->id, 'reset_competency_ratings' => true]);
+
+ delete_user($u1);
+
+ // Assert the records don't exist anymore.
+ $this->assertEquals(0, user_competency_course::count_records(['courseid' => $c1->id, 'userid' => $u1->id]));
+ }
}
// $CFG->supportuserid = -20;
//
// Moodle 2.7 introduces a locking api for critical tasks (e.g. cron).
-// The default locking system to use is DB locking for Postgres, and file locking for
-// MySQL, Oracle and SQLServer. If $CFG->preventfilelocking is set, then the default
-// will always be DB locking. It can be manually set to one of the lock
+// The default locking system to use is DB locking for Postgres, MySQL, MariaDB and
+// file locking for Oracle and SQLServer. If $CFG->preventfilelocking is set, then the
+// default will always be DB locking. It can be manually set to one of the lock
// factory classes listed below, or one of your own custom classes implementing the
// \core\lock\lock_factory interface.
//
//
// "\\core\\lock\\db_record_lock_factory" - DB locking based on table rows.
//
+// "\\core\\lock\\mysql_lock_factory" - DB locking based on MySQL / MariaDB locks.
+//
// "\\core\\lock\\postgres_lock_factory" - DB locking based on postgres advisory locks.
//
// Settings used by the lock factories
return self::$singleton;
}
+ /**
+ * Run reset code after unit tests to reset the singleton usage.
+ */
+ public static function reset_caches(): void {
+ if (!PHPUNIT_TEST) {
+ throw new \coding_exception('This feature is only intended for use in unit tests');
+ }
+
+ static::$singleton = null;
+ }
+
/**
* The current user can configure custom fields on this component.
*
// Output section activities summary:
$o = '';
- $o.= html_writer::start_tag('div', array('class' => 'section-summary-activities mdl-right'));
+ $o.= html_writer::start_tag('div', array('class' => 'section-summary-activities pr-2 mdl-right'));
foreach ($sectionmods as $mod) {
$o.= html_writer::start_tag('span', array('class' => 'activity-count'));
$o.= $mod['name'].': '.$mod['count'];
$a->complete = $complete;
$a->total = $total;
- $o.= html_writer::start_tag('div', array('class' => 'section-summary-activities mdl-right'));
+ $o.= html_writer::start_tag('div', array('class' => 'section-summary-activities pr-2 mdl-right'));
$o.= html_writer::tag('span', get_string('progresstotal', 'completion', $a), array('class' => 'activity-count'));
$o.= html_writer::end_tag('div');
}
}
// Delete activity context questions and question categories.
- question_delete_activity($cm);
+ $showinfo = !defined('AJAX_SCRIPT') || AJAX_SCRIPT == '0';
+
+ question_delete_activity($cm, $showinfo);
// Call the delete_instance function, if it returns false throw an exception.
if (!$deleteinstancefunction($cm->instance)) {
}
/**
- * Get the list of admin settings for this module and apply any defaults/advanced/locked settings.
+ * Get the list of admin settings for this module and apply any defaults/advanced/locked/required settings.
*
* @param $datetimeoffsets array - If passed, this is an array of fieldnames => times that the
* default date/time value should be relative to. If not passed, all
if (!empty($settings->$advancedsetting)) {
$mform->setAdvanced($name);
}
+ $requiredsetting = $name . '_required';
+ if (!empty($settings->$requiredsetting)) {
+ $mform->addRule($name, null, 'required', null, 'client');
+ }
}
}
}
class core_customfield_api_testcase extends advanced_testcase {
/**
- * This method is called after the last test of this test class is run.
- */
- public static function tearDownAfterClass() {
- $handler = core_course\customfield\course_handler::create();
- $handler->delete_all();
- }
-
- /**
- * Tests set up.
- */
- public function setUp() {
- $this->resetAfterTest();
- }
-
- /**
- * Get generator
+ * Get generator.
+ *
* @return core_customfield_generator
*/
- protected function get_generator() : core_customfield_generator {
+ protected function get_generator(): core_customfield_generator {
return $this->getDataGenerator()->get_plugin_generator('core_customfield');
}
*
* @param array $expected
* @param array $array array of objects with "get($property)" method
- * @param sring $propertyname
+ * @param string $propertyname
*/
protected function assert_property_in_array($expected, $array, $propertyname) {
$this->assertEquals($expected, array_values(array_map(function($a) use ($propertyname) {
* in the interface using drag-drop.
*/
public function test_move_category() {
+ $this->resetAfterTest();
+
// Create the categories.
$params = ['component' => 'core_course', 'area' => 'course', 'itemid' => 0];
$id0 = $this->get_generator()->create_category($params)->get('id');
* Tests for \core_customfield\api::get_categories_with_fields() behaviour.
*/
public function test_get_categories_with_fields() {
+ $this->resetAfterTest();
+
// Create the categories.
$options = [
'component' => 'core_course',
* Test for functions api::save_category() and rename_category)
*/
public function test_save_category() {
+ $this->resetAfterTest();
+
$params = ['component' => 'core_course', 'area' => 'course', 'itemid' => 0, 'name' => 'Cat1',
'contextid' => context_system::instance()->id];
$c1 = category_controller::create(0, (object)$params);
* Test for function handler::create_category
*/
public function test_create_category() {
+ $this->resetAfterTest();
+
$handler = \core_course\customfield\course_handler::create();
$c1id = $handler->create_category();
$c1 = $handler->get_categories_with_fields()[$c1id];
* Tests for \core_customfield\api::delete_category() behaviour.
*/
public function test_delete_category_with_fields() {
+ $this->resetAfterTest();
+
global $DB;
// Create two categories with fields and data.
$options = [
class core_customfield_category_controller_testcase extends advanced_testcase {
/**
- * This method is called after the last test of this test class is run.
+ * Get generator.
+ *
+ * @return core_customfield_generator
*/
- public static function tearDownAfterClass() {
- $handler = core_course\customfield\course_handler::create();
- $handler->delete_all();
+ protected function get_generator(): core_customfield_generator {
+ return $this->getDataGenerator()->get_plugin_generator('core_customfield');
}
/**
- * Tests set up.
+ * Test for the field_controller::__construct function.
*/
- public function setUp() {
+ public function test_constructor() {
$this->resetAfterTest();
- }
-
- /**
- * Get generator
- * @return core_customfield_generator
- */
- protected function get_generator() : core_customfield_generator {
- return $this->getDataGenerator()->get_plugin_generator('core_customfield');
- }
- public function test_constructor() {
$c = category_controller::create(0, (object)['component' => 'core_course', 'area' => 'course', 'itemid' => 0]);
$handler = $c->get_handler();
$this->assertTrue($c instanceof category_controller);
*/
public function test_constructor_errors() {
global $DB;
+ $this->resetAfterTest();
+
$cat = $this->get_generator()->create_category();
$catrecord = $cat->to_record();
* \core_customfield\category_controller::get()
*/
public function test_create_category() {
+ $this->resetAfterTest();
// Create the category.
$lpg = $this->get_generator();
* Tests for \core_customfield\category_controller::set() behaviour.
*/
public function test_rename_category() {
+ $this->resetAfterTest();
+
// Create the category.
$params = ['component' => 'core_course', 'area' => 'course', 'itemid' => 0, 'name' => 'Cat1',
'contextid' => context_system::instance()->id];
* Tests for \core_customfield\category_controller::delete() behaviour.
*/
public function test_delete_category() {
+ $this->resetAfterTest();
+
// Create the category.
$lpg = $this->get_generator();
$category0 = $lpg->create_category();
class core_customfield_data_controller_testcase extends advanced_testcase {
/**
- * This method is called after the last test of this test class is run.
- */
- public static function tearDownAfterClass() {
- $handler = core_course\customfield\course_handler::create();
- $handler->delete_all();
- }
-
- /**
- * Tests set up.
- */
- public function setUp() {
- $this->resetAfterTest();
- }
-
- /**
- * Get generator
+ * Get generator.
+ *
* @return core_customfield_generator
*/
- protected function get_generator() : core_customfield_generator {
+ protected function get_generator(): core_customfield_generator {
return $this->getDataGenerator()->get_plugin_generator('core_customfield');
}
*/
public function test_constructor() {
global $DB;
+ $this->resetAfterTest();
+
// Create a course, fields category and fields.
$course = $this->getDataGenerator()->create_course();
$category0 = $this->get_generator()->create_category(['name' => 'aaaa']);
*/
public function test_constructor_errors() {
global $DB;
+ $this->resetAfterTest();
+
// Create a category, field and data.
$category = $this->get_generator()->create_category();
$field = $this->get_generator()->create_field(['categoryid' => $category->get('id')]);
$this->assertEquals(moodle_exception::class, get_class($e));
}
}
-}
\ No newline at end of file
+}
class core_customfield_field_controller_testcase extends advanced_testcase {
/**
- * This method is called after the last test of this test class is run.
- */
- public static function tearDownAfterClass() {
- $handler = core_course\customfield\course_handler::create();
- $handler->delete_all();
- }
-
- /**
- * Tests set up.
- */
- public function setUp() {
- $this->resetAfterTest();
- }
-
- /**
- * Get generator
+ * Get generator.
+ *
* @return core_customfield_generator
*/
- protected function get_generator() : core_customfield_generator {
+ protected function get_generator(): core_customfield_generator {
return $this->getDataGenerator()->get_plugin_generator('core_customfield');
}
*/
public function test_constructor() {
global $DB;
+ $this->resetAfterTest();
+
// Create the category.
$category0 = $this->get_generator()->create_category();
*/
public function test_constructor_errors() {
global $DB;
+ $this->resetAfterTest();
+
// Create a category and a field.
$category = $this->get_generator()->create_category();
$field = $this->get_generator()->create_field(['categoryid' => $category->get('id')]);
*/
public function test_create_field() {
global $DB;
+ $this->resetAfterTest();
+
$lpg = $this->get_generator();
$category = $lpg->create_category();
$fields = $DB->get_records(\core_customfield\field::TABLE, ['categoryid' => $category->get('id')]);
*/
public function test_delete_field() {
global $DB;
+ $this->resetAfterTest();
+
$lpg = $this->get_generator();
$category = $lpg->create_category();
$fields = $DB->get_records(\core_customfield\field::TABLE, ['categoryid' => $category->get('id')]);
* Tests for \core_customfield\field_controller::get_configdata_property() behaviour.
*/
public function test_get_configdata_property() {
+ $this->resetAfterTest();
+
$lpg = $this->get_generator();
$category = $lpg->create_category();
$configdata = ['a' => 'b', 'c' => ['d', 'e']];
$this->assertEquals(['d', 'e'], $field->get_configdata_property('c'));
$this->assertEquals(null, $field->get_configdata_property('x'));
}
-}
\ No newline at end of file
+}
*/
class core_customfield_generator_testcase extends advanced_testcase {
- /**
- * This method is called after the last test of this test class is run.
- */
- public static function tearDownAfterClass() {
- $handler = core_course\customfield\course_handler::create();
- $handler->delete_all();
- }
-
/**
* Get generator
* @return core_customfield_generator
*/
- protected function get_generator() : core_customfield_generator {
+ protected function get_generator(): core_customfield_generator {
return $this->getDataGenerator()->get_plugin_generator('core_customfield');
}
*/
class core_customfield_privacy_testcase extends provider_testcase {
- /** @var stdClass[] */
- private $courses = [];
- /** @var \core_customfield\category_controller[] */
- private $cfcats = [];
- /** @var \core_customfield\field_controller[] */
- private $cffields = [];
-
- /**
- * This method is called after the last test of this test class is run.
- */
- public static function tearDownAfterClass() {
- $handler = core_course\customfield\course_handler::create();
- $handler->delete_all();
- }
-
/**
- * Set up
+ * Generate data.
+ *
+ * @return array
*/
- public function setUp() {
+ protected function generate_test_data(): array {
$this->resetAfterTest();
- $this->cfcats[1] = $this->get_generator()->create_category();
- $this->cfcats[2] = $this->get_generator()->create_category();
- $this->cffields[11] = $this->get_generator()->create_field(
- ['categoryid' => $this->cfcats[1]->get('id'), 'type' => 'checkbox']);
- $this->cffields[12] = $this->get_generator()->create_field(
- ['categoryid' => $this->cfcats[1]->get('id'), 'type' => 'date']);
- $this->cffields[13] = $this->get_generator()->create_field(
- ['categoryid' => $this->cfcats[1]->get('id'),
+ $generator = $this->getDataGenerator()->get_plugin_generator('core_customfield');
+ $cfcats[1] = $generator->create_category();
+ $cfcats[2] = $generator->create_category();
+ $cffields[11] = $generator->create_field(
+ ['categoryid' => $cfcats[1]->get('id'), 'type' => 'checkbox']);
+ $cffields[12] = $generator->create_field(
+ ['categoryid' => $cfcats[1]->get('id'), 'type' => 'date']);
+ $cffields[13] = $generator->create_field(
+ ['categoryid' => $cfcats[1]->get('id'),
'type' => 'select', 'configdata' => ['options' => "a\nb\nc"]]);
- $this->cffields[14] = $this->get_generator()->create_field(
- ['categoryid' => $this->cfcats[1]->get('id'), 'type' => 'text']);
- $this->cffields[15] = $this->get_generator()->create_field(
- ['categoryid' => $this->cfcats[1]->get('id'), 'type' => 'textarea']);
- $this->cffields[21] = $this->get_generator()->create_field(
- ['categoryid' => $this->cfcats[2]->get('id')]);
- $this->cffields[22] = $this->get_generator()->create_field(
- ['categoryid' => $this->cfcats[2]->get('id')]);
-
- $this->courses[1] = $this->getDataGenerator()->create_course();
- $this->courses[2] = $this->getDataGenerator()->create_course();
- $this->courses[3] = $this->getDataGenerator()->create_course();
-
- $this->get_generator()->add_instance_data($this->cffields[11], $this->courses[1]->id, 1);
- $this->get_generator()->add_instance_data($this->cffields[12], $this->courses[1]->id, 1546300800);
- $this->get_generator()->add_instance_data($this->cffields[13], $this->courses[1]->id, 2);
- $this->get_generator()->add_instance_data($this->cffields[14], $this->courses[1]->id, 'Hello1');
- $this->get_generator()->add_instance_data($this->cffields[15], $this->courses[1]->id,
+ $cffields[14] = $generator->create_field(
+ ['categoryid' => $cfcats[1]->get('id'), 'type' => 'text']);
+ $cffields[15] = $generator->create_field(
+ ['categoryid' => $cfcats[1]->get('id'), 'type' => 'textarea']);
+ $cffields[21] = $generator->create_field(
+ ['categoryid' => $cfcats[2]->get('id')]);
+ $cffields[22] = $generator->create_field(
+ ['categoryid' => $cfcats[2]->get('id')]);
+
+ $courses[1] = $this->getDataGenerator()->create_course();
+ $courses[2] = $this->getDataGenerator()->create_course();
+ $courses[3] = $this->getDataGenerator()->create_course();
+
+ $generator->add_instance_data($cffields[11], $courses[1]->id, 1);
+ $generator->add_instance_data($cffields[12], $courses[1]->id, 1546300800);
+ $generator->add_instance_data($cffields[13], $courses[1]->id, 2);
+ $generator->add_instance_data($cffields[14], $courses[1]->id, 'Hello1');
+ $generator->add_instance_data($cffields[15], $courses[1]->id,
['text' => '<p>Hi there</p>', 'format' => FORMAT_HTML]);
- $this->get_generator()->add_instance_data($this->cffields[21], $this->courses[1]->id, 'hihi1');
+ $generator->add_instance_data($cffields[21], $courses[1]->id, 'hihi1');
- $this->get_generator()->add_instance_data($this->cffields[14], $this->courses[2]->id, 'Hello2');
+ $generator->add_instance_data($cffields[14], $courses[2]->id, 'Hello2');
- $this->get_generator()->add_instance_data($this->cffields[21], $this->courses[2]->id, 'hihi2');
+ $generator->add_instance_data($cffields[21], $courses[2]->id, 'hihi2');
- $this->setUser($this->getDataGenerator()->create_user());
- }
+ $user = $this->getDataGenerator()->create_user();
+ $this->setUser($user);
- /**
- * Get generator
- * @return core_customfield_generator
- */
- protected function get_generator() : core_customfield_generator {
- return $this->getDataGenerator()->get_plugin_generator('core_customfield');
+ return [
+ 'user' => $user,
+ 'cfcats' => $cfcats,
+ 'cffields' => $cffields,
+ 'courses' => $courses,
+ ];
}
/**
*/
public function test_get_customfields_data_contexts() {
global $DB;
- list($sql, $params) = $DB->get_in_or_equal([$this->courses[1]->id, $this->courses[2]->id], SQL_PARAMS_NAMED);
+ [
+ 'cffields' => $cffields,
+ 'cfcats' => $cfcats,
+ 'courses' => $courses,
+ ] = $this->generate_test_data();
+
+ list($sql, $params) = $DB->get_in_or_equal([$courses[1]->id, $courses[2]->id], SQL_PARAMS_NAMED);
$r = provider::get_customfields_data_contexts('core_course', 'course', '=0',
$sql, $params);
- $this->assertEquals([context_course::instance($this->courses[1]->id)->id,
- context_course::instance($this->courses[2]->id)->id],
+ $this->assertEquals([context_course::instance($courses[1]->id)->id,
+ context_course::instance($courses[2]->id)->id],
$r->get_contextids(), '', 0, 10, true);
}
* Test for provider::get_customfields_configuration_contexts()
*/
public function test_get_customfields_configuration_contexts() {
+ $this->generate_test_data();
+
$r = provider::get_customfields_configuration_contexts('core_course', 'course');
$this->assertEquals([context_system::instance()->id], $r->get_contextids());
}
*/
public function test_export_customfields_data() {
global $USER, $DB;
+ $this->resetAfterTest();
+ [
+ 'cffields' => $cffields,
+ 'cfcats' => $cfcats,
+ 'courses' => $courses,
+ ] = $this->generate_test_data();
+
// Hack one of the fields so it has an invalid field type.
- $invalidfieldid = $this->cffields[21]->get('id');
+ $invalidfieldid = $cffields[21]->get('id');
$DB->update_record('customfield_field', ['id' => $invalidfieldid, 'type' => 'invalid']);
- $context = context_course::instance($this->courses[1]->id);
+ $context = context_course::instance($courses[1]->id);
$contextlist = new approved_contextlist($USER, 'core_customfield', [$context->id]);
- provider::export_customfields_data($contextlist, 'core_course', 'course', '=0', '=:i', ['i' => $this->courses[1]->id]);
+ provider::export_customfields_data($contextlist, 'core_course', 'course', '=0', '=:i', ['i' => $courses[1]->id]);
/** @var core_privacy\tests\request\content_writer $writer */
$writer = writer::with_context($context);
$invaldfieldischecked = false;
foreach ($DB->get_records('customfield_data', []) as $dbrecord) {
$data = $writer->get_data(['Custom fields data', $dbrecord->id]);
- if ($dbrecord->instanceid == $this->courses[1]->id) {
+ if ($dbrecord->instanceid == $courses[1]->id) {
$this->assertEquals($dbrecord->fieldid, $data->fieldid);
$this->assertNotEmpty($data->fieldtype);
$this->assertNotEmpty($data->fieldshortname);
*/
public function test_delete_customfields_data() {
global $USER, $DB;
- $approvedcontexts = new approved_contextlist($USER, 'core_course', [context_course::instance($this->courses[1]->id)->id]);
+ $this->resetAfterTest();
+ [
+ 'cffields' => $cffields,
+ 'cfcats' => $cfcats,
+ 'courses' => $courses,
+ ] = $this->generate_test_data();
+
+ $approvedcontexts = new approved_contextlist($USER, 'core_course', [context_course::instance($courses[1]->id)->id]);
provider::delete_customfields_data($approvedcontexts, 'core_course', 'course');
- $this->assertEmpty($DB->get_records('customfield_data', ['instanceid' => $this->courses[1]->id]));
- $this->assertNotEmpty($DB->get_records('customfield_data', ['instanceid' => $this->courses[2]->id]));
+ $this->assertEmpty($DB->get_records('customfield_data', ['instanceid' => $courses[1]->id]));
+ $this->assertNotEmpty($DB->get_records('customfield_data', ['instanceid' => $courses[2]->id]));
}
/**
*/
public function test_delete_customfields_configuration() {
global $USER, $DB;
+ $this->resetAfterTest();
+ [
+ 'cffields' => $cffields,
+ 'cfcats' => $cfcats,
+ 'courses' => $courses,
+ ] = $this->generate_test_data();
+
// Remember the list of fields in the category 2 before we delete it.
- $catid1 = $this->cfcats[1]->get('id');
- $catid2 = $this->cfcats[2]->get('id');
+ $catid1 = $cfcats[1]->get('id');
+ $catid2 = $cfcats[2]->get('id');
$fids2 = $DB->get_fieldset_select('customfield_field', 'id', 'categoryid=?', [$catid2]);
$this->assertNotEmpty($fids2);
list($fsql, $fparams) = $DB->get_in_or_equal($fids2, SQL_PARAMS_NAMED);
*/
public function test_delete_customfields_configuration_for_context() {
global $USER, $DB;
+ $this->resetAfterTest();
+ [
+ 'cffields' => $cffields,
+ 'cfcats' => $cfcats,
+ 'courses' => $courses,
+ ] = $this->generate_test_data();
+
// Remember the list of fields in the category 2 before we delete it.
- $catid1 = $this->cfcats[1]->get('id');
- $catid2 = $this->cfcats[2]->get('id');
+ $catid1 = $cfcats[1]->get('id');
+ $catid2 = $cfcats[2]->get('id');
$fids2 = $DB->get_fieldset_select('customfield_field', 'id', 'categoryid=?', [$catid2]);
$this->assertNotEmpty($fids2);
list($fsql, $fparams) = $DB->get_in_or_equal($fids2, SQL_PARAMS_NAMED);
*/
public function test_delete_customfields_data_for_context() {
global $DB;
+ $this->resetAfterTest();
+ [
+ 'cffields' => $cffields,
+ 'cfcats' => $cfcats,
+ 'courses' => $courses,
+ ] = $this->generate_test_data();
+
provider::delete_customfields_data_for_context('core_course', 'course',
- context_course::instance($this->courses[1]->id));
+ context_course::instance($courses[1]->id));
$fids2 = $DB->get_fieldset_select('customfield_field', 'id', '1=1', []);
list($fsql, $fparams) = $DB->get_in_or_equal($fids2, SQL_PARAMS_NAMED);
- $fparams['course1'] = $this->courses[1]->id;
- $fparams['course2'] = $this->courses[2]->id;
+ $fparams['course1'] = $courses[1]->id;
+ $fparams['course2'] = $courses[2]->id;
$this->assertEmpty($DB->get_records_select('customfield_data', 'instanceid = :course1 AND fieldid ' . $fsql, $fparams));
$this->assertNotEmpty($DB->get_records_select('customfield_data', 'instanceid = :course2 AND fieldid ' . $fsql, $fparams));
}
// Check the mockstore for all objects with properties matching the key => val pairs in $criteria.
foreach ($mockstore as $index => $mockrow) {
$mockrowarr = (array)$mockrow;
- if (array_diff($criteria, $mockrowarr) == []) {
+ if (array_diff_assoc($criteria, $mockrowarr) == []) {
$returns[$index] = $mockrow;
}
}
$crit = ['userid' => $userid, 'component' => $comp, 'itemtype' => $type, 'itemid' => $id, 'contextid' => $ctxid];
foreach ($mockstore as $fakerow) {
$fakerowarr = (array)$fakerow;
- if (array_diff($crit, $fakerowarr) == []) {
+ if (array_diff_assoc($crit, $fakerowarr) == []) {
return $fakerow;
}
}
// Check the mockstore for all objects with properties matching the key => val pairs in $criteria.
foreach ($mockstore as $index => $mockrow) {
$mockrowarr = (array)$mockrow;
- if (array_diff($criteria, $mockrowarr) == []) {
+ if (array_diff_assoc($criteria, $mockrowarr) == []) {
$count++;
}
}
// Check the mockstore for all objects with properties matching the key => val pairs in $criteria.
foreach ($mockstore as $index => $mockrow) {
$mockrowarr = (array)$mockrow;
- if (array_diff($criteria, $mockrowarr) == []) {
+ if (array_diff_assoc($criteria, $mockrowarr) == []) {
unset($mockstore[$index]);
}
}
foreach ($mockstore as $index => $mockrow) {
$mockrowarr = (array)$mockrow;
echo "Here";
- if (array_diff($criteria, $mockrowarr) == []) {
+ if (array_diff_assoc($criteria, $mockrowarr) == []) {
return true;
}
}
// Check the mockstore for all objects with properties matching the key => val pairs in $criteria.
foreach ($mockstore as $index => $mockrow) {
$mockrowarr = (array)$mockrow;
- if (array_diff($criteria, $mockrowarr) == []) {
+ if (array_diff_assoc($criteria, $mockrowarr) == []) {
$returns[$index] = $mockrow;
}
}
$crit = ['userid' => $userid, 'component' => $comp, 'itemtype' => $type, 'itemid' => $id, 'contextid' => $ctxid];
foreach ($mockstore as $fakerow) {
$fakerowarr = (array)$fakerow;
- if (array_diff($crit, $fakerowarr) == []) {
+ if (array_diff_assoc($crit, $fakerowarr) == []) {
return $fakerow;
}
}
// Check the mockstore for all objects with properties matching the key => val pairs in $criteria.
foreach ($mockstore as $index => $mockrow) {
$mockrowarr = (array)$mockrow;
- if (array_diff($criteria, $mockrowarr) == []) {
+ if (array_diff_assoc($criteria, $mockrowarr) == []) {
$count++;
}
}
// Check the mockstore for all objects with properties matching the key => val pairs in $criteria.
foreach ($mockstore as $index => $mockrow) {
$mockrowarr = (array)$mockrow;
- if (array_diff($criteria, $mockrowarr) == []) {
+ if (array_diff_assoc($criteria, $mockrowarr) == []) {
return true;
}
}
$string['invalidmd5'] = 'Η μεταβλητή ελέγχου ήταν λανθασμένη - δοκιμάστε ξανά';
$string['missingrequiredfield'] = 'Κάποιο απαιτούμενο πεδίο λείπει';
$string['remotedownloaderror'] = '<p>Η λήψη του στοιχείου λογισμικού στον εξυπηρετητή σας απέτυχε. Παρακαλούμε επαληθεύστε τις ρυθμίσεις του διακομιστή μεσολάβησης (proxy)· η επέκταση PHP cURL συνιστάται θερμά.</p><br /><p>Πρέπει να κατεβάσετε το αρχείο <a href="{$a->url}">{$a->url}</a> χειροκίνητα, να το αντιγράψετε στο «{$a->dest}» στον εξυπηρετητή σας και να το αποσυμπιέσετε εκεί.</p>';
-$string['wrongdestpath'] = 'Î\9bανθαÏ\83μÎνη διαδÏ\81ομή Ï\80Ï\81οοÏ\81ιÏ\83μοÏ\8d (Ï\80λήÏ\81εÏ\82 Ï\8cνομα).';
+$string['wrongdestpath'] = 'Î\9bανθαÏ\83μÎνο μονοÏ\80άÏ\84ι Ï\80Ï\81οοÏ\81ιÏ\83μοÏ\8d.';
$string['wrongsourcebase'] = 'Λανθασμένη βάση πηγής URL.';
$string['wrongzipfilename'] = 'Λανθασμένo όνομα αρχείου ZIP.';
--- /dev/null
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package installer
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['clianswerno'] = 'ל';
+$string['cliansweryes'] = 'כ';
+$string['cliincorrectvalueretry'] = 'ערך לא תקין, נסו שוב בבקשה';
$string['requiredentrieschanged'] = 'Note: After upgrading, the setting \'Required entries before viewing\' is now enforced in the following database activities:<br/>{$a->text}<br/>';
$string['requiremodintro'] = 'Require activity description';
$string['requiremodintro_desc'] = 'If enabled, users will be forced to enter a description for each activity.';
+$string['required'] = 'Required';
$string['requires'] = 'Requires';
$string['purgecaches'] = 'Purge all caches';
$string['purgecachesconfirm'] = 'Moodle can cache themes, javascript, language strings, filtered text, rss feeds and many other pieces of calculated data. Purging these caches will delete that data from the server and force browsers to refetch data, so that you can be sure you are seeing the most up-to-date values produced by the current code. There is no danger in purging caches, but your site may appear slower for a while until the server and clients calculate new information and cache it.';
$this->set_flag_options($enabled, $default, 'locked', new lang_string('locked', 'core_admin'));
}
+ /**
+ * Set the required options flag on this admin setting.
+ *
+ * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED.
+ * @param bool $default - The default for the flag.
+ */
+ public function set_required_flag_options($enabled, $default) {
+ $this->set_flag_options($enabled, $default, 'required', new lang_string('required', 'core_admin'));
+ }
+
/**
* Get the currently saved value for a setting flag
*
/**
* An additional option that can be applied to an admin setting.
- * The currently supported options are 'ADVANCED' and 'LOCKED'.
+ * The currently supported options are 'ADVANCED', 'LOCKED' and 'REQUIRED'.
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Latest version is available at http://adodb.org/
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Latest version is available at http://adodb.org/
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- * @version v5.20.15 24-Nov-2019
+ * @version v5.20.16 12-Jan-2020
* @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
* @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
* Released under both BSD license and Lesser GPL library license.
<?php
/**
- * @version v5.20.15 24-Nov-2019
+ * @version v5.20.16 12-Jan-2020
* @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
* @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
* Released under both BSD license and Lesser GPL library license.
<?php
/**
- * @version v5.20.15 24-Nov-2019
+ * @version v5.20.16 12-Jan-2020
* @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
* @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
* Released under both BSD license and Lesser GPL library license.
<?php
/**
- * @version v5.20.15 24-Nov-2019
+ * @version v5.20.16 12-Jan-2020
* @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
* @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
* Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
$ADODB_INCLUDED_LIB = 1;
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- * @version v5.20.15 24-Nov-2019
+ * @version v5.20.16 12-Jan-2020
* @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
* @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
* Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
Latest version is available at http://adodb.org/
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
/**
\mainpage
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
/**
* ADODB version as a string.
*/
- $ADODB_vers = 'v5.20.15 24-Nov-2019';
+ $ADODB_vers = 'v5.20.16 12-Jan-2020';
/**
* Determines whether recordset->RecordCount() is used.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
-* @version v5.20.15 24-Nov-2019
+* @version v5.20.16 12-Jan-2020
* @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
* @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
* Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim. All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
is running. All associated result memory for the specified result identifier will automatically be freed. */
function _close()
{
- if(is_object($this->_queryID)) {
+ if(is_resource($this->_queryID)) {
$rez = sqlsrv_free_stmt($this->_queryID);
$this->_queryID = false;
return $rez;
<?php
/**
-* @version v5.20.15 24-Nov-2019
+* @version v5.20.16 12-Jan-2020
* @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
* @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
* Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim. All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
<?php
/**
- * @version v5.20.15 24-Nov-2019
+ * @version v5.20.16 12-Jan-2020
* @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
* @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
* Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim. All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
/*
* find the next character of the string
*/
- $c = $sql[$i];
+ $c = $sql[$i];
if ($c == "'" && !$inString && $escaped==0)
/*
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim. All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
reserved.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim. All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/*
-@version v5.20.15 24-Nov-2019
+@version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
<?php
/**
- * @version v5.20.15 24-Nov-2019
+ * @version v5.20.16 12-Jan-2020
* @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
* @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
* Released under both BSD license and Lesser GPL library license.
-Description of ADODB V5.20.15 library import into Moodle
+Description of ADODB V5.20.16 library import into Moodle
This library will be probably removed in Moodle 2.1,
it is now used only in enrol and auth db plugins.
Our changes:
* MDL-67034 Fixes to make the library php74 compliant.
-
-skodak, iarenaza, moodler, stronk7, abgreeve, lameze, ankitagarwal, marinaglancy, rezaie9
<?php
/**
- * @version v5.20.15 24-Nov-2019
+ * @version v5.20.16 12-Jan-2020
* @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
* @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
* Released under both BSD license and Lesser GPL library license.
<?php
/**
- * @version v5.20.15 24-Nov-2019
+ * @version v5.20.16 12-Jan-2020
* @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
* @copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
* Released under both BSD license and Lesser GPL library license.
<?php
/*
- @version v5.20.15 24-Nov-2019
+ @version v5.20.16 12-Jan-2020
@copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
@copyright (c) 2014 Damien Regad, Mark Newnham and the ADOdb community
Released under both BSD license and Lesser GPL library license.
});
var context = $.extend({items: items}, options, state);
// Render the template.
- return templates.render('core/form_autocomplete_selection', context)
+ return templates.render('core/form_autocomplete_selection_items', context)
.then(function(html, js) {
// Add it to the page.
templates.replaceNodeContents(newSelection, html, js);
// return SCAN_RESULT_FOUND result.
if ($this->get_config('clamfailureonupload') === 'actlikevirus') {
return self::SCAN_RESULT_FOUND;
+ } else if ($this->get_config('clamfailureonupload') === 'tryagain') {
+ // Do not upload the file, just give a message to the user to try again later.
+ unlink($file);
+ throw new \core\antivirus\scanner_exception('antivirusfailed', '', ['item' => $filename],
+ null, 'antivirus_clamav');
}
}
return $return;
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+$string['antivirusfailed'] = 'There is a problem with AntiVirus scanning at the moment. Your file {$a->item} has not been uploaded. Please try again later.';
$string['configclamactlikevirus'] = 'Treat files like viruses';
$string['configclamdonothing'] = 'Treat files as OK';
-$string['configclamfailureonupload'] = 'If you have configured clam to scan uploaded files, but it is configured incorrectly or fails to run for some unknown reason, how should it behave? If you choose \'Treat files like viruses\', they\'ll be moved into the quarantine area, or deleted. If you choose \'Treat files as OK\', the files will be moved to the destination directory like normal. Either way, admins will be alerted that clam has failed. If you choose \'Treat files like viruses\' and for some reason clam fails to run (usually because you have entered an invalid pathtoclam), ALL files that are uploaded will be moved to the given quarantine area, or deleted. Be careful with this setting.';
+$string['configclamfailureonupload'] = 'If you have configured clam to scan uploaded files, but it is configured incorrectly or fails to run for some unknown reason, how should it behave? If you choose \'Treat files like viruses\', they\'ll be moved into the quarantine area, or deleted. If you choose \'Treat files as OK\', the files will be moved to the destination directory like normal. If you choose \'Refuse upload, try again\' (useful if failures occur during regular virus updating periods) a try again later message will be displayed to the user. Either way, admins will be alerted that clam has failed. If you choose \'Treat files like viruses\' and for some reason clam fails to run (usually because you have entered an invalid pathtoclam), ALL files that are uploaded will be moved to the given quarantine area, or deleted. Be careful with this setting.';
+$string['configclamtryagain'] = 'Refuse upload, try again';
$string['clamfailed'] = 'ClamAV has failed to run. The return error message was "{$a}". Here is the output from ClamAV:';
$string['clamfailureonupload'] = 'On ClamAV failure';
$string['errorcantopensocket'] = 'Connecting to Unix domain socket resulted in error {$a}';
$options = array(
'donothing' => new lang_string('configclamdonothing', 'antivirus_clamav'),
'actlikevirus' => new lang_string('configclamactlikevirus', 'antivirus_clamav'),
+ 'tryagain' => new lang_string('configclamtryagain', 'antivirus_clamav')
);
$settings->add(new admin_setting_configselect('antivirus_clamav/clamfailureonupload',
new lang_string('clamfailureonupload', 'antivirus_clamav'),
$this->assertEquals(1, $antivirus->scan_file($this->tempfile, ''));
}
+ public function test_scan_file_error_tryagain() {
+ $methods = array(
+ 'scan_file_execute_commandline',
+ 'scan_file_execute_unixsocket',
+ 'message_admins',
+ 'get_config',
+ 'get_scanning_notice',
+ );
+ $antivirus = $this->getMockBuilder('\antivirus_clamav\scanner')->setMethods($methods)->getMock();
+
+ // Configure scan_file_execute_commandline and scan_file_execute_unixsocket
+ // method stubs to behave as if there is a scanning error (SCAN_RESULT_ERROR).
+ $antivirus->method('scan_file_execute_commandline')->willReturn(2);
+ $antivirus->method('scan_file_execute_unixsocket')->willReturn(2);
+ $antivirus->method('get_scanning_notice')->willReturn('someerror');
+
+ // Set expectation that message_admins is called.
+ $antivirus->expects($this->atLeastOnce())->method('message_admins')->with($this->equalTo('someerror'));
+
+ // Initiate mock scanning with configuration setting to act like virus on
+ // scanning error and using commandline.
+ $configmap = array(array('clamfailureonupload', 'tryagain'), array('runningmethod', 'commandline'));
+ $antivirus->method('get_config')->will($this->returnValueMap($configmap));
+
+ // Run mock scanning.
+ $this->assertFileExists($this->tempfile);
+ $this->expectException(\core\antivirus\scanner_exception::class);
+ $antivirus->scan_file($this->tempfile, '');
+ $this->assertEquals('antivirusfailed', $this->getExpectedExceptionCode());
+ $this->assertFileNotExists($this->tempfile);
+ }
+
public function test_scan_data_no_virus() {
$methods = array(
'scan_data_execute_socket',
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2019122900; // The current plugin version (Date: YYYYMMDDXX).
+$plugin->version = 2020012400; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2019111200; // Requires this Moodle version.
$plugin->component = 'antivirus_clamav'; // Full name of the plugin (used for diagnostics).
* @param string $link
* @param mixed $a
* @param mixed $debuginfo
+ * @param string $module optional plugin name
*/
- public function __construct($errorcode, $link = '', $a = null, $debuginfo = null) {
- parent::__construct($errorcode, 'antivirus', $link, $a, $debuginfo);
+ public function __construct($errorcode, $link = '', $a = null, $debuginfo = null, $module = 'antivirus') {
+ parent::__construct($errorcode, $module, $link, $a, $debuginfo);
}
}
* Collection of components related methods.
*/
class core_component {
- /** @var array list of ignored directories - watch out for auth/db exception */
- protected static $ignoreddirs = array('CVS'=>true, '_vti_cnf'=>true, 'simpletest'=>true, 'db'=>true, 'yui'=>true, 'tests'=>true, 'classes'=>true, 'fonts'=>true);
+ /** @var array list of ignored directories in plugin type roots - watch out for auth/db exception */
+ protected static $ignoreddirs = [
+ 'CVS' => true,
+ '_vti_cnf' => true,
+ 'amd' => true,
+ 'classes' => true,
+ 'db' => true,
+ 'fonts' => true,
+ 'lang' => true,
+ 'pix' => true,
+ 'simpletest' => true,
+ 'templates' => true,
+ 'tests' => true,
+ 'yui' => true,
+ ];
/** @var array list plugin types that support subplugins, do not add more here unless absolutely necessary */
protected static $supportsubplugins = array('mod', 'editor', 'tool', 'local');
--- /dev/null
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * MySQL / MariaDB locking factory.
+ *
+ * @package core
+ * @category lock
+ * @copyright Brendan Heywood <brendan@catalyst-au.net>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core\lock;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * MySQL / MariaDB locking factory.
+ *
+ * @package core
+ * @category lock
+ * @copyright Brendan Heywood <brendan@catalyst-au.net>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class mysql_lock_factory implements lock_factory {
+
+ /** @var string $dbprefix - used as a namespace for these types of locks */
+ protected $dbprefix = '';
+
+ /** @var \moodle_database $db Hold a reference to the global $DB */
+ protected $db;
+
+ /** @var array $openlocks - List of held locks - used by auto-release */
+ protected $openlocks = [];
+
+ /**
+ * Return a unique prefix based on the database name and prefix.
+ * @param string $type - Used to prefix lock keys.
+ * @return string.
+ */
+ protected function get_unique_db_prefix($type) {
+ global $CFG;
+ $prefix = $CFG->dbname . ':';
+ if (isset($CFG->prefix)) {
+ $prefix .= $CFG->prefix;
+ }
+ $prefix .= '_' . $type . '_';
+ return $prefix;
+ }
+
+ /**
+ * Lock constructor.
+ * @param string $type - Used to prefix lock keys.
+ */
+ public function __construct($type) {
+ global $DB;
+
+ $this->dbprefix = $this->get_unique_db_prefix($type);
+ // Save a reference to the global $DB so it will not be released while we still have open locks.
+ $this->db = $DB;
+
+ \core_shutdown_manager::register_function([$this, 'auto_release']);
+ }
+
+ /**
+ * Is available.
+ * @return boolean - True if this lock type is available in this environment.
+ */
+ public function is_available() {
+ return $this->db->get_dbfamily() === 'mysql';
+ }
+
+ /**
+ * Return information about the blocking behaviour of the lock type on this platform.
+ * @return boolean - Defer to the DB driver.
+ */
+ public function supports_timeout() {
+ return true;
+ }
+
+ /**
+ * Will this lock type will be automatically released when a process ends.
+ *
+ * @return boolean - Via shutdown handler.
+ */
+ public function supports_auto_release() {
+ return true;
+ }
+
+ /**
+ * Multiple locks for the same resource can NOT be held by a single process.
+ *
+ * Hard coded to false and workaround inconsistent support in different
+ * versions of MySQL / MariaDB.
+ *
+ * @return boolean - false
+ */
+ public function supports_recursion() {
+ return false;
+ }
+
+ /**
+ * Create and get a lock
+ * @param string $resource - The identifier for the lock. Should use frankenstyle prefix.
+ * @param int $timeout - The number of seconds to wait for a lock before giving up.
+ * @param int $maxlifetime - Unused by this lock type.
+ * @return boolean - true if a lock was obtained.
+ */
+ public function get_lock($resource, $timeout, $maxlifetime = 86400) {
+
+ // We sha1 to avoid long key names hitting the mysql str limit.
+ $resourcekey = sha1($this->dbprefix . $resource);
+
+ // Even though some versions of MySQL and MariaDB can support stacked locks
+ // just never stack them and always fail fast.
+ if (isset($this->openlocks[$resourcekey])) {
+ return false;
+ }
+
+ $params = [
+ 'resourcekey' => $resourcekey,
+ 'timeout' => $timeout
+ ];
+
+ $result = $this->db->get_record_sql('SELECT GET_LOCK(:resourcekey, :timeout) AS locked', $params);
+ $locked = $result->locked == 1;
+
+ if ($locked) {
+ $this->openlocks[$resourcekey] = 1;
+ return new lock($resourcekey, $this);
+ }
+ return false;
+ }
+
+ /**
+ * Release a lock that was previously obtained with @lock.
+ * @param lock $lock - a lock obtained from this factory.
+ * @return boolean - true if the lock is no longer held (including if it was never held).
+ */
+ public function release_lock(lock $lock) {
+
+ $params = [
+ 'resourcekey' => $lock->get_key()
+ ];
+ $result = $this->db->get_record_sql('SELECT RELEASE_LOCK(:resourcekey) AS unlocked', $params);
+ $result = $result->unlocked == 1;
+ if ($result) {
+ unset($this->openlocks[$lock->get_key()]);
+ }
+ return $result;
+ }
+
+ /**
+ * Extend a lock that was previously obtained with @lock.
+ * @param lock $lock - a lock obtained from this factory.
+ * @param int $maxlifetime - the new lifetime for the lock (in seconds).
+ * @return boolean - true if the lock was extended.
+ */
+ public function extend_lock(lock $lock, $maxlifetime = 86400) {
+ // Not supported by this factory.
+ return false;
+ }
+
+ /**
+ * Auto release any open locks on shutdown.
+ * This is required, because we may be using persistent DB connections.
+ */
+ public function auto_release() {
+ // Called from the shutdown handler. Must release all open locks.
+ foreach ($this->openlocks as $key => $unused) {
+ $lock = new lock($key, $this);
+ $lock->release();
+ }
+ }
+
+}
// Trigger event for sending a message or notification - we need to do this before marking as read!
self::trigger_message_events($eventdata, $savemessage);
- if ($eventdata->notification or empty($CFG->messaging)) {
- // If they have deselected all processors and its a notification mark it read. The user doesn't want to be bothered.
- // The same goes if the messaging is completely disabled.
- if ($eventdata->notification) {
- $savemessage->timeread = null;
- \core_message\api::mark_notification_as_read($savemessage);
- } else {
- \core_message\api::mark_message_as_read($eventdata->userto->id, $savemessage);
- }
+ if ($eventdata->notification) {
+ // If they have deselected all processors and it's a notification mark it read. The user doesn't want to be
+ // bothered.
+ $savemessage->timeread = null;
+ \core_message\api::mark_notification_as_read($savemessage);
+ } else if (empty($CFG->messaging)) {
+ // If it's a message and messaging is disabled mark it read.
+ \core_message\api::mark_message_as_read($eventdata->userto->id, $savemessage);
}
return $savemessage->id;
// Trigger event for sending a message or notification - we need to do this before marking as read!
self::trigger_message_events($eventdata, $savemessage);
- if (empty($CFG->messaging)) {
- // If they have deselected all processors and its a notification mark it read. The user doesn't want to be bothered.
- // The same goes if the messaging is completely disabled.
- if ($eventdata->notification) {
- $savemessage->timeread = null;
- \core_message\api::mark_notification_as_read($savemessage);
- } else {
- \core_message\api::mark_message_as_read($eventdata->userto->id, $savemessage);
- }
+ if (!$eventdata->notification && empty($CFG->messaging)) {
+ // If it's a message and messaging is disabled mark it read.
+ \core_message\api::mark_message_as_read($eventdata->userto->id, $savemessage);
}
return $savemessage->id;
return;
}
- $DB->delete_records_list('task_log', 'id', $logids);
+ $chunks = array_chunk($logids, 1000);
+ foreach ($chunks as $chunk) {
+ $DB->delete_records_list('task_log', 'id', $chunk);
+ }
}
}
// Now loop through again and remove old files and directories.
for ($iter->rewind(); $iter->valid(); $iter->next()) {
$node = $iter->getRealPath();
- if (!is_readable($node)) {
+ if (!isset($modifieddateobject[$node]) || !is_readable($node)) {
continue;
}
upgrade_main_savepoint(true, 2020011700.02);
}
+ if ($oldversion < 2020013000.01) {
+ global $DB;
+ // Delete any associated files.
+ $fs = get_file_storage();
+ $sql = "SELECT cuc.id, cuc.userid
+ FROM {competency_usercomp} cuc
+ LEFT JOIN {user} u ON cuc.userid = u.id
+ WHERE u.deleted = 1";
+ $usercompetencies = $DB->get_records_sql($sql);
+ foreach ($usercompetencies as $usercomp) {
+ $DB->delete_records('competency_evidence', ['usercompetencyid' => $usercomp->id]);
+ $DB->delete_records('competency_usercompcourse', ['userid' => $usercomp->userid]);
+ $DB->delete_records('competency_usercompplan', ['userid' => $usercomp->userid]);
+ $DB->delete_records('competency_usercomp', ['userid' => $usercomp->userid]);
+ }
+
+ $sql = "SELECT cue.id, cue.userid
+ FROM {competency_userevidence} cue
+ LEFT JOIN {user} u ON cue.userid = u.id
+ WHERE u.deleted = 1";
+ $userevidences = $DB->get_records_sql($sql);
+ foreach ($userevidences as $userevidence) {
+ $DB->delete_records('competency_userevidencecomp', ['userevidenceid' => $userevidence->id]);
+ $DB->delete_records('competency_userevidence', ['id' => $userevidence->id]);
+
+ $context = context_user::instance($userevidence->userid);
+ $fs->delete_area_files($context->id, 'core_competency', 'userevidence', $userevidence->id);
+ }
+
+ $sql = "SELECT cp.id
+ FROM {competency_plan} cp
+ LEFT JOIN {user} u ON cp.userid = u.id
+ WHERE u.deleted = 1";
+ $userplans = $DB->get_records_sql($sql);
+ foreach ($userplans as $userplan) {
+ $DB->delete_records('competency_plancomp', ['planid' => $userplan->id]);
+ $DB->delete_records('competency_plan', ['id' => $userplan->id]);
+ }
+
+ // Main savepoint reached.
+ upgrade_main_savepoint(true, 2020013000.01);
+ }
+
return true;
}
throw new ddl_exception('ddlunknownerror', null, 'table drop sql not generated');
}
$this->execute_sql_arr($sqlarr, array($xmldb_table->getName()));
+
+ $this->generator->cleanup_after_drop($xmldb_table);
}
/**
return $sqlarr;
}
- /**
- * Given one correct xmldb_table, returns the SQL statements
- * to drop it (inside one array).
- *
- * @param xmldb_table $xmldb_table The table to drop.
- * @return array SQL statement(s) for dropping the specified table.
- */
- public function getDropTableSQL($xmldb_table) {
- $sqlarr = parent::getDropTableSQL($xmldb_table);
- if ($this->temptables->is_temptable($xmldb_table->getName())) {
- $this->temptables->delete_temptable($xmldb_table->getName());
- }
- return $sqlarr;
- }
-
/**
* Given one XMLDB Type, length and decimals, returns the DB proper SQL type.
*
$sqlarr = parent::getDropTableSQL($xmldb_table);
if ($this->temptables->is_temptable($xmldb_table->getName())) {
$sqlarr = preg_replace('/^DROP TABLE/', "DROP TEMPORARY TABLE", $sqlarr);
- $this->temptables->delete_temptable($xmldb_table->getName());
}
return $sqlarr;
}
$sqlarr = parent::getDropTableSQL($xmldb_table);
if ($this->temptables->is_temptable($xmldb_table->getName())) {
array_unshift($sqlarr, "TRUNCATE TABLE ". $this->getTableName($xmldb_table)); // oracle requires truncate before being able to drop a temp table
- $this->temptables->delete_temptable($xmldb_table->getName());
}
return $sqlarr;
}
return $sqlarr;
}
- /**
- * Given one correct xmldb_table, returns the SQL statements
- * to drop it (inside one array).
- *
- * @param xmldb_table $xmldb_table The table to drop.
- * @return array SQL statement(s) for dropping the specified table.
- */
- public function getDropTableSQL($xmldb_table) {
- $sqlarr = parent::getDropTableSQL($xmldb_table);
- if ($this->temptables->is_temptable($xmldb_table->getName())) {
- $this->temptables->delete_temptable($xmldb_table->getName());
- }
- return $sqlarr;
- }
-
/**
* Given one correct xmldb_index, returns the SQL statements
* needed to create it (in array).
}
/**
- * Given one correct xmldb_table and the new name, returns the SQL statements
+ * Given one correct xmldb_table, returns the SQL statements
* to drop it (inside one array). Works also for temporary tables.
*
* @param xmldb_table $xmldb_table The table to drop.
return $results;
}
+ /**
+ * Performs any clean up that needs to be done after a table is dropped.
+ *
+ * @param xmldb_table $table
+ */
+ public function cleanup_after_drop(xmldb_table $table): void {
+ if ($this->temptables->is_temptable($table->getName())) {
+ $this->temptables->delete_temptable($table->getName());
+ }
+ }
+
/**
* Given one xmldb_table and one xmldb_field, return the SQL statements needed to add the field to the table.
*
$this->assertFalse($dbman->table_exists('test_table1'));
}
+ /**
+ * get_columns should return an empty array for ex-temptables.
+ */
+ public function test_leftover_temp_tables_columns() {
+ $DB = $this->tdb; // Do not use global $DB!
+ $dbman = $this->tdb->get_manager();
+
+ // Create temp table0.
+ $table0 = $this->tables['test_table0'];
+ $dbman->create_temp_table($table0);
+
+ $dbman->drop_table($table0);
+
+ // Get columns and perform some basic tests.
+ $columns = $DB->get_columns('test_table0');
+ $this->assertEquals([], $columns);
+ }
+
+ /**
+ * Deleting a temp table should not purge the whole cache
+ */
+ public function test_leftover_temp_tables_cache() {
+ $DB = $this->tdb; // Do not use global $DB!
+ $dbman = $this->tdb->get_manager();
+
+ // Create 2 temp tables.
+ $table0 = $this->tables['test_table0'];
+ $dbman->create_temp_table($table0);
+ $table1 = $this->tables['test_table1'];
+ $dbman->create_temp_table($table1);
+
+ // Create a normal table.
+ $table2 = new xmldb_table ('test_table2');
+ $table2->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+ $table2->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0');
+ $table2->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+ $table2->setComment("This is a test'n drop table. You can drop it safely");
+ $this->tables[$table2->getName()] = $table2;
+ $dbman->create_table($table2);
+
+ // Get columns for the tables, so that relevant caches are populated with their data.
+ $DB->get_columns('test_table0');
+ $DB->get_columns('test_table1');
+ $DB->get_columns('test_table2');
+
+ $dbman->drop_table($table0);
+
+ $rc = new ReflectionClass('moodle_database');
+ $rcm = $rc->getMethod('get_temp_tables_cache');
+ $rcm->setAccessible(true);
+ $metacachetemp = $rcm->invokeArgs($DB, []);
+
+ // Data of test_table0 should be removed from the cache.
+ $this->assertEquals(false, $metacachetemp->has('test_table0'));
+
+ // Data of test_table1 should be intact.
+ $this->assertEquals(true, $metacachetemp->has('test_table1'));
+
+ $rc = new ReflectionClass('moodle_database');
+ $rcm = $rc->getMethod('get_metacache');
+ $rcm->setAccessible(true);
+ $metacache = $rcm->invokeArgs($DB, []);
+
+ // Data of test_table2 should be intact.
+ $this->assertEquals(true, $metacache->has('test_table2'));
+
+ }
+
public function test_reset_sequence() {
$DB = $this->tdb;
$dbman = $DB->get_manager();
/**
* Returns detailed information about columns in table. This information is cached internally.
+ *
* @param string $table The table's name.
* @param bool $usecache Flag to use internal cacheing. The default is true.
* @return database_column_info[] of database_column_info objects indexed with column names
*/
- public abstract function get_columns($table, $usecache=true);
+ public function get_columns($table, $usecache = true): array {
+ if (!$table) { // Table not specified, return empty array directly.
+ return [];
+ }
+
+ if ($usecache) {
+ if ($this->temptables->is_temptable($table)) {
+ if ($data = $this->get_temp_tables_cache()->get($table)) {
+ return $data;
+ }
+ } else {
+ if ($data = $this->get_metacache()->get($table)) {
+ return $data;
+ }
+ }
+ }
+
+ $structure = $this->fetch_columns($table);
+
+ if ($usecache) {
+ if ($this->temptables->is_temptable($table)) {
+ $this->get_temp_tables_cache()->set($table, $structure);
+ } else {
+ $this->get_metacache()->set($table, $structure);
+ }
+ }
+
+ return $structure;
+ }
+
+ /**
+ * Returns detailed information about columns in table. This information is cached internally.
+ *
+ * @param string $table The table's name.
+ * @return database_column_info[] of database_column_info objects indexed with column names
+ */
+ protected abstract function fetch_columns(string $table): array;
/**
* Normalise values based on varying RDBMS's dependencies (booleans, LOBs...)
}
/**
- * Returns detailed information about columns in table. This information is cached internally.
+ * Fetches detailed information about columns in table.
+ *
* @param string $table name
- * @param bool $usecache
* @return database_column_info[] array of database_column_info objects indexed with column names
*/
- public function get_columns($table, $usecache=true) {
- if ($usecache) {
- if ($this->temptables->is_temptable($table)) {
- if ($data = $this->get_temp_tables_cache()->get($table)) {
- return $data;
- }
- } else {
- if ($data = $this->get_metacache()->get($table)) {
- return $data;
- }
- }
- }
-
+ protected function fetch_columns(string $table): array {
$structure = array();
$sql = "SELECT column_name, data_type, character_maximum_length, numeric_precision,
$result->close();
}
- if ($usecache) {
- if ($this->temptables->is_temptable($table)) {
- $this->get_temp_tables_cache()->set($table, $structure);
- } else {
- $this->get_metacache()->set($table, $structure);
- }
- }
-
return $structure;
}
}
/**
- * Returns detailed information about columns in table. This information is cached internally.
+ * Fetches detailed information about columns in table.
+ *
* @param string $table name
- * @param bool $usecache
* @return array array of database_column_info objects indexed with column names
*/
- public function get_columns($table, $usecache=true) {
-
- if ($usecache) {
- if ($this->temptables->is_temptable($table)) {
- if ($data = $this->get_temp_tables_cache()->get($table)) {
- return $data;
- }
- } else {
- if ($data = $this->get_metacache()->get($table)) {
- return $data;
- }
- }
- }
-
- if (!$table) { // table not specified, return empty array directly
- return array();
- }
-
+ protected function fetch_columns(string $table): array {
$structure = array();
// We give precedence to CHAR_LENGTH for VARCHAR2 columns over WIDTH because the former is always
$structure[$info->name] = new database_column_info($info);
}
- if ($usecache) {
- if ($this->temptables->is_temptable($table)) {
- $this->get_temp_tables_cache()->set($table, $structure);
- } else {
- $this->get_metacache()->set($table, $structure);
- }
- }
-
return $structure;
}
}
/**
- * Returns detailed information about columns in table. This information is cached internally.
+ * Returns detailed information about columns in table.
+ *
* @param string $table name
- * @param bool $usecache
* @return database_column_info[] array of database_column_info objects indexed with column names
*/
- public function get_columns($table, $usecache=true) {
- if ($usecache) {
- if ($this->temptables->is_temptable($table)) {
- if ($data = $this->get_temp_tables_cache()->get($table)) {
- return $data;
- }
- } else {
- if ($data = $this->get_metacache()->get($table)) {
- return $data;
- }
- }
- }
-
+ protected function fetch_columns(string $table): array {
$structure = array();
$tablename = $this->prefix.$table;
- $sql = "SELECT a.attnum, a.attname AS field, t.typname AS type, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, d.adsrc
+ $sql = "SELECT a.attnum, a.attname AS field, t.typname AS type, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef,
+ CASE WHEN a.atthasdef THEN pg_catalog.pg_get_expr(d.adbin, d.adrelid) END AS adsrc
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace as ns ON ns.oid = c.relnamespace
JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
pg_free_result($result);
- if ($usecache) {
- if ($this->temptables->is_temptable($table)) {
- $this->get_temp_tables_cache()->set($table, $structure);
- } else {
- $this->get_metacache()->set($table, $structure);
- }
- }
-
return $structure;
}
}
/**
- * Returns detailed information about columns in table. This information is cached internally.
+ * Returns detailed information about columns in table.
+ *
* @param string $table name
- * @param bool $usecache
* @return array array of database_column_info objects indexed with column names
*/
- public function get_columns($table, $usecache=true) {
-
- if ($usecache) {
- if ($this->temptables->is_temptable($table)) {
- if ($data = $this->get_temp_tables_cache()->get($table)) {
- return $data;
- }
- } else {
- if ($data = $this->get_metacache()->get($table)) {
- return $data;
- }
- }
- }
-
+ protected function fetch_columns(string $table): array {
$structure = array();
// get table's CREATE TABLE command (we'll need it for autoincrement fields)
$structure[$columninfo['name']] = new database_column_info($columninfo);
}
- if ($usecache) {
- if ($this->temptables->is_temptable($table)) {
- $this->get_temp_tables_cache()->set($table, $structure);
- } else {
- $this->get_metacache()->set($table, $structure);
- }
- }
-
return $structure;
}
return 'mssql';
}
- /**
+ /**
* Returns more specific database driver type
* Note: can be used before connect()
* @return string db type mysqli, pgsql, oci, mssql, sqlsrv
return 'sqlsrv';
}
- /**
+ /**
* Returns general database library name
* Note: can be used before connect()
* @return string db type pdo, native
}
/**
- * Returns detailed information about columns in table. This information is cached internally.
+ * Returns detailed information about columns in table.
+ *
* @param string $table name
- * @param bool $usecache
* @return array array of database_column_info objects indexed with column names
*/
- public function get_columns($table, $usecache = true) {
- if ($usecache) {
- if ($this->temptables->is_temptable($table)) {
- if ($data = $this->get_temp_tables_cache()->get($table)) {
- return $data;
- }
- } else {
- if ($data = $this->get_metacache()->get($table)) {
- return $data;
- }
- }
- }
-
+ protected function fetch_columns(string $table): array {
$structure = array();
if (!$this->temptables->is_temptable($table)) { // normal table, get metadata from own schema
}
$this->free_result($result);
- if ($usecache) {
- if ($this->temptables->is_temptable($table)) {
- $this->get_temp_tables_cache()->set($table, $structure);
- } else {
- $this->get_metacache()->set($table, $structure);
- }
- }
-
return $structure;
}
public function get_last_error() {}
public function get_tables($usecache=true) {}
public function get_indexes($table) {}
- public function get_columns($table, $usecache=true) {}
+ protected function fetch_columns(string $table): array {
+ return [];
+ }
protected function normalise_value($column, $value) {}
public function set_debug($state) {}
public function get_debug() {}
// This might be slow but it is really needed - modules might do some extra cleanup!
role_unassign_all(array('userid' => $user->id));
+ // Notify the competency subsystem.
+ \core_competency\api::hook_user_deleted($user->id);
+
// Now do a brute force cleanup.
// Delete all user events and subscription events.
if (class_exists('\core\update\checker')) {
\core\update\checker::reset_caches(true);
}
+ if (class_exists('\core_course\customfield\course_handler')) {
+ \core_course\customfield\course_handler::reset_caches();
+ }
// Clear static cache within restore.
if (class_exists('restore_section_structure_step')) {
{{!
@template core/form_autocomplete_selection
- Moodle template for the currently selected items in an autocomplate form element.
+ Moodle template for the wrapper of currently selected items in an autocomplate form element.
Classes required for JS:
* none
Context variables required for this template:
* multiple True if this field allows multiple selections
* selectionId The dom id of the current selection list.
- * items List of items with label and value fields.
- * noSelectionString String to use when no items are selected
+ * items List of items with label and value fields (used by the partial).
+ * noSelectionString String to use when no items are selected (used by the partial).
Example context (json):
{ "multiple": true, "selectionId": 1, "items": [
}}
<div class="form-autocomplete-selection w-100 {{#multiple}}form-autocomplete-multiple{{/multiple}}" id="{{selectionId}}" role="list" aria-atomic="true" {{#multiple}}tabindex="0" aria-multiselectable="true"{{/multiple}}>
<span class="accesshide">{{#str}}selecteditems, form{{/str}}</span>
- {{#items}}
- <span role="listitem" data-value="{{value}}" aria-selected="true" class="badge badge-info mb-3 mr-1" style="font-size: 100%">
- <span aria-hidden="true">× </span>{{{label}}}
- </span>
- {{/items}}
- {{^items}}
- <span class="mb-3 mr-1">{{noSelectionString}}</span>
- {{/items}}
+ {{> core/form_autocomplete_selection_items }}
</div>
--- /dev/null
+{{!
+ This file is part of Moodle - http://moodle.org/
+
+ Moodle is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Moodle is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+}}
+{{!
+ @template core/form_autocomplete_selection_items
+
+ Moodle template for the currently selected items in an autocomplate form element.
+
+ Classes required for JS:
+ * none
+
+ Data attributes required for JS:
+ * data-value
+
+ Context variables required for this template:
+ * items List of items with label and value fields.
+ * - value Value of the selected item.
+ * - label HTML representing the value.
+ * noSelectionString String to use when no items are selected
+
+ Example context (json):
+ { "items": [
+ { "label": "Item label with <strong>tags</strong>", "value": "5" },
+ { "label": "Another item label with <strong>tags</strong>", "value": "4" }
+ ], "noSelectionString": "No selection" }
+}}
+{{#items}}
+ <span role="listitem" data-value="{{value}}" aria-selected="true" class="badge badge-info mb-3 mr-1" style="font-size: 100%">
+ <span aria-hidden="true">× </span>{{{label}}}
+ </span>
+{{/items}}
+{{^items}}
+ <span class="mb-3 mr-1">{{noSelectionString}}</span>
+{{/items}}
-<div class="toast-wrapper mx-auto py-3 fixed-top" role="status" aria-live="polite"></div>
+<div class="toast-wrapper mx-auto py-0 fixed-top" role="status" aria-live="polite"></div>
* Test filters.
*/
class core_filterlib_testcase extends advanced_testcase {
- private $syscontext;
- private $childcontext;
- private $childcontext2;
- private $catcontext;
- private $coursecontext;
- private $course;
- private $activity1context;
- private $activity2context;
-
- protected function setUp() {
- global $DB;
- parent::setUp();
-
- $this->resetAfterTest();
- $DB->delete_records('filter_active', array());
- $DB->delete_records('filter_config', array());
- }
private function assert_only_one_filter_globally($filter, $state) {
global $DB;
}
public function test_set_filter_globally_on() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
// Exercise SUT.
filter_set_global_state('name', TEXTFILTER_ON);
}
public function test_set_filter_globally_off() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
// Exercise SUT.
filter_set_global_state('name', TEXTFILTER_OFF);
}
public function test_set_filter_globally_disabled() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
// Exercise SUT.
filter_set_global_state('name', TEXTFILTER_DISABLED);
* @expectedException coding_exception
*/
public function test_global_config_exception_on_invalid_state() {
+ $this->resetAfterTest();
filter_set_global_state('name', 0);
}
public function test_auto_sort_order() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
// Exercise SUT.
filter_set_global_state('one', TEXTFILTER_DISABLED);
}
public function test_auto_sort_order_enabled() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
// Exercise SUT.
filter_set_global_state('one', TEXTFILTER_ON);
}
public function test_update_existing_dont_duplicate() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
// Exercise SUT.
filter_set_global_state('name', TEXTFILTER_ON);
}
public function test_update_reorder_down() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
filter_set_global_state('one', TEXTFILTER_ON);
filter_set_global_state('two', TEXTFILTER_ON);
}
public function test_update_reorder_up() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
filter_set_global_state('one', TEXTFILTER_ON);
filter_set_global_state('two', TEXTFILTER_ON);
}
public function test_auto_sort_order_change_to_enabled() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
filter_set_global_state('one', TEXTFILTER_ON);
filter_set_global_state('two', TEXTFILTER_DISABLED);
}
public function test_auto_sort_order_change_to_disabled() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
filter_set_global_state('one', TEXTFILTER_ON);
filter_set_global_state('two', TEXTFILTER_ON);
}
public function test_filter_get_global_states() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
filter_set_global_state('one', TEXTFILTER_ON);
filter_set_global_state('two', TEXTFILTER_OFF);
}
public function test_local_on() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Exercise SUT.
filter_set_local_state('name', 123, TEXTFILTER_ON);
// Validate.
}
public function test_local_off() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Exercise SUT.
filter_set_local_state('name', 123, TEXTFILTER_OFF);
// Validate.
}
public function test_local_inherit() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Exercise SUT.
filter_set_local_state('name', 123, TEXTFILTER_INHERIT);
// Validate.
* @expectedException coding_exception
*/
public function test_local_invalid_state_throws_exception() {
+ $this->resetAfterTest();
// Exercise SUT.
filter_set_local_state('name', 123, -9999);
}
* @expectedException coding_exception
*/
public function test_throws_exception_when_setting_global() {
+ $this->resetAfterTest();
// Exercise SUT.
filter_set_local_state('name', context_system::instance()->id, TEXTFILTER_INHERIT);
}
public function test_local_inherit_deletes_existing() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
filter_set_local_state('name', 123, TEXTFILTER_INHERIT);
// Exercise SUT.
}
public function test_set_new_config() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Exercise SUT.
filter_set_local_config('name', 123, 'settingname', 'An arbitrary value');
// Validate.
}
public function test_update_existing_config() {
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
filter_set_local_config('name', 123, 'settingname', 'An arbitrary value');
// Exercise SUT.
}
public function test_filter_get_local_config() {
+ $this->resetAfterTest();
// Setup fixture.
filter_set_local_config('name', 123, 'setting1', 'An arbitrary value');
filter_set_local_config('name', 123, 'setting2', 'Another arbitrary value');
protected function setup_available_in_context_tests() {
$course = $this->getDataGenerator()->create_course(array('category'=>1));
- $this->childcontext = context_coursecat::instance(1);
- $this->childcontext2 = context_course::instance($course->id);
- $this->syscontext = context_system::instance();
+ $childcontext = context_coursecat::instance(1);
+ $childcontext2 = context_course::instance($course->id);
+ $syscontext = context_system::instance();
+
+ return [
+ 'syscontext' => $syscontext,
+ 'childcontext' => $childcontext,
+ 'childcontext2' => $childcontext2
+ ];
+ }
+
+ protected function remove_all_filters_from_config() {
+ global $DB;
+ $DB->delete_records('filter_active', array());
+ $DB->delete_records('filter_config', array());
}
private function assert_filter_list($expectedfilters, $filters) {
- $this->setup_available_in_context_tests();
$this->assertEquals($expectedfilters, array_keys($filters), '', 0, 10, true);
}
public function test_globally_on_is_returned() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
+ [
+ 'syscontext' => $syscontext
+ ] = $this->setup_available_in_context_tests();
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_ON);
// Exercise SUT.
- $filters = filter_get_active_in_context($this->syscontext);
+ $filters = filter_get_active_in_context($syscontext);
// Validate.
$this->assert_filter_list(array('name'), $filters);
// Check no config returned correctly.
}
public function test_globally_off_not_returned() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
+ [
+ 'childcontext2' => $childcontext2
+ ] = $this->setup_available_in_context_tests();
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_OFF);
// Exercise SUT.
- $filters = filter_get_active_in_context($this->childcontext2);
+ $filters = filter_get_active_in_context($childcontext2);
// Validate.
$this->assert_filter_list(array(), $filters);
}
public function test_globally_off_overridden() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
+ [
+ 'childcontext' => $childcontext,
+ 'childcontext2' => $childcontext2
+ ] = $this->setup_available_in_context_tests();
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_OFF);
- filter_set_local_state('name', $this->childcontext->id, TEXTFILTER_ON);
+ filter_set_local_state('name', $childcontext->id, TEXTFILTER_ON);
// Exercise SUT.
- $filters = filter_get_active_in_context($this->childcontext2);
+ $filters = filter_get_active_in_context($childcontext2);
// Validate.
$this->assert_filter_list(array('name'), $filters);
}
public function test_globally_on_overridden() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
+ [
+ 'childcontext' => $childcontext,
+ 'childcontext2' => $childcontext2
+ ] = $this->setup_available_in_context_tests();
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_ON);
- filter_set_local_state('name', $this->childcontext->id, TEXTFILTER_OFF);
+ filter_set_local_state('name', $childcontext->id, TEXTFILTER_OFF);
// Exercise SUT.
- $filters = filter_get_active_in_context($this->childcontext2);
+ $filters = filter_get_active_in_context($childcontext2);
// Validate.
$this->assert_filter_list(array(), $filters);
}
public function test_globally_disabled_not_overridden() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
+ [
+ 'syscontext' => $syscontext,
+ 'childcontext' => $childcontext
+ ] = $this->setup_available_in_context_tests();
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_DISABLED);
- filter_set_local_state('name', $this->childcontext->id, TEXTFILTER_ON);
+ filter_set_local_state('name', $childcontext->id, TEXTFILTER_ON);
// Exercise SUT.
- $filters = filter_get_active_in_context($this->syscontext);
+ $filters = filter_get_active_in_context($syscontext);
// Validate.
$this->assert_filter_list(array(), $filters);
}
public function test_single_config_returned() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ [
+ 'childcontext' => $childcontext
+ ] = $this->setup_available_in_context_tests();
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_ON);
- filter_set_local_config('name', $this->childcontext->id, 'settingname', 'A value');
+ filter_set_local_config('name', $childcontext->id, 'settingname', 'A value');
// Exercise SUT.
- $filters = filter_get_active_in_context($this->childcontext);
+ $filters = filter_get_active_in_context($childcontext);
// Validate.
$this->assertEquals(array('settingname' => 'A value'), $filters['name']);
}
public function test_multi_config_returned() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ [
+ 'childcontext' => $childcontext
+ ] = $this->setup_available_in_context_tests();
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_ON);
- filter_set_local_config('name', $this->childcontext->id, 'settingname', 'A value');
- filter_set_local_config('name', $this->childcontext->id, 'anothersettingname', 'Another value');
+ filter_set_local_config('name', $childcontext->id, 'settingname', 'A value');
+ filter_set_local_config('name', $childcontext->id, 'anothersettingname', 'Another value');
// Exercise SUT.
- $filters = filter_get_active_in_context($this->childcontext);
+ $filters = filter_get_active_in_context($childcontext);
// Validate.
$this->assertEquals(array('settingname' => 'A value', 'anothersettingname' => 'Another value'), $filters['name']);
}
public function test_config_from_other_context_not_returned() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ [
+ 'childcontext' => $childcontext,
+ 'childcontext2' => $childcontext2
+ ] = $this->setup_available_in_context_tests();
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_ON);
- filter_set_local_config('name', $this->childcontext->id, 'settingname', 'A value');
- filter_set_local_config('name', $this->childcontext2->id, 'anothersettingname', 'Another value');
+ filter_set_local_config('name', $childcontext->id, 'settingname', 'A value');
+ filter_set_local_config('name', $childcontext2->id, 'anothersettingname', 'Another value');
// Exercise SUT.
- $filters = filter_get_active_in_context($this->childcontext2);
+ $filters = filter_get_active_in_context($childcontext2);
// Validate.
$this->assertEquals(array('anothersettingname' => 'Another value'), $filters['name']);
}
public function test_config_from_other_filter_not_returned() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ [
+ 'childcontext' => $childcontext
+ ] = $this->setup_available_in_context_tests();
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_ON);
- filter_set_local_config('name', $this->childcontext->id, 'settingname', 'A value');
- filter_set_local_config('other', $this->childcontext->id, 'anothersettingname', 'Another value');
+ filter_set_local_config('name', $childcontext->id, 'settingname', 'A value');
+ filter_set_local_config('other', $childcontext->id, 'anothersettingname', 'Another value');
// Exercise SUT.
- $filters = filter_get_active_in_context($this->childcontext);
+ $filters = filter_get_active_in_context($childcontext);
// Validate.
$this->assertEquals(array('settingname' => 'A value'), $filters['name']);
}
protected function assert_one_available_filter($filter, $localstate, $inheritedstate, $filters) {
- $this->setup_available_in_context_tests();
$this->assertEquals(1, count($filters), 'More than one record returned %s.');
$rec = $filters[$filter];
unset($rec->id);
}
public function test_available_in_context_localoverride() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
+ [
+ 'childcontext' => $childcontext
+ ] = $this->setup_available_in_context_tests();
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_ON);
- filter_set_local_state('name', $this->childcontext->id, TEXTFILTER_OFF);
+ filter_set_local_state('name', $childcontext->id, TEXTFILTER_OFF);
// Exercise SUT.
- $filters = filter_get_available_in_context($this->childcontext);
+ $filters = filter_get_available_in_context($childcontext);
// Validate.
$this->assert_one_available_filter('name', TEXTFILTER_OFF, TEXTFILTER_ON, $filters);
}
public function test_available_in_context_nolocaloverride() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
+ [
+ 'childcontext' => $childcontext,
+ 'childcontext2' => $childcontext2
+ ] = $this->setup_available_in_context_tests();
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_ON);
- filter_set_local_state('name', $this->childcontext->id, TEXTFILTER_OFF);
+ filter_set_local_state('name', $childcontext->id, TEXTFILTER_OFF);
// Exercise SUT.
- $filters = filter_get_available_in_context($this->childcontext2);
+ $filters = filter_get_available_in_context($childcontext2);
// Validate.
$this->assert_one_available_filter('name', TEXTFILTER_INHERIT, TEXTFILTER_OFF, $filters);
}
public function test_available_in_context_disabled_not_returned() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
+ [
+ 'childcontext' => $childcontext
+ ] = $this->setup_available_in_context_tests();
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_DISABLED);
- filter_set_local_state('name', $this->childcontext->id, TEXTFILTER_ON);
+ filter_set_local_state('name', $childcontext->id, TEXTFILTER_ON);
// Exercise SUT.
- $filters = filter_get_available_in_context($this->childcontext);
+ $filters = filter_get_available_in_context($childcontext);
// Validate.
$this->assertEquals(array(), $filters);
}
* @expectedException coding_exception
*/
public function test_available_in_context_exception_with_syscontext() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ [
+ 'syscontext' => $syscontext
+ ] = $this->setup_available_in_context_tests();
// Exercise SUT.
- filter_get_available_in_context($this->syscontext);
+ filter_get_available_in_context($syscontext);
}
protected function setup_preload_activities_test() {
- $this->syscontext = context_system::instance();
- $this->catcontext = context_coursecat::instance(1);
- $this->course = $this->getDataGenerator()->create_course(array('category'=>1));
- $this->coursecontext = context_course::instance($this->course->id);
- $page1 = $this->getDataGenerator()->create_module('page', array('course'=>$this->course->id));
- $this->activity1context = context_module::instance($page1->cmid);
- $page2 = $this->getDataGenerator()->create_module('page', array('course'=>$this->course->id));
- $this->activity2context = context_module::instance($page2->cmid);
- }
-
- private function assert_matches($modinfo) {
+ $syscontext = context_system::instance();
+ $catcontext = context_coursecat::instance(1);
+ $course = $this->getDataGenerator()->create_course(array('category' => 1));
+ $coursecontext = context_course::instance($course->id);
+ $page1 = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
+ $activity1context = context_module::instance($page1->cmid);
+ $page2 = $this->getDataGenerator()->create_module('page', array('course' => $course->id));
+ $activity2context = context_module::instance($page2->cmid);
+ return [
+ 'syscontext' => $syscontext,
+ 'catcontext' => $catcontext,
+ 'course' => $course,
+ 'coursecontext' => $coursecontext,
+ 'activity1context' => $activity1context,
+ 'activity2context' => $activity2context
+ ];
+ }
+
+ private function assert_matches($modinfo, $activity1context, $activity2context) {
global $FILTERLIB_PRIVATE, $DB;
// Use preload cache...
// Get data and check no queries are made.
$before = $DB->perf_get_reads();
- $plfilters1 = filter_get_active_in_context($this->activity1context);
- $plfilters2 = filter_get_active_in_context($this->activity2context);
+ $plfilters1 = filter_get_active_in_context($activity1context);
+ $plfilters2 = filter_get_active_in_context($activity2context);
$after = $DB->perf_get_reads();
$this->assertEquals($before, $after);
// Repeat without cache and check it makes queries now.
$FILTERLIB_PRIVATE = new stdClass;
$before = $DB->perf_get_reads();
- $filters1 = filter_get_active_in_context($this->activity1context);
- $filters2 = filter_get_active_in_context($this->activity2context);
+ $filters1 = filter_get_active_in_context($activity1context);
+ $filters2 = filter_get_active_in_context($activity2context);
$after = $DB->perf_get_reads();
$this->assertTrue($after > $before);
}
public function test_preload() {
- $this->setup_preload_activities_test();
+ $this->resetAfterTest();
+ [
+ 'catcontext' => $catcontext,
+ 'course' => $course,
+ 'coursecontext' => $coursecontext,
+ 'activity1context' => $activity1context,
+ 'activity2context' => $activity2context
+ ] = $this->setup_preload_activities_test();
// Get course and modinfo.
- $modinfo = new course_modinfo($this->course, 2);
+ $modinfo = new course_modinfo($course, 2);
// Note: All the tests in this function check that the result from the
// preloaded cache is the same as the result from calling the standard
// function without preloading.
// Initially, check with no filters enabled.
- $this->assert_matches($modinfo);
+ $this->assert_matches($modinfo, $activity1context, $activity2context);
// Enable filter globally, check.
filter_set_global_state('name', TEXTFILTER_ON);
- $this->assert_matches($modinfo);
+ $this->assert_matches($modinfo, $activity1context, $activity2context);
// Disable for activity 2.
- filter_set_local_state('name', $this->activity2context->id, TEXTFILTER_OFF);
- $this->assert_matches($modinfo);
+ filter_set_local_state('name', $activity2context->id, TEXTFILTER_OFF);
+ $this->assert_matches($modinfo, $activity1context, $activity2context);
// Disable at category.
- filter_set_local_state('name', $this->catcontext->id, TEXTFILTER_OFF);
- $this->assert_matches($modinfo);
+ filter_set_local_state('name', $catcontext->id, TEXTFILTER_OFF);
+ $this->assert_matches($modinfo, $activity1context, $activity2context);
// Enable for activity 1.
- filter_set_local_state('name', $this->activity1context->id, TEXTFILTER_ON);
- $this->assert_matches($modinfo);
+ filter_set_local_state('name', $activity1context->id, TEXTFILTER_ON);
+ $this->assert_matches($modinfo, $activity1context, $activity2context);
// Disable globally.
filter_set_global_state('name', TEXTFILTER_DISABLED);
- $this->assert_matches($modinfo);
+ $this->assert_matches($modinfo, $activity1context, $activity2context);
// Add another 2 filters.
filter_set_global_state('frog', TEXTFILTER_ON);
filter_set_global_state('zombie', TEXTFILTER_ON);
- $this->assert_matches($modinfo);
+ $this->assert_matches($modinfo, $activity1context, $activity2context);
// Disable random one of these in each context.
- filter_set_local_state('zombie', $this->activity1context->id, TEXTFILTER_OFF);
- filter_set_local_state('frog', $this->activity2context->id, TEXTFILTER_OFF);
- $this->assert_matches($modinfo);
+ filter_set_local_state('zombie', $activity1context->id, TEXTFILTER_OFF);
+ filter_set_local_state('frog', $activity2context->id, TEXTFILTER_OFF);
+ $this->assert_matches($modinfo, $activity1context, $activity2context);
// Now do some filter options.
- filter_set_local_config('name', $this->activity1context->id, 'a', 'x');
- filter_set_local_config('zombie', $this->activity1context->id, 'a', 'y');
- filter_set_local_config('frog', $this->activity1context->id, 'a', 'z');
+ filter_set_local_config('name', $activity1context->id, 'a', 'x');
+ filter_set_local_config('zombie', $activity1context->id, 'a', 'y');
+ filter_set_local_config('frog', $activity1context->id, 'a', 'z');
// These last two don't do anything as they are not at final level but I
// thought it would be good to have that verified in test.
- filter_set_local_config('frog', $this->coursecontext->id, 'q', 'x');
- filter_set_local_config('frog', $this->catcontext->id, 'q', 'z');
- $this->assert_matches($modinfo);
+ filter_set_local_config('frog', $coursecontext->id, 'q', 'x');
+ filter_set_local_config('frog', $catcontext->id, 'q', 'z');
+ $this->assert_matches($modinfo, $activity1context, $activity2context);
}
public function test_filter_delete_all_for_filter() {
global $DB;
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_ON);
public function test_filter_delete_all_for_context() {
global $DB;
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Setup fixture.
filter_set_global_state('name', TEXTFILTER_ON);
public function test_set() {
global $CFG;
+ $this->resetAfterTest();
$this->assertFileExists("$CFG->dirroot/filter/emailprotect"); // Any standard filter.
$this->assertFileExists("$CFG->dirroot/filter/tidy"); // Any standard filter.
public function test_unset_to_empty() {
global $CFG;
+ $this->resetAfterTest();
$this->assertFileExists("$CFG->dirroot/filter/tidy"); // Any standard filter.
public function test_unset_multi() {
global $CFG;
+ $this->resetAfterTest();
$this->assertFileExists("$CFG->dirroot/filter/emailprotect"); // Any standard filter.
$this->assertFileExists("$CFG->dirroot/filter/tidy"); // Any standard filter.
}
public function test_filter_manager_instance() {
+ $this->resetAfterTest();
set_config('perfdebug', 7);
filter_manager::reset_caches();
}
public function test_filter_get_globally_enabled_default() {
+ $this->resetAfterTest();
$enabledfilters = filter_get_globally_enabled();
$this->assertArrayNotHasKey('glossary', $enabledfilters);
}
public function test_filter_get_globally_enabled_after_change() {
+ $this->resetAfterTest();
filter_set_global_state('glossary', TEXTFILTER_ON);
$enabledfilters = filter_get_globally_enabled();
$this->assertArrayHasKey('glossary', $enabledfilters);
}
public function test_filter_get_globally_enabled_filters_with_config() {
- $this->setup_available_in_context_tests();
+ $this->resetAfterTest();
+ $this->remove_all_filters_from_config(); // Remove all filters.
+ [
+ 'syscontext' => $syscontext,
+ 'childcontext' => $childcontext
+ ] = $this->setup_available_in_context_tests();
+ $this->remove_all_filters_from_config(); // Remove all filters.
// Set few filters.
filter_set_global_state('one', TEXTFILTER_ON);
filter_set_global_state('two', TEXTFILTER_DISABLED);
// Set global config.
- filter_set_local_config('one', $this->syscontext->id, 'test1a', 'In root');
- filter_set_local_config('one', $this->syscontext->id, 'test1b', 'In root');
- filter_set_local_config('two', $this->syscontext->id, 'test2a', 'In root');
- filter_set_local_config('two', $this->syscontext->id, 'test2b', 'In root');
+ filter_set_local_config('one', $syscontext->id, 'test1a', 'In root');
+ filter_set_local_config('one', $syscontext->id, 'test1b', 'In root');
+ filter_set_local_config('two', $syscontext->id, 'test2a', 'In root');
+ filter_set_local_config('two', $syscontext->id, 'test2b', 'In root');
// Set child config.
- filter_set_local_config('one', $this->childcontext->id, 'test1a', 'In child');
- filter_set_local_config('one', $this->childcontext->id, 'test1b', 'In child');
- filter_set_local_config('two', $this->childcontext->id, 'test2a', 'In child');
- filter_set_local_config('two', $this->childcontext->id, 'test2b', 'In child');
- filter_set_local_config('three', $this->childcontext->id, 'test3a', 'In child');
- filter_set_local_config('three', $this->childcontext->id, 'test3b', 'In child');
+ filter_set_local_config('one', $childcontext->id, 'test1a', 'In child');
+ filter_set_local_config('one', $childcontext->id, 'test1b', 'In child');
+ filter_set_local_config('two', $childcontext->id, 'test2a', 'In child');
+ filter_set_local_config('two', $childcontext->id, 'test2b', 'In child');
+ filter_set_local_config('three', $childcontext->id, 'test3a', 'In child');
+ filter_set_local_config('three', $childcontext->id, 'test3b', 'In child');
// Check.
$actual = filter_get_globally_enabled_filters_with_config();
$lock2 = $lockfactory->get_lock('abc', 2);
$duration += microtime(true);
$this->assertFalse($lock2, 'Cannot get a stacked lock');
- $this->assertTrue($duration > 1, 'Lock should timeout after more than 1 second');
+ $this->assertTrue($duration < 2.5, 'Lock should timeout after no more than 2 seconds');
// This should timeout almost instantly.
$duration = -microtime(true);
<location>adodb</location>
<name>AdoDB</name>
<license>BSD/GPL</license>
- <version>5.20.15</version>
+ <version>5.20.16</version>
<licenseversion>2.1+</licenseversion>
</library>
<library>
information provided here is intended especially for developers.
=== 3.9 ===
+* behat_data_generators::the_following_exist() has been removed, please use
+ behat_data_generators::the_following_entities_exist() instead. See MDL-67691 for more info.
* admin/tool/task/cli/adhoc_task.php now observers the concurrency limits.
If you want to get the previous (unlimited) behavior, use the --ignorelimits switch).
* Removed the following deprecated functions:
- $plugin->incompatible = 36;
incompatible takes a single int corresponding to the first incompatible branch. Any Moodle versions including and
above this will be prevented from installing the plugin, and a message will be given when attempting installation.
+* Added the <component>_bulk_user_actions() callback which returns a list of custom action_links objects
+* Add 'required' admin flag for mod forms allows elements to be toggled between being required or not in admin settings.
+ - In mod settings, along with lock, advanced flags, the required flag can now be set with $setting->set_required_flag_options().
+ The name of the admin setting must be exactly the same as the mod_form element.
+ - Currently supported by:
+ - mod_assign
+ - mod_quiz
+* Added a native MySQL / MariaDB lock implementation
+* The database drivers (moodle_database and subclasses) don't need to implement get_columns() anymore.
+ They have to implement fetch_columns instead.
+* Added function cleanup_after_drop to the database_manager class to take care of all the cleanups that need to be done after a table is dropped.
=== 3.8 ===
* Add CLI option to notify all cron tasks to stop: admin/cli/cron.php --stop
*
* @param int $touserid the id of the message recipient
* @param int|null $fromuserid the id of the message sender, null if all messages
+ * @param int|null $timecreatedto mark notifications created before this time as read
* @return void
*/
- public static function mark_all_notifications_as_read($touserid, $fromuserid = null) {
+ public static function mark_all_notifications_as_read($touserid, $fromuserid = null, $timecreatedto = null) {
global $DB;
$notificationsql = "SELECT n.*
$notificationsql .= " AND useridfrom = ?";
$notificationsparams[] = $fromuserid;
}
+ if (!empty($timecreatedto)) {
+ $notificationsql .= " AND timecreated <= ?";
+ $notificationsparams[] = $timecreatedto;
+ }
$notifications = $DB->get_recordset_sql($notificationsql, $notificationsparams);
foreach ($notifications as $notification) {
'useridfrom' => new external_value(
PARAM_INT, 'the user id who send the message, 0 for any user. -10 or -20 for no-reply or support user',
VALUE_DEFAULT, 0),
+ 'timecreatedto' => new external_value(
+ PARAM_INT, 'mark messages created before this time as read, 0 for all messages',
+ VALUE_DEFAULT, 0),
)
);
}
* @throws moodle_exception
* @param int $useridto the user id who received the message
* @param int $useridfrom the user id who send the message. -10 or -20 for no-reply or support user
+ * @param int $timecreatedto mark message created before this time as read, 0 for all messages
* @return external_description
*/
- public static function mark_all_notifications_as_read($useridto, $useridfrom) {
+ public static function mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto = 0) {
global $USER;
$params = self::validate_parameters(
array(
'useridto' => $useridto,
'useridfrom' => $useridfrom,
+ 'timecreatedto' => $timecreatedto,
)
);
$useridto = $params['useridto'];
$useridfrom = $params['useridfrom'];
+ $timecreatedto = $params['timecreatedto'];
if (!empty($useridto)) {
if (core_user::is_real_user($useridto)) {
throw new moodle_exception('accessdenied', 'admin');
}
- \core_message\api::mark_all_notifications_as_read($useridto, $useridfrom);
+ \core_message\api::mark_all_notifications_as_read($useridto, $useridfrom, $timecreatedto);
return true;
}
return $preferences;
}
-/**
- * Renders the popup.
- *
- * @param renderer_base $renderer
- * @return string The HTML
- */
-function core_message_render_navbar_output(\renderer_base $renderer) {
- global $USER, $CFG;
-
- // Early bail out conditions.
- if (!isloggedin() || isguestuser() || user_not_fully_set_up($USER) ||
- get_user_preferences('auth_forcepasswordchange') ||
- (!$USER->policyagreed && !is_siteadmin() &&
- ($manager = new \core_privacy\local\sitepolicy\manager()) && $manager->is_defined())) {
- return '';
- }
-
- $output = '';
-
- // Add the messages popover.
- if (!empty($CFG->messaging)) {
- $unreadcount = \core_message\api::count_unread_conversations($USER);
- $requestcount = \core_message\api::get_received_contact_requests_count($USER->id);
- $context = [
- 'userid' => $USER->id,
- 'unreadcount' => $unreadcount + $requestcount
- ];
- $output .= $renderer->render_from_template('core_message/message_popover', $context);
- }
-
- return $output;
-}
-
/**
* Render the message drawer to be included in the top of the body of each page.
*
this.markAllReadButton = this.root.find(SELECTORS.MARK_ALL_READ_BUTTON);
this.unreadCount = 0;
+ this.lastQueried = 0;
this.userId = this.root.attr('data-userid');
this.container = this.root.find(SELECTORS.ALL_NOTIFICATIONS_CONTAINER);
this.limit = 20;
return NotificationRepo.query(request).then(function(result) {
var notifications = result.notifications;
this.unreadCount = result.unreadcount;
+ this.lastQueried = Math.floor(new Date().getTime() / 1000);
this.setLoadedAllContent(!notifications.length || notifications.length < this.limit);
this.initialLoad = true;
this.updateButtonAriaLabel();
NotificationPopoverController.prototype.markAllAsRead = function() {
this.markAllReadButton.addClass('loading');
- return NotificationRepo.markAllAsRead({useridto: this.userId})
+ var request = {
+ useridto: this.userId,
+ timecreatedto: this.lastQueried,
+ };
+
+ return NotificationRepo.markAllAsRead(request)
.then(function() {
this.unreadCount = 0;
this.root.find(SELECTORS.UNREAD_NOTIFICATION).removeClass('unread');
$output .= $renderer->render_from_template('message_popup/notification_popover', $context);
}
+ // Add the messages popover.
+ if (!empty($CFG->messaging)) {
+ $unreadcount = \core_message\api::count_unread_conversations($USER);
+ $requestcount = \core_message\api::get_received_contact_requests_count($USER->id);
+ $context = [
+ 'userid' => $USER->id,
+ 'unreadcount' => $unreadcount + $requestcount
+ ];
+ $output .= $renderer->render_from_template('core_message/message_popover', $context);
+ }
+
return $output;
}
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2019111800; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version = 2020012300; // The current plugin version (Date: YYYYMMDDXX)
$plugin->requires = 2019111200; // Requires this Moodle version
$plugin->component = 'message_popup'; // Full name of the plugin (used for diagnostics)
}
}}
-<div class="float-right popover-region collapsed">
+<div class="popover-region collapsed" data-region="popover-region-messages">
<a id="message-drawer-toggle-{{uniqid}}" class="nav-link d-inline-block popover-region-toggle position-relative" href="#"
role="button">
{{#pix}} t/message, core, {{#str}} togglemessagemenu, message {{/str}} {{/pix}}
And I should see "Contact request sent"
And I log out
And I log in as "student4"
- Then I should see "2" in the "//*[@data-region='count-container']" "xpath_element"
+ Then I should see "2" in the "//div[@data-region='popover-region-messages']//*[@data-region='count-container']" "xpath_element"
And I open messaging
And I click on "Contacts" "link"
Then I should see "2" in the "//div[@data-region='view-contacts']//*[@data-region='contact-request-count']" "xpath_element"
And I should see "Contact request sent"
And I log out
And I log in as "student3"
- Then I should see "1" in the "//*[@data-region='count-container']" "xpath_element"
+ Then I should see "1" in the "//div[@data-region='popover-region-messages']//*[@data-region='count-container']" "xpath_element"
And I open messaging
And I click on "Contacts" "link"
Then I should see "1" in the "//div[@data-region='view-contacts']//*[@data-region='contact-request-count']" "xpath_element"
$this->assertCount(0, $unreadnotifications);
}
+ public function test_mark_all_notifications_as_read_time_created_to() {
+ global $DB;
+
+ $this->resetAfterTest(true);
+
+ $sender1 = $this->getDataGenerator()->create_user();
+ $sender2 = $this->getDataGenerator()->create_user();
+
+ $recipient = $this->getDataGenerator()->create_user();
+ $this->setUser($recipient);
+
+ // Record messages as sent on one second intervals.
+ $time = time();
+
+ $this->send_message($sender1, $recipient, 'Message 1', 1, $time);
+ $this->send_message($sender2, $recipient, 'Message 2', 1, $time + 1);
+ $this->send_message($sender1, $recipient, 'Message 3', 1, $time + 2);
+ $this->send_message($sender2, $recipient, 'Message 4', 1, $time + 3);
+
+ // Mark notifications sent from sender1 up until the second message; should only mark the first notification as read.
+ core_message_external::mark_all_notifications_as_read($recipient->id, $sender1->id, $time + 1);
+
+ $params = [$recipient->id];
+
+ $this->assertEquals(1, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', $params));
+ $this->assertEquals(3, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NULL', $params));
+
+ // Mark all notifications as read from any sender up to the time the third message was sent.
+ core_message_external::mark_all_notifications_as_read($recipient->id, 0, $time + 2);
+
+ $this->assertEquals(3, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', $params));
+ $this->assertEquals(1, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NULL', $params));
+
+ // Mark all notifications as read from any sender with a time after all messages were sent.
+ core_message_external::mark_all_notifications_as_read($recipient->id, 0, $time + 10);
+
+ $this->assertEquals(4, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NOT NULL', $params));
+ $this->assertEquals(0, $DB->count_records_select('notifications', 'useridto = ? AND timeread IS NULL', $params));
+ }
+
/**
* Test get_user_notification_preferences
*/
}
.path-mod-assign.jsenabled .expandsummaryicon {
- display: inline;
+ display: inline-block;
}
.path-mod-assign .hidefull {
Scenario: Time limit expires
Given I log out
And I log in as "admin"
- And I navigate to "Security > Site security settings" in site administration
- And I set the field "Maximum time to edit posts" to "1 minutes"
- And I press "Save changes"
+ And the following config values are set as admin:
+ | maxeditingtime | 1 |
And I am on "Course 1" course homepage with editing mode on
And I add the "Recent activity" block
And I log out