Merge branch 'master' into install_master
This commit is contained in:
@@ -61,15 +61,4 @@ trait reader {
|
||||
}
|
||||
return $this->store;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current user can access current store or not.
|
||||
*
|
||||
* @param \context $context
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function can_access(\context $context) {
|
||||
return has_capability('logstore/' . $this->store . ':read', $context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Defines the capabilities used by standard log store.
|
||||
*
|
||||
* @package logstore_legacy
|
||||
* @copyright 2013 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$capabilities = array(
|
||||
'logstore/legacy:read' => array(
|
||||
'riskbitmask' => RISK_PERSONAL,
|
||||
'captype' => 'read',
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'archetypes' => array(
|
||||
'manager' => CAP_ALLOW,
|
||||
'editingteacher' => CAP_ALLOW,
|
||||
'teacher' => CAP_ALLOW,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -23,7 +23,6 @@
|
||||
*/
|
||||
|
||||
$string['event_legacy_logged'] = 'Legacy event logged';
|
||||
$string['legacy:read'] = 'Read logs';
|
||||
$string['loglegacy'] = 'Log legacy data';
|
||||
$string['loglegacy_help'] = 'This plugin records log data to the legacy log table (mdl_log). This functionality has been replaced by newer, richer and more efficient logging plugins, so you should only run this plugin if you have old custom reports that directly query the old log table. Writing to the legacy logs will increase load, so it is recommended that you disable this plugin for performance reasons when it is not needed.';
|
||||
$string['pluginname'] = 'Legacy log';
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2014031300; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2014041700; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2014031200; // Requires this Moodle version.
|
||||
$plugin->component = 'logstore_legacy'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Defines the capabilities used by standard log store.
|
||||
*
|
||||
* @package logstore_standard
|
||||
* @copyright 2013 Petr Skoda {@link http://skodak.org}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$capabilities = array(
|
||||
'logstore/standard:read' => array(
|
||||
'riskbitmask' => RISK_PERSONAL,
|
||||
'captype' => 'read',
|
||||
'contextlevel' => CONTEXT_MODULE,
|
||||
'archetypes' => array(
|
||||
'manager' => CAP_ALLOW,
|
||||
'editingteacher' => CAP_ALLOW,
|
||||
'teacher' => CAP_ALLOW,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -25,5 +25,4 @@
|
||||
$string['buffersize'] = 'Write buffer size';
|
||||
$string['pluginname'] = 'Standard log';
|
||||
$string['pluginname_desc'] = 'A log plugin stores log entries in a Moodle database table.';
|
||||
$string['standard:read'] = 'Read logs';
|
||||
$string['taskcleanup'] = 'Log table cleanup';
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2014041500; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2014041700; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2014031200; // Requires this Moodle version.
|
||||
$plugin->component = 'logstore_standard'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
require('../../config.php');
|
||||
|
||||
$context = context_system::instance();
|
||||
$PAGE->set_url('/auth/shibboleth/index.php');
|
||||
$PAGE->set_context($context);
|
||||
|
||||
// Support for WAYFless URLs.
|
||||
$target = optional_param('target', '', PARAM_LOCALURL);
|
||||
|
||||
@@ -172,7 +172,7 @@ abstract class frontend {
|
||||
$decoded = json_decode($data['availabilityconditionsjson']);
|
||||
if (!$decoded) {
|
||||
// This shouldn't be possible.
|
||||
throw new coding_exception('Invalid JSON from availabilityconditionsjson field');
|
||||
throw new \coding_exception('Invalid JSON from availabilityconditionsjson field');
|
||||
}
|
||||
if (!empty($decoded->errors)) {
|
||||
$error = '';
|
||||
|
||||
@@ -193,12 +193,7 @@ abstract class info {
|
||||
$tree = $this->get_availability_tree();
|
||||
$result = $tree->check_available(false, $this, $grabthelot, $userid);
|
||||
} catch (\coding_exception $e) {
|
||||
// We catch the message because it causes fatal problems in most of
|
||||
// the GUI if this exception gets thrown (you can't edit the
|
||||
// activity to fix it). Obviously it should never happen anyway, but
|
||||
// just in case.
|
||||
debugging('Error processing availability data for ‘' .
|
||||
$this->get_thing_name() . '’: ' . s($e->a), DEBUG_DEVELOPER);
|
||||
$this->warn_about_invalid_availability($e);
|
||||
$this->modinfo = null;
|
||||
return false;
|
||||
}
|
||||
@@ -237,7 +232,12 @@ abstract class info {
|
||||
if (is_null($this->availability)) {
|
||||
return true;
|
||||
} else {
|
||||
return $this->get_availability_tree()->is_available_for_all();
|
||||
try {
|
||||
return $this->get_availability_tree()->is_available_for_all();
|
||||
} catch (\coding_exception $e) {
|
||||
$this->warn_about_invalid_availability($e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,13 +273,32 @@ abstract class info {
|
||||
$this->modinfo = null;
|
||||
return $result;
|
||||
} catch (\coding_exception $e) {
|
||||
// Again we catch the message to avoid problems in GUI.
|
||||
debugging('Error processing availability data for ‘' .
|
||||
$this->get_thing_name() . '’: ' . s($e->a), DEBUG_DEVELOPER);
|
||||
$this->warn_about_invalid_availability($e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In some places we catch coding_exception because if a bug happens, it
|
||||
* would be fatal for the course page GUI; instead we just show a developer
|
||||
* debug message.
|
||||
*
|
||||
* @param \coding_exception $e Exception that occurred
|
||||
*/
|
||||
protected function warn_about_invalid_availability(\coding_exception $e) {
|
||||
$name = $this->get_thing_name();
|
||||
// If it occurs while building modinfo based on somebody calling $cm->name,
|
||||
// we can't get $cm->name, and this line will cause a warning.
|
||||
$htmlname = @$this->format_info($name, $this->course);
|
||||
if ($htmlname === '') {
|
||||
// So instead use the numbers (cmid) from the tag.
|
||||
$htmlname = preg_replace('~[^0-9]~', '', $name);
|
||||
}
|
||||
$info = 'Error processing availability data for ‘' . $htmlname
|
||||
. '’: ' . s($e->a);
|
||||
debugging($info, DEBUG_DEVELOPER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during restore (near end of restore). Updates any necessary ids
|
||||
* and writes the updated tree to the database. May output warnings if
|
||||
|
||||
@@ -79,9 +79,11 @@ class frontend extends \core_availability\frontend {
|
||||
\section_info $section = null) {
|
||||
global $CFG;
|
||||
|
||||
// Group option can be used on sections, and on modules but only
|
||||
// if groupmembersonly is turned off. (To avoid confusion.)
|
||||
if (!is_null($cm) && $CFG->enablegroupmembersonly) {
|
||||
// If groupmembersonly is turned on, then you can only add group
|
||||
// restrictions on sections (which don't use groupmembersonly) and
|
||||
// not on modules. This is to avoid confusion - otherwise
|
||||
// there would be two ways to add restrictions based on groups.
|
||||
if (is_null($section) && $CFG->enablegroupmembersonly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,9 +74,11 @@ class frontend extends \core_availability\frontend {
|
||||
\section_info $section = null) {
|
||||
global $CFG, $DB;
|
||||
|
||||
// Grouping option can be used on sections, and on modules but only
|
||||
// if groupmembersonly is turned off. (To avoid confusion.)
|
||||
if (!is_null($cm) && $CFG->enablegroupmembersonly) {
|
||||
// If groupmembersonly is turned on, then you can only add group
|
||||
// restrictions on sections (which don't use groupmembersonly) and
|
||||
// not on modules. This is to avoid confusion - otherwise
|
||||
// there would be two ways to add restrictions based on groups.
|
||||
if (is_null($section) && $CFG->enablegroupmembersonly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,22 @@ Feature: edit_availability
|
||||
When I click on ".availability-item .availability-delete img" "css_element"
|
||||
Then I should not see "or" in the "Restrict access" "fieldset"
|
||||
|
||||
# Add a nested restriction set with two dates so there will be inner connector.
|
||||
When I click on "Add restriction..." "button"
|
||||
And I click on "Restriction set" "button" in the "Add restriction..." "dialogue"
|
||||
And I click on "Add restriction..." "button" in the ".availability-children .availability-list" "css_element"
|
||||
And I click on "Date" "button" in the "Add restriction..." "dialogue"
|
||||
And I click on "Add restriction..." "button" in the ".availability-children .availability-list" "css_element"
|
||||
And I click on "Date" "button" in the "Add restriction..." "dialogue"
|
||||
Then I should see "and" in the ".availability-children .availability-list .availability-connector" "css_element"
|
||||
|
||||
# Check changing the outer one does not affect the inner one.
|
||||
When I set the field "Required restrictions" to "all"
|
||||
Then I should not see "or" in the "Restrict access" "fieldset"
|
||||
When I set the field "Required restrictions" to "any"
|
||||
Then I should see "or" in the "Restrict access" "fieldset"
|
||||
And I should not see "or" in the ".availability-children .availability-list .availability-connector" "css_element"
|
||||
|
||||
@javascript
|
||||
Scenario: Edit availability using settings in section form
|
||||
# Set up.
|
||||
|
||||
Vendored
+10
-6
@@ -96,12 +96,16 @@ M.core_availability.form = {
|
||||
|
||||
// Get top-level tree as JSON.
|
||||
var value = this.field.get('value');
|
||||
if (value === '') {
|
||||
this.rootList = new M.core_availability.List(null, true);
|
||||
} else {
|
||||
var data = Y.JSON.parse(value);
|
||||
this.rootList = new M.core_availability.List(data, true);
|
||||
var data = null;
|
||||
if (value !== '') {
|
||||
try {
|
||||
data = Y.JSON.parse(value);
|
||||
} catch(x) {
|
||||
// If the JSON data is not valid, treat it as empty.
|
||||
this.field.set('value', '');
|
||||
}
|
||||
}
|
||||
this.rootList = new M.core_availability.List(data, true);
|
||||
this.mainDiv.appendChild(this.rootList.node);
|
||||
|
||||
// Update JSON value after loading (to reflect any changes that need
|
||||
@@ -509,7 +513,7 @@ M.core_availability.List.prototype.updateHtml = function() {
|
||||
} else {
|
||||
connectorText = M.str.availability.or;
|
||||
}
|
||||
this.inner.all('.availability-connector span.label').each(function(span) {
|
||||
this.inner.all('> .availability-children > .availability-connector span.label').each(function(span) {
|
||||
span.set('innerHTML', connectorText);
|
||||
});
|
||||
};
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
+10
-6
@@ -96,12 +96,16 @@ M.core_availability.form = {
|
||||
|
||||
// Get top-level tree as JSON.
|
||||
var value = this.field.get('value');
|
||||
if (value === '') {
|
||||
this.rootList = new M.core_availability.List(null, true);
|
||||
} else {
|
||||
var data = Y.JSON.parse(value);
|
||||
this.rootList = new M.core_availability.List(data, true);
|
||||
var data = null;
|
||||
if (value !== '') {
|
||||
try {
|
||||
data = Y.JSON.parse(value);
|
||||
} catch(x) {
|
||||
// If the JSON data is not valid, treat it as empty.
|
||||
this.field.set('value', '');
|
||||
}
|
||||
}
|
||||
this.rootList = new M.core_availability.List(data, true);
|
||||
this.mainDiv.appendChild(this.rootList.node);
|
||||
|
||||
// Update JSON value after loading (to reflect any changes that need
|
||||
@@ -509,7 +513,7 @@ M.core_availability.List.prototype.updateHtml = function() {
|
||||
} else {
|
||||
connectorText = M.str.availability.or;
|
||||
}
|
||||
this.inner.all('.availability-connector span.label').each(function(span) {
|
||||
this.inner.all('> .availability-children > .availability-connector span.label').each(function(span) {
|
||||
span.set('innerHTML', connectorText);
|
||||
});
|
||||
};
|
||||
|
||||
Vendored
+10
-6
@@ -94,12 +94,16 @@ M.core_availability.form = {
|
||||
|
||||
// Get top-level tree as JSON.
|
||||
var value = this.field.get('value');
|
||||
if (value === '') {
|
||||
this.rootList = new M.core_availability.List(null, true);
|
||||
} else {
|
||||
var data = Y.JSON.parse(value);
|
||||
this.rootList = new M.core_availability.List(data, true);
|
||||
var data = null;
|
||||
if (value !== '') {
|
||||
try {
|
||||
data = Y.JSON.parse(value);
|
||||
} catch(x) {
|
||||
// If the JSON data is not valid, treat it as empty.
|
||||
this.field.set('value', '');
|
||||
}
|
||||
}
|
||||
this.rootList = new M.core_availability.List(data, true);
|
||||
this.mainDiv.appendChild(this.rootList.node);
|
||||
|
||||
// Update JSON value after loading (to reflect any changes that need
|
||||
@@ -507,7 +511,7 @@ M.core_availability.List.prototype.updateHtml = function() {
|
||||
} else {
|
||||
connectorText = M.str.availability.or;
|
||||
}
|
||||
this.inner.all('.availability-connector span.label').each(function(span) {
|
||||
this.inner.all('> .availability-children > .availability-connector span.label').each(function(span) {
|
||||
span.set('innerHTML', connectorText);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -379,13 +379,7 @@ class backup_section_structure_step extends backup_structure_step {
|
||||
$section->add_child($formatoptions);
|
||||
|
||||
// Define sources.
|
||||
// The 'availability' field needs to be renamed because it clashes with
|
||||
// the old nested element structure for availability data.
|
||||
$section->set_source_sql("
|
||||
SELECT *, availability AS availabilityjson
|
||||
FROM {course_sections} WHERE id = ?",
|
||||
array('id' => backup::VAR_SECTIONID));
|
||||
|
||||
$section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
|
||||
$formatoptions->set_source_sql('SELECT cfo.id, cfo.format, cfo.name, cfo.value
|
||||
FROM {course} c
|
||||
JOIN {course_format_options} cfo
|
||||
@@ -395,6 +389,9 @@ class backup_section_structure_step extends backup_structure_step {
|
||||
|
||||
// Aliases
|
||||
$section->set_source_alias('section', 'number');
|
||||
// The 'availability' field needs to be renamed because it clashes with
|
||||
// the old nested element structure for availability data.
|
||||
$section->set_source_alias('availability', 'availabilityjson');
|
||||
|
||||
// Set annotations
|
||||
$section->annotate_files('course', 'section', 'id');
|
||||
|
||||
@@ -221,11 +221,11 @@ class core_backup_renderer extends plugin_renderer_base {
|
||||
*/
|
||||
public function backup_details_unknown(moodle_url $nextstageurl) {
|
||||
|
||||
$html = html_writer::start_tag('div', array('class' => 'unknownformat'));
|
||||
$html .= $this->output->heading(get_string('errorinvalidformat', 'backup'), 2, 'notifyproblem');
|
||||
$html .= html_writer::tag('div', get_string('errorinvalidformatinfo', 'backup'), array('class' => 'notifyproblem'));
|
||||
$html = html_writer::start_div('unknownformat');
|
||||
$html .= $this->output->heading(get_string('errorinvalidformat', 'backup'), 2);
|
||||
$html .= $this->output->notification(get_string('errorinvalidformatinfo', 'backup'), 'notifyproblem');
|
||||
$html .= $this->output->single_button($nextstageurl, get_string('continue'), 'post');
|
||||
$html .= html_writer::end_tag('div');
|
||||
$html .= html_writer::end_div();
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,8 @@ class block_mentees extends block_base {
|
||||
$this->content = new stdClass();
|
||||
|
||||
// get all the mentees, i.e. users you have a direct assignment to
|
||||
if ($usercontexts = $DB->get_records_sql("SELECT c.instanceid, c.instanceid, u.firstname, u.lastname
|
||||
$allusernames = get_all_user_name_fields(true, 'u');
|
||||
if ($usercontexts = $DB->get_records_sql("SELECT c.instanceid, c.instanceid, $allusernames
|
||||
FROM {role_assignments} ra, {context} c, {user} u
|
||||
WHERE ra.userid = ?
|
||||
AND ra.contextid = c.id
|
||||
|
||||
+7
-1
@@ -105,7 +105,13 @@ if (!in_array($mode, $modes)) {
|
||||
$mode = reset($modes);
|
||||
}
|
||||
|
||||
add_to_log($course->id, "course", "user report", "user.php?id=$course->id&user=$user->id&mode=$mode", "$user->id");
|
||||
$eventdata = array(
|
||||
'context' => $coursecontext,
|
||||
'relateduserid' => $user->id,
|
||||
'other' => array('mode' => $mode),
|
||||
);
|
||||
$event = \core\event\course_user_report_viewed::create($eventdata);
|
||||
$event->trigger();
|
||||
|
||||
$stractivityreport = get_string("activityreport");
|
||||
|
||||
|
||||
+8
-7
@@ -94,11 +94,7 @@
|
||||
|
||||
require_once($CFG->dirroot.'/calendar/lib.php'); /// This is after login because it needs $USER
|
||||
|
||||
$logparam = 'id='. $course->id;
|
||||
$loglabel = 'view';
|
||||
$infoid = $course->id;
|
||||
if ($section and $section > 0) {
|
||||
$loglabel = 'view section';
|
||||
|
||||
// Get section details and check it exists.
|
||||
$modinfo = get_fast_modinfo($course);
|
||||
@@ -111,10 +107,7 @@
|
||||
// correct error message shown.
|
||||
require_capability('moodle/course:viewhiddensections', $context);
|
||||
}
|
||||
$infoid = $coursesections->id;
|
||||
$logparam .= '§ionid='. $infoid;
|
||||
}
|
||||
add_to_log($course->id, 'course', $loglabel, "view.php?". $logparam, $infoid);
|
||||
|
||||
// Fix course format if it is no longer installed
|
||||
$course->format = course_get_format($course)->get_format();
|
||||
@@ -288,6 +281,14 @@
|
||||
|
||||
echo html_writer::end_tag('div');
|
||||
|
||||
// Trigger course viewed event.
|
||||
$eventdata = array('context' => $context);
|
||||
if (!empty($section)) {
|
||||
$eventdata['other'] = array('coursesectionid' => $section);
|
||||
}
|
||||
$event = \core\event\course_viewed::create($eventdata);
|
||||
$event->trigger();
|
||||
|
||||
// Include course AJAX
|
||||
include_course_ajax($course, $modnamesused);
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ function import_xml_grades($text, $course, &$error) {
|
||||
$newgrade->importer = $USER->id;
|
||||
|
||||
// check grade value exists and is a numeric grade
|
||||
if (isset($result['#']['score'][0]['#'])) {
|
||||
if (isset($result['#']['score'][0]['#']) && $result['#']['score'][0]['#'] !== '-') {
|
||||
if (is_numeric($result['#']['score'][0]['#'])) {
|
||||
$newgrade->finalgrade = $result['#']['score'][0]['#'];
|
||||
} else {
|
||||
|
||||
@@ -75,9 +75,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (isloggedin()) {
|
||||
add_to_log(SITEID, 'course', 'view', 'view.php?id='.SITEID, SITEID);
|
||||
}
|
||||
$eventparams = array('context' => context_course::instance(SITEID));
|
||||
$event = \core\event\course_viewed::create($eventparams);
|
||||
$event->trigger();
|
||||
|
||||
/// If the hub plugin is installed then we let it take over the homepage here
|
||||
if (file_exists($CFG->dirroot.'/local/hub/lib.php') and get_config('local_hub', 'hubenabled')) {
|
||||
|
||||
+3
-3
@@ -68,15 +68,15 @@ $string['awards'] = 'Recipients';
|
||||
$string['backpackavailability'] = 'External badge verification';
|
||||
$string['backpackavailability_help'] = 'For badge recipients to be able to prove they earned their badges from you, an external backpack service should be able to access your site and verify badges issued from it. Your site does not currently appear to be accessible, which means that badges you have already issued or will issue in the future cannot be verified.
|
||||
|
||||
##Why am I seeing this message?
|
||||
**Why am I seeing this message?**
|
||||
|
||||
It may be that your firewall prevents access from users outside your network, your site is password protected, or you are running the site on a computer that is not available from the Internet (such as a local development machine).
|
||||
|
||||
##Is this a problem?
|
||||
**Is this a problem?**
|
||||
|
||||
You should fix this issue on any production site where you are planning to issue badges, otherwise the recipients will not be able to prove they earned their badges from you. If your site is not yet live you can create and issue test badges, as long as the site is accessible before you go live.
|
||||
|
||||
##What if I can\'t make my whole site publicly accessible?
|
||||
**What if I can\'t make my whole site publicly accessible?**
|
||||
|
||||
The only URL required for verification is [your-site-url]/badges/assertion.php so if you are able to modify your firewall to allow external access to that file, badge verification will still work.';
|
||||
$string['backpackbadges'] = 'You have {$a->totalbadges} badge(s) displayed from {$a->totalcollections} collection(s). <a href="mybackpack.php">Change backpack settings</a>.';
|
||||
|
||||
@@ -737,6 +737,8 @@ $string['eventcourserestored'] = 'Course restored';
|
||||
$string['eventcourseupdated'] = 'Course updated';
|
||||
$string['eventcoursesectionupdated'] = ' Course section updated';
|
||||
$string['eventcoursemoduleinstancelistviewed'] = 'Course module instance list viewed';
|
||||
$string['eventcourseuserreportviewed'] = 'Course user report viewed';
|
||||
$string['eventcourseviewed'] = 'Course viewed';
|
||||
$string['eventemailfailed'] = 'Email failed to send';
|
||||
$string['eventname'] = 'Event name';
|
||||
$string['eventunknownlogged'] = 'Unknown event';
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
- AlfrescoWebService::__soapCall() arguments do not match SoapClient::__soapCall()
|
||||
- AlfrescoWebService::__doRequest() arguments do not match SoapClient::__soapCall()
|
||||
|
||||
6. Apply the changes from MDL-41975 in regard with the timestamp
|
||||
|
||||
== Alfresco PHP Library ==
|
||||
|
||||
Installation and developer documentation for the Alfresco PHP Library can be found on the Alfresco Wiki.
|
||||
|
||||
@@ -79,8 +79,8 @@ class AlfrescoWebService extends SoapClient
|
||||
|
||||
// Construct Timestamp Header
|
||||
$timeStamp = $dom->createElementNS($this->wsUtilityNS, "Timestamp");
|
||||
$createdDate = date("Y-m-d\TH:i:s\Z", mktime(date("H")+24, date("i"), date("s"), date("m"), date("d"), date("Y")));
|
||||
$expiresDate = date("Y-m-d\TH:i:s\Z", mktime(date("H")+25, date("i"), date("s"), date("m"), date("d"), date("Y")));
|
||||
$createdDate = gmdate("Y-m-d\TH:i:s\Z", gmmktime(gmdate("H"), gmdate("i"), gmdate("s"), gmdate("m"), gmdate("d"), gmdate("Y")));
|
||||
$expiresDate = gmdate("Y-m-d\TH:i:s\Z", gmmktime(gmdate("H")+1, gmdate("i"), gmdate("s"), gmdate("m"), gmdate("d"), gmdate("Y")));
|
||||
$created = new DOMElement("Created", $createdDate, $this->wsUtilityNS);
|
||||
$expires = new DOMElement("Expires", $expiresDate, $this->wsUtilityNS);
|
||||
$timeStamp->appendChild($created);
|
||||
|
||||
+6
-1
@@ -481,6 +481,7 @@ class block_manager {
|
||||
if (!isset($bc->attributes['data-block'])) {
|
||||
$bc->attributes['data-block'] = '_fake';
|
||||
}
|
||||
$bc->attributes['class'] .= ' block_fake';
|
||||
$this->extracontent[$region][] = $bc;
|
||||
}
|
||||
|
||||
@@ -518,7 +519,11 @@ class block_manager {
|
||||
|
||||
$this->check_is_loaded();
|
||||
$this->ensure_content_created($region, $output);
|
||||
foreach($this->visibleblockcontent[$region] as $instance) {
|
||||
if (!$this->region_has_content($region, $output)) {
|
||||
// If the region has no content then nothing is docked at all of course.
|
||||
return false;
|
||||
}
|
||||
foreach ($this->visibleblockcontent[$region] as $instance) {
|
||||
if (!empty($instance->content) && !get_user_preferences('docked_block_instance_'.$instance->blockinstanceid, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Course user report viewed event.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2014 Adrian Greeve <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core\event;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Course user report viewed event class.
|
||||
*
|
||||
* Class for event to be triggered when a course user report is viewed.
|
||||
* @property-read array $other Extra information about the event.
|
||||
* -string mode: Mode is used to show the user different data.
|
||||
*
|
||||
* @package core
|
||||
* @since Moodle 2.7
|
||||
* @copyright 2014 Adrian Greeve <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class course_user_report_viewed extends \core\event\base {
|
||||
|
||||
/**
|
||||
* Init method.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function init() {
|
||||
$this->data['crud'] = 'r';
|
||||
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of what happened.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description() {
|
||||
return "A user with the id '$this->userid' viewed the user report in the course '$this->courseid' for user '$this->relateduserid'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('eventcourseuserreportviewed', 'core');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL related to the action.
|
||||
*
|
||||
* @return \moodle_url
|
||||
*/
|
||||
public function get_url() {
|
||||
return new \moodle_url("/course/user.php", array('id' => $this->courseid, 'user' => $this->relateduserid,
|
||||
'mode' => $this->other['mode']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the legacy event log data.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
protected function get_legacy_logdata() {
|
||||
return array($this->courseid, 'course', 'user report', 'user.php?id=' . $this->courseid . '&user='
|
||||
. $this->relateduserid . '&mode=' . $this->other['mode'], $this->relateduserid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom validation.
|
||||
*
|
||||
* @throws \coding_exception
|
||||
* @return void
|
||||
*/
|
||||
protected function validate_data() {
|
||||
parent::validate_data();
|
||||
|
||||
if ($this->contextlevel != CONTEXT_COURSE) {
|
||||
throw new \coding_exception('Context passed must be course context.');
|
||||
}
|
||||
|
||||
if (empty($this->relateduserid)) {
|
||||
throw new \coding_exception('relateduserid needs to be set.');
|
||||
}
|
||||
|
||||
// Make sure this class is never used without proper object details.
|
||||
if (!isset($this->other['mode'])) {
|
||||
throw new \coding_exception('mode needs to be set in $other.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Course viewed event.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2014 Adrian Greeve <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core\event;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Course viewed event class.
|
||||
*
|
||||
* Class for event to be triggered when a course is viewed.
|
||||
* @property-read array $other Extra information about the event.
|
||||
* -int coursesectionid: The course section ID (Optional!).
|
||||
*
|
||||
* @package core
|
||||
* @since Moodle 2.7
|
||||
* @copyright 2014 Adrian Greeve <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class course_viewed extends \core\event\base {
|
||||
|
||||
/**
|
||||
* Init method.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function init() {
|
||||
$this->data['crud'] = 'r';
|
||||
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of what happened.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description() {
|
||||
return "A user with the id '$this->userid' viewed the course '$this->courseid'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return localised event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return get_string('eventcourseviewed', 'core');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL related to the action.
|
||||
*
|
||||
* @return \moodle_url|null
|
||||
*/
|
||||
public function get_url() {
|
||||
$sectionid = null;
|
||||
if (isset($this->other['coursesectionid'])) {
|
||||
$sectionid = $this->other['coursesectionid'];
|
||||
}
|
||||
try {
|
||||
return \course_get_url($this->courseid, $sectionid);
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the legacy event log data.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
protected function get_legacy_logdata() {
|
||||
if ($this->courseid == SITEID and !isloggedin()) {
|
||||
// We did not log frontpage access in older Moodle versions.
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isset($this->other['coursesectionid'])) {
|
||||
return array($this->courseid, 'course', 'view section', 'view.php?id=' . $this->courseid . '&sectionid='
|
||||
. $this->other['coursesectionid'], $this->other['coursesectionid']);
|
||||
}
|
||||
return array($this->courseid, 'course', 'view', 'view.php?id=' . $this->courseid, $this->courseid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom validation.
|
||||
*
|
||||
* @throws \coding_exception
|
||||
* @return void
|
||||
*/
|
||||
protected function validate_data() {
|
||||
parent::validate_data();
|
||||
|
||||
if ($this->contextlevel != CONTEXT_COURSE) {
|
||||
throw new \coding_exception('Context passed must be course context.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,13 +42,6 @@ interface reader {
|
||||
*/
|
||||
public function get_description();
|
||||
|
||||
/**
|
||||
* Can the current user access this store?
|
||||
* @param \context $context
|
||||
* @return bool
|
||||
*/
|
||||
public function can_access(\context $context);
|
||||
|
||||
/**
|
||||
* Are the new events appearing in the reader?
|
||||
*
|
||||
|
||||
+2
@@ -85,6 +85,8 @@ Y.namespace('M.atto_collapse').Button = Y.Base.create('button', Y.M.editor_atto.
|
||||
} else {
|
||||
this._setVisibility(button);
|
||||
}
|
||||
|
||||
this.buttons[this.name].focus();
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-atto_collapse-button",function(e,t){var n="atto_collapse",r="showgroups",i="collapse",s="collapsed",o=".atto_group";e.namespace("M.atto_collapse").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{initializer:function(){var t=e.Object.size(this.get("host").get("plugins"));if(t<=1+parseInt(this.get(r),10))return;if(this.toolbar.all(o).size()>this.get(r))return;var i=this.addButton({icon:M.util.image_url("icon",n),callback:this._toggle});this.get("host").on("pluginsloaded",function(e,t){this._setVisibility(t);var n=this.toolbar.all(o).item(this.get(r));n.insert('<div class="toolbarbreak"></div>',"before")},this,i)},_toggle:function(e){e.preventDefault();var t=this.buttons[i];t.getData(s)?this._setVisibility(t,!0):this._setVisibility(t)},_setVisibility:function(e,t){var i=this.toolbar.all(o).slice(this.get(r));t?(e.set("title",M.util.get_string("showfewer",n)),i.show(),e.setData(s,!1)):(e.set("title",M.util.get_string("showmore",n)),i.hide(),e.setData(s,!0))}},{ATTRS:{showgroups:{value:3}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
|
||||
YUI.add("moodle-atto_collapse-button",function(e,t){var n="atto_collapse",r="showgroups",i="collapse",s="collapsed",o=".atto_group";e.namespace("M.atto_collapse").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{initializer:function(){var t=e.Object.size(this.get("host").get("plugins"));if(t<=1+parseInt(this.get(r),10))return;if(this.toolbar.all(o).size()>this.get(r))return;var i=this.addButton({icon:M.util.image_url("icon",n),callback:this._toggle});this.get("host").on("pluginsloaded",function(e,t){this._setVisibility(t);var n=this.toolbar.all(o).item(this.get(r));n.insert('<div class="toolbarbreak"></div>',"before")},this,i)},_toggle:function(e){e.preventDefault();var t=this.buttons[i];t.getData(s)?this._setVisibility(t,!0):this._setVisibility(t),this.buttons[this.name].focus()},_setVisibility:function(e,t){var i=this.toolbar.all(o).slice(this.get(r));t?(e.set("title",M.util.get_string("showfewer",n)),i.show(),e.setData(s,!1)):(e.set("title",M.util.get_string("showmore",n)),i.hide(),e.setData(s,!0))}},{ATTRS:{showgroups:{value:3}}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
|
||||
|
||||
+2
@@ -81,6 +81,8 @@ Y.namespace('M.atto_collapse').Button = Y.Base.create('button', Y.M.editor_atto.
|
||||
} else {
|
||||
this._setVisibility(button);
|
||||
}
|
||||
|
||||
this.buttons[this.name].focus();
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -83,6 +83,8 @@ Y.namespace('M.atto_collapse').Button = Y.Base.create('button', Y.M.editor_atto.
|
||||
} else {
|
||||
this._setVisibility(button);
|
||||
}
|
||||
|
||||
this.buttons[this.name].focus();
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,11 +28,12 @@ define('AJAX_SCRIPT', true);
|
||||
require_once(dirname(__FILE__) . '/../../../../../config.php');
|
||||
|
||||
$contextid = required_param('contextid', PARAM_INT);
|
||||
$context = context::instance_by_id($contextid, MUST_EXIST);
|
||||
|
||||
list($context, $course, $cm) = get_context_info_array($contextid);
|
||||
$PAGE->set_url('/lib/editor/atto/plugins/equation/ajax.php');
|
||||
$PAGE->set_context($context);
|
||||
|
||||
require_login();
|
||||
require_login($course, false, $cm);
|
||||
require_sesskey();
|
||||
|
||||
$action = required_param('action', PARAM_ALPHA);
|
||||
|
||||
@@ -20,3 +20,7 @@
|
||||
padding: 5px;
|
||||
min-width: 206px;
|
||||
}
|
||||
|
||||
.atto_form .atto_equation_preview {
|
||||
margin-bottom: 0px; /* Turn off the margin-bottom from the class "well" */
|
||||
}
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ var COMPONENTNAME = 'atto_equation',
|
||||
'<label for="{{elementid}}_{{CSS.EQUATION_TEXT}}">{{{get_string "editequation" component texdocsurl}}}</label>' +
|
||||
'<textarea class="fullwidth {{CSS.EQUATION_TEXT}}" id="{{elementid}}_{{CSS.EQUATION_TEXT}}" rows="8"></textarea><br/>' +
|
||||
'<label for="{{elementid}}_{{CSS.EQUATION_PREVIEW}}">{{get_string "preview" component}}</label>' +
|
||||
'<div class="fullwidth {{CSS.EQUATION_PREVIEW}}" id="{{elementid}}_{{CSS.EQUATION_PREVIEW}}"></div>' +
|
||||
'<div class="well well-small fullwidth {{CSS.EQUATION_PREVIEW}}" id="{{elementid}}_{{CSS.EQUATION_PREVIEW}}"></div>' +
|
||||
'<div class="mdl-align">' +
|
||||
'<br/>' +
|
||||
'<button class="{{CSS.SUBMIT}}">{{get_string "saveequation" component}}</button>' +
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
@@ -62,7 +62,7 @@ var COMPONENTNAME = 'atto_equation',
|
||||
'<label for="{{elementid}}_{{CSS.EQUATION_TEXT}}">{{{get_string "editequation" component texdocsurl}}}</label>' +
|
||||
'<textarea class="fullwidth {{CSS.EQUATION_TEXT}}" id="{{elementid}}_{{CSS.EQUATION_TEXT}}" rows="8"></textarea><br/>' +
|
||||
'<label for="{{elementid}}_{{CSS.EQUATION_PREVIEW}}">{{get_string "preview" component}}</label>' +
|
||||
'<div class="fullwidth {{CSS.EQUATION_PREVIEW}}" id="{{elementid}}_{{CSS.EQUATION_PREVIEW}}"></div>' +
|
||||
'<div class="well well-small fullwidth {{CSS.EQUATION_PREVIEW}}" id="{{elementid}}_{{CSS.EQUATION_PREVIEW}}"></div>' +
|
||||
'<div class="mdl-align">' +
|
||||
'<br/>' +
|
||||
'<button class="{{CSS.SUBMIT}}">{{get_string "saveequation" component}}</button>' +
|
||||
|
||||
@@ -60,7 +60,7 @@ var COMPONENTNAME = 'atto_equation',
|
||||
'<label for="{{elementid}}_{{CSS.EQUATION_TEXT}}">{{{get_string "editequation" component texdocsurl}}}</label>' +
|
||||
'<textarea class="fullwidth {{CSS.EQUATION_TEXT}}" id="{{elementid}}_{{CSS.EQUATION_TEXT}}" rows="8"></textarea><br/>' +
|
||||
'<label for="{{elementid}}_{{CSS.EQUATION_PREVIEW}}">{{get_string "preview" component}}</label>' +
|
||||
'<div class="fullwidth {{CSS.EQUATION_PREVIEW}}" id="{{elementid}}_{{CSS.EQUATION_PREVIEW}}"></div>' +
|
||||
'<div class="well well-small fullwidth {{CSS.EQUATION_PREVIEW}}" id="{{elementid}}_{{CSS.EQUATION_PREVIEW}}"></div>' +
|
||||
'<div class="mdl-align">' +
|
||||
'<br/>' +
|
||||
'<button class="{{CSS.SUBMIT}}">{{get_string "saveequation" component}}</button>' +
|
||||
|
||||
+3
-15
@@ -312,7 +312,6 @@ EditorPluginButtons.prototype = {
|
||||
* specified, in the class for the button.
|
||||
* @param {function} config.callback A callback function to call when the button is clicked.
|
||||
* @param {object} [config.callbackArgs] Any arguments to pass to the callback.
|
||||
* @param {boolean} [config.wrapCallback=true] Whether to wrap the callback in default functionality.
|
||||
* @return {Node} The Node representing the newly created button.
|
||||
*/
|
||||
addButton: function(config) {
|
||||
@@ -429,7 +428,6 @@ EditorPluginButtons.prototype = {
|
||||
* @param {string} [config.title=this.name] The string identifier in the plugin's language file.
|
||||
* @param {string} [config.buttonName=this.name] The name of the button. This is used in the buttons object, and if
|
||||
* specified, in the class for the button.
|
||||
* @param {boolean} [config.wrapCallback=true] Whether to wrap the callback in default functionality.
|
||||
* @return {Node} The Node representing the newly created button.
|
||||
*/
|
||||
addBasicButton: function(config) {
|
||||
@@ -469,7 +467,6 @@ EditorPluginButtons.prototype = {
|
||||
* specified, in the class for the button.
|
||||
* @param {function} config.callback A callback function to call when the button is clicked.
|
||||
* @param {object} [config.callbackArgs] Any arguments to pass to the callback.
|
||||
* @param {boolean} [config.wrapCallback=true] Whether to wrap the callback in default functionality.
|
||||
* @param {array} config.entries List of menu entries with the string (entry.text) and the handlers (entry.handler).
|
||||
* @param {number} [config.overlayWidth=14] The width of the menu. This will be suffixed with the 'em' unit.
|
||||
* @param {string} [config.menuColor] menu icon background color
|
||||
@@ -656,7 +653,6 @@ EditorPluginButtons.prototype = {
|
||||
* @param {object} config
|
||||
* @param {function} config.callback A callback function to call when the button is clicked.
|
||||
* @param {object} [config.callbackArgs] Any arguments to pass to the callback.
|
||||
* @param {boolean} [config.wrapCallback=true] Whether to wrap the callback in default functionality.
|
||||
* @param {object} [inheritFrom] A parent configuration that this configuration may inherit from.
|
||||
* @return {object} The normalized configuration
|
||||
* @private
|
||||
@@ -672,18 +668,10 @@ EditorPluginButtons.prototype = {
|
||||
inheritFrom = {};
|
||||
}
|
||||
|
||||
// By default, we wrap the callback in function to prevent the default action, check whether the editor is
|
||||
// We wrap the callback in function to prevent the default action, check whether the editor is
|
||||
// active and focus it, and then mark the field as updated.
|
||||
if (typeof config.wrapCallback === 'undefined') {
|
||||
config.wrapCallback = inheritFrom.wrapCallback || true;
|
||||
}
|
||||
|
||||
if (config.wrapCallback) {
|
||||
config._callback = config.callback || inheritFrom.callback;
|
||||
config.callback = Y.rbind(this._callbackWrapper, this, config._callback, config.callbackArgs);
|
||||
} else {
|
||||
config.callback = config.callback || inheritFrom.callback;
|
||||
}
|
||||
config._callback = config.callback || inheritFrom.callback;
|
||||
config.callback = Y.rbind(this._callbackWrapper, this, config._callback, config.callbackArgs);
|
||||
|
||||
config._callbackNormalized = true;
|
||||
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
+3
-15
@@ -312,7 +312,6 @@ EditorPluginButtons.prototype = {
|
||||
* specified, in the class for the button.
|
||||
* @param {function} config.callback A callback function to call when the button is clicked.
|
||||
* @param {object} [config.callbackArgs] Any arguments to pass to the callback.
|
||||
* @param {boolean} [config.wrapCallback=true] Whether to wrap the callback in default functionality.
|
||||
* @return {Node} The Node representing the newly created button.
|
||||
*/
|
||||
addButton: function(config) {
|
||||
@@ -429,7 +428,6 @@ EditorPluginButtons.prototype = {
|
||||
* @param {string} [config.title=this.name] The string identifier in the plugin's language file.
|
||||
* @param {string} [config.buttonName=this.name] The name of the button. This is used in the buttons object, and if
|
||||
* specified, in the class for the button.
|
||||
* @param {boolean} [config.wrapCallback=true] Whether to wrap the callback in default functionality.
|
||||
* @return {Node} The Node representing the newly created button.
|
||||
*/
|
||||
addBasicButton: function(config) {
|
||||
@@ -467,7 +465,6 @@ EditorPluginButtons.prototype = {
|
||||
* specified, in the class for the button.
|
||||
* @param {function} config.callback A callback function to call when the button is clicked.
|
||||
* @param {object} [config.callbackArgs] Any arguments to pass to the callback.
|
||||
* @param {boolean} [config.wrapCallback=true] Whether to wrap the callback in default functionality.
|
||||
* @param {array} config.entries List of menu entries with the string (entry.text) and the handlers (entry.handler).
|
||||
* @param {number} [config.overlayWidth=14] The width of the menu. This will be suffixed with the 'em' unit.
|
||||
* @param {string} [config.menuColor] menu icon background color
|
||||
@@ -654,7 +651,6 @@ EditorPluginButtons.prototype = {
|
||||
* @param {object} config
|
||||
* @param {function} config.callback A callback function to call when the button is clicked.
|
||||
* @param {object} [config.callbackArgs] Any arguments to pass to the callback.
|
||||
* @param {boolean} [config.wrapCallback=true] Whether to wrap the callback in default functionality.
|
||||
* @param {object} [inheritFrom] A parent configuration that this configuration may inherit from.
|
||||
* @return {object} The normalized configuration
|
||||
* @private
|
||||
@@ -670,18 +666,10 @@ EditorPluginButtons.prototype = {
|
||||
inheritFrom = {};
|
||||
}
|
||||
|
||||
// By default, we wrap the callback in function to prevent the default action, check whether the editor is
|
||||
// We wrap the callback in function to prevent the default action, check whether the editor is
|
||||
// active and focus it, and then mark the field as updated.
|
||||
if (typeof config.wrapCallback === 'undefined') {
|
||||
config.wrapCallback = inheritFrom.wrapCallback || true;
|
||||
}
|
||||
|
||||
if (config.wrapCallback) {
|
||||
config._callback = config.callback || inheritFrom.callback;
|
||||
config.callback = Y.rbind(this._callbackWrapper, this, config._callback, config.callbackArgs);
|
||||
} else {
|
||||
config.callback = config.callback || inheritFrom.callback;
|
||||
}
|
||||
config._callback = config.callback || inheritFrom.callback;
|
||||
config.callback = Y.rbind(this._callbackWrapper, this, config._callback, config.callbackArgs);
|
||||
|
||||
config._callbackNormalized = true;
|
||||
|
||||
|
||||
+3
-15
@@ -164,7 +164,6 @@ EditorPluginButtons.prototype = {
|
||||
* specified, in the class for the button.
|
||||
* @param {function} config.callback A callback function to call when the button is clicked.
|
||||
* @param {object} [config.callbackArgs] Any arguments to pass to the callback.
|
||||
* @param {boolean} [config.wrapCallback=true] Whether to wrap the callback in default functionality.
|
||||
* @return {Node} The Node representing the newly created button.
|
||||
*/
|
||||
addButton: function(config) {
|
||||
@@ -281,7 +280,6 @@ EditorPluginButtons.prototype = {
|
||||
* @param {string} [config.title=this.name] The string identifier in the plugin's language file.
|
||||
* @param {string} [config.buttonName=this.name] The name of the button. This is used in the buttons object, and if
|
||||
* specified, in the class for the button.
|
||||
* @param {boolean} [config.wrapCallback=true] Whether to wrap the callback in default functionality.
|
||||
* @return {Node} The Node representing the newly created button.
|
||||
*/
|
||||
addBasicButton: function(config) {
|
||||
@@ -321,7 +319,6 @@ EditorPluginButtons.prototype = {
|
||||
* specified, in the class for the button.
|
||||
* @param {function} config.callback A callback function to call when the button is clicked.
|
||||
* @param {object} [config.callbackArgs] Any arguments to pass to the callback.
|
||||
* @param {boolean} [config.wrapCallback=true] Whether to wrap the callback in default functionality.
|
||||
* @param {array} config.entries List of menu entries with the string (entry.text) and the handlers (entry.handler).
|
||||
* @param {number} [config.overlayWidth=14] The width of the menu. This will be suffixed with the 'em' unit.
|
||||
* @param {string} [config.menuColor] menu icon background color
|
||||
@@ -508,7 +505,6 @@ EditorPluginButtons.prototype = {
|
||||
* @param {object} config
|
||||
* @param {function} config.callback A callback function to call when the button is clicked.
|
||||
* @param {object} [config.callbackArgs] Any arguments to pass to the callback.
|
||||
* @param {boolean} [config.wrapCallback=true] Whether to wrap the callback in default functionality.
|
||||
* @param {object} [inheritFrom] A parent configuration that this configuration may inherit from.
|
||||
* @return {object} The normalized configuration
|
||||
* @private
|
||||
@@ -524,18 +520,10 @@ EditorPluginButtons.prototype = {
|
||||
inheritFrom = {};
|
||||
}
|
||||
|
||||
// By default, we wrap the callback in function to prevent the default action, check whether the editor is
|
||||
// We wrap the callback in function to prevent the default action, check whether the editor is
|
||||
// active and focus it, and then mark the field as updated.
|
||||
if (typeof config.wrapCallback === 'undefined') {
|
||||
config.wrapCallback = inheritFrom.wrapCallback || true;
|
||||
}
|
||||
|
||||
if (config.wrapCallback) {
|
||||
config._callback = config.callback || inheritFrom.callback;
|
||||
config.callback = Y.rbind(this._callbackWrapper, this, config._callback, config.callbackArgs);
|
||||
} else {
|
||||
config.callback = config.callback || inheritFrom.callback;
|
||||
}
|
||||
config._callback = config.callback || inheritFrom.callback;
|
||||
config.callback = Y.rbind(this._callbackWrapper, this, config._callback, config.callbackArgs);
|
||||
|
||||
config._callbackNormalized = true;
|
||||
|
||||
|
||||
@@ -1559,6 +1559,7 @@ function &get_mimetypes_array() {
|
||||
'webm' => array ('type'=>'video/webm', 'icon'=>'video', 'groups'=>array('video'), 'string'=>'video'),
|
||||
'wmv' => array ('type'=>'video/x-ms-wmv', 'icon'=>'wmv', 'groups'=>array('video'), 'string'=>'video'),
|
||||
'asf' => array ('type'=>'video/x-ms-asf', 'icon'=>'wmv', 'groups'=>array('video'), 'string'=>'video'),
|
||||
'wma' => array ('type'=>'audio/x-ms-wma', 'icon'=>'audio', 'groups'=>array('audio'), 'string'=>'audio'),
|
||||
|
||||
'xbk' => array ('type'=>'application/x-smarttech-notebook', 'icon'=>'archive'),
|
||||
'xdp' => array ('type'=>'application/pdf', 'icon'=>'pdf'),
|
||||
|
||||
@@ -355,7 +355,10 @@ M.form_dndupload.init = function(Y, options) {
|
||||
* @return boolean true if event has files
|
||||
*/
|
||||
has_files: function(e) {
|
||||
var types = e._event.dataTransfer.types;
|
||||
// In some browsers, dataTransfer.types may be null for a
|
||||
// 'dragover' event, so ensure a valid Array is always
|
||||
// inspected.
|
||||
var types = e._event.dataTransfer.types || [];
|
||||
for (var i=0; i<types.length; i++) {
|
||||
if (types[i] == 'Files') {
|
||||
return true;
|
||||
|
||||
+1
-1
@@ -391,7 +391,7 @@ function install_print_footer($config, $reload=false) {
|
||||
|
||||
$homelink = '<div class="sitelink">'.
|
||||
'<a title="Moodle '. $CFG->target_release .'" href="http://docs.moodle.org/en/Administrator_documentation" onclick="this.target=\'_blank\'">'.
|
||||
'<img style="width:100px;height:30px" src="pix/moodlelogo.gif" alt="moodlelogo" /></a></div>';
|
||||
'<img src="pix/moodlelogo.png" alt="moodlelogo" /></a></div>';
|
||||
|
||||
echo '</form></div>';
|
||||
echo '<div id="footer"><hr />'.$homelink.'</div>';
|
||||
|
||||
@@ -4933,6 +4933,11 @@ function delete_course($courseorid, $showfeedback = true) {
|
||||
$DB->delete_records("course", array("id" => $courseid));
|
||||
$DB->delete_records("course_format_options", array("courseid" => $courseid));
|
||||
|
||||
// Reset all course related caches here.
|
||||
if (class_exists('format_base', false)) {
|
||||
format_base::reset_course_cache($courseid);
|
||||
}
|
||||
|
||||
// Trigger a course deleted event.
|
||||
$event = \core\event\course_deleted::create(array(
|
||||
'objectid' => $course->id,
|
||||
|
||||
@@ -698,13 +698,13 @@ class core_renderer extends renderer_base {
|
||||
// Special case for site home page - please do not remove
|
||||
return '<div class="sitelink">' .
|
||||
'<a title="Moodle" href="http://moodle.org/">' .
|
||||
'<img style="width:100px;height:30px" src="' . $this->pix_url('moodlelogo') . '" alt="moodlelogo" /></a></div>';
|
||||
'<img src="' . $this->pix_url('moodlelogo') . '" alt="moodlelogo" /></a></div>';
|
||||
|
||||
} else if (!empty($CFG->target_release) && $CFG->target_release != $CFG->release) {
|
||||
// Special case for during install/upgrade.
|
||||
return '<div class="sitelink">'.
|
||||
'<a title="Moodle" href="http://docs.moodle.org/en/Administrator_documentation" onclick="this.target=\'_blank\'">' .
|
||||
'<img style="width:100px;height:30px" src="' . $this->pix_url('moodlelogo') . '" alt="moodlelogo" /></a></div>';
|
||||
'<img src="' . $this->pix_url('moodlelogo') . '" alt="moodlelogo" /></a></div>';
|
||||
|
||||
} else if ($this->page->course->id == $SITE->id || strpos($this->page->pagetype, 'course-view') === 0) {
|
||||
return '<div class="homelink"><a href="' . $CFG->wwwroot . '/">' .
|
||||
|
||||
@@ -178,4 +178,88 @@ class core_events_testcase extends advanced_testcase {
|
||||
$this->assertEventLegacyLogData($expected, $event);
|
||||
$this->assertEventContextNotUsed($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* There is no api involved so the best we can do is test legacy data by triggering event manually.
|
||||
*/
|
||||
public function test_course_user_report_viewed() {
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$context = context_course::instance($course->id);
|
||||
|
||||
$eventparams = array();
|
||||
$eventparams['context'] = $context;
|
||||
$eventparams['relateduserid'] = $user->id;
|
||||
$eventparams['other'] = array();
|
||||
$eventparams['other']['mode'] = 'grade';
|
||||
$event = \core\event\course_user_report_viewed::create($eventparams);
|
||||
|
||||
// Trigger and capture the event.
|
||||
$sink = $this->redirectEvents();
|
||||
$event->trigger();
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
$this->assertInstanceOf('\core\event\course_user_report_viewed', $event);
|
||||
$this->assertEquals(context_course::instance($course->id), $event->get_context());
|
||||
$expected = array($course->id, 'course', 'user report', 'user.php?id=' . $course->id . '&user='
|
||||
. $user->id . '&mode=grade', $user->id);
|
||||
$this->assertEventLegacyLogData($expected, $event);
|
||||
$this->assertEventContextNotUsed($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* There is no api involved so the best we can do is test legacy data by triggering event manually.
|
||||
*/
|
||||
public function test_course_viewed() {
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$context = context_course::instance($course->id);
|
||||
|
||||
// First try with no optional parameters.
|
||||
$eventparams = array();
|
||||
$eventparams['context'] = $context;
|
||||
$event = \core\event\course_viewed::create($eventparams);
|
||||
|
||||
// Trigger and capture the event.
|
||||
$sink = $this->redirectEvents();
|
||||
$event->trigger();
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
$this->assertInstanceOf('\core\event\course_viewed', $event);
|
||||
$this->assertEquals(context_course::instance($course->id), $event->get_context());
|
||||
$expected = array($course->id, 'course', 'view', 'view.php?id=' . $course->id, $course->id);
|
||||
$this->assertEventLegacyLogData($expected, $event);
|
||||
$this->assertEventContextNotUsed($event);
|
||||
|
||||
// Now try with optional parameters.
|
||||
$sectionid = 34;
|
||||
$eventparams = array();
|
||||
$eventparams['context'] = $context;
|
||||
$eventparams['other'] = array('coursesectionid' => $sectionid);
|
||||
$event = \core\event\course_viewed::create($eventparams);
|
||||
|
||||
// Trigger and capture the event.
|
||||
$sink = $this->redirectEvents();
|
||||
$event->trigger();
|
||||
$loggeddata = $event->get_data();
|
||||
$events = $sink->get_events();
|
||||
$event = reset($events);
|
||||
|
||||
|
||||
$this->assertInstanceOf('\core\event\course_viewed', $event);
|
||||
$this->assertEquals(context_course::instance($course->id), $event->get_context());
|
||||
$expected = array($course->id, 'course', 'view section', 'view.php?id=' . $course->id . '&sectionid='
|
||||
. $sectionid, $sectionid);
|
||||
$this->assertEventLegacyLogData($expected, $event);
|
||||
$this->assertEventContextNotUsed($event);
|
||||
|
||||
delete_course($course->id, false);
|
||||
$restored = \core\event\base::restore($loggeddata, array('origin' => 'web', 'ip' => '127.0.0.1'));
|
||||
$this->assertInstanceOf('\core\event\course_viewed', $restored);
|
||||
$this->assertNull($restored->get_url());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ if ($getpdf) {
|
||||
$doc->setPrintHeader(true);
|
||||
$doc->setHeaderMargin(10);
|
||||
$doc->setHeaderFont(array($fontfamily, 'b', 10));
|
||||
$doc->setHeaderData('pix/moodlelogo-med-white.gif', 40, $SITE->fullname, $CFG->wwwroot);
|
||||
$doc->setHeaderData('pix/moodlelogo-med.png', 40, $SITE->fullname, $CFG->wwwroot);
|
||||
|
||||
$doc->setPrintFooter(true);
|
||||
$doc->setFooterMargin(10);
|
||||
|
||||
+2
-2
@@ -2840,7 +2840,7 @@ function debugging($message = '', $level = DEBUG_NORMAL, $backtrace = null) {
|
||||
if (!$backtrace) {
|
||||
$backtrace = debug_backtrace();
|
||||
}
|
||||
$from = format_backtrace($backtrace, CLI_SCRIPT);
|
||||
$from = format_backtrace($backtrace, CLI_SCRIPT || NO_DEBUG_DISPLAY);
|
||||
if (PHPUNIT_TEST) {
|
||||
if (phpunit_util::debugging_triggered($message, $level, $from)) {
|
||||
// We are inside test, the debug message was logged.
|
||||
@@ -2851,7 +2851,7 @@ function debugging($message = '', $level = DEBUG_NORMAL, $backtrace = null) {
|
||||
if (NO_DEBUG_DISPLAY) {
|
||||
// Script does not want any errors or debugging in output,
|
||||
// we send the info to error log instead.
|
||||
error_log('Debugging: ' . $message . $from);
|
||||
error_log('Debugging: ' . $message . ' in '. PHP_EOL . $from);
|
||||
|
||||
} else if ($forcedebug or $CFG->debugdisplay) {
|
||||
if (!defined('DEBUGGING_PRINTED')) {
|
||||
|
||||
@@ -351,7 +351,7 @@ M.core.blockdraganddrop.is_using_blocks_render_method = function() {
|
||||
var goodregions = Y.all('.block-region[data-blockregion]').size();
|
||||
var allregions = Y.all('.block-region').size();
|
||||
this._isusingnewblocksmethod = (allregions === goodregions);
|
||||
if (goodregions > 0 && allregions > 0) {
|
||||
if (goodregions > 0 && allregions > 0 && goodregions !== allregions) {
|
||||
Y.log('Both core_renderer::blocks and core_renderer::blocks_for_region have been used.', 'warn', 'moodle-core_blocks');
|
||||
}
|
||||
}
|
||||
@@ -443,7 +443,6 @@ MANAGER.prototype = {
|
||||
i = 0,
|
||||
region,
|
||||
regionname,
|
||||
droptarget,
|
||||
dragdelegation;
|
||||
|
||||
// Evil required by M.core.dragdrop.
|
||||
@@ -472,7 +471,7 @@ MANAGER.prototype = {
|
||||
// Setting blockregion as droptarget (the case when it is empty)
|
||||
// The region-post (the right one)
|
||||
// is very narrow, so add extra padding on the left to drop block on it.
|
||||
droptarget = new Y.DD.Drop({
|
||||
new Y.DD.Drop({
|
||||
node: region.get_droptarget(),
|
||||
groups: this.groups,
|
||||
padding: '40 240 40 240'
|
||||
@@ -484,7 +483,7 @@ MANAGER.prototype = {
|
||||
nodes: '.'+CSS.BLOCK,
|
||||
target: true,
|
||||
handles: [SELECTOR.DRAGHANDLE],
|
||||
invalid: '.block-hider-hide, .block-hider-show, .moveto',
|
||||
invalid: '.block-hider-hide, .block-hider-show, .moveto, .block_fake',
|
||||
dragConfig: {groups: this.groups}
|
||||
});
|
||||
dragdelegation.dd.plug(Y.Plugin.DDProxy, {
|
||||
@@ -492,9 +491,10 @@ MANAGER.prototype = {
|
||||
moveOnEnd: false
|
||||
});
|
||||
dragdelegation.dd.plug(Y.Plugin.DDWinScroll);
|
||||
// On the mouse down event we will enable all block regions so that they can be dragged to.
|
||||
// This is VERY important as without it dnd won't work for empty block regions.
|
||||
dragdelegation.on('drag:mouseDown', this.enable_all_regions, this);
|
||||
|
||||
// On the DD Manager start operation, we enable all block regions so that they can be drop targets. This
|
||||
// must be done *before* drag:start but after dragging has been initialised.
|
||||
Y.DD.DDM.on('ddm:start', this.enable_all_regions, this);
|
||||
|
||||
region.change_block_move_icons(this);
|
||||
}
|
||||
@@ -536,11 +536,24 @@ MANAGER.prototype = {
|
||||
|
||||
/**
|
||||
* Enables all fo the regions so that they are all visible while dragging is occuring.
|
||||
*
|
||||
* @method enable_all_regions
|
||||
*/
|
||||
enable_all_regions : function() {
|
||||
var i = 0;
|
||||
var groups = Y.DD.DDM.activeDrag.get('groups');
|
||||
|
||||
// As we're called by Y.DD.DDM, we can't be certain that the call
|
||||
// relates specifically to a block drag/drop operation. Test
|
||||
// whether the relevant group applies here.
|
||||
if (!groups || Y.Array.indexOf(groups, 'block') === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var i;
|
||||
for (i in this.regionobjects) {
|
||||
if (!this.regionobjects.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
this.regionobjects[i].enable();
|
||||
}
|
||||
},
|
||||
|
||||
File diff suppressed because one or more lines are too long
+21
-8
@@ -351,7 +351,7 @@ M.core.blockdraganddrop.is_using_blocks_render_method = function() {
|
||||
var goodregions = Y.all('.block-region[data-blockregion]').size();
|
||||
var allregions = Y.all('.block-region').size();
|
||||
this._isusingnewblocksmethod = (allregions === goodregions);
|
||||
if (goodregions > 0 && allregions > 0) {
|
||||
if (goodregions > 0 && allregions > 0 && goodregions !== allregions) {
|
||||
}
|
||||
}
|
||||
return this._isusingnewblocksmethod;
|
||||
@@ -439,7 +439,6 @@ MANAGER.prototype = {
|
||||
i = 0,
|
||||
region,
|
||||
regionname,
|
||||
droptarget,
|
||||
dragdelegation;
|
||||
|
||||
// Evil required by M.core.dragdrop.
|
||||
@@ -468,7 +467,7 @@ MANAGER.prototype = {
|
||||
// Setting blockregion as droptarget (the case when it is empty)
|
||||
// The region-post (the right one)
|
||||
// is very narrow, so add extra padding on the left to drop block on it.
|
||||
droptarget = new Y.DD.Drop({
|
||||
new Y.DD.Drop({
|
||||
node: region.get_droptarget(),
|
||||
groups: this.groups,
|
||||
padding: '40 240 40 240'
|
||||
@@ -480,7 +479,7 @@ MANAGER.prototype = {
|
||||
nodes: '.'+CSS.BLOCK,
|
||||
target: true,
|
||||
handles: [SELECTOR.DRAGHANDLE],
|
||||
invalid: '.block-hider-hide, .block-hider-show, .moveto',
|
||||
invalid: '.block-hider-hide, .block-hider-show, .moveto, .block_fake',
|
||||
dragConfig: {groups: this.groups}
|
||||
});
|
||||
dragdelegation.dd.plug(Y.Plugin.DDProxy, {
|
||||
@@ -488,9 +487,10 @@ MANAGER.prototype = {
|
||||
moveOnEnd: false
|
||||
});
|
||||
dragdelegation.dd.plug(Y.Plugin.DDWinScroll);
|
||||
// On the mouse down event we will enable all block regions so that they can be dragged to.
|
||||
// This is VERY important as without it dnd won't work for empty block regions.
|
||||
dragdelegation.on('drag:mouseDown', this.enable_all_regions, this);
|
||||
|
||||
// On the DD Manager start operation, we enable all block regions so that they can be drop targets. This
|
||||
// must be done *before* drag:start but after dragging has been initialised.
|
||||
Y.DD.DDM.on('ddm:start', this.enable_all_regions, this);
|
||||
|
||||
region.change_block_move_icons(this);
|
||||
}
|
||||
@@ -531,11 +531,24 @@ MANAGER.prototype = {
|
||||
|
||||
/**
|
||||
* Enables all fo the regions so that they are all visible while dragging is occuring.
|
||||
*
|
||||
* @method enable_all_regions
|
||||
*/
|
||||
enable_all_regions : function() {
|
||||
var i = 0;
|
||||
var groups = Y.DD.DDM.activeDrag.get('groups');
|
||||
|
||||
// As we're called by Y.DD.DDM, we can't be certain that the call
|
||||
// relates specifically to a block drag/drop operation. Test
|
||||
// whether the relevant group applies here.
|
||||
if (!groups || Y.Array.indexOf(groups, 'block') === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var i;
|
||||
for (i in this.regionobjects) {
|
||||
if (!this.regionobjects.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
this.regionobjects[i].enable();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -750,7 +750,9 @@ DOCK.prototype = {
|
||||
BODY.addClass('used-region-'+regionname);
|
||||
BODY.removeClass('empty-region-'+regionname);
|
||||
BODY.removeClass('docked-region-'+regionname);
|
||||
} else {
|
||||
} else if (region.all('.block_dock_placeholder').size() > 0) {
|
||||
// There are no blocks in the region but there are placeholders.
|
||||
// All blocks in this region have been docked.
|
||||
BODY.addClass('empty-region-'+regionname);
|
||||
BODY.addClass('docked-region-'+regionname);
|
||||
BODY.removeClass('used-region-'+regionname);
|
||||
|
||||
+3
-3
File diff suppressed because one or more lines are too long
+3
-1
@@ -743,7 +743,9 @@ DOCK.prototype = {
|
||||
BODY.addClass('used-region-'+regionname);
|
||||
BODY.removeClass('empty-region-'+regionname);
|
||||
BODY.removeClass('docked-region-'+regionname);
|
||||
} else {
|
||||
} else if (region.all('.block_dock_placeholder').size() > 0) {
|
||||
// There are no blocks in the region but there are placeholders.
|
||||
// All blocks in this region have been docked.
|
||||
BODY.addClass('empty-region-'+regionname);
|
||||
BODY.addClass('docked-region-'+regionname);
|
||||
BODY.removeClass('used-region-'+regionname);
|
||||
|
||||
Vendored
+1
-1
@@ -349,7 +349,7 @@ M.core.blockdraganddrop.is_using_blocks_render_method = function() {
|
||||
var goodregions = Y.all('.block-region[data-blockregion]').size();
|
||||
var allregions = Y.all('.block-region').size();
|
||||
this._isusingnewblocksmethod = (allregions === goodregions);
|
||||
if (goodregions > 0 && allregions > 0) {
|
||||
if (goodregions > 0 && allregions > 0 && goodregions !== allregions) {
|
||||
Y.log('Both core_renderer::blocks and core_renderer::blocks_for_region have been used.', 'warn', 'moodle-core_blocks');
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+20
-7
@@ -58,7 +58,6 @@ MANAGER.prototype = {
|
||||
i = 0,
|
||||
region,
|
||||
regionname,
|
||||
droptarget,
|
||||
dragdelegation;
|
||||
|
||||
// Evil required by M.core.dragdrop.
|
||||
@@ -87,7 +86,7 @@ MANAGER.prototype = {
|
||||
// Setting blockregion as droptarget (the case when it is empty)
|
||||
// The region-post (the right one)
|
||||
// is very narrow, so add extra padding on the left to drop block on it.
|
||||
droptarget = new Y.DD.Drop({
|
||||
new Y.DD.Drop({
|
||||
node: region.get_droptarget(),
|
||||
groups: this.groups,
|
||||
padding: '40 240 40 240'
|
||||
@@ -99,7 +98,7 @@ MANAGER.prototype = {
|
||||
nodes: '.'+CSS.BLOCK,
|
||||
target: true,
|
||||
handles: [SELECTOR.DRAGHANDLE],
|
||||
invalid: '.block-hider-hide, .block-hider-show, .moveto',
|
||||
invalid: '.block-hider-hide, .block-hider-show, .moveto, .block_fake',
|
||||
dragConfig: {groups: this.groups}
|
||||
});
|
||||
dragdelegation.dd.plug(Y.Plugin.DDProxy, {
|
||||
@@ -107,9 +106,10 @@ MANAGER.prototype = {
|
||||
moveOnEnd: false
|
||||
});
|
||||
dragdelegation.dd.plug(Y.Plugin.DDWinScroll);
|
||||
// On the mouse down event we will enable all block regions so that they can be dragged to.
|
||||
// This is VERY important as without it dnd won't work for empty block regions.
|
||||
dragdelegation.on('drag:mouseDown', this.enable_all_regions, this);
|
||||
|
||||
// On the DD Manager start operation, we enable all block regions so that they can be drop targets. This
|
||||
// must be done *before* drag:start but after dragging has been initialised.
|
||||
Y.DD.DDM.on('ddm:start', this.enable_all_regions, this);
|
||||
|
||||
region.change_block_move_icons(this);
|
||||
}
|
||||
@@ -151,11 +151,24 @@ MANAGER.prototype = {
|
||||
|
||||
/**
|
||||
* Enables all fo the regions so that they are all visible while dragging is occuring.
|
||||
*
|
||||
* @method enable_all_regions
|
||||
*/
|
||||
enable_all_regions : function() {
|
||||
var i = 0;
|
||||
var groups = Y.DD.DDM.activeDrag.get('groups');
|
||||
|
||||
// As we're called by Y.DD.DDM, we can't be certain that the call
|
||||
// relates specifically to a block drag/drop operation. Test
|
||||
// whether the relevant group applies here.
|
||||
if (!groups || Y.Array.indexOf(groups, 'block') === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var i;
|
||||
for (i in this.regionobjects) {
|
||||
if (!this.regionobjects.hasOwnProperty(i)) {
|
||||
continue;
|
||||
}
|
||||
this.regionobjects[i].enable();
|
||||
}
|
||||
},
|
||||
|
||||
Vendored
+3
-1
@@ -748,7 +748,9 @@ DOCK.prototype = {
|
||||
BODY.addClass('used-region-'+regionname);
|
||||
BODY.removeClass('empty-region-'+regionname);
|
||||
BODY.removeClass('docked-region-'+regionname);
|
||||
} else {
|
||||
} else if (region.all('.block_dock_placeholder').size() > 0) {
|
||||
// There are no blocks in the region but there are placeholders.
|
||||
// All blocks in this region have been docked.
|
||||
BODY.addClass('empty-region-'+regionname);
|
||||
BODY.addClass('docked-region-'+regionname);
|
||||
BODY.removeClass('used-region-'+regionname);
|
||||
|
||||
+2
-2
@@ -290,7 +290,7 @@ function chat_print_recent_activity($course, $viewfullnames, $timestart) {
|
||||
$strftimerecent = get_string('strftimerecent');
|
||||
|
||||
if ($past) {
|
||||
echo $OUTPUT->heading(get_string("pastchats", 'chat').':');
|
||||
echo $OUTPUT->heading(get_string("pastchats", 'chat').':', 3);
|
||||
|
||||
foreach ($past as $cm) {
|
||||
$link = $CFG->wwwroot.'/mod/chat/view.php?id='.$cm->id;
|
||||
@@ -301,7 +301,7 @@ function chat_print_recent_activity($course, $viewfullnames, $timestart) {
|
||||
}
|
||||
|
||||
if ($current) {
|
||||
echo $OUTPUT->heading(get_string("currentchats", 'chat').':');
|
||||
echo $OUTPUT->heading(get_string("currentchats", 'chat').':', 3);
|
||||
|
||||
$oldest = floor((time()-$CFG->chat_old_ping)/10)*10; // better db caching
|
||||
|
||||
|
||||
@@ -372,6 +372,11 @@ function quiz_delete_attempt($attempt, $quiz) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($quiz->cmid)) {
|
||||
$cm = get_coursemodule_from_instance('quiz', $quiz->id, $quiz->course);
|
||||
$quiz->cmid = $cm->id;
|
||||
}
|
||||
|
||||
question_engine::delete_questions_usage_by_activity($attempt->uniqueid);
|
||||
$DB->delete_records('quiz_attempts', array('id' => $attempt->id));
|
||||
|
||||
|
||||
@@ -85,8 +85,9 @@ if ($override->groupid) {
|
||||
$group = $DB->get_record('groups', array('id' => $override->groupid), 'id, name');
|
||||
$confirmstr = get_string("overridedeletegroupsure", "quiz", $group->name);
|
||||
} else {
|
||||
$namefields = get_all_user_name_fields(true);
|
||||
$user = $DB->get_record('user', array('id' => $override->userid),
|
||||
'id, firstname, lastname');
|
||||
'id, ' . $namefields);
|
||||
$confirmstr = get_string("overridedeleteusersure", "quiz", fullname($user));
|
||||
}
|
||||
|
||||
|
||||
@@ -318,6 +318,8 @@ abstract class quiz_attempts_report extends quiz_default_report {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set the course module id before calling quiz_delete_attempt().
|
||||
$quiz->cmid = $cm->id;
|
||||
quiz_delete_attempt($attempt, $quiz);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,9 @@ class quiz_responses_options extends mod_quiz_attempts_report_options {
|
||||
$params['qtext'] = $this->showqtext;
|
||||
$params['resp'] = $this->showresponses;
|
||||
$params['right'] = $this->showright;
|
||||
$params['whichtries'] = $this->whichtries;
|
||||
if (quiz_allows_multiple_tries($this->quiz)) {
|
||||
$params['whichtries'] = $this->whichtries;
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
|
||||
@@ -62,7 +64,9 @@ class quiz_responses_options extends mod_quiz_attempts_report_options {
|
||||
$toform->qtext = $this->showqtext;
|
||||
$toform->resp = $this->showresponses;
|
||||
$toform->right = $this->showright;
|
||||
$toform->whichtries = $this->whichtries;
|
||||
if (quiz_allows_multiple_tries($this->quiz)) {
|
||||
$toform->whichtries = $this->whichtries;
|
||||
}
|
||||
|
||||
return $toform;
|
||||
}
|
||||
@@ -73,7 +77,9 @@ class quiz_responses_options extends mod_quiz_attempts_report_options {
|
||||
$this->showqtext = $fromform->qtext;
|
||||
$this->showresponses = $fromform->resp;
|
||||
$this->showright = $fromform->right;
|
||||
$this->whichtries = $fromform->whichtries;
|
||||
if (quiz_allows_multiple_tries($this->quiz)) {
|
||||
$this->whichtries = $fromform->whichtries;
|
||||
}
|
||||
}
|
||||
|
||||
public function setup_from_params() {
|
||||
@@ -82,7 +88,9 @@ class quiz_responses_options extends mod_quiz_attempts_report_options {
|
||||
$this->showqtext = optional_param('qtext', $this->showqtext, PARAM_BOOL);
|
||||
$this->showresponses = optional_param('resp', $this->showresponses, PARAM_BOOL);
|
||||
$this->showright = optional_param('right', $this->showright, PARAM_BOOL);
|
||||
$this->whichtries = optional_param('whichtries', $this->whichtries, PARAM_ALPHA);
|
||||
if (quiz_allows_multiple_tries($this->quiz)) {
|
||||
$this->whichtries = optional_param('whichtries', $this->whichtries, PARAM_ALPHA);
|
||||
}
|
||||
}
|
||||
|
||||
public function setup_from_user_preferences() {
|
||||
@@ -91,7 +99,9 @@ class quiz_responses_options extends mod_quiz_attempts_report_options {
|
||||
$this->showqtext = get_user_preferences('quiz_report_responses_qtext', $this->showqtext);
|
||||
$this->showresponses = get_user_preferences('quiz_report_responses_resp', $this->showresponses);
|
||||
$this->showright = get_user_preferences('quiz_report_responses_right', $this->showright);
|
||||
$this->whichtries = get_user_preferences('quiz_report_responses_which_tries', $this->whichtries);
|
||||
if (quiz_allows_multiple_tries($this->quiz)) {
|
||||
$this->whichtries = get_user_preferences('quiz_report_responses_which_tries', $this->whichtries);
|
||||
}
|
||||
}
|
||||
|
||||
public function update_user_preferences() {
|
||||
@@ -100,7 +110,9 @@ class quiz_responses_options extends mod_quiz_attempts_report_options {
|
||||
set_user_preference('quiz_report_responses_qtext', $this->showqtext);
|
||||
set_user_preference('quiz_report_responses_resp', $this->showresponses);
|
||||
set_user_preference('quiz_report_responses_right', $this->showright);
|
||||
set_user_preference('quiz_report_responses_which_tries', $this->whichtries);
|
||||
if (quiz_allows_multiple_tries($this->quiz)) {
|
||||
set_user_preference('quiz_report_responses_which_tries', $this->whichtries);
|
||||
}
|
||||
}
|
||||
|
||||
public function resolve_dependencies() {
|
||||
|
||||
@@ -72,48 +72,13 @@ class quiz_report_statistics_from_steps_testcase extends mod_quiz_attempt_walkth
|
||||
*/
|
||||
public function test_walkthrough_from_csv($quizsettings, $csvdata) {
|
||||
|
||||
// CSV data files for these tests were generated using :
|
||||
// https://github.com/jamiepratt/moodle-quiz-tools/tree/master/responsegenerator
|
||||
$this->create_quiz_simulate_attempts_and_check_results($quizsettings, $csvdata);
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
question_bank::get_qtype('random')->clear_caches_before_testing();
|
||||
|
||||
$this->create_quiz($quizsettings, $csvdata['questions']);
|
||||
|
||||
$attemptids = $this->walkthrough_attempts($csvdata['steps']);
|
||||
|
||||
if (isset($csvdata['results'])) {
|
||||
$this->check_attempts_results($csvdata['results'], $attemptids);
|
||||
}
|
||||
|
||||
$this->report = new quiz_statistics_report();
|
||||
$whichattempts = QUIZ_GRADEAVERAGE; // All attempts.
|
||||
$whichtries = question_attempt::ALL_TRIES;
|
||||
$groupstudents = array();
|
||||
$questions = $this->report->load_and_initialise_questions_for_calculations($this->quiz);
|
||||
list($quizstats, $questionstats) = $this->report->get_all_stats_and_analysis($this->quiz,
|
||||
$whichattempts,
|
||||
$whichtries,
|
||||
$groupstudents,
|
||||
$questions);
|
||||
|
||||
$qubaids = quiz_statistics_qubaids_condition($this->quiz->id, $groupstudents, $whichattempts);
|
||||
|
||||
// We will create some quiz and question stat calculator instances and some response analyser instances, just in order
|
||||
// to check the last analysed time then returned.
|
||||
$quizcalc = new \quiz_statistics\calculator();
|
||||
// Should not be a delay of more than one second between the calculation of stats above and here.
|
||||
$this->assertTimeCurrent($quizcalc->get_last_calculated_time($qubaids));
|
||||
|
||||
$qcalc = new \core_question\statistics\questions\calculator($questions);
|
||||
$this->assertTimeCurrent($qcalc->get_last_calculated_time($qubaids));
|
||||
|
||||
if (isset($csvdata['responsecounts'])) {
|
||||
$this->check_response_counts($csvdata['responsecounts'], $qubaids, $questions, $whichtries);
|
||||
}
|
||||
if (isset($csvdata['qstats'])) {
|
||||
$this->check_question_stats($csvdata['qstats'], $questionstats);
|
||||
}
|
||||
list($questions, $quizstats, $questionstats, $qubaids) =
|
||||
$this->check_stats_calculations_and_response_analysis($csvdata, $whichattempts, $whichtries, $groupstudents);
|
||||
if ($quizsettings['testnumber'] === '00') {
|
||||
$this->check_variants_count_for_quiz_00($questions, $questionstats, $whichtries, $qubaids);
|
||||
$this->check_quiz_stats_for_quiz_00($quizstats);
|
||||
@@ -389,4 +354,44 @@ class quiz_report_statistics_from_steps_testcase extends mod_quiz_attempt_walkth
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the question stats and the response counts used in the statistics report. If the appropriate files exist in fixtures/.
|
||||
*
|
||||
* @param PHPUnit_Extensions_Database_DataSet_ITable[] $csvdata Data loaded from csv files for this test.
|
||||
* @param string $whichattempts
|
||||
* @param string $whichtries
|
||||
* @param int[] $groupstudents
|
||||
* @return array with contents 0 => $questions, 1 => $quizstats, 2=> $questionstats, 3=> $qubaids Might be needed for further
|
||||
* testing.
|
||||
*/
|
||||
protected function check_stats_calculations_and_response_analysis($csvdata, $whichattempts, $whichtries, $groupstudents) {
|
||||
$this->report = new quiz_statistics_report();
|
||||
$questions = $this->report->load_and_initialise_questions_for_calculations($this->quiz);
|
||||
list($quizstats, $questionstats) = $this->report->get_all_stats_and_analysis($this->quiz,
|
||||
$whichattempts,
|
||||
$whichtries,
|
||||
$groupstudents,
|
||||
$questions);
|
||||
|
||||
$qubaids = quiz_statistics_qubaids_condition($this->quiz->id, $groupstudents, $whichattempts);
|
||||
|
||||
// We will create some quiz and question stat calculator instances and some response analyser instances, just in order
|
||||
// to check the last analysed time then returned.
|
||||
$quizcalc = new \quiz_statistics\calculator();
|
||||
// Should not be a delay of more than one second between the calculation of stats above and here.
|
||||
$this->assertTimeCurrent($quizcalc->get_last_calculated_time($qubaids));
|
||||
|
||||
$qcalc = new \core_question\statistics\questions\calculator($questions);
|
||||
$this->assertTimeCurrent($qcalc->get_last_calculated_time($qubaids));
|
||||
|
||||
if (isset($csvdata['responsecounts'])) {
|
||||
$this->check_response_counts($csvdata['responsecounts'], $qubaids, $questions, $whichtries);
|
||||
}
|
||||
if (isset($csvdata['qstats'])) {
|
||||
$this->check_question_stats($csvdata['qstats'], $questionstats);
|
||||
return array($questions, $quizstats, $questionstats, $qubaids);
|
||||
}
|
||||
return array($questions, $quizstats, $questionstats, $qubaids);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,6 +53,23 @@ class mod_quiz_attempt_walkthrough_from_csv_testcase extends advanced_testcase {
|
||||
*/
|
||||
protected $randqids;
|
||||
|
||||
/**
|
||||
* The only test in this class. This is run multiple times depending on how many sets of files there are in fixtures/
|
||||
* directory.
|
||||
*
|
||||
* @param array $quizsettings of settings read from csv file quizzes.csv
|
||||
* @param PHPUnit_Extensions_Database_DataSet_ITable[] $csvdata of data read from csv file "questionsXX.csv",
|
||||
* "stepsXX.csv" and "resultsXX.csv".
|
||||
* @dataProvider get_data_for_walkthrough
|
||||
*/
|
||||
public function test_walkthrough_from_csv($quizsettings, $csvdata) {
|
||||
|
||||
// CSV data files for these tests were generated using :
|
||||
// https://github.com/jamiepratt/moodle-quiz-tools/tree/master/responsegenerator
|
||||
|
||||
$this->create_quiz_simulate_attempts_and_check_results($quizsettings, $csvdata);
|
||||
}
|
||||
|
||||
public function create_quiz($quizsettings, $qs) {
|
||||
global $SITE, $DB;
|
||||
$this->setAdminUser();
|
||||
@@ -103,7 +120,7 @@ class mod_quiz_attempt_walkthrough_from_csv_testcase extends advanced_testcase {
|
||||
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
|
||||
|
||||
// Settings from param override defaults.
|
||||
$aggregratedsettings = $quizsettings + array('course'=>$SITE->id,
|
||||
$aggregratedsettings = $quizsettings + array('course' => $SITE->id,
|
||||
'questionsperpage' => 0,
|
||||
'grade' => 100.0,
|
||||
'sumgrades' => $sumofgrades);
|
||||
@@ -121,6 +138,25 @@ class mod_quiz_attempt_walkthrough_from_csv_testcase extends advanced_testcase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create quiz, simulate attempts and check results (if resultsXX.csv exists).
|
||||
*
|
||||
* @param array $quizsettings Quiz overrides for this quiz.
|
||||
* @param PHPUnit_Extensions_Database_DataSet_ITable[] $csvdata Data loaded from csv files for this test.
|
||||
*/
|
||||
protected function create_quiz_simulate_attempts_and_check_results($quizsettings, $csvdata) {
|
||||
$this->resetAfterTest(true);
|
||||
question_bank::get_qtype('random')->clear_caches_before_testing();
|
||||
|
||||
$this->create_quiz($quizsettings, $csvdata['questions']);
|
||||
|
||||
$attemptids = $this->walkthrough_attempts($csvdata['steps']);
|
||||
|
||||
if (isset($csvdata['results'])) {
|
||||
$this->check_attempts_results($csvdata['results'], $attemptids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get full path of CSV file.
|
||||
*
|
||||
@@ -192,29 +228,6 @@ class mod_quiz_attempt_walkthrough_from_csv_testcase extends advanced_testcase {
|
||||
return $datasets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a quiz add questions to it, walk through quiz attempts and then check results.
|
||||
*
|
||||
* @param $quizsettings array of settings read from csv file quizzes.csv
|
||||
* @param $csvdata \PHPUnit_Extensions_Database_DataSet_ITable[] of data read from csv file "questionsXX.csv",
|
||||
* "stepsXX.csv" and "resultsXX.csv".
|
||||
* @dataProvider get_data_for_walkthrough
|
||||
*/
|
||||
public function test_walkthrough_from_csv($quizsettings, $csvdata) {
|
||||
|
||||
// CSV data files for these tests were generated using :
|
||||
// https://github.com/jamiepratt/moodle-quiz-tools/tree/master/responsegenerator
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
question_bank::get_qtype('random')->clear_caches_before_testing();
|
||||
|
||||
$this->create_quiz($quizsettings, $csvdata['questions']);
|
||||
|
||||
$attemptids = $this->walkthrough_attempts($csvdata['steps']);
|
||||
|
||||
$this->check_attempts_results($csvdata['results'], $attemptids);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $steps PHPUnit_Extensions_Database_DataSet_ITable the step data from the csv file.
|
||||
* @return array attempt no as in csv file => the id of the quiz_attempt as stored in the db.
|
||||
|
||||
+3
-2
@@ -232,12 +232,13 @@ function survey_print_recent_activity($course, $viewfullnames, $timestart) {
|
||||
|
||||
$slist = implode(',', $ids); // there should not be hundreds of glossaries in one course, right?
|
||||
|
||||
$allusernames = user_picture::fields('u');
|
||||
$rs = $DB->get_recordset_sql("SELECT sa.userid, sa.survey, MAX(sa.time) AS time,
|
||||
u.firstname, u.lastname, u.email, u.picture
|
||||
$allusernames
|
||||
FROM {survey_answers} sa
|
||||
JOIN {user} u ON u.id = sa.userid
|
||||
WHERE sa.survey IN ($slist) AND sa.time > ?
|
||||
GROUP BY sa.userid, sa.survey, u.firstname, u.lastname, u.email, u.picture
|
||||
GROUP BY sa.userid, sa.survey, $allusernames
|
||||
ORDER BY time ASC", array($timestart));
|
||||
if (!$rs->valid()) {
|
||||
$rs->close(); // Not going to iterate (but exit), close rs
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
@mod @mod_wiki
|
||||
Feature: Edited wiki pages handle tags correctly
|
||||
In order to get wiki pages properly labelled
|
||||
As a user
|
||||
I need to introduce the tags while editing
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@asd.com |
|
||||
| student1 | Student | 1 | student1@asd.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| student1 | C1 | student |
|
||||
And I log in as "teacher1"
|
||||
And I follow "Course 1"
|
||||
And I turn editing mode on
|
||||
And I add a "Wiki" to section "1" and I fill the form with:
|
||||
| Wiki name | Test wiki name |
|
||||
| Description | Test wiki description |
|
||||
| First page name | First page |
|
||||
| Wiki mode | Collaborative wiki |
|
||||
And I log out
|
||||
|
||||
Scenario: Wiki page edition of custom tags works as expected
|
||||
Given I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test wiki name"
|
||||
And I press "Create page"
|
||||
When I set the following fields to these values:
|
||||
| HTML format | Student page contents to be tagged |
|
||||
| Other tags (enter tags separated by commas) | Example, Page, Cool |
|
||||
And I press "Save"
|
||||
Then I should see "Example" in the ".wiki-tags" "css_element"
|
||||
And I should see "Page" in the ".wiki-tags" "css_element"
|
||||
And I should see "Cool" in the ".wiki-tags" "css_element"
|
||||
And I follow "Edit"
|
||||
And the field "Other tags (enter tags separated by commas)" matches value "Example, Page, Cool"
|
||||
And I press "Cancel"
|
||||
|
||||
Scenario: Wiki page edition of official tags works as expected
|
||||
Given I log in as "admin"
|
||||
And I expand "Site administration" node
|
||||
And I expand "Appearance" node
|
||||
And I follow "Manage tags"
|
||||
And I set the field "otagsadd" to "OT1, OT2, OT3"
|
||||
And I press "Add official tags"
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "Course 1"
|
||||
And I follow "Test wiki name"
|
||||
And I press "Create page"
|
||||
And the "tags[officialtags][]" select box should contain "OT1"
|
||||
And the "tags[officialtags][]" select box should contain "OT2"
|
||||
And the "tags[officialtags][]" select box should contain "OT3"
|
||||
When I set the following fields to these values:
|
||||
| HTML format | Student page contents to be tagged |
|
||||
| tags[officialtags][] | OT1, OT3 |
|
||||
And I press "Save"
|
||||
Then I should see "OT1" in the ".wiki-tags" "css_element"
|
||||
And I should see "OT3" in the ".wiki-tags" "css_element"
|
||||
And I should not see "OT2" in the ".wiki-tags" "css_element"
|
||||
And I follow "Edit"
|
||||
And the field "tags[officialtags][]" matches value "OT1, OT3"
|
||||
And the field "tags[officialtags][]" does not match value "OT2"
|
||||
And I press "Cancel"
|
||||
@@ -0,0 +1,42 @@
|
||||
@core @core_my
|
||||
Feature: Restrict which blocks can be added to My home
|
||||
In order to restrict which blocks can be added
|
||||
As a student I need to ensure I can add the blocks
|
||||
As an admin I need to remove the capability to add a blocks
|
||||
As a student I need to ensure I can't add the blocks any more
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| student1 | Student | 1 | student1@asd.com |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | format |
|
||||
| Course 1 | C1 | topics |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
|
||||
Scenario: The comments block can be added to My home by default
|
||||
And I log in as "student1"
|
||||
And I follow "My home"
|
||||
And I press "Customise this page"
|
||||
Then the "Add a block" select box should contain "Comments"
|
||||
And the "Add a block" select box should contain "Courses"
|
||||
And the "Add a block" select box should contain "HTML"
|
||||
And the "Add a block" select box should contain "Tags"
|
||||
|
||||
@javascript
|
||||
Scenario: Remove the ability to add the comments block to My home
|
||||
When I log in as "admin"
|
||||
And I set the following system permissions of "Authenticated user" role:
|
||||
| block/comments:myaddinstance | Prohibit |
|
||||
| block/course_list:myaddinstance | Prohibit |
|
||||
| block/html:myaddinstance | Prohibit |
|
||||
And I log out
|
||||
And I log in as "student1"
|
||||
And I follow "My home"
|
||||
And I press "Customise this page"
|
||||
Then the "Add a block" select box should not contain "Comments"
|
||||
And the "Add a block" select box should not contain "Courses"
|
||||
And the "Add a block" select box should not contain "HTML"
|
||||
And the "Add a block" select box should contain "Tags"
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
@@ -197,7 +197,7 @@ class qtype_multichoice_single_question extends qtype_multichoice_base {
|
||||
public function prepare_simulated_post_data($simulatedresponse) {
|
||||
$ansid = 0;
|
||||
foreach ($this->answers as $answer) {
|
||||
if ($answer->answer == $simulatedresponse['answer']) {
|
||||
if (clean_param($answer->answer, PARAM_NOTAGS) == $simulatedresponse['answer']) {
|
||||
$ansid = $answer->id;
|
||||
}
|
||||
}
|
||||
@@ -360,12 +360,11 @@ class qtype_multichoice_multi_question extends qtype_multichoice_base {
|
||||
public function prepare_simulated_post_data($simulatedresponse) {
|
||||
$postdata = array();
|
||||
foreach ($simulatedresponse as $ans => $checked) {
|
||||
if ($checked) {
|
||||
foreach ($this->answers as $ansid => $answer) {
|
||||
if ($answer->answer == $ans) {
|
||||
$fieldno = array_search($ansid, $this->order);
|
||||
$postdata[$this->field($fieldno)] = '1';
|
||||
}
|
||||
foreach ($this->answers as $ansid => $answer) {
|
||||
if (clean_param($answer->answer, PARAM_NOTAGS) == $ans) {
|
||||
$fieldno = array_search($ansid, $this->order);
|
||||
$postdata[$this->field($fieldno)] = $checked;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -376,7 +375,8 @@ class qtype_multichoice_multi_question extends qtype_multichoice_base {
|
||||
$simulatedresponse = array();
|
||||
foreach ($this->order as $fieldno => $ansid) {
|
||||
if (isset($postdata[$this->field($fieldno)])) {
|
||||
$simulatedresponse[$this->answers[$ansid]->answer] = 1;
|
||||
$checked = $postdata[$this->field($fieldno)];
|
||||
$simulatedresponse[clean_param($this->answers[$ansid]->answer, PARAM_NOTAGS)] = $checked;
|
||||
}
|
||||
}
|
||||
ksort($simulatedresponse);
|
||||
|
||||
@@ -91,27 +91,23 @@ class qtype_multichoice_multi_question_test extends advanced_testcase {
|
||||
|
||||
public function test_grading() {
|
||||
$question = test_question_maker::make_a_multichoice_multi_question();
|
||||
$question->shuffleanswers = false;
|
||||
$question->start_attempt(new question_attempt_step(), 1);
|
||||
|
||||
$this->assertEquals(array(1, question_state::$gradedright),
|
||||
$question->grade_response(array('choice0' => '1', 'choice2' => '1')));
|
||||
$question->grade_response($question->prepare_simulated_post_data(array('A' => 1, 'C' => 1))));
|
||||
$this->assertEquals(array(0.5, question_state::$gradedpartial),
|
||||
$question->grade_response(array('choice0' => '1')));
|
||||
$question->grade_response($question->prepare_simulated_post_data(array('A' => 1))));
|
||||
$this->assertEquals(array(0, question_state::$gradedwrong),
|
||||
$question->grade_response(
|
||||
array('choice0' => '1', 'choice1' => '1', 'choice2' => '1')));
|
||||
$question->grade_response($question->prepare_simulated_post_data(array('A' => 1, 'B' => 1, 'C' => 1))));
|
||||
$this->assertEquals(array(0, question_state::$gradedwrong),
|
||||
$question->grade_response(array('choice1' => '1')));
|
||||
$question->grade_response($question->prepare_simulated_post_data(array('B' => 1))));
|
||||
}
|
||||
|
||||
public function test_get_correct_response() {
|
||||
$question = test_question_maker::make_a_multichoice_multi_question();
|
||||
$question->shuffleanswers = false;
|
||||
$question->start_attempt(new question_attempt_step(), 1);
|
||||
|
||||
$this->assertEquals(array('choice0' => '1', 'choice2' => '1'),
|
||||
$question->get_correct_response());
|
||||
$this->assertEquals($question->prepare_simulated_post_data(array('A' => 1, 'C' => 1)), $question->get_correct_response());
|
||||
}
|
||||
|
||||
public function test_get_question_summary() {
|
||||
@@ -131,7 +127,7 @@ class qtype_multichoice_multi_question_test extends advanced_testcase {
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->start_attempt(new question_attempt_step(), 1);
|
||||
|
||||
$summary = $mc->summarise_response(array('choice1' => 1, 'choice2' => 1),
|
||||
$summary = $mc->summarise_response($mc->prepare_simulated_post_data(array('B' => 1, 'C' => 1)),
|
||||
test_question_maker::get_a_qa($mc));
|
||||
|
||||
$this->assertEquals('B; C', $summary);
|
||||
@@ -139,14 +135,33 @@ class qtype_multichoice_multi_question_test extends advanced_testcase {
|
||||
|
||||
public function test_classify_response() {
|
||||
$mc = test_question_maker::make_a_multichoice_multi_question();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->start_attempt(new question_attempt_step(), 1);
|
||||
|
||||
$this->assertEquals(array(
|
||||
13 => new question_classified_response(13, 'A', 0.5),
|
||||
14 => new question_classified_response(14, 'B', -1.0),
|
||||
), $mc->classify_response(array('choice0' => 1, 'choice1' => 1)));
|
||||
), $mc->classify_response($mc->prepare_simulated_post_data(array('A' => 1, 'B' => 1))));
|
||||
|
||||
$this->assertEquals(array(), $mc->classify_response(array()));
|
||||
}
|
||||
|
||||
public function test_prepare_simulated_post_data() {
|
||||
$mc = test_question_maker::make_a_multichoice_multi_question();
|
||||
$mc->start_attempt(new question_attempt_step(), 1);
|
||||
$correctanswers = array(
|
||||
array(),
|
||||
array('A' => 1),
|
||||
array('B' => 1, 'D' => 0),
|
||||
array('A' => 0, 'B' => 0, 'C' => 0, 'D' => 0),
|
||||
array('A' => 1, 'B' => 0, 'C' => 1, 'D' => 0),
|
||||
array('A' => 1, 'B' => 0, 'C' => 1, 'D' => 1),
|
||||
array('A' => 1, 'B' => 1, 'C' => 1, 'D' => 1)
|
||||
);
|
||||
foreach ($correctanswers as $correctanswer) {
|
||||
$postdata = $mc->prepare_simulated_post_data($correctanswer);
|
||||
$simulatedreponse = $mc->get_student_response_values_for_simulation($postdata);
|
||||
$this->assertEquals($correctanswer, $simulatedreponse, '', 0, 10, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -84,15 +84,14 @@ class qtype_multichoice_single_question_test extends advanced_testcase {
|
||||
|
||||
public function test_grading() {
|
||||
$question = test_question_maker::make_a_multichoice_single_question();
|
||||
$question->shuffleanswers = false;
|
||||
$question->start_attempt(new question_attempt_step(), 1);
|
||||
|
||||
$this->assertEquals(array(1, question_state::$gradedright),
|
||||
$question->grade_response(array('answer' => 0)));
|
||||
$question->grade_response($question->prepare_simulated_post_data(array('answer' => 'A'))));
|
||||
$this->assertEquals(array(-0.3333333, question_state::$gradedwrong),
|
||||
$question->grade_response(array('answer' => 1)));
|
||||
$question->grade_response($question->prepare_simulated_post_data(array('answer' => 'B'))));
|
||||
$this->assertEquals(array(-0.3333333, question_state::$gradedwrong),
|
||||
$question->grade_response(array('answer' => 2)));
|
||||
$question->grade_response($question->prepare_simulated_post_data(array('answer' => 'C'))));
|
||||
}
|
||||
|
||||
public function test_grading_rounding_three_right() {
|
||||
@@ -104,7 +103,6 @@ class qtype_multichoice_single_question_test extends advanced_testcase {
|
||||
$mc->generalfeedback = '1, 3 and 5 are the odd numbers.';
|
||||
$mc->qtype = question_bank::get_qtype('multichoice');
|
||||
|
||||
$mc->shuffleanswers = 0;
|
||||
$mc->answernumbering = 'abc';
|
||||
|
||||
test_question_maker::set_standard_combined_feedback_fields($mc);
|
||||
@@ -120,40 +118,34 @@ class qtype_multichoice_single_question_test extends advanced_testcase {
|
||||
|
||||
$mc->start_attempt(new question_attempt_step(), 1);
|
||||
|
||||
list($grade, $state) = $mc->grade_response(
|
||||
array('choice0' => 1, 'choice2' => 1, 'choice4' => 1));
|
||||
list($grade, $state) = $mc->grade_response($mc->prepare_simulated_post_data(array('1' => '1', '3' => '1', '5' => '1')));
|
||||
$this->assertEquals(1, $grade, '', 0.000001);
|
||||
$this->assertEquals(question_state::$gradedright, $state);
|
||||
}
|
||||
|
||||
public function test_get_correct_response() {
|
||||
$question = test_question_maker::make_a_multichoice_single_question();
|
||||
$question->shuffleanswers = false;
|
||||
$question->start_attempt(new question_attempt_step(), 1);
|
||||
|
||||
$this->assertEquals(array('answer' => 0),
|
||||
$question->get_correct_response());
|
||||
$this->assertEquals($question->prepare_simulated_post_data(array('answer' => 'A')), $question->get_correct_response());
|
||||
}
|
||||
|
||||
public function test_summarise_response() {
|
||||
$mc = test_question_maker::make_a_multichoice_single_question();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->start_attempt(new question_attempt_step(), 1);
|
||||
|
||||
$summary = $mc->summarise_response(array('answer' => 0),
|
||||
test_question_maker::get_a_qa($mc));
|
||||
$summary = $mc->summarise_response($mc->prepare_simulated_post_data(array('answer' => 'A')),
|
||||
test_question_maker::get_a_qa($mc));
|
||||
|
||||
$this->assertEquals('A', $summary);
|
||||
}
|
||||
|
||||
public function test_classify_response() {
|
||||
$mc = test_question_maker::make_a_multichoice_single_question();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->start_attempt(new question_attempt_step(), 1);
|
||||
|
||||
$this->assertEquals(array(
|
||||
$mc->id => new question_classified_response(14, 'B', -0.3333333),
|
||||
), $mc->classify_response(array('answer' => 1)));
|
||||
$this->assertEquals(array($mc->id => new question_classified_response(14, 'B', -0.3333333)),
|
||||
$mc->classify_response($mc->prepare_simulated_post_data(array('answer' => 'B'))));
|
||||
|
||||
$this->assertEquals(array(
|
||||
$mc->id => question_classified_response::no_response(),
|
||||
|
||||
@@ -6,3 +6,6 @@
|
||||
#page-report-completion-index .export-actions {
|
||||
text-align: center;
|
||||
}
|
||||
#page-report-completion-index.dir-rtl #completion-progress th svg {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
@@ -481,7 +481,7 @@ class report_log_table_log extends table_sql {
|
||||
$ccselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
|
||||
$ccjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
|
||||
$courseparams['contextlevel'] = CONTEXT_COURSE;
|
||||
$sql = "SELECT c.id,c.shortname $ccselect FROM {course} as c
|
||||
$sql = "SELECT c.id,c.shortname $ccselect FROM {course} c
|
||||
$ccjoin
|
||||
WHERE c.id " . $coursesql;
|
||||
|
||||
@@ -496,4 +496,4 @@ class report_log_table_log extends table_sql {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,7 +387,7 @@ class report_loglive_table_log extends table_sql {
|
||||
$ccselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
|
||||
$ccjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel)";
|
||||
$courseparams['contextlevel'] = CONTEXT_COURSE;
|
||||
$sql = "SELECT c.id,c.shortname $ccselect FROM {course} as c
|
||||
$sql = "SELECT c.id,c.shortname $ccselect FROM {course} c
|
||||
$ccjoin
|
||||
WHERE c.id " . $coursesql;
|
||||
|
||||
|
||||
@@ -5,3 +5,4 @@
|
||||
#page-report-progress-index .completion_prev {display:inline;margin-right:2em;}
|
||||
#page-report-progress-index .completion_pagingbar p {display:inline;margin:0;}
|
||||
#page-report-progress-index .completion_next {display:inline;margin-left:2em;}
|
||||
#page-report-progress-index.dir-rtl #completion-progress th svg {direction:ltr;}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
*/
|
||||
|
||||
$string['alfresco_url'] = 'Alfresco URL';
|
||||
$string['alfrescourltext'] = 'Afresco API url should be: http://yoursite.com/alfresco/api';
|
||||
$string['alfrescourltext'] = 'Afresco API URL should be: http://yoursite.com/alfresco/api or http://yoursite.com/alfresco/soapapi for Alfresco 4.2.d or greater.';
|
||||
$string['alfresco:view'] = 'View alfresco repository';
|
||||
$string['configplugin'] = 'Alfresco configuration';
|
||||
$string['notitle'] = 'notitle';
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 894 B After Width: | Height: | Size: 1.1 KiB |
@@ -2027,4 +2027,16 @@ body.lockscroll {
|
||||
.editor_atto_menu .moodle-dialogue-content {
|
||||
padding: 0px;
|
||||
border: inherit;
|
||||
|
||||
.well {
|
||||
min-height: 20px;
|
||||
padding: 19px;
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #e3e3e3;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.well-small {
|
||||
padding: 9px;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ class user_filter_courserole extends user_filter_type {
|
||||
$objs[] = $mform->createElement('select', $this->_name .'_ct', null, $this->get_course_categories());
|
||||
$objs[] = $mform->createElement('text', $this->_name, null);
|
||||
$grp =& $mform->addElement('group', $this->_name.'_grp', $this->_label, $objs, '', false);
|
||||
$mform->setType($this->_name, PARAM_ALPHANUMEXT);
|
||||
$mform->setType($this->_name, PARAM_TEXT);
|
||||
if ($this->_advanced) {
|
||||
$mform->setAdvanced($this->_name.'_grp');
|
||||
}
|
||||
|
||||
@@ -451,9 +451,9 @@ function profile_list_categories() {
|
||||
* @param string $redirect
|
||||
*/
|
||||
function profile_edit_category($id, $redirect) {
|
||||
global $DB, $OUTPUT;
|
||||
global $DB, $OUTPUT, $CFG;
|
||||
|
||||
require_once('index_category_form.php');
|
||||
require_once($CFG->dirroot.'/user/profile/index_category_form.php');
|
||||
$categoryform = new category_form();
|
||||
|
||||
if ($category = $DB->get_record('user_info_category', array('id' => $id))) {
|
||||
@@ -515,7 +515,7 @@ function profile_edit_field($id, $datatype, $redirect) {
|
||||
$field->description = clean_text($field->description, $field->descriptionformat);
|
||||
$field->description = array('text' => $field->description, 'format' => $field->descriptionformat, 'itemid' => 0);
|
||||
|
||||
require_once('index_field_form.php');
|
||||
require_once($CFG->dirroot.'/user/profile/index_field_form.php');
|
||||
$fieldform = new field_form(null, $field->datatype);
|
||||
|
||||
// Convert the data format for.
|
||||
|
||||
@@ -108,12 +108,11 @@ switch ($action) {
|
||||
// Normal form.
|
||||
}
|
||||
|
||||
// Print the header.
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading(get_string('profilefields', 'admin'));
|
||||
// Show all categories.
|
||||
$categories = $DB->get_records('user_info_category', null, 'sortorder ASC');
|
||||
|
||||
// Check that we have at least one category defined.
|
||||
if ($DB->count_records('user_info_category') == 0) {
|
||||
if (empty($categories)) {
|
||||
$defaultcategory = new stdClass();
|
||||
$defaultcategory->name = $strdefaultcategory;
|
||||
$defaultcategory->sortorder = 1;
|
||||
@@ -121,8 +120,9 @@ if ($DB->count_records('user_info_category') == 0) {
|
||||
redirect($redirect);
|
||||
}
|
||||
|
||||
// Show all categories.
|
||||
$categories = $DB->get_records('user_info_category', null, 'sortorder ASC');
|
||||
// Print the header.
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading(get_string('profilefields', 'admin'));
|
||||
|
||||
foreach ($categories as $category) {
|
||||
$table = new html_table();
|
||||
|
||||
@@ -73,13 +73,13 @@ class category_form extends moodleform {
|
||||
|
||||
$data = (object)$data;
|
||||
|
||||
$duplicate = $DB->record_exists('user_info_category', array('name' => $data->name));
|
||||
$duplicate = $DB->get_field('user_info_category', 'id', array('name' => $data->name));
|
||||
|
||||
// Check the name is unique.
|
||||
if (!empty($data->id)) { // We are editing an existing record.
|
||||
$olddata = $DB->get_record('user_info_category', array('id' => $data->id));
|
||||
// Name has changed, new name in use, new name in use by another record.
|
||||
$dupfound = (($olddata->name !== $data->name) && $duplicate && ($data->id != $duplicate->id));
|
||||
$dupfound = (($olddata->name !== $data->name) && $duplicate && ($data->id != $duplicate));
|
||||
} else { // New profile category.
|
||||
$dupfound = $duplicate;
|
||||
}
|
||||
|
||||
+2
-2
@@ -29,11 +29,11 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2014041700.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2014042200.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
|
||||
$release = '2.7beta+ (Build: 20140417)'; // Human-friendly version name
|
||||
$release = '2.7beta+ (Build: 20140422)'; // Human-friendly version name
|
||||
|
||||
$branch = '27'; // This version's branch.
|
||||
$maturity = MATURITY_BETA; // This version's maturity level.
|
||||
|
||||
Reference in New Issue
Block a user