Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9650f3f56f | |||
| d44c73e438 | |||
| 3020929da8 | |||
| 3f4f2da9e0 | |||
| 5a89ac9640 | |||
| 6bbf56b9e2 | |||
| 6be97cb9ac | |||
| 2fc307e4d4 | |||
| c426429a08 | |||
| b848c97164 | |||
| df5502034a | |||
| c2c3db84b1 | |||
| 52bd62f2be | |||
| 53392df219 | |||
| 344386f873 | |||
| 8a2e9542c9 | |||
| 1f01b678df | |||
| 90740716be | |||
| 2cebb87682 | |||
| 907b377e51 | |||
| b77dcd23d8 | |||
| 1a3612bb5b | |||
| fba7dcd90a | |||
| e836242e1c | |||
| 6f05d22622 | |||
| 141271b726 | |||
| aae3a2fdd4 | |||
| abd634d64c | |||
| e24f8994b5 | |||
| d16e730116 | |||
| 3cc11668c8 | |||
| 3c72d44b18 | |||
| 4546269170 | |||
| ad4dfb1e8c | |||
| 4f6373bf05 | |||
| 31c05704b8 | |||
| 384c4c29db | |||
| 2282d6932b | |||
| 47265ccf62 | |||
| 2d0d9a4748 | |||
| 0cbb46b944 | |||
| 3dcf5f9930 | |||
| 3456505930 | |||
| 18c80ae8a5 | |||
| 4fccbafa46 | |||
| 11fc8b3d02 | |||
| 4e190d7eae | |||
| acd27d90c8 | |||
| d81447751e | |||
| eb3df7ecad | |||
| a9d153e61d | |||
| c2c8ce4b6d | |||
| 27f608a5c9 | |||
| 181cf34341 | |||
| 56c15f5c99 | |||
| 87711249c3 | |||
| 0a03b2fd63 | |||
| 398c4c107d | |||
| 5789961754 | |||
| 587a8a9732 | |||
| 53d0efba1f | |||
| 16045c4846 | |||
| 57d2a28425 | |||
| 33cbe249dd | |||
| 82e23b9443 |
@@ -109,6 +109,17 @@ require_once(__DIR__ . '/../../../../lib/behat/lib.php');
|
||||
require_once(__DIR__ . '/../../../../lib/behat/classes/behat_command.php');
|
||||
require_once(__DIR__ . '/../../../../lib/behat/classes/behat_config_manager.php');
|
||||
|
||||
// Remove error handling overrides done in config.php. This is consistent with admin/tool/behat/cli/util_single_run.php.
|
||||
$CFG->debug = (E_ALL | E_STRICT);
|
||||
$CFG->debugdisplay = 1;
|
||||
error_reporting($CFG->debug);
|
||||
ini_set('display_errors', '1');
|
||||
ini_set('log_errors', '1');
|
||||
|
||||
// Import the necessary libraries.
|
||||
require_once($CFG->libdir . '/setuplib.php');
|
||||
require_once($CFG->libdir . '/behat/classes/util.php');
|
||||
|
||||
// For drop option check if parallel site.
|
||||
if ((empty($options['parallel'])) && ($options['drop']) || $options['updatesteps']) {
|
||||
$options['parallel'] = behat_config_manager::get_behat_run_config_value('parallel');
|
||||
|
||||
@@ -920,9 +920,17 @@ class expired_contexts_manager {
|
||||
* @return bool
|
||||
*/
|
||||
protected static function is_course_context_expired_or_unprotected_for_user(\context $context, \stdClass $user) {
|
||||
$expiryrecords = self::get_nested_expiry_info_for_courses($context->path);
|
||||
|
||||
$info = $expiryrecords[$context->path]->info;
|
||||
if ($context->get_course_context()->instanceid == SITEID) {
|
||||
// The is an activity in the site course (front page).
|
||||
$purpose = data_registry::get_effective_contextlevel_value(CONTEXT_SYSTEM, 'purpose');
|
||||
$info = static::get_expiry_info($purpose);
|
||||
|
||||
} else {
|
||||
$expiryrecords = self::get_nested_expiry_info_for_courses($context->path);
|
||||
$info = $expiryrecords[$context->path]->info;
|
||||
}
|
||||
|
||||
if ($info->is_fully_expired()) {
|
||||
// This context is fully expired.
|
||||
return true;
|
||||
|
||||
@@ -2222,6 +2222,40 @@ class tool_dataprivacy_expired_contexts_testcase extends advanced_testcase {
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($blockcontext, $user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the is_context_expired functions when supplied with the front page course.
|
||||
*/
|
||||
public function test_is_context_expired_frontpage() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$purposes = $this->setup_basics('PT1H', 'PT1H', 'P1D');
|
||||
|
||||
$frontcourse = get_site();
|
||||
$frontcoursecontext = \context_course::instance($frontcourse->id);
|
||||
|
||||
$sitenews = $this->getDataGenerator()->create_module('forum', ['course' => $frontcourse->id]);
|
||||
$cm = get_coursemodule_from_instance('forum', $sitenews->id);
|
||||
$sitenewscontext = \context_module::instance($cm->id);
|
||||
|
||||
$user = $this->getDataGenerator()->create_user(['lastaccess' => time() - YEARSECS]);
|
||||
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired($frontcoursecontext));
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired($sitenewscontext));
|
||||
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($frontcoursecontext, $user));
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($sitenewscontext, $user));
|
||||
|
||||
// Protecting the course contextlevel does not impact the front page.
|
||||
$purposes->course->set('protected', 1)->save();
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($frontcoursecontext, $user));
|
||||
$this->assertTrue(expired_contexts_manager::is_context_expired_or_unprotected_for_user($sitenewscontext, $user));
|
||||
|
||||
// Protecting the system contextlevel affects the front page, too.
|
||||
$purposes->system->set('protected', 1)->save();
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($frontcoursecontext, $user));
|
||||
$this->assertFalse(expired_contexts_manager::is_context_expired_or_unprotected_for_user($sitenewscontext, $user));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the is_context_expired functions when supplied with an expired course.
|
||||
*/
|
||||
|
||||
@@ -116,7 +116,7 @@ class tool_lp_external_testcase extends externallib_advanced_testcase {
|
||||
$this->userrole = create_role('User role', 'lpuserrole', 'learning plan user role description');
|
||||
|
||||
assign_capability('moodle/competency:competencymanage', CAP_ALLOW, $this->creatorrole, $syscontext->id);
|
||||
assign_capability('moodle/competency:competencycompetencyconfigure', CAP_ALLOW, $this->creatorrole, $syscontext->id);
|
||||
assign_capability('moodle/competency:coursecompetencyconfigure', CAP_ALLOW, $this->creatorrole, $syscontext->id);
|
||||
assign_capability('moodle/competency:planmanage', CAP_ALLOW, $this->creatorrole, $syscontext->id);
|
||||
assign_capability('moodle/competency:planmanagedraft', CAP_ALLOW, $this->creatorrole, $syscontext->id);
|
||||
assign_capability('moodle/competency:planmanageown', CAP_ALLOW, $this->creatorrole, $syscontext->id);
|
||||
|
||||
@@ -672,7 +672,7 @@ class manager {
|
||||
$attachment->charset = $partdata->getCharset();
|
||||
$attachment->description = $partdata->getDescription();
|
||||
$attachment->contentid = $partdata->getContentId();
|
||||
$attachment->filesize = $messagedata->getBodyPartSize($part);
|
||||
$attachment->filesize = $partdata->getBytes();
|
||||
|
||||
if (!empty($CFG->antiviruses)) {
|
||||
mtrace("--> Attempting virus scan of '{$attachment->filename}'");
|
||||
|
||||
@@ -4446,6 +4446,12 @@ class restore_create_categories_and_questions extends restore_structure_step {
|
||||
|
||||
// With newitemid = 0, let's create the question
|
||||
if (!$questionmapping->newitemid) {
|
||||
if ($data->qtype === 'random') {
|
||||
// Ensure that this newly created question is considered by
|
||||
// \qtype_random\task\remove_unused_questions.
|
||||
$data->hidden = 0;
|
||||
}
|
||||
|
||||
$newitemid = $DB->insert_record('question', $data);
|
||||
$this->set_mapping('question', $oldid, $newitemid);
|
||||
// Also annotate them as question_created, we need
|
||||
|
||||
@@ -3561,6 +3561,11 @@ function calendar_output_fragment_event_form($args) {
|
||||
$mform->set_data($data);
|
||||
} else {
|
||||
$event = calendar_event::load($eventid);
|
||||
|
||||
if (!calendar_edit_event_allowed($event)) {
|
||||
print_error('nopermissiontoupdatecalendar');
|
||||
}
|
||||
|
||||
$mapper = new \core_calendar\local\event\mappers\create_update_form_mapper();
|
||||
$eventdata = $mapper->from_legacy_event_to_data($event);
|
||||
$data = array_merge((array) $eventdata, $data);
|
||||
|
||||
+2
-7
@@ -28,7 +28,6 @@ require_once($CFG->dirroot.'/cohort/upload_form.php');
|
||||
require_once($CFG->libdir . '/csvlib.class.php');
|
||||
|
||||
$contextid = optional_param('contextid', 0, PARAM_INT);
|
||||
$returnurl = optional_param('returnurl', '', PARAM_URL);
|
||||
|
||||
require_login();
|
||||
|
||||
@@ -56,13 +55,9 @@ if ($context->contextlevel == CONTEXT_COURSECAT) {
|
||||
navigation_node::override_active_url(new moodle_url('/cohort/index.php', array()));
|
||||
}
|
||||
|
||||
$uploadform = new cohort_upload_form(null, array('contextid' => $context->id, 'returnurl' => $returnurl));
|
||||
$uploadform = new cohort_upload_form(null, array('contextid' => $context->id));
|
||||
|
||||
if ($returnurl) {
|
||||
$returnurl = new moodle_url($returnurl);
|
||||
} else {
|
||||
$returnurl = new moodle_url('/cohort/index.php', array('contextid' => $context->id));
|
||||
}
|
||||
$returnurl = new moodle_url('/cohort/index.php', array('contextid' => $context->id));
|
||||
|
||||
if ($uploadform->is_cancelled()) {
|
||||
redirect($returnurl);
|
||||
|
||||
@@ -48,9 +48,6 @@ class cohort_upload_form extends moodleform {
|
||||
$mform = $this->_form;
|
||||
$data = (object)$this->_customdata;
|
||||
|
||||
$mform->addElement('hidden', 'returnurl');
|
||||
$mform->setType('returnurl', PARAM_URL);
|
||||
|
||||
$mform->addElement('header', 'cohortfileuploadform', get_string('uploadafile'));
|
||||
|
||||
$filepickeroptions = array();
|
||||
|
||||
@@ -102,6 +102,7 @@ class core_comment_external extends external_api {
|
||||
if ($comments === false) {
|
||||
throw new moodle_exception('nopermissions', 'error', '', 'view comments');
|
||||
}
|
||||
$options = array('blanktarget' => true);
|
||||
|
||||
foreach ($comments as $key => $comment) {
|
||||
|
||||
@@ -110,7 +111,8 @@ class core_comment_external extends external_api {
|
||||
$context->id,
|
||||
$params['component'],
|
||||
'',
|
||||
0);
|
||||
0,
|
||||
$options);
|
||||
}
|
||||
|
||||
$results = array(
|
||||
|
||||
+3
-2
@@ -570,7 +570,7 @@ class comment {
|
||||
$params['itemid'] = $this->itemid;
|
||||
|
||||
$comments = array();
|
||||
$formatoptions = array('overflowdiv' => true);
|
||||
$formatoptions = array('overflowdiv' => true, 'blanktarget' => true);
|
||||
$rs = $DB->get_recordset_sql($sql, $params, $start, $perpage);
|
||||
foreach ($rs as $u) {
|
||||
$c = new stdClass();
|
||||
@@ -717,7 +717,8 @@ class comment {
|
||||
$newcmt->fullname = fullname($USER);
|
||||
$url = new moodle_url('/user/view.php', array('id' => $USER->id, 'course' => $this->courseid));
|
||||
$newcmt->profileurl = $url->out();
|
||||
$newcmt->content = format_text($newcmt->content, $newcmt->format, array('overflowdiv'=>true));
|
||||
$formatoptions = array('overflowdiv' => true, 'blanktarget' => true);
|
||||
$newcmt->content = format_text($newcmt->content, $newcmt->format, $formatoptions);
|
||||
$newcmt->avatar = $OUTPUT->user_picture($USER, array('size'=>16));
|
||||
|
||||
$commentlist = array($newcmt);
|
||||
|
||||
@@ -68,7 +68,7 @@ class comment_manager {
|
||||
ON u.id=c.userid
|
||||
ORDER BY c.timecreated ASC";
|
||||
$rs = $DB->get_recordset_sql($sql, null, $start, $this->perpage);
|
||||
$formatoptions = array('overflowdiv' => true);
|
||||
$formatoptions = array('overflowdiv' => true, 'blanktarget' => true);
|
||||
foreach ($rs as $item) {
|
||||
// Set calculated fields
|
||||
$item->fullname = fullname($item);
|
||||
|
||||
@@ -139,7 +139,7 @@ class core_competency_external_testcase extends externallib_advanced_testcase {
|
||||
$this->userrole = create_role('User role', 'userrole', 'learning plan user role description');
|
||||
|
||||
assign_capability('moodle/competency:competencymanage', CAP_ALLOW, $this->creatorrole, $syscontext->id);
|
||||
assign_capability('moodle/competency:competencycompetencyconfigure', CAP_ALLOW, $this->creatorrole, $syscontext->id);
|
||||
assign_capability('moodle/competency:coursecompetencyconfigure', CAP_ALLOW, $this->creatorrole, $syscontext->id);
|
||||
assign_capability('moodle/competency:competencyview', CAP_ALLOW, $this->userrole, $syscontext->id);
|
||||
assign_capability('moodle/competency:planmanage', CAP_ALLOW, $this->creatorrole, $syscontext->id);
|
||||
assign_capability('moodle/competency:planmanagedraft', CAP_ALLOW, $this->creatorrole, $syscontext->id);
|
||||
|
||||
@@ -161,47 +161,55 @@ class provider implements
|
||||
$completioninfo = new \completion_info($course);
|
||||
$completion = $completioninfo->is_enabled();
|
||||
|
||||
if ($completion == COMPLETION_ENABLED) {
|
||||
if ($completion != COMPLETION_ENABLED) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$coursecomplete = $completioninfo->is_course_complete($user->id);
|
||||
$coursecomplete = $completioninfo->is_course_complete($user->id);
|
||||
|
||||
if ($coursecomplete) {
|
||||
$status = get_string('complete');
|
||||
} else {
|
||||
$criteriacomplete = $completioninfo->count_course_user_data($user->id);
|
||||
$ccompletion = new \completion_completion(['userid' => $user->id, 'course' => $course->id]);
|
||||
|
||||
$status = ($coursecomplete) ? get_string('complete') : '';
|
||||
$status = (!$criteriacomplete && !$ccompletion->timestarted) ? get_string('notyetstarted', 'completion') :
|
||||
get_string('inprogress', 'completion');
|
||||
|
||||
$completions = $completioninfo->get_completions($user->id);
|
||||
$overall = get_string('nocriteriaset', 'completion');
|
||||
if (!empty($completions)) {
|
||||
if ($completioninfo->get_aggregation_method() == COMPLETION_AGGREGATION_ALL) {
|
||||
$overall = get_string('criteriarequiredall', 'completion');
|
||||
} else {
|
||||
$overall = get_string('criteriarequiredany', 'completion');
|
||||
}
|
||||
if (!$criteriacomplete && !$ccompletion->timestarted) {
|
||||
$status = get_string('notyetstarted', 'completion');
|
||||
} else {
|
||||
$status = get_string('inprogress', 'completion');
|
||||
}
|
||||
|
||||
$coursecompletiondata = [
|
||||
'status' => $status,
|
||||
'required' => $overall,
|
||||
];
|
||||
|
||||
$coursecompletiondata['criteria'] = array_map(function($completion) use ($completioninfo) {
|
||||
$criteria = $completion->get_criteria();
|
||||
$aggregation = $completioninfo->get_aggregation_method($criteria->criteriatype);
|
||||
$required = ($aggregation == COMPLETION_AGGREGATION_ALL) ? get_string('all', 'completion') :
|
||||
get_string('any', 'completion');
|
||||
$data = [
|
||||
'required' => $required,
|
||||
'completed' => transform::yesno($completion->is_complete()),
|
||||
'timecompleted' => isset($completion->timecompleted) ? transform::datetime($completion->timecompleted) : ''
|
||||
];
|
||||
$details = $criteria->get_details($completion);
|
||||
$data = array_merge($data, $details);
|
||||
return $data;
|
||||
}, $completions);
|
||||
return $coursecompletiondata;
|
||||
}
|
||||
|
||||
$completions = $completioninfo->get_completions($user->id);
|
||||
$overall = get_string('nocriteriaset', 'completion');
|
||||
if (!empty($completions)) {
|
||||
if ($completioninfo->get_aggregation_method() == COMPLETION_AGGREGATION_ALL) {
|
||||
$overall = get_string('criteriarequiredall', 'completion');
|
||||
} else {
|
||||
$overall = get_string('criteriarequiredany', 'completion');
|
||||
}
|
||||
}
|
||||
|
||||
$coursecompletiondata = [
|
||||
'status' => $status,
|
||||
'required' => $overall,
|
||||
];
|
||||
|
||||
$coursecompletiondata['criteria'] = array_map(function($completion) use ($completioninfo) {
|
||||
$criteria = $completion->get_criteria();
|
||||
$aggregation = $completioninfo->get_aggregation_method($criteria->criteriatype);
|
||||
$required = ($aggregation == COMPLETION_AGGREGATION_ALL) ? get_string('all', 'completion') :
|
||||
get_string('any', 'completion');
|
||||
$data = [
|
||||
'required' => $required,
|
||||
'completed' => transform::yesno($completion->is_complete()),
|
||||
'timecompleted' => isset($completion->timecompleted) ? transform::datetime($completion->timecompleted) : ''
|
||||
];
|
||||
$details = $criteria->get_details($completion);
|
||||
$data = array_merge($data, $details);
|
||||
return $data;
|
||||
}, $completions);
|
||||
return $coursecompletiondata;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -193,13 +193,31 @@ class core_completion_privacy_test extends \core_privacy\tests\provider_testcase
|
||||
$hasno = array_search('No', $coursecompletion1['criteria'], true);
|
||||
$this->assertFalse($hasno);
|
||||
$coursecompletion2 = \core_completion\privacy\provider::get_course_completion_info($user2, $this->course);
|
||||
$hasyes = array_search('Yes', $coursecompletion1['criteria'], true);
|
||||
$hasyes = array_search('Yes', $coursecompletion2['criteria'], true);
|
||||
$this->assertFalse($hasyes);
|
||||
$coursecompletion3 = \core_completion\privacy\provider::get_course_completion_info($user3, $this->course);
|
||||
$hasno = array_search('No', $coursecompletion1['criteria'], true);
|
||||
$hasno = array_search('No', $coursecompletion3['criteria'], true);
|
||||
$this->assertFalse($hasno);
|
||||
$coursecompletion4 = \core_completion\privacy\provider::get_course_completion_info($user4, $this->course);
|
||||
$hasyes = array_search('Yes', $coursecompletion1['criteria'], true);
|
||||
$hasyes = array_search('Yes', $coursecompletion4['criteria'], true);
|
||||
$this->assertFalse($hasyes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getting course completion information with completion disabled.
|
||||
*/
|
||||
public function test_get_course_completion_info_completion_disabled() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course(['enablecompletion' => 0]);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
$coursecompletion = \core_completion\privacy\provider::get_course_completion_info($user, $course);
|
||||
|
||||
$this->assertTrue(is_array($coursecompletion));
|
||||
$this->assertEmpty($coursecompletion);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@
|
||||
"homepage": "https://moodle.org",
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "6.5.*",
|
||||
"phpunit/dbUnit": "3.0.*",
|
||||
"phpunit/dbunit": "3.0.*",
|
||||
"moodlehq/behat-extension": "3.34.2",
|
||||
"mikey179/vfsStream": "^1.6"
|
||||
"mikey179/vfsstream": "^1.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,3 +42,4 @@ $string['cliunknowoption'] = 'خيارات غير معروفة
|
||||
$string['cliyesnoprompt'] = 'ادخل (Y) تعني نعم أو (N) تعني لأ';
|
||||
$string['environmentrequireinstall'] = 'مطلوب تثبيته/تفيله';
|
||||
$string['environmentrequireversion'] = 'مطلوب الأصدار {$a->needed} ، وانت تستخدم الأصدار {$a->current}';
|
||||
$string['upgradekeyset'] = 'مفتاح التحديث (اتركها فارغة إن لم ترغب بتحديده)';
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?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['thislanguage'] = 'Bamanankan';
|
||||
@@ -0,0 +1,34 @@
|
||||
<?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['parentlanguage'] = 'de';
|
||||
$string['thislanguage'] = 'Deutsch für Arbeitsplatz';
|
||||
@@ -67,7 +67,9 @@ $string['pathsunsecuredataroot'] = 'Η τοποθεσία του Φάκελου
|
||||
$string['pathswrongadmindir'] = 'Ο Φάκελος Admin δεν υπάρχει';
|
||||
$string['phpextension'] = 'Επέκταση {$a} της PHP';
|
||||
$string['phpversion'] = 'Έκδοση της PHP';
|
||||
$string['phpversionhelp'] = 'p>Το Moodle απαιτεί η έκδοση της PHP να είναι τουλάχιστον 4.3.0 ή 5.1.0 (η 5.0.x έχει έναν αριθμό γνωστών προβλημάτων).</p> <p>Αυτή τη στιγμή έχετε την έκδοση {$a}</p> <p>Πρέπει να αναβαθμίσετε την PHP ή να μεταφερθείτε σε έναν κεντρικό υπολογιστή με μια νεότερη έκδοση της PHP!<br/> (Σε περίπτωση που έχετε την 5.0.x μπορείτε επίσης να κάνετε και υποβάθμιση στην έκδοση 4.4.x)</p>';
|
||||
$string['phpversionhelp'] = 'p>Το Moodle απαιτεί η έκδοση της PHP να είναι τουλάχιστον 5.6.5 ή 7.1 (η 7.0.x έχει κάποιους περιορισμούς στη μηχανή).</p>
|
||||
<p>Αυτή τη στιγμή έχετε την έκδοση {$a}</p>
|
||||
<p>Πρέπει να αναβαθμίσετε την PHP ή να μεταφερθείτε σε έναν κεντρικό Η/Υ με μια νεότερη έκδοση της PHP!</p>';
|
||||
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
|
||||
$string['welcomep20'] = 'Βλέπετε αυτή τη σελίδα γιατί εγκαταστήσατε και ξεκινήσατε με επιτυχία το πακέτο <strong>{$a->packname} {$a->packversion}</strong> στον υπολογιστή σας. Συγχαρητήρια!';
|
||||
$string['welcomep30'] = 'Αυτή η έκδοση/διανομή <strong>{$a->installername}</strong> περιλαμβάνει τις εφαρμογές για τη δημιουργία ενός περιβάλλοντος μέσα στο οποίο θα λειτουργεί το <strong>Moodle</strong>, ονομαστικά:';
|
||||
|
||||
@@ -30,5 +30,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['parentlanguage'] = '';
|
||||
$string['thisdirection'] = 'ltr';
|
||||
$string['thislanguage'] = 'Ελληνικά';
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?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['thislanguage'] = 'English for Workplace';
|
||||
@@ -0,0 +1,34 @@
|
||||
<?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['parentlanguage'] = 'es';
|
||||
$string['thislanguage'] = 'Español para la Empresa';
|
||||
@@ -35,7 +35,7 @@ $string['cliansweryes'] = 'b';
|
||||
$string['cliincorrectvalueerror'] = 'Errorea, "{$a->value}" balioa ez da egokia "{$a->option}"-rako';
|
||||
$string['cliincorrectvalueretry'] = 'Balio ezegokia, mesedez saiatu berriz';
|
||||
$string['clitypevalue'] = 'Sartu balioa';
|
||||
$string['clitypevaluedefault'] = 'sartu balioa, sakatu Enter-i berezko balioa erabiltzeko ({$a})';
|
||||
$string['clitypevaluedefault'] = 'sartu balioa, sakatu Enter-i lehenetsitako balioa erabiltzeko ({$a})';
|
||||
$string['cliunknowoption'] = 'Aukera ezezagunak:
|
||||
{$a}
|
||||
Mesedez, erabili --help aukera.';
|
||||
|
||||
@@ -33,7 +33,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
$string['admindirname'] = 'Admin direktorioa';
|
||||
$string['availablelangs'] = 'Hizkuntza-pakete eskuragarriak';
|
||||
$string['chooselanguagehead'] = 'Aukeratu hizkuntza bat';
|
||||
$string['chooselanguagesub'] = 'Mesedez, aukeratu instalaziorako hizkuntza bat. Hizkuntza hori erabiliko da gunearen berezko hizkuntza gisa, baina aurrerago alda daiteke.';
|
||||
$string['chooselanguagesub'] = 'Mesedez, aukeratu instalaziorako hizkuntza bat. Hizkuntza hori erabiliko da gunearen hizkuntza lehenetsi gisa, baina aurrerago alda daiteke.';
|
||||
$string['clialreadyconfigured'] = 'Dagoeneko badago config.php konfigurazio-fitxategia. Mesedez erabili admin/cli/install_database.php Moodle gune honetan instalatu nahi baduzu.';
|
||||
$string['clialreadyinstalled'] = 'Dagoeneko badago config.php konfigurazio-fitxategia. Mesedez erabili admin/cli/upgrade.php zure Moodle gunea eguneratu nahi baduzu.';
|
||||
$string['cliinstallheader'] = 'Moodle {$a} komando-lerro bidezko instalaziorako programa';
|
||||
@@ -71,7 +71,7 @@ $string['pathserrcreatedataroot'] = 'Instalatzaileak ezin du datu-karpeta ({$a->
|
||||
$string['pathshead'] = 'Egiaztatu bideak';
|
||||
$string['pathsrodataroot'] = 'Dataroot direktorioa ez da idazteko modukoa.';
|
||||
$string['pathsroparentdataroot'] = 'Goragoko karpeta ({$a->parent}) ez da idazteko modukoa. Instalatzaileak ezin du datu-karpeta ({$a->dataroot}) sortu.';
|
||||
$string['pathssubadmindir'] = 'Web ostalari gutxi batzuk /admin URL berezi gisa erabiltzen dute kontrol-panel edo antzekora sarbidea emateko. Zoritxarrez, honek Moodle-ren kudeatze-orrien berezko kokapenarekin gatazka sortzen du. Hau konpondu dezakezu zure instalazioko admin karpeta berrizendatuz, eta izen berria hemen sartuta. Adibidez <em>moodleadmin</em>. Honek Moodle-ko admin estekak konponduko du.';
|
||||
$string['pathssubadmindir'] = 'Web ostalari gutxi batzuk /admin URL berezi gisa erabiltzen dute kontrol-panel edo antzekora sarbidea emateko. Zoritxarrez, honek Moodle-ren kudeatze-orrien lehenetsitako kokapenarekin gatazka sortzen du. Hau konpondu dezakezu zure instalazioko admin karpeta berrizendatuz, eta izen berria hemen sartuta. Adibidez <em>moodleadmin</em>. Honek Moodle-ko admin estekak konponduko du.';
|
||||
$string['pathssubdataroot'] = '<p>Moodle-k erabiltzaileek igotako fitxategien edukiak bilduko dituen direktorio bat.</p>
|
||||
<p>Direktorio honetan web-zerbitzariaren erabiltzaileak irakurtzeko eta idazteko baimena izan beharko ditu (normalean \'www-data\', \'nobody\', edo \'apache\').</p>
|
||||
<p>Ez litzateke web bidez eskuragarri egon beharko.</p>
|
||||
@@ -90,9 +90,8 @@ $string['phpversionhelp'] = '<p>Moodle-k PHP 5.6.5 edo 7.1 bertsioetako bat beha
|
||||
<p>PHP eguneratu edo PHP bertsio berriagoa duen zerbitzari batera jo.</p>';
|
||||
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
|
||||
$string['welcomep20'] = 'Orri hau ikusten baduzu <strong>{$a->packname} {$a->packversion}</strong> paketea zure ordenagailuan instalatu ahal izan duzu. Zorionak!';
|
||||
$string['welcomep30'] = '<strong>{$a->installername}</strong>ren bertsio honek <strong>Moodle</strong>k
|
||||
zure ordenadorean funtzionatzeko behar diren aplikazioak dauzka,
|
||||
bereziki:';
|
||||
$string['welcomep30'] = '<strong>{$a->installername}</strong>ren bertsio honek <strong>Moodle</strong>-k
|
||||
zure ordenagailuan funtzionatzeko behar diren aplikazioak dauzka, zehazki hurrengoak:';
|
||||
$string['welcomep40'] = 'Paketeak ere zera dauka: <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
|
||||
$string['welcomep50'] = 'Paketeko aplikazio guztien erabilpena dagozkien lizentziek
|
||||
arautzen dute. <strong>{$a->installername}</strong> aplikazioak
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['language'] = 'Kieli';
|
||||
$string['moodlelogo'] = 'Moodlen logo';
|
||||
$string['next'] = 'Seuraava';
|
||||
$string['previous'] = 'Edellinen';
|
||||
$string['reload'] = 'Lataa uudelleen';
|
||||
|
||||
@@ -32,3 +32,4 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['language'] = 'भाषा';
|
||||
$string['next'] = 'अगला';
|
||||
$string['reload'] = 'सीमा से अधिक लादना';
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['clianswerno'] = 'n';
|
||||
$string['cliansweryes'] = 'y';
|
||||
$string['cliansweryes'] = 'd';
|
||||
$string['cliincorrectvalueerror'] = 'Pogreška, netočna vrijednost "{$a->value}" za "{$a->option}"';
|
||||
$string['cliincorrectvalueretry'] = 'Netočna vrijednost, pokušajte ponovno';
|
||||
$string['clitypevalue'] = 'unesite vrijednost';
|
||||
@@ -40,3 +40,4 @@ $string['cliunknowoption'] = 'Nepoznate opcije: {$a} Molimo koristite --help opc
|
||||
$string['cliyesnoprompt'] = 'unesite y (znači da) ili n (znači ne)';
|
||||
$string['environmentrequireinstall'] = 'je neophodno instalirati/omogućiti';
|
||||
$string['environmentrequireversion'] = 'neophodna inačica je {$a->needed}, a vi trenutačno koristite inačicu {$a->current}';
|
||||
$string['upgradekeyset'] = 'Ključ za ažuriranje (ostavite prazno kako ga ne bi zadali)';
|
||||
|
||||
@@ -42,6 +42,7 @@ $string['cannotsavemd5file'] = 'Nije moguće pohraniti md5 datoteku';
|
||||
$string['cannotsavezipfile'] = 'Nije moguće pohraniti ZIP datoteku';
|
||||
$string['cannotunzipfile'] = 'Nije moguće otpakirati datoteku';
|
||||
$string['componentisuptodate'] = 'Komponenta je dostupna u svojoj najnovijoj inačici.';
|
||||
$string['dmlexceptiononinstall'] = '<p>Dogodila se pogreška baze podataka [{$a->errorcode}].<br />{$a->debuginfo}';
|
||||
$string['downloadedfilecheckfailed'] = 'Došlo je do pogreške pri provjeri preuzete datoteke';
|
||||
$string['invalidmd5'] = 'Neispravna md5 datoteka';
|
||||
$string['missingrequiredfield'] = 'Nedostaje neko obvezatno polje';
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['admindirname'] = 'Admin mapa';
|
||||
$string['availablelangs'] = 'Popis dostupnih jezika';
|
||||
$string['availablelangs'] = 'Popis dostupnih jezičnih paketa';
|
||||
$string['chooselanguagehead'] = 'Odaberite jezik';
|
||||
$string['chooselanguagesub'] = 'Molimo odaberite jezik instalacije. Ovaj jezik će biti korišten kao zadani jezik sustava, a kasnije to možete lagano promijeniti.';
|
||||
$string['clialreadyconfigured'] = 'Datoteka config.php već postoji. Upotrijebite naredbu admin/cli/install_database.php ako želite nastaviti instalaciju.';
|
||||
@@ -68,7 +68,7 @@ $string['pathsroparentdataroot'] = 'Nije moguće zapisivati podatke u nadređenu
|
||||
$string['pathssubadmindir'] = 'Manji broj webhosting tvrtki koristi /admin kao posebni URL za Vaš pristup upravljanju vašim hosting paketom. Nažalost, to rezultira konfliktom sa standardnom lokacijom za Moodle administratorsku stranicu. Navedenu lokaciju unutar Moodle sustava možete preimenovati. Na primjer: <br /> <br /><em>moodleadmin</em><br /> <br />
|
||||
Ovo će promijeniti administratorsku poveznicu na Moodle sustavu u novu vrijednost.';
|
||||
$string['pathssubdataroot'] = 'Mora postojati mapa u koju Moodle može pohraniti prenešene datoteke. Korisnik pod kojim je pokrenut web server (obično \'nobody\' ili \'apache\') bi morao imati mogućnost čitanja/pisanja podataka u toj mapi, ali oni ne bi trebali biti dostupni direktno preko weba. Instalacijska skripta će pokušati stvoriti navedenu mapu ako ista ne postoji.';
|
||||
$string['pathssubdirroot'] = 'Puna putanja (PATH) do Moodle instalacije.';
|
||||
$string['pathssubdirroot'] = '<p>Puna putanja (PATH) do Moodle instalacije.</p>';
|
||||
$string['pathssubwwwroot'] = 'Unesite punu web adresu putem koje će se pristupati vašem Moodle sustavu.
|
||||
Moodle sustavu NIJE MOGUĆE pristupiti preko više URL-ova, odaberite onaj koji vam najviše odgovara.
|
||||
Ako vaš poslužitelj ima višestruke javne adrese, onda morate postaviti tzv. permanent redirect na sve osim ove adrese.
|
||||
@@ -85,5 +85,5 @@ $string['welcomep30'] = 'Ova inačica <strong>{$a->installername}</strong> uklju
|
||||
$string['welcomep40'] = 'Ovaj paket obuhvaća i <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
|
||||
$string['welcomep50'] = 'Korištenje svih aplikacija u ovom paketu je uređeno licencama pod kojima su izdane. Cijeli paket <strong>{$a->installername}</strong> je rješenje <a href="http://www.opensource.org/docs/definition_plain.html">otvorenog koda</a> distribuirano pod <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a> licencom.';
|
||||
$string['welcomep60'] = 'Sljedeće stranice će vas voditi kroz postupak instalacije i podešavanja sustava <em>Moodle</em> na poslužitelju. Možete prihvatiti zadane postavke ili promijeniti iste sukladno vašim potrebama.';
|
||||
$string['welcomep70'] = 'Kliknite na "Next" gumb kako bi započeli <strong>Moodle</strong> instalaciju.';
|
||||
$string['welcomep70'] = 'Kliknite na "Next" gumb kako biste započeli <strong>Moodle</strong> instalaciju.';
|
||||
$string['wwwroot'] = 'Web adresa';
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?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['parentlanguage'] = 'it';
|
||||
$string['thislanguage'] = 'Italiano per Workplace';
|
||||
@@ -73,9 +73,9 @@ $string['pathssubdataroot'] = '<p>ユーザによってアップロードされ
|
||||
<p>ウェブからは直接アクセスできないようにしてください。</p>
|
||||
<p>現在ディレクトリが存在しない場合、インストレーションプロセスは作成を試みます。</p';
|
||||
$string['pathssubdirroot'] = '<p>Moodleコードを含むディレクトリに関するフルパスです。</p>';
|
||||
$string['pathssubwwwroot'] = '<p>Moodleにアクセスすることのできるフルウェブアドレスです。例えばユーザがブラウザのアドレスバーに入力してMoodleにアクセスするためのアドレスです。</p>
|
||||
$string['pathssubwwwroot'] = '<p>Moodleにアクセスできるフルウェブアドレスです。例えばユーザがブラウザのアドレスバーに入力してMoodleにアクセスするためのアドレスです。</p>
|
||||
|
||||
<p>複数アドレスを使用してMoodleにアクセスすることはできません。あなたのサイトに複数アドレスからアクセスできる場合、最も簡単なアドレスを選択して、すべてのアドレスにパーマネントリダイレクトを設定してください。</p>
|
||||
<p>複数アドレスを使用したMoodleへのアクセスはできません。あなたのサイトに複数アドレスからアクセスできる場合、最も簡単なアドレスを選択して、すべてのアドレスにパーマネントリダイレクトを設定してください。</p>
|
||||
|
||||
<p>あなたのサイトにインターネットおよび内部ネットワーク (イントラネットと呼ばれます) からアクセスできる場合、ここではパブリックアドレスを使用してください。</p>
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?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['parentlanguage'] = 'no';
|
||||
$string['thislanguage'] = 'Norsk Workplace';
|
||||
@@ -33,10 +33,10 @@ defined('MOODLE_INTERNAL') || die();
|
||||
$string['clianswerno'] = 'n';
|
||||
$string['cliansweryes'] = 's';
|
||||
$string['cliincorrectvalueerror'] = 'Erro: o valor "{$a->value}" não é permitido para a opção "{$a->option}"';
|
||||
$string['cliincorrectvalueretry'] = 'Valor incorreto, por favor tente novamente';
|
||||
$string['cliincorrectvalueretry'] = 'Valor incorreto, por favor, tente novamente';
|
||||
$string['clitypevalue'] = 'introduza valor';
|
||||
$string['clitypevaluedefault'] = 'introduza valor, pressione a tecla Enter para usar o valor predefinido ({$a})';
|
||||
$string['cliunknowoption'] = 'Opções desconhecidas: {$a}. Por favor use a opção --help';
|
||||
$string['clitypevaluedefault'] = 'introduza valor, pressione a tecla \'Enter\' para usar o valor predefinido ({$a})';
|
||||
$string['cliunknowoption'] = 'Opções desconhecidas: {$a}. Por favor, use a opção --help';
|
||||
$string['cliyesnoprompt'] = 'digite s (para sim) ou n (para não)';
|
||||
$string['environmentrequireinstall'] = 'deve estar instalada e ativa';
|
||||
$string['environmentrequireversion'] = 'é requerida a versão {$a->needed} e está a correr a versão {$a->current}';
|
||||
|
||||
@@ -49,7 +49,7 @@ $string['environmentsub2'] = 'Cada nova versão do Moodle tem pré-requisitos m
|
||||
$string['errorsinenvironment'] = 'A verificação do sistema falhou!';
|
||||
$string['installation'] = 'Instalação';
|
||||
$string['langdownloaderror'] = 'Não foi possível descarregar o idioma <b>{$a}</b> . O processo de instalação continuará em Inglês.';
|
||||
$string['memorylimithelp'] = '<p>O limite de memória para o PHP definido atualmente no servidor é <b>{$a}</b>.</p><p>Um número elevado de módulos em utilização ou de utilizadores registados pode fazer com que o Moodle apresente problemas de falta de memória.</p><p>É recomendado que o PHP seja configurado com um limite de memória de pelo menos 40MB. Esta configuração pode ser definida de diversas formas:</p><ol><li>Compilação do PHP com o parâmetro <b>--enable-memory-limit</b>. Esta definição permitirá ao próprio Moodle definir o valor a utilizar.</li><li>Alteração do parâmetro <b>memory_limit</b> no ficheiro de configuração do PHP para um valor igual ou superior a 40MB.</li><li>Criação de um ficheiro <b>.htaccess</b> na raiz da pasta do Moodle com a linha <b>php_value memory_limit 40M</b><p>ATENÇÃO: Em alguns servidores esta configuração impedirá o funcionamento de <b>todas</b> as páginas PHP. Nestes casos, não poderá ser utilizado o ficheiro <b>.htaccess</b>.</p></li></ol>';
|
||||
$string['memorylimithelp'] = '<p>O limite de memória para o PHP definido atualmente no servidor é <b>{$a}</b>.</p><p>Um número elevado de módulos em utilização ou de utilizadores registados pode fazer com que o Moodle apresente problemas de falta de memória.</p><p>É recomendado que o PHP seja configurado com um limite de memória de pelo menos 40MB. Esta configuração pode ser definida de diversas formas:</p><ol><li>Compilação do PHP com o parâmetro <b>--enable-memory-limit</b>. Esta definição permitirá ao próprio Moodle definir o valor a utilizar.</li><li>Alteração do parâmetro <b>memory_limit</b> no ficheiro de configuração do PHP para um valor igual ou superior a 40MB.</li><li>Criação de um ficheiro <b>.htaccess</b> na raiz da pasta do Moodle com a linha <b>php_value memory_limit 40M</b><p><strong>Atenção</strong>: Em alguns servidores esta configuração impedirá o funcionamento de <b>todas</b> as páginas PHP. Nestes casos, não poderá ser utilizado o ficheiro <b>.htaccess</b>.</p></li></ol>';
|
||||
$string['paths'] = 'Caminhos';
|
||||
$string['pathserrcreatedataroot'] = 'O programa de instalação não conseguiu criar a pasta de dados <b>{$a->dataroot}</b>.';
|
||||
$string['pathshead'] = 'Confirmar caminhos';
|
||||
|
||||
@@ -54,6 +54,7 @@ $string['paths'] = 'Căi';
|
||||
$string['pathserrcreatedataroot'] = 'Data directory ({$a->dataroot}) nu poate fi creat de către installer.';
|
||||
$string['pathshead'] = 'Confirmare căi';
|
||||
$string['pathsrodataroot'] = 'Directorul dataroot nu poate fi scris.';
|
||||
$string['pathsroparentdataroot'] = 'Directorul parent ({$a->parent}) nu poate fi scris. Directorul data ({$a->dataroot}) nu poate fi creat de persoana care îl instalează.';
|
||||
$string['pathssubdataroot'] = '<p>Un director unde Moodle va stoca tot conținutul unui fișier încărcat de către utilizatori.</p>
|
||||
<p>Acest director trebuie să poată fi citit și scris de către utilizatrii serverului web (de obicei \'www-data\', \'nobody\', or \'apache\').</p>
|
||||
<p>Nu trebuie să fie direct accesibil de pe web.</p>
|
||||
|
||||
@@ -39,3 +39,4 @@ $string['reseteveryonesprofile'] = 'Reset profile for all users';
|
||||
$string['resetpage'] = 'Reset page to default';
|
||||
$string['reseterror'] = 'There was an error resetting your page';
|
||||
$string['privacy:metadata:core_my:preference:user_home_page_preference'] = 'The user home page preference configured for the Dashboard page.';
|
||||
$string['unabletoaccess'] = 'As a security precaution, you may not access another user\'s dashboard';
|
||||
|
||||
+43
-6
@@ -330,6 +330,7 @@ function get_role_definitions_uncached(array $roleids) {
|
||||
$sql = "SELECT ctx.path, rc.roleid, rc.capability, rc.permission
|
||||
FROM {role_capabilities} rc
|
||||
JOIN {context} ctx ON rc.contextid = ctx.id
|
||||
JOIN {capabilities} cap ON rc.capability = cap.name
|
||||
WHERE rc.roleid $sql
|
||||
ORDER BY ctx.path, rc.roleid, rc.capability";
|
||||
$rs = $DB->get_recordset_sql($sql, $params);
|
||||
@@ -1144,7 +1145,17 @@ function is_safe_capability($capability) {
|
||||
*/
|
||||
function get_local_override($roleid, $contextid, $capability) {
|
||||
global $DB;
|
||||
return $DB->get_record('role_capabilities', array('roleid'=>$roleid, 'capability'=>$capability, 'contextid'=>$contextid));
|
||||
|
||||
return $DB->get_record_sql("
|
||||
SELECT rc.*
|
||||
FROM {role_capabilities} rc
|
||||
JOIN {capability} cap ON rc.capability = cap.name
|
||||
WHERE rc.roleid = :roleid AND rc.capability = :capability AND rc.contextid = :contextid", [
|
||||
'roleid' => $roleid,
|
||||
'contextid' => $contextid,
|
||||
'capability' => $capability,
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1290,6 +1301,11 @@ function assign_capability($capability, $permission, $roleid, $contextid, $overw
|
||||
$context = context::instance_by_id($contextid);
|
||||
}
|
||||
|
||||
// Capability must exist.
|
||||
if (!$capinfo = get_capability_info($capability)) {
|
||||
throw new coding_exception("Capability '{$capability}' was not found! This has to be fixed in code.");
|
||||
}
|
||||
|
||||
if (empty($permission) || $permission == CAP_INHERIT) { // if permission is not set
|
||||
unassign_capability($capability, $roleid, $context->id);
|
||||
return true;
|
||||
@@ -1337,6 +1353,11 @@ function assign_capability($capability, $permission, $roleid, $contextid, $overw
|
||||
function unassign_capability($capability, $roleid, $contextid = null) {
|
||||
global $DB;
|
||||
|
||||
// Capability must exist.
|
||||
if (!$capinfo = get_capability_info($capability)) {
|
||||
throw new coding_exception("Capability '{$capability}' was not found! This has to be fixed in code.");
|
||||
}
|
||||
|
||||
if (!empty($contextid)) {
|
||||
if ($contextid instanceof context) {
|
||||
$context = $contextid;
|
||||
@@ -1391,6 +1412,7 @@ function get_roles_with_capability($capability, $permission = null, $context = n
|
||||
FROM {role} r
|
||||
WHERE r.id IN (SELECT rc.roleid
|
||||
FROM {role_capabilities} rc
|
||||
JOIN {capabilities} cap ON rc.capability = cap.name
|
||||
WHERE rc.capability = :capname
|
||||
$contextsql
|
||||
$permissionsql)";
|
||||
@@ -2177,6 +2199,9 @@ function update_capabilities($component = 'moodle') {
|
||||
|
||||
$DB->insert_record('capabilities', $capability, false);
|
||||
|
||||
// Flush the cached, as we have changed DB.
|
||||
cache::make('core', 'capabilities')->delete('core_capabilities');
|
||||
|
||||
if (isset($capdef['clonepermissionsfrom']) && in_array($capdef['clonepermissionsfrom'], $existingcaps)){
|
||||
if ($rolecapabilities = $DB->get_records('role_capabilities', array('capability'=>$capdef['clonepermissionsfrom']))){
|
||||
foreach ($rolecapabilities as $rolecapability){
|
||||
@@ -2230,9 +2255,6 @@ function capabilities_cleanup($component, $newcapdef = null) {
|
||||
if (empty($newcapdef) ||
|
||||
array_key_exists($cachedcap->name, $newcapdef) === false) {
|
||||
|
||||
// Remove from capabilities cache.
|
||||
$DB->delete_records('capabilities', array('name'=>$cachedcap->name));
|
||||
$removedcount++;
|
||||
// Delete from roles.
|
||||
if ($roles = get_roles_with_capability($cachedcap->name)) {
|
||||
foreach($roles as $role) {
|
||||
@@ -2241,6 +2263,13 @@ function capabilities_cleanup($component, $newcapdef = null) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from role_capabilities for any old ones.
|
||||
$DB->delete_records('role_capabilities', array('capability' => $cachedcap->name));
|
||||
|
||||
// Remove from capabilities cache.
|
||||
$DB->delete_records('capabilities', array('name' => $cachedcap->name));
|
||||
$removedcount++;
|
||||
} // End if.
|
||||
}
|
||||
}
|
||||
@@ -2305,10 +2334,12 @@ function role_context_capabilities($roleid, context $context, $cap = '') {
|
||||
}
|
||||
|
||||
$sql = "SELECT rc.*
|
||||
FROM {role_capabilities} rc, {context} c
|
||||
FROM {role_capabilities} rc
|
||||
JOIN {context} c ON rc.contextid = c.id
|
||||
JOIN {capabilities} cap ON rc.capability = cap.name
|
||||
WHERE rc.contextid in $contexts
|
||||
AND rc.roleid = ?
|
||||
AND rc.contextid = c.id $search
|
||||
$search
|
||||
ORDER BY c.contextlevel DESC, rc.capability DESC";
|
||||
|
||||
$capabilities = array();
|
||||
@@ -3024,6 +3055,7 @@ function get_switchable_roles(context $context) {
|
||||
SELECT r.id, r.name, r.shortname, rn.name AS coursealias
|
||||
FROM (SELECT DISTINCT rc.roleid
|
||||
FROM {role_capabilities} rc
|
||||
|
||||
$extrajoins
|
||||
$extrawhere) idlist
|
||||
JOIN {role} r ON r.id = idlist.roleid
|
||||
@@ -3286,6 +3318,7 @@ function get_users_by_capability(context $context, $capability, $fields = '', $s
|
||||
$params = array_merge($params, $params2);
|
||||
$sql = "SELECT rc.id, rc.roleid, rc.permission, rc.capability, ctx.path
|
||||
FROM {role_capabilities} rc
|
||||
JOIN {capabilities} cap ON rc.capability = cap.name
|
||||
JOIN {context} ctx on rc.contextid = ctx.id
|
||||
WHERE rc.contextid $incontexts AND rc.capability $incaps";
|
||||
|
||||
@@ -4381,6 +4414,7 @@ function get_roles_with_cap_in_context($context, $capability) {
|
||||
$sql = "SELECT rc.id, rc.roleid, rc.permission, ctx.depth
|
||||
FROM {role_capabilities} rc
|
||||
JOIN {context} ctx ON ctx.id = rc.contextid
|
||||
JOIN {capabilities} cap ON rc.capability = cap.name
|
||||
WHERE rc.capability = :cap AND ctx.id IN ($ctxids)
|
||||
ORDER BY rc.roleid ASC, ctx.depth DESC";
|
||||
$params = array('cap'=>$capability);
|
||||
@@ -4500,6 +4534,7 @@ function prohibit_is_removable($roleid, context $context, $capability) {
|
||||
$sql = "SELECT ctx.id
|
||||
FROM {role_capabilities} rc
|
||||
JOIN {context} ctx ON ctx.id = rc.contextid
|
||||
JOIN {capabilities} cap ON rc.capability = cap.name
|
||||
WHERE rc.roleid = :roleid AND rc.permission = :prohibit AND rc.capability = :cap AND ctx.id IN ($ctxids)
|
||||
ORDER BY ctx.depth DESC";
|
||||
|
||||
@@ -4543,6 +4578,7 @@ function role_change_permission($roleid, $context, $capname, $permission) {
|
||||
$sql = "SELECT ctx.id, rc.permission, ctx.depth
|
||||
FROM {role_capabilities} rc
|
||||
JOIN {context} ctx ON ctx.id = rc.contextid
|
||||
JOIN {capabilities} cap ON rc.capability = cap.name
|
||||
WHERE rc.roleid = :roleid AND rc.capability = :cap AND ctx.id IN ($ctxids)
|
||||
ORDER BY ctx.depth DESC";
|
||||
|
||||
@@ -7224,6 +7260,7 @@ function get_with_capability_join(context $context, $capability, $useridcolumn)
|
||||
$defs = array();
|
||||
$sql = "SELECT rc.id, rc.roleid, rc.permission, ctx.path
|
||||
FROM {role_capabilities} rc
|
||||
JOIN {capabilities} cap ON rc.capability = cap.name
|
||||
JOIN {context} ctx on rc.contextid = ctx.id
|
||||
WHERE rc.contextid $incontexts AND rc.capability $incaps";
|
||||
$rcs = $DB->get_records_sql($sql, array_merge($cparams, $capsparams));
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
define(["core/config"],function(a){var b=function(b){this.storage=b,this.supported=this.detectSupport(),this.hashSource=a.wwwroot+"/"+a.jsrev,this.hash=this.hashString(this.hashSource),this.prefix=this.hash+"/",this.jsrevPrefix=this.hash+"/jsrev"};return b.prototype.detectSupport=function(){if(a.jsrev==-1)return!1;if("undefined"==typeof this.storage)return!1;var b="test";try{return null!==this.storage&&(this.storage.setItem(b,"1"),this.storage.removeItem(b),!0)}catch(c){return!1}},b.prototype.prefixKey=function(a){return this.prefix+a},b.prototype.validateCache=function(){var b=this.storage.getItem(this.jsrevPrefix);if(null===b)return void this.storage.setItem(this.jsrevPrefix,a.jsrev);var c=a.jsrev;c!=b&&(this.storage.clear(),this.storage.setItem(this.jsrevPrefix,a.jsrev))},b.prototype.hashString=function(a){var b,c,d,e=0;if(0===a.length)return e;for(b=0,d=a.length;b<d;b++)c=a.charCodeAt(b),e=(e<<5)-e+c,e|=0;return e},b.prototype.get=function(a){return!!this.supported&&(this.validateCache(),a=this.prefixKey(a),this.storage.getItem(a))},b.prototype.set=function(a,b){if(!this.supported)return!1;this.validateCache(),a=this.prefixKey(a);try{this.storage.setItem(a,b)}catch(c){return!1}return!0},b});
|
||||
define(["core/config"],function(a){var b=function(b){this.storage=b,this.supported=this.detectSupport(),this.hashSource=a.wwwroot+"/"+a.jsrev,this.hash=this.hashString(this.hashSource),this.prefix=this.hash+"/",this.jsrevPrefix=this.hashString(a.wwwroot)+"/jsrev",this.validateCache()};return b.prototype.detectSupport=function(){if(a.jsrev==-1)return!1;if("undefined"==typeof this.storage)return!1;var b="test";try{return null!==this.storage&&(this.storage.setItem(b,"1"),this.storage.removeItem(b),!0)}catch(c){return!1}},b.prototype.prefixKey=function(a){return this.prefix+a},b.prototype.validateCache=function(){var b=this.storage.getItem(this.jsrevPrefix);if(null===b)return void this.storage.setItem(this.jsrevPrefix,a.jsrev);var c=a.jsrev;c!=b&&(this.storage.clear(),this.storage.setItem(this.jsrevPrefix,a.jsrev))},b.prototype.hashString=function(a){var b,c,d,e=0;if(0===a.length)return e;for(b=0,d=a.length;b<d;b++)c=a.charCodeAt(b),e=(e<<5)-e+c,e|=0;return e},b.prototype.get=function(a){return!!this.supported&&(a=this.prefixKey(a),this.storage.getItem(a))},b.prototype.set=function(a,b){if(!this.supported)return!1;a=this.prefixKey(a);try{this.storage.setItem(a,b)}catch(c){return!1}return!0},b});
|
||||
@@ -35,7 +35,8 @@ define(['core/config'], function(config) {
|
||||
this.hashSource = config.wwwroot + '/' + config.jsrev;
|
||||
this.hash = this.hashString(this.hashSource);
|
||||
this.prefix = this.hash + '/';
|
||||
this.jsrevPrefix = this.hash + '/jsrev';
|
||||
this.jsrevPrefix = this.hashString(config.wwwroot) + '/jsrev';
|
||||
this.validateCache();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -89,8 +90,8 @@ define(['core/config'], function(config) {
|
||||
this.storage.setItem(this.jsrevPrefix, config.jsrev);
|
||||
return;
|
||||
}
|
||||
var moodleVersion = config.jsrev;
|
||||
|
||||
var moodleVersion = config.jsrev;
|
||||
if (moodleVersion != cacheVersion) {
|
||||
this.storage.clear();
|
||||
this.storage.setItem(this.jsrevPrefix, config.jsrev);
|
||||
@@ -132,7 +133,6 @@ define(['core/config'], function(config) {
|
||||
if (!this.supported) {
|
||||
return false;
|
||||
}
|
||||
this.validateCache();
|
||||
key = this.prefixKey(key);
|
||||
|
||||
return this.storage.getItem(key);
|
||||
@@ -150,7 +150,6 @@ define(['core/config'], function(config) {
|
||||
if (!this.supported) {
|
||||
return false;
|
||||
}
|
||||
this.validateCache();
|
||||
key = this.prefixKey(key);
|
||||
// This can throw exceptions when the storage limit is reached.
|
||||
try {
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace core\message\inbound;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
|
||||
/**
|
||||
* A Handler to store attachments sent in e-mails as private files.
|
||||
*
|
||||
@@ -98,15 +100,17 @@ class private_files_handler extends handler {
|
||||
$uploadedfiles = array();
|
||||
$failedfiles = array();
|
||||
|
||||
$usedspace = file_get_user_used_space();
|
||||
$fs = get_file_storage();
|
||||
foreach ($data->attachments as $attachmenttype => $attachments) {
|
||||
foreach ($attachments as $attachment) {
|
||||
mtrace("--- Processing attachment '{$attachment->filename}'");
|
||||
|
||||
if (file_is_draft_area_limit_reached($itemid, $maxbytes, $attachment->filesize)) {
|
||||
if ($maxbytes != USER_CAN_IGNORE_FILE_SIZE_LIMITS &&
|
||||
($attachment->filesize + $usedspace) > $maxbytes) {
|
||||
// The user quota will be exceeded if this file is included.
|
||||
$skippedfiles[] = $attachment;
|
||||
mtrace("---- Skipping attacment. User will be over quota.");
|
||||
mtrace("---- Skipping attachment. User will be over quota.");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -132,8 +136,9 @@ class private_files_handler extends handler {
|
||||
// File created successfully.
|
||||
mtrace("---- File uploaded successfully as {$record->filename}.");
|
||||
$uploadedfiles[] = $attachment;
|
||||
$usedspace += $attachment->filesize;
|
||||
} else {
|
||||
mtrace("---- Skipping attacment. Unknown failure during creation.");
|
||||
mtrace("---- Skipping attachment. Unknown failure during creation.");
|
||||
$failedfiles[] = $attachment;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,23 +118,30 @@ class provider implements
|
||||
|
||||
$user = $contextlist->get_user();
|
||||
|
||||
// Firstly export all autosave records from all contexts in the list owned by the given user.
|
||||
|
||||
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
|
||||
$contextparams['userid'] = $user->id;
|
||||
|
||||
$sql = "SELECT *
|
||||
FROM {editor_atto_autosave}
|
||||
WHERE userid = :userid AND contextid {$contextsql}";
|
||||
|
||||
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
|
||||
$contextparams['userid'] = $contextlist->get_user()->id;
|
||||
$autosaves = $DB->get_recordset_sql($sql, $contextparams);
|
||||
self::export_autosaves($user, $autosaves);
|
||||
|
||||
$sql = "SELECT *
|
||||
FROM {editor_atto_autosave}
|
||||
JOIN {context} c ON c.id = eas.contextid
|
||||
WHERE c.id {$contextsql} AND contextlevel = :contextuser AND c.instanceid = :userid";
|
||||
// Additionally export all eventual records in the given user's context regardless the actual owner.
|
||||
// We still consider them to be the user's personal data even when edited by someone else.
|
||||
|
||||
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
|
||||
$contextparams['userid'] = $contextlist->get_user()->id;
|
||||
$contextparams['userid'] = $user->id;
|
||||
$contextparams['contextuser'] = CONTEXT_USER;
|
||||
|
||||
$sql = "SELECT eas.*
|
||||
FROM {editor_atto_autosave} eas
|
||||
JOIN {context} c ON c.id = eas.contextid
|
||||
WHERE c.id {$contextsql} AND c.contextlevel = :contextuser AND c.instanceid = :userid";
|
||||
|
||||
$autosaves = $DB->get_recordset_sql($sql, $contextparams);
|
||||
self::export_autosaves($user, $autosaves);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ Some changes from the upstream version have been made:
|
||||
* Added context type property for Context class
|
||||
* Set context type if 'context_type' parameter was submitted through POST
|
||||
* Do not require tool_consumer_instance_guid
|
||||
These changes can be reverted once the following pull requests/issues have been integrated upstream:
|
||||
* https://github.com/IMSGlobal/LTI-Tool-Provider-Library-PHP/pull/10/commits/a9a1641f1a593eba4638133245c21d9ad47d8680
|
||||
* https://github.com/IMSGlobal/LTI-Tool-Provider-Library-PHP/pull/11/commits/0bae60389bd020a02be5554516b86336e651e237
|
||||
* https://github.com/IMSGlobal/LTI-Tool-Provider-Library-PHP/issues/19
|
||||
* Prevent modification of the request to the provider
|
||||
These changes can be reverted once the following pull requests have been integrated upstream:
|
||||
* https://github.com/IMSGlobal/LTI-Tool-Provider-Library-PHP/pull/10
|
||||
* https://github.com/IMSGlobal/LTI-Tool-Provider-Library-PHP/pull/11
|
||||
* https://github.com/IMSGlobal/LTI-Tool-Provider-Library-PHP/pull/47
|
||||
* https://github.com/IMSGlobal/LTI-Tool-Provider-Library-PHP/pull/48
|
||||
|
||||
It is recommended by upstream to install depdencies via composer - but the composer installation is bundled
|
||||
with an autoloader so it's better to do it manually.
|
||||
|
||||
@@ -17,7 +17,6 @@ class OAuthRequest {
|
||||
// for debug purposes
|
||||
public $base_string;
|
||||
public static $version = '1.0';
|
||||
public static $POST_INPUT = 'php://input';
|
||||
|
||||
function __construct($http_method, $http_url, $parameters = null) {
|
||||
|
||||
@@ -60,22 +59,15 @@ class OAuthRequest {
|
||||
$parameters = array();
|
||||
}
|
||||
|
||||
// It's a POST request of the proper content-type, so parse POST
|
||||
// parameters and add those overriding any duplicates from GET
|
||||
if ($http_method == "POST"
|
||||
&& isset($request_headers['Content-Type'])
|
||||
&& strstr($request_headers['Content-Type'], 'application/x-www-form-urlencoded')) {
|
||||
$post_data = OAuthUtil::parse_parameters(file_get_contents(self::$POST_INPUT));
|
||||
$parameters = array_merge($parameters, $post_data);
|
||||
}
|
||||
|
||||
// We have a Authorization-header with OAuth data. Parse the header
|
||||
// and add those overriding any duplicates from GET or POST
|
||||
// We have a Authorization-header with OAuth data. Parse the header and add those.
|
||||
if (isset($request_headers['Authorization']) && substr($request_headers['Authorization'], 0, 6) == 'OAuth ') {
|
||||
$header_parameters = OAuthUtil::split_header($request_headers['Authorization']);
|
||||
$parameters = array_merge($parameters, $header_parameters);
|
||||
}
|
||||
|
||||
// If there are parameters in $_POST, these are likely what will be used. Therefore, they should be considered
|
||||
// the final value in the case of any duplicates from sources parsed above.
|
||||
$parameters = array_merge($parameters, $_POST);
|
||||
}
|
||||
|
||||
return new OAuthRequest($http_method, $http_url, $parameters);
|
||||
|
||||
@@ -1696,6 +1696,101 @@ class core_accesslib_testcase extends advanced_testcase {
|
||||
$this->assertFalse(has_all_capabilities($sca, $coursecontext, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that assigning a fake cap does not return.
|
||||
*/
|
||||
public function test_fake_capability() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
|
||||
$teacher = $this->getDataGenerator()->create_user();
|
||||
|
||||
$fakecapname = 'moodle/fake:capability';
|
||||
|
||||
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
||||
$admin = $DB->get_record('user', array('username' => 'admin'));
|
||||
|
||||
// Test a capability which does not exist.
|
||||
// Note: Do not use assign_capability because it will not allow fake caps.
|
||||
$DB->insert_record('role_capabilities', (object) [
|
||||
'contextid' => $coursecontext->id,
|
||||
'roleid' => $teacherrole->id,
|
||||
'capability' => $fakecapname,
|
||||
'permission' => CAP_ALLOW,
|
||||
'timemodified' => time(),
|
||||
'modifierid' => 0,
|
||||
]);
|
||||
|
||||
// Check `has_capability`.
|
||||
$this->assertFalse(has_capability($fakecapname, $coursecontext, $teacher));
|
||||
$this->assertDebuggingCalled("Capability \"{$fakecapname}\" was not found! This has to be fixed in code.");
|
||||
$this->assertFalse(has_capability($fakecapname, $coursecontext, $admin));
|
||||
$this->assertDebuggingCalled("Capability \"{$fakecapname}\" was not found! This has to be fixed in code.");
|
||||
|
||||
// Check `get_with_capability_sql` (with uses `get_with_capability_join`).
|
||||
list($sql, $params) = get_with_capability_sql($coursecontext, $fakecapname);
|
||||
$users = $DB->get_records_sql($sql, $params);
|
||||
|
||||
$this->assertFalse(array_key_exists($teacher->id, $users));
|
||||
$this->assertFalse(array_key_exists($admin->id, $users));
|
||||
|
||||
// Check `get_users_by_capability`.
|
||||
$users = get_users_by_capability($coursecontext, $fakecapname);
|
||||
|
||||
$this->assertFalse(array_key_exists($teacher->id, $users));
|
||||
$this->assertFalse(array_key_exists($admin->id, $users));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that assigning a fake cap does not return.
|
||||
*/
|
||||
public function test_fake_capability_assign() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
|
||||
$teacher = $this->getDataGenerator()->create_user();
|
||||
|
||||
$capability = 'moodle/fake:capability';
|
||||
|
||||
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
||||
$admin = $DB->get_record('user', array('username' => 'admin'));
|
||||
|
||||
$this->expectException('coding_exception');
|
||||
$this->expectExceptionMessage("Capability '{$capability}' was not found! This has to be fixed in code.");
|
||||
assign_capability($capability, CAP_ALLOW, $teacherrole->id, $coursecontext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that assigning a fake cap does not return.
|
||||
*/
|
||||
public function test_fake_capability_unassign() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'), '*', MUST_EXIST);
|
||||
$teacher = $this->getDataGenerator()->create_user();
|
||||
|
||||
$capability = 'moodle/fake:capability';
|
||||
|
||||
role_assign($teacherrole->id, $teacher->id, $coursecontext);
|
||||
$admin = $DB->get_record('user', array('username' => 'admin'));
|
||||
|
||||
$this->expectException('coding_exception');
|
||||
$this->expectExceptionMessage("Capability '{$capability}' was not found! This has to be fixed in code.");
|
||||
unassign_capability($capability, CAP_ALLOW, $teacherrole->id, $coursecontext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the caching in get_role_definitions() and get_role_definitions_uncached()
|
||||
* works as intended.
|
||||
@@ -2815,7 +2910,7 @@ class core_accesslib_testcase extends advanced_testcase {
|
||||
assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagepagecontext, true);
|
||||
assign_capability('mod/page:view', CAP_PREVENT, $allroles['guest'], $frontpagepagecontext, true);
|
||||
assign_capability('mod/page:view', CAP_ALLOW, $allroles['user'], $frontpagepagecontext, true);
|
||||
assign_capability('moodle/page:view', CAP_ALLOW, $allroles['student'], $frontpagepagecontext, true);
|
||||
assign_capability('mod/page:view', CAP_ALLOW, $allroles['student'], $frontpagepagecontext, true);
|
||||
|
||||
assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultuserroleid, $frontpagecontext, true);
|
||||
assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext, true);
|
||||
@@ -3443,10 +3538,10 @@ class core_accesslib_testcase extends advanced_testcase {
|
||||
$this->assertFalse(array_key_exists($guest->id, $users));
|
||||
|
||||
// Test role override.
|
||||
assign_capability('moodle/site:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext, true);
|
||||
assign_capability('moodle/site:backupcourse', CAP_ALLOW, $studentrole->id, $coursecontext, true);
|
||||
assign_capability('moodle/backup:backupcourse', CAP_PROHIBIT, $teacherrole->id, $coursecontext, true);
|
||||
assign_capability('moodle/backup:backupcourse', CAP_ALLOW, $studentrole->id, $coursecontext, true);
|
||||
|
||||
list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/site:backupcourse');
|
||||
list($sql, $params) = get_with_capability_sql($coursecontext, 'moodle/backup:backupcourse');
|
||||
$users = $DB->get_records_sql($sql, $params);
|
||||
|
||||
$this->assertFalse(array_key_exists($teacher->id, $users));
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
This files describes API changes in core libraries and APIs,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 3.4.6 ===
|
||||
|
||||
* The \core_rating provider's get_sql_join function now accepts an optional $innerjoin parameter.
|
||||
It is recommended that privacy providers using this function call rewrite any long query into a number of separate
|
||||
calls to add_from_sql for improved performance, and that the new argument is used.
|
||||
This will allow queries to remain backwards-compatible with older versions of Moodle but will have significantly better performance in version supporting the innerjoin parameter.
|
||||
|
||||
=== 3.4.5 ===
|
||||
|
||||
* Custom AJAX handlers for the form autocomplete fields can now optionally return string in their processResults()
|
||||
|
||||
@@ -103,29 +103,57 @@ class provider implements
|
||||
* @return contextlist the list of contexts containing user info for the user.
|
||||
*/
|
||||
public static function get_contexts_for_userid(int $userid) : contextlist {
|
||||
// Fetch all data records.
|
||||
$contextlist = new contextlist();
|
||||
|
||||
// Fetch all data records that the user rote.
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
INNER JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
INNER JOIN {data} d ON d.id = cm.instance
|
||||
INNER JOIN {data_records} dr ON dr.dataid = d.id
|
||||
LEFT JOIN {comments} com ON com.commentarea=:commentarea and com.itemid = dr.id AND com.userid = :userid1
|
||||
LEFT JOIN {rating} r ON r.contextid = c.id AND r.itemid = dr.id AND r.component = :moddata
|
||||
AND r.ratingarea = :ratingarea AND r.userid = :userid2
|
||||
WHERE dr.userid = :userid OR com.id IS NOT NULL OR r.id IS NOT NULL";
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {data} d ON d.id = cm.instance
|
||||
JOIN {data_records} dr ON dr.dataid = d.id
|
||||
WHERE dr.userid = :userid";
|
||||
|
||||
$params = [
|
||||
'modname' => 'data',
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'modname' => 'data',
|
||||
'userid' => $userid,
|
||||
'userid1' => $userid,
|
||||
'userid2' => $userid,
|
||||
'commentarea' => 'database_entry',
|
||||
'moddata' => 'mod_data',
|
||||
'ratingarea' => 'entry',
|
||||
];
|
||||
$contextlist = new contextlist();
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
// Fetch contexts where the user commented.
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {data} d ON d.id = cm.instance
|
||||
JOIN {data_records} dr ON dr.dataid = d.id
|
||||
JOIN {comments} com ON com.commentarea = :commentarea and com.itemid = dr.id
|
||||
WHERE com.userid = :userid";
|
||||
|
||||
$params = [
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'modname' => 'data',
|
||||
'commentarea' => 'database_entry',
|
||||
'userid' => $userid,
|
||||
];
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
// Fetch all data records.
|
||||
$ratingquery = \core_rating\privacy\provider::get_sql_join('r', 'mod_data', 'entry', 'dr.id', $userid, true);
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {data} d ON d.id = cm.instance
|
||||
JOIN {data_records} dr ON dr.dataid = d.id
|
||||
{$ratingquery->join}
|
||||
WHERE {$ratingquery->userwhere}";
|
||||
|
||||
$params = [
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'modname' => 'data',
|
||||
] + $ratingquery->params;
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
return $contextlist;
|
||||
|
||||
@@ -274,7 +274,7 @@ class mod_data_external_testcase extends externallib_advanced_testcase {
|
||||
public function test_view_database_no_capabilities() {
|
||||
// Test user with no capabilities.
|
||||
// We need a explicit prohibit since this capability is allowed for students by default.
|
||||
assign_capability('mod/data:viewpage', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
|
||||
assign_capability('mod/data:view', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
|
||||
accesslib_clear_all_caches_for_unit_testing();
|
||||
|
||||
$this->expectException('moodle_exception');
|
||||
|
||||
@@ -152,46 +152,105 @@ class provider implements
|
||||
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
|
||||
*/
|
||||
public static function get_contexts_for_userid(int $userid) : \core_privacy\local\request\contextlist {
|
||||
$ratingsql = \core_rating\privacy\provider::get_sql_join('rat', 'mod_forum', 'post', 'p.id', $userid);
|
||||
// Fetch all forum discussions, and forum posts.
|
||||
$contextlist = new \core_privacy\local\request\contextlist();
|
||||
|
||||
$params = [
|
||||
'modname' => 'forum',
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'userid' => $userid,
|
||||
];
|
||||
|
||||
// Discussion creators.
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {forum} f ON f.id = cm.instance
|
||||
LEFT JOIN {forum_discussions} d ON d.forum = f.id
|
||||
LEFT JOIN {forum_posts} p ON p.discussion = d.id
|
||||
LEFT JOIN {forum_digests} dig ON dig.forum = f.id AND dig.userid = :digestuserid
|
||||
LEFT JOIN {forum_subscriptions} sub ON sub.forum = f.id AND sub.userid = :subuserid
|
||||
LEFT JOIN {forum_track_prefs} pref ON pref.forumid = f.id AND pref.userid = :prefuserid
|
||||
LEFT JOIN {forum_read} hasread ON hasread.forumid = f.id AND hasread.userid = :hasreaduserid
|
||||
LEFT JOIN {forum_discussion_subs} dsub ON dsub.forum = f.id AND dsub.userid = :dsubuserid
|
||||
{$ratingsql->join}
|
||||
WHERE (
|
||||
p.userid = :postuserid OR
|
||||
d.userid = :discussionuserid OR
|
||||
dig.id IS NOT NULL OR
|
||||
sub.id IS NOT NULL OR
|
||||
pref.id IS NOT NULL OR
|
||||
hasread.id IS NOT NULL OR
|
||||
dsub.id IS NOT NULL OR
|
||||
{$ratingsql->userwhere}
|
||||
)
|
||||
JOIN {forum_discussions} d ON d.forum = f.id
|
||||
WHERE d.userid = :userid
|
||||
";
|
||||
$params = [
|
||||
'modname' => 'forum',
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'postuserid' => $userid,
|
||||
'discussionuserid' => $userid,
|
||||
'digestuserid' => $userid,
|
||||
'subuserid' => $userid,
|
||||
'prefuserid' => $userid,
|
||||
'hasreaduserid' => $userid,
|
||||
'dsubuserid' => $userid,
|
||||
];
|
||||
$params += $ratingsql->params;
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
$contextlist = new \core_privacy\local\request\contextlist();
|
||||
// Post authors.
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {forum} f ON f.id = cm.instance
|
||||
JOIN {forum_discussions} d ON d.forum = f.id
|
||||
JOIN {forum_posts} p ON p.discussion = d.id
|
||||
WHERE p.userid = :userid
|
||||
";
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
// Forum digest records.
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {forum} f ON f.id = cm.instance
|
||||
JOIN {forum_digests} dig ON dig.forum = f.id
|
||||
WHERE dig.userid = :userid
|
||||
";
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
// Forum subscriptions.
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {forum} f ON f.id = cm.instance
|
||||
JOIN {forum_subscriptions} sub ON sub.forum = f.id
|
||||
WHERE sub.userid = :userid
|
||||
";
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
// Discussion subscriptions.
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {forum} f ON f.id = cm.instance
|
||||
JOIN {forum_discussion_subs} dsub ON dsub.forum = f.id
|
||||
WHERE dsub.userid = :userid
|
||||
";
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
// Discussion tracking preferences.
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {forum} f ON f.id = cm.instance
|
||||
JOIN {forum_track_prefs} pref ON pref.forumid = f.id
|
||||
WHERE pref.userid = :userid
|
||||
";
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
// Discussion read records.
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {forum} f ON f.id = cm.instance
|
||||
JOIN {forum_read} hasread ON hasread.forumid = f.id
|
||||
WHERE hasread.userid = :userid
|
||||
";
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
// Rating authors.
|
||||
$ratingsql = \core_rating\privacy\provider::get_sql_join('rat', 'mod_forum', 'post', 'p.id', $userid, true);
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {forum} f ON f.id = cm.instance
|
||||
JOIN {forum_discussions} d ON d.forum = f.id
|
||||
JOIN {forum_posts} p ON p.discussion = d.id
|
||||
{$ratingsql->join}
|
||||
WHERE {$ratingsql->userwhere}
|
||||
";
|
||||
$params += $ratingsql->params;
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
return $contextlist;
|
||||
@@ -365,30 +424,68 @@ class provider implements
|
||||
$userid = $user->id;
|
||||
|
||||
list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
|
||||
$params = $contextparams;
|
||||
|
||||
// Digested forums.
|
||||
$sql = "SELECT
|
||||
c.id AS contextid,
|
||||
dig.maildigest AS maildigest
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid
|
||||
JOIN {forum} f ON f.id = cm.instance
|
||||
JOIN {forum_digests} dig ON dig.forum = f.id
|
||||
WHERE (
|
||||
dig.userid = :userid AND
|
||||
c.id {$contextsql}
|
||||
)
|
||||
";
|
||||
$params['userid'] = $userid;
|
||||
$digests = $DB->get_records_sql_menu($sql, $params);
|
||||
|
||||
// Forum subscriptions.
|
||||
$sql = "SELECT
|
||||
c.id AS contextid,
|
||||
sub.userid AS subscribed
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid
|
||||
JOIN {forum} f ON f.id = cm.instance
|
||||
JOIN {forum_subscriptions} sub ON sub.forum = f.id
|
||||
WHERE (
|
||||
sub.userid = :userid AND
|
||||
c.id {$contextsql}
|
||||
)
|
||||
";
|
||||
$params['userid'] = $userid;
|
||||
$subscriptions = $DB->get_records_sql_menu($sql, $params);
|
||||
|
||||
// Tracked forums.
|
||||
$sql = "SELECT
|
||||
c.id AS contextid,
|
||||
f.*,
|
||||
cm.id AS cmid,
|
||||
dig.maildigest,
|
||||
sub.userid AS subscribed,
|
||||
pref.userid AS tracked
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid
|
||||
JOIN {forum} f ON f.id = cm.instance
|
||||
LEFT JOIN {forum_digests} dig ON dig.forum = f.id AND dig.userid = :digestuserid
|
||||
LEFT JOIN {forum_subscriptions} sub ON sub.forum = f.id AND sub.userid = :subuserid
|
||||
LEFT JOIN {forum_track_prefs} pref ON pref.forumid = f.id AND pref.userid = :prefuserid
|
||||
JOIN {forum_track_prefs} pref ON pref.forumid = f.id
|
||||
WHERE (
|
||||
pref.userid = :userid AND
|
||||
c.id {$contextsql}
|
||||
)
|
||||
";
|
||||
$params['userid'] = $userid;
|
||||
$tracked = $DB->get_records_sql_menu($sql, $params);
|
||||
|
||||
$sql = "SELECT
|
||||
c.id AS contextid,
|
||||
f.*,
|
||||
cm.id AS cmid
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid
|
||||
JOIN {forum} f ON f.id = cm.instance
|
||||
WHERE (
|
||||
c.id {$contextsql}
|
||||
)
|
||||
";
|
||||
|
||||
$params = [
|
||||
'digestuserid' => $userid,
|
||||
'subuserid' => $userid,
|
||||
'prefuserid' => $userid,
|
||||
];
|
||||
$params += $contextparams;
|
||||
|
||||
// Keep a mapping of forumid to contextid.
|
||||
@@ -407,9 +504,15 @@ class provider implements
|
||||
request_helper::export_context_files($context, $user);
|
||||
|
||||
// Store relevant metadata about this forum instance.
|
||||
static::export_digest_data($userid, $forum);
|
||||
static::export_subscription_data($userid, $forum);
|
||||
static::export_tracking_data($userid, $forum);
|
||||
if (isset($digests[$forum->contextid])) {
|
||||
static::export_digest_data($userid, $forum, $digests[$forum->contextid]);
|
||||
}
|
||||
if (isset($subscriptions[$forum->contextid])) {
|
||||
static::export_subscription_data($userid, $forum, $subscriptions[$forum->contextid]);
|
||||
}
|
||||
if (isset($tracked[$forum->contextid])) {
|
||||
static::export_tracking_data($userid, $forum, $tracked[$forum->contextid]);
|
||||
}
|
||||
}
|
||||
$forums->close();
|
||||
|
||||
@@ -704,16 +807,17 @@ class provider implements
|
||||
*
|
||||
* @param int $userid The userid of the user whose data is to be exported.
|
||||
* @param \stdClass $forum The forum whose data is being exported.
|
||||
* @param int $maildigest The mail digest setting for this forum.
|
||||
* @return bool Whether any data was stored.
|
||||
*/
|
||||
protected static function export_digest_data(int $userid, \stdClass $forum) {
|
||||
if (null !== $forum->maildigest) {
|
||||
protected static function export_digest_data(int $userid, \stdClass $forum, int $maildigest) {
|
||||
if (null !== $maildigest) {
|
||||
// The user has a specific maildigest preference for this forum.
|
||||
$a = (object) [
|
||||
'forum' => format_string($forum->name, true),
|
||||
];
|
||||
|
||||
switch ($forum->maildigest) {
|
||||
switch ($maildigest) {
|
||||
case 0:
|
||||
$a->type = get_string('emaildigestoffshort', 'mod_forum');
|
||||
break;
|
||||
@@ -726,7 +830,7 @@ class provider implements
|
||||
}
|
||||
|
||||
writer::with_context(\context_module::instance($forum->cmid))
|
||||
->export_metadata([], 'digestpreference', $forum->maildigest,
|
||||
->export_metadata([], 'digestpreference', $maildigest,
|
||||
get_string('privacy:digesttypepreference', 'mod_forum', $a));
|
||||
|
||||
return true;
|
||||
@@ -740,10 +844,11 @@ class provider implements
|
||||
*
|
||||
* @param int $userid The userid of the user whose data is to be exported.
|
||||
* @param \stdClass $forum The forum whose data is being exported.
|
||||
* @param int $subscribed if the user is subscribed
|
||||
* @return bool Whether any data was stored.
|
||||
*/
|
||||
protected static function export_subscription_data(int $userid, \stdClass $forum) {
|
||||
if (null !== $forum->subscribed) {
|
||||
protected static function export_subscription_data(int $userid, \stdClass $forum, int $subscribed) {
|
||||
if (null !== $subscribed) {
|
||||
// The user is subscribed to this forum.
|
||||
writer::with_context(\context_module::instance($forum->cmid))
|
||||
->export_metadata([], 'subscriptionpreference', 1, get_string('privacy:subscribedtoforum', 'mod_forum'));
|
||||
@@ -798,10 +903,11 @@ class provider implements
|
||||
*
|
||||
* @param int $userid The userid of the user whose data is to be exported.
|
||||
* @param \stdClass $forum The forum whose data is being exported.
|
||||
* @param int $tracke if the user is subscribed
|
||||
* @return bool Whether any data was stored.
|
||||
*/
|
||||
protected static function export_tracking_data(int $userid, \stdClass $forum) {
|
||||
if (null !== $forum->tracked) {
|
||||
protected static function export_tracking_data(int $userid, \stdClass $forum, int $tracked) {
|
||||
if (null !== $tracked) {
|
||||
// The user has a main preference to track all forums, but has opted out of this one.
|
||||
writer::with_context(\context_module::instance($forum->cmid))
|
||||
->export_metadata([], 'trackreadpreference', 0, get_string('privacy:readtrackingdisabled', 'mod_forum'));
|
||||
|
||||
+2
-5
@@ -3925,7 +3925,7 @@ function forum_print_discussion_header(&$post, $forum, $group = -1, $datestring
|
||||
}
|
||||
|
||||
echo '<td class="lastpost">';
|
||||
$usedate = (empty($post->created)) ? $post->timemodified : $post->created;
|
||||
$usedate = (empty($post->timemodified)) ? $post->created : $post->timemodified;
|
||||
$parenturl = '';
|
||||
$usermodified = new stdClass();
|
||||
$usermodified->id = $post->usermodified;
|
||||
@@ -4558,10 +4558,6 @@ function forum_update_post($newpost, $mform, $unused = null) {
|
||||
}
|
||||
$post->modified = time();
|
||||
|
||||
// Last post modified tracking.
|
||||
$discussion->timemodified = $post->modified;
|
||||
$discussion->usermodified = $post->userid;
|
||||
|
||||
if (!$post->parent) { // Post is a discussion starter - update discussion title and times too
|
||||
$discussion->name = $post->subject;
|
||||
$discussion->timestart = $post->timestart;
|
||||
@@ -4574,6 +4570,7 @@ function forum_update_post($newpost, $mform, $unused = null) {
|
||||
$post->message = file_save_draft_area_files($newpost->itemid, $context->id, 'mod_forum', 'post', $post->id,
|
||||
mod_forum_post_form::editor_options($context, $post->id), $post->message);
|
||||
$DB->update_record('forum_posts', $post);
|
||||
// Note: Discussion modified time/user are intentionally not updated, to enable them to track the latest new post.
|
||||
$DB->update_record('forum_discussions', $discussion);
|
||||
|
||||
forum_add_attachment($post, $forum, $cm, $mform);
|
||||
|
||||
+1
-1
@@ -574,7 +574,7 @@ file_prepare_draft_area($draftitemid, $modcontext->id, 'mod_forum', 'attachment'
|
||||
|
||||
if ($USER->id != $post->userid) { // Not the original author, so add a message to the end
|
||||
$data = new stdClass();
|
||||
$data->date = userdate($post->modified);
|
||||
$data->date = userdate($post->created);
|
||||
if ($post->messageformat == FORMAT_HTML) {
|
||||
$data->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$USER->id.'&course='.$post->course.'">'.
|
||||
fullname($USER).'</a>';
|
||||
|
||||
@@ -71,14 +71,14 @@ Feature: New discussions and discussions with recently added replies are display
|
||||
And I am on "Course 1" course homepage
|
||||
And I follow "Course general forum"
|
||||
#
|
||||
# Make sure the order of the forum posts is as expected (most recently participated first).
|
||||
# Make sure the order of the forum posts is as expected, with most recent new participation first (ie excluding edits).
|
||||
#
|
||||
Then I should see "Forum post 3" in the "//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ')][position()=3]" "xpath_element"
|
||||
And I should see "Edited forum post 2" in the "//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ')][position()=2]" "xpath_element"
|
||||
And I should see "Forum post 1" in the "//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ')][position()=1]" "xpath_element"
|
||||
Then I should see "Forum post 1" in the "//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ')][position()=1]" "xpath_element"
|
||||
And I should see "Forum post 3" in the "//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ')][position()=2]" "xpath_element"
|
||||
And I should see "Edited forum post 2" in the "//tr[contains(concat(' ', normalize-space(@class), ' '), ' discussion ')][position()=3]" "xpath_element"
|
||||
#
|
||||
# Make sure the next/prev navigation uses the same order of the posts.
|
||||
#
|
||||
And I follow "Edited forum post 2"
|
||||
And I follow "Forum post 3"
|
||||
And "//a[@aria-label='Next discussion: Forum post 1']" "xpath_element" should exist
|
||||
And "//a[@aria-label='Previous discussion: Forum post 3']" "xpath_element" should exist
|
||||
And "//a[@aria-label='Previous discussion: Edited forum post 2']" "xpath_element" should exist
|
||||
|
||||
@@ -3298,6 +3298,17 @@ class mod_forum_lib_testcase extends advanced_testcase {
|
||||
// On this freshly created discussion, the teacher is the author of the last post.
|
||||
$this->assertEquals($teacher->id, $DB->get_field('forum_discussions', 'usermodified', ['id' => $discussion->id]));
|
||||
|
||||
// Fetch modified timestamp of the discussion.
|
||||
$discussionmodified = $DB->get_field('forum_discussions', 'timemodified', ['id' => $discussion->id]);
|
||||
$pasttime = $discussionmodified - 3600;
|
||||
|
||||
// Adjust the discussion modified timestamp back an hour, so it's in the past.
|
||||
$adjustment = (object)[
|
||||
'id' => $discussion->id,
|
||||
'timemodified' => $pasttime,
|
||||
];
|
||||
$DB->update_record('forum_discussions', $adjustment);
|
||||
|
||||
// Let the student reply to the teacher's post.
|
||||
$reply = $generator->create_post((object)[
|
||||
'course' => $course->id,
|
||||
@@ -3310,6 +3321,30 @@ class mod_forum_lib_testcase extends advanced_testcase {
|
||||
// The student should now be the last post's author.
|
||||
$this->assertEquals($student->id, $DB->get_field('forum_discussions', 'usermodified', ['id' => $discussion->id]));
|
||||
|
||||
// Fetch modified timestamp of the discussion and student's post.
|
||||
$discussionmodified = $DB->get_field('forum_discussions', 'timemodified', ['id' => $discussion->id]);
|
||||
$postmodified = $DB->get_field('forum_posts', 'modified', ['id' => $reply->id]);
|
||||
|
||||
// Discussion modified time should be updated to be equal to the newly created post's time.
|
||||
$this->assertEquals($discussionmodified, $postmodified);
|
||||
|
||||
// Adjust the discussion and post timestamps, so they are in the past.
|
||||
$adjustment = (object)[
|
||||
'id' => $discussion->id,
|
||||
'timemodified' => $pasttime,
|
||||
];
|
||||
$DB->update_record('forum_discussions', $adjustment);
|
||||
|
||||
$adjustment = (object)[
|
||||
'id' => $reply->id,
|
||||
'modified' => $pasttime,
|
||||
];
|
||||
$DB->update_record('forum_posts', $adjustment);
|
||||
|
||||
// The discussion and student's post time should now be an hour in the past.
|
||||
$this->assertEquals($pasttime, $DB->get_field('forum_discussions', 'timemodified', ['id' => $discussion->id]));
|
||||
$this->assertEquals($pasttime, $DB->get_field('forum_posts', 'modified', ['id' => $reply->id]));
|
||||
|
||||
// Let the teacher edit the student's reply.
|
||||
$this->setUser($teacher->id);
|
||||
$newpost = (object)[
|
||||
@@ -3319,8 +3354,14 @@ class mod_forum_lib_testcase extends advanced_testcase {
|
||||
];
|
||||
forum_update_post($newpost, null);
|
||||
|
||||
// The student should be still the last post's author.
|
||||
// The student should still be the last post's author.
|
||||
$this->assertEquals($student->id, $DB->get_field('forum_discussions', 'usermodified', ['id' => $discussion->id]));
|
||||
|
||||
// The discussion modified time should not have changed.
|
||||
$this->assertEquals($pasttime, $DB->get_field('forum_discussions', 'timemodified', ['id' => $discussion->id]));
|
||||
|
||||
// The post time should be updated.
|
||||
$this->assertGreaterThan($pasttime, $DB->get_field('forum_posts', 'modified', ['id' => $reply->id]));
|
||||
}
|
||||
|
||||
public function test_forum_core_calendar_provide_event_action() {
|
||||
|
||||
@@ -80,26 +80,55 @@ class provider implements
|
||||
* @return contextlist the list of contexts containing user info for the user.
|
||||
*/
|
||||
public static function get_contexts_for_userid(int $userid) : contextlist {
|
||||
$ratingquery = \core_rating\privacy\provider::get_sql_join('r', 'mod_glossary', 'entry', 'ge.id', $userid);
|
||||
$contextlist = new contextlist();
|
||||
|
||||
// Glossary entries.
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
INNER JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
INNER JOIN {glossary} g ON g.id = cm.instance
|
||||
INNER JOIN {glossary_entries} ge ON ge.glossaryid = g.id
|
||||
LEFT JOIN {comments} com ON com.commentarea =:commentarea AND com.itemid = ge.id
|
||||
{$ratingquery->join}
|
||||
WHERE ge.userid = :glossaryentryuserid OR com.userid = :commentuserid OR {$ratingquery->userwhere}";
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {glossary} g ON g.id = cm.instance
|
||||
JOIN {glossary_entries} ge ON ge.glossaryid = g.id
|
||||
WHERE ge.userid = :glossaryentryuserid";
|
||||
$params = [
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'modname' => 'glossary',
|
||||
'commentarea' => 'glossary_entry',
|
||||
'glossaryentryuserid' => $userid,
|
||||
'commentuserid' => $userid,
|
||||
] + $ratingquery->params;
|
||||
];
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
$contextlist = new contextlist();
|
||||
// Where the user has rated something.
|
||||
$ratingquery = \core_rating\privacy\provider::get_sql_join('r', 'mod_glossary', 'entry', 'ge.id', $userid, true);
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {glossary} g ON g.id = cm.instance
|
||||
JOIN {glossary_entries} ge ON ge.glossaryid = g.id
|
||||
{$ratingquery->join}
|
||||
WHERE {$ratingquery->userwhere}";
|
||||
$params = [
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'modname' => 'glossary',
|
||||
] + $ratingquery->params;
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
// Comments.
|
||||
$sql = "SELECT c.id
|
||||
FROM {context} c
|
||||
JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel
|
||||
JOIN {modules} m ON m.id = cm.module AND m.name = :modname
|
||||
JOIN {glossary} g ON g.id = cm.instance
|
||||
JOIN {glossary_entries} ge ON ge.glossaryid = g.id
|
||||
JOIN {comments} com ON com.commentarea =:commentarea AND com.itemid = ge.id
|
||||
WHERE com.userid = :commentuserid";
|
||||
$params = [
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'modname' => 'glossary',
|
||||
'commentarea' => 'glossary_entry',
|
||||
'commentuserid' => $userid,
|
||||
];
|
||||
$contextlist->add_from_sql($sql, $params);
|
||||
|
||||
return $contextlist;
|
||||
|
||||
@@ -45,6 +45,13 @@ $reset = optional_param('reset', null, PARAM_BOOL);
|
||||
|
||||
require_login();
|
||||
|
||||
if (\core\session\manager::is_loggedinas()) {
|
||||
// Disable access to the user's dashboard for "logged in as" sessions
|
||||
// to mitigate risks associated with loading other users' JavaScript.
|
||||
// See MDL-63786 for more information.
|
||||
redirect(new moodle_url('/', ['redirect' => 0]), get_string('unabletoaccess', 'core_my'));
|
||||
}
|
||||
|
||||
$hassiteconfig = has_capability('moodle/site:config', context_system::instance());
|
||||
if ($hassiteconfig && moodle_needs_upgrading()) {
|
||||
redirect(new moodle_url('/admin/index.php'));
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* A scheduled task to remove unneeded random questions.
|
||||
*
|
||||
* @package qtype_random
|
||||
* @category task
|
||||
* @copyright 2018 Bo Pierce <email.bO.pierce@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace qtype_random\task;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
/**
|
||||
* A scheduled task to remove unneeded random questions.
|
||||
*
|
||||
* @copyright 2018 Bo Pierce <email.bO.pierce@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class remove_unused_questions extends \core\task\scheduled_task {
|
||||
|
||||
public function get_name() {
|
||||
return get_string('taskunusedrandomscleanup', 'qtype_random');
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
global $DB, $CFG;
|
||||
require_once($CFG->libdir . '/questionlib.php');
|
||||
|
||||
// Find potentially unused random questions (up to 10000).
|
||||
// Note, because we call question_delete_question below,
|
||||
// the question will not actually be deleted if something else
|
||||
// is using them, but nothing else in Moodle core uses qtype_random,
|
||||
// and not many third-party plugins do.
|
||||
$unusedrandomids = $DB->get_records_sql("
|
||||
SELECT q.id, 1
|
||||
FROM {question} q
|
||||
LEFT JOIN {quiz_slots} qslots ON q.id = qslots.questionid
|
||||
WHERE qslots.questionid IS NULL
|
||||
AND q.qtype = ? AND hidden = ?", ['random', 0], 0, 10000);
|
||||
|
||||
$count = 0;
|
||||
foreach ($unusedrandomids as $unusedrandomid => $notused) {
|
||||
question_delete_question($unusedrandomid);
|
||||
// In case the question was not actually deleted (because it was in use somehow
|
||||
// mark it as hidden so the query above will not return it again.
|
||||
$DB->set_field('question', 'hidden', 1, ['id' => $unusedrandomid]);
|
||||
$count += 1;
|
||||
}
|
||||
mtrace('Cleaned up ' . $count . ' unused random questions.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Definition of question/type/random scheduled tasks.
|
||||
*
|
||||
* @package qtype_random
|
||||
* @category task
|
||||
* @copyright 2018 Bo Pierce <email.bO.pierce@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$tasks = array(
|
||||
array(
|
||||
'classname' => 'qtype_random\task\remove_unused_questions',
|
||||
'blocking' => 0,
|
||||
'minute' => 'R',
|
||||
'hour' => '*',
|
||||
'day' => '*',
|
||||
'month' => '*',
|
||||
'dayofweek' => '*'
|
||||
)
|
||||
);
|
||||
@@ -33,3 +33,4 @@ $string['randomqname'] = 'Random ({$a})';
|
||||
$string['randomqplusname'] = 'Random ({$a} and subcategories)';
|
||||
$string['selectedby'] = '{$a->questionname} selected by {$a->randomname}';
|
||||
$string['selectmanualquestions'] = 'Random questions can use manually graded questions';
|
||||
$string['taskunusedrandomscleanup'] = 'Remove unused random questions';
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Tests of the scheduled task for cleaning up random questions.
|
||||
*
|
||||
* @package qtype_random
|
||||
* @copyright 2018 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
|
||||
|
||||
/**
|
||||
* Tests of the scheduled task for cleaning up random questions.
|
||||
*
|
||||
* @copyright 2018 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class qtype_random_cleanup_task_testcase extends advanced_testcase {
|
||||
|
||||
public function test_cleanup_task_removes_unused_question() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
$questiongenerator = $generator->get_plugin_generator('core_question');
|
||||
$quizgenerator = $generator->get_plugin_generator('mod_quiz');
|
||||
$cat = $questiongenerator->create_question_category();
|
||||
$quiz = $quizgenerator->create_instance(['course' => SITEID]);
|
||||
|
||||
// Add two random questions.
|
||||
quiz_add_random_questions($quiz, 0, $cat->id, 2, false);
|
||||
$quizslots = $DB->get_records('quiz_slots', ['quizid' => $quiz->id],
|
||||
'slot', 'slot, id, questionid');
|
||||
|
||||
// Now remove the second from the quiz. (Do it manually,
|
||||
// because the API cleans up the random question, but we are trying to
|
||||
// create an orphaned random question.)
|
||||
$DB->delete_records('quiz_slots', array('id' => $quizslots[2]->id));
|
||||
|
||||
// Run the scheduled task.
|
||||
$task = new \qtype_random\task\remove_unused_questions();
|
||||
$this->expectOutputString("Cleaned up 1 unused random questions.\n");
|
||||
$task->execute();
|
||||
|
||||
// Verify.
|
||||
$this->assertTrue($DB->record_exists('question', ['id' => $quizslots[1]->questionid]));
|
||||
$this->assertFalse($DB->record_exists('question', ['id' => $quizslots[2]->questionid]));
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->component = 'qtype_random';
|
||||
$plugin->version = 2017111300;
|
||||
$plugin->version = 2017111301;
|
||||
|
||||
$plugin->requires = 2017110800;
|
||||
|
||||
|
||||
@@ -129,26 +129,41 @@ class provider implements
|
||||
/**
|
||||
* Get the SQL required to find all submission items where this user has had any involvements.
|
||||
*
|
||||
* If possible an inner join should be used.
|
||||
*
|
||||
* @param string $alias The name of the table alias to use.
|
||||
* @param string $component The na eof the component to fetch ratings for.
|
||||
* @param string $ratingarea The rating area to fetch results for.
|
||||
* @param string $itemidjoin The right-hand-side of the JOIN ON clause.
|
||||
* @param int $userid The ID of the user being stored.
|
||||
* @param bool $innerjoin Whether to use an inner join (preferred)
|
||||
* @return \stdClass
|
||||
*/
|
||||
public static function get_sql_join($alias, $component, $ratingarea, $itemidjoin, $userid) {
|
||||
public static function get_sql_join($alias, $component, $ratingarea, $itemidjoin, $userid, $innerjoin = false) {
|
||||
static $count = 0;
|
||||
$count++;
|
||||
|
||||
// Join the rating table with the specified alias and the relevant join params.
|
||||
$join = "LEFT JOIN {rating} {$alias} ON ";
|
||||
$join .= "{$alias}.userid = :ratinguserid{$count} AND ";
|
||||
$join .= "{$alias}.component = :ratingcomponent{$count} AND ";
|
||||
$join .= "{$alias}.ratingarea = :ratingarea{$count} AND ";
|
||||
$join .= "{$alias}.itemid = {$itemidjoin}";
|
||||
$userwhere = '';
|
||||
|
||||
// Match against the specified user.
|
||||
$userwhere = "{$alias}.id IS NOT NULL";
|
||||
if ($innerjoin) {
|
||||
// Join the rating table with the specified alias and the relevant join params.
|
||||
$join = "JOIN {rating} {$alias} ON ";
|
||||
$join .= "{$alias}.itemid = {$itemidjoin}";
|
||||
|
||||
$userwhere .= "{$alias}.userid = :ratinguserid{$count} AND ";
|
||||
$userwhere .= "{$alias}.component = :ratingcomponent{$count} AND ";
|
||||
$userwhere .= "{$alias}.ratingarea = :ratingarea{$count}";
|
||||
} else {
|
||||
// Join the rating table with the specified alias and the relevant join params.
|
||||
$join = "LEFT JOIN {rating} {$alias} ON ";
|
||||
$join .= "{$alias}.userid = :ratinguserid{$count} AND ";
|
||||
$join .= "{$alias}.component = :ratingcomponent{$count} AND ";
|
||||
$join .= "{$alias}.ratingarea = :ratingarea{$count} AND ";
|
||||
$join .= "{$alias}.itemid = {$itemidjoin}";
|
||||
|
||||
// Match against the specified user.
|
||||
$userwhere = "{$alias}.id IS NOT NULL";
|
||||
}
|
||||
|
||||
$params = [
|
||||
'ratingcomponent' . $count => $component,
|
||||
|
||||
@@ -153,6 +153,94 @@ class core_rating_privacy_testcase extends \core_privacy\tests\provider_testcase
|
||||
$this->assertFalse(isset($courses[$course3->id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the get_sql_join function returns valid SQL which returns the correct list of rated itemids.
|
||||
* This makes use of the optional inner join argument.
|
||||
*/
|
||||
public function test_get_sql_join_inner() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course1 = $this->getDataGenerator()->create_course();
|
||||
$course2 = $this->getDataGenerator()->create_course();
|
||||
$course3 = $this->getDataGenerator()->create_course();
|
||||
|
||||
$u1 = $this->getDataGenerator()->create_user();
|
||||
$u2 = $this->getDataGenerator()->create_user();
|
||||
$u3 = $this->getDataGenerator()->create_user();
|
||||
|
||||
// Rate the courses.
|
||||
$rm = new rating_manager();
|
||||
$ratingoptions = (object) [
|
||||
'component' => 'core_course',
|
||||
'ratingarea' => 'course',
|
||||
'scaleid' => 100,
|
||||
];
|
||||
|
||||
// Rate all courses as u1, and something else in the same context.
|
||||
$this->rate_as_user($u1->id, 'core_course', 'course', $course1->id, \context_course::instance($course1->id), 25);
|
||||
$this->rate_as_user($u1->id, 'core_course', 'course', $course2->id, \context_course::instance($course2->id), 50);
|
||||
$this->rate_as_user($u1->id, 'core_course', 'course', $course3->id, \context_course::instance($course3->id), 75);
|
||||
$this->rate_as_user($u1->id, 'core_course', 'files', $course3->id, \context_course::instance($course3->id), 99);
|
||||
|
||||
// Rate course2 as u2, and something else in a different context/component..
|
||||
$this->rate_as_user($u2->id, 'core_course', 'course', $course2->id, \context_course::instance($course2->id), 90);
|
||||
$this->rate_as_user($u2->id, 'user', 'user', $u3->id, \context_user::instance($u3->id), 10);
|
||||
|
||||
// Return any course which the u1 has rated.
|
||||
// u1 rated all three courses.
|
||||
$ratingquery = provider::get_sql_join('r', 'core_course', 'course', 'c.id', $u1->id, true);
|
||||
$sql = "SELECT c.id FROM {course} c {$ratingquery->join} WHERE {$ratingquery->userwhere}";
|
||||
$courses = $DB->get_records_sql($sql, $ratingquery->params);
|
||||
|
||||
$this->assertCount(3, $courses);
|
||||
$this->assertTrue(isset($courses[$course1->id]));
|
||||
$this->assertTrue(isset($courses[$course2->id]));
|
||||
$this->assertTrue(isset($courses[$course3->id]));
|
||||
|
||||
// User u1 rated files in course 3 only.
|
||||
$ratingquery = provider::get_sql_join('r', 'core_course', 'files', 'c.id', $u1->id, true);
|
||||
$sql = "SELECT c.id FROM {course} c {$ratingquery->join} WHERE {$ratingquery->userwhere}";
|
||||
$courses = $DB->get_records_sql($sql, $ratingquery->params);
|
||||
|
||||
$this->assertCount(1, $courses);
|
||||
$this->assertFalse(isset($courses[$course1->id]));
|
||||
$this->assertFalse(isset($courses[$course2->id]));
|
||||
$this->assertTrue(isset($courses[$course3->id]));
|
||||
|
||||
// Return any course which the u2 has rated.
|
||||
// User u2 rated only course 2.
|
||||
$ratingquery = provider::get_sql_join('r', 'core_course', 'course', 'c.id', $u2->id, true);
|
||||
$sql = "SELECT c.id FROM {course} c {$ratingquery->join} WHERE {$ratingquery->userwhere}";
|
||||
$courses = $DB->get_records_sql($sql, $ratingquery->params);
|
||||
|
||||
$this->assertCount(1, $courses);
|
||||
$this->assertFalse(isset($courses[$course1->id]));
|
||||
$this->assertTrue(isset($courses[$course2->id]));
|
||||
$this->assertFalse(isset($courses[$course3->id]));
|
||||
|
||||
// User u2 rated u3.
|
||||
$ratingquery = provider::get_sql_join('r', 'user', 'user', 'u.id', $u2->id, true);
|
||||
$sql = "SELECT u.id FROM {user} u {$ratingquery->join} WHERE {$ratingquery->userwhere}";
|
||||
$users = $DB->get_records_sql($sql, $ratingquery->params);
|
||||
|
||||
$this->assertCount(1, $users);
|
||||
$this->assertFalse(isset($users[$u1->id]));
|
||||
$this->assertFalse(isset($users[$u2->id]));
|
||||
$this->assertTrue(isset($users[$u3->id]));
|
||||
|
||||
// Return any course which the u3 has rated.
|
||||
// User u3 did not rate anything.
|
||||
$ratingquery = provider::get_sql_join('r', 'core_course', 'course', 'c.id', $u3->id, true);
|
||||
$sql = "SELECT c.id FROM {course} c {$ratingquery->join} WHERE {$ratingquery->userwhere}";
|
||||
$courses = $DB->get_records_sql($sql, $ratingquery->params);
|
||||
|
||||
$this->assertCount(0, $courses);
|
||||
$this->assertFalse(isset($courses[$course1->id]));
|
||||
$this->assertFalse(isset($courses[$course2->id]));
|
||||
$this->assertFalse(isset($courses[$course3->id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that export_area_ratings exports all ratings that a user has made, and all ratings for a users own content.
|
||||
*/
|
||||
|
||||
+2
-2
@@ -29,11 +29,11 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2017111307.00; // 20171113 = branching date YYYYMMDD - do not modify!
|
||||
$version = 2017111309.00; // 20171113 = branching date YYYYMMDD - do not modify!
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
|
||||
$release = '3.4.7 (Build: 20190114)'; // Human-friendly version name
|
||||
$release = '3.4.9 (Build: 20190513)'; // Human-friendly version name
|
||||
|
||||
$branch = '34'; // This version's branch.
|
||||
$maturity = MATURITY_STABLE; // This version's maturity level.
|
||||
|
||||
Reference in New Issue
Block a user