mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 06:18:28 +01:00
Merge branch 'master' into install_master
This commit is contained in:
commit
b87f163a1b
@ -53,7 +53,7 @@ Options:
|
||||
-h, --help Print out this help
|
||||
|
||||
Example:
|
||||
\$ sudo -u www-data /usr/bin/php admin/cli/mysql_collation.php --collation=utf8_general_ci
|
||||
\$ sudo -u www-data /usr/bin/php admin/cli/mysql_collation.php --collation=utf8mb4_unicode_ci
|
||||
";
|
||||
|
||||
if (!empty($options['collation'])) {
|
||||
@ -145,9 +145,22 @@ if (!empty($options['collation'])) {
|
||||
$skipped++;
|
||||
|
||||
} else {
|
||||
$DB->change_database_structure("ALTER TABLE $table->name DEFAULT CHARACTER SET $charset DEFAULT COLLATE = $collation");
|
||||
echo "CONVERTED\n";
|
||||
$converted++;
|
||||
try {
|
||||
$DB->change_database_structure("ALTER TABLE $table->name CONVERT TO CHARACTER SET $charset COLLATE $collation");
|
||||
echo "CONVERTED\n";
|
||||
$converted++;
|
||||
} catch (ddl_exception $e) {
|
||||
$result = mysql_set_row_format($table->name, $charset, $collation, $engine);
|
||||
if ($result) {
|
||||
echo "CONVERTED\n";
|
||||
$converted++;
|
||||
} else {
|
||||
// We don't know what the problem is. Stop the conversion.
|
||||
cli_error("Error: Tried to convert $table->name, but there was a problem. Please check the details of this
|
||||
table and try again.");
|
||||
die();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "SHOW FULL COLUMNS FROM $table->name WHERE collation IS NOT NULL";
|
||||
@ -290,3 +303,26 @@ function mysql_get_column_collations($tablename) {
|
||||
$rs->close();
|
||||
return $collations;
|
||||
}
|
||||
|
||||
function mysql_set_row_format($tablename, $charset, $collation, $engine) {
|
||||
global $DB;
|
||||
|
||||
$sql = "SELECT row_format
|
||||
FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE table_schema = DATABASE() AND table_name = ?";
|
||||
$rs = $DB->get_record_sql($sql, array($tablename));
|
||||
if ($rs) {
|
||||
if ($rs->row_format == 'Compact' || $rs->row_format == 'Redundant') {
|
||||
$rowformat = $DB->get_row_format_sql($engine, $collation);
|
||||
// Try to convert to compressed format and then try updating the collation again.
|
||||
$DB->change_database_structure("ALTER TABLE $tablename $rowformat");
|
||||
$DB->change_database_structure("ALTER TABLE $tablename CONVERT TO CHARACTER SET $charset COLLATE $collation");
|
||||
} else {
|
||||
// Row format may not be the problem. Can not diagnose problem. Send fail reply.
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ class site_registration_form extends moodleform {
|
||||
}
|
||||
$language = get_config('hub', 'site_language_' . $cleanhuburl);
|
||||
if ($language === false) {
|
||||
$language = current_language();
|
||||
$language = explode('_', current_language())[0];
|
||||
}
|
||||
$geolocation = get_config('hub', 'site_geolocation_' . $cleanhuburl);
|
||||
$contactable = get_config('hub', 'site_contactable_' . $cleanhuburl);
|
||||
|
@ -193,8 +193,8 @@ function print_report_tree($contextid, $contexts, $systemcontext, $fullname, $al
|
||||
$strgoto = get_string('gotoassignroles', 'core_role', $a);
|
||||
$strcheck = get_string('checkuserspermissionshere', 'core_role', $a);
|
||||
}
|
||||
echo ' <a title="' . $strgoto . '" href="' . $raurl . '">' . $OUTPUT->pix_icon('t/edit', 'core', $stredit) . '</a> ';
|
||||
echo ' <a title="' . $strcheck . '" href="' . $churl . '">' . $OUTPUT->pix_icon('t/preview', 'core', $strcheckpermissions) . '</a> ';
|
||||
echo ' <a title="' . $strgoto . '" href="' . $raurl . '">' . $OUTPUT->pix_icon('t/edit', $stredit) . '</a> ';
|
||||
echo ' <a title="' . $strcheck . '" href="' . $churl . '">' . $OUTPUT->pix_icon('t/preview', $strcheckpermissions) . '</a> ';
|
||||
echo "</p>\n";
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ if (empty($CFG->langotherroot)) {
|
||||
$mode = optional_param('mode', 0, PARAM_INT); // action
|
||||
$pack = optional_param_array('pack', array(), PARAM_SAFEDIR); // pack to install
|
||||
$uninstalllang = optional_param_array('uninstalllang', array(), PARAM_LANG);// installed pack to uninstall
|
||||
$confirmtounistall = optional_param('confirmtouninstall', '', PARAM_ALPHAEXT); // uninstallation confirmation
|
||||
$confirmtounistall = optional_param('confirmtouninstall', '', PARAM_SAFEPATH); // uninstallation confirmation
|
||||
$purgecaches = optional_param('purgecaches', false, PARAM_BOOL); // explicit caches reset
|
||||
|
||||
if ($purgecaches) {
|
||||
@ -74,7 +74,7 @@ if ($mode == DELETION_OF_SELECTED_LANG and (!empty($uninstalllang) or !empty($co
|
||||
// Actually deleting languages, languages to delete are passed as GET parameter as string
|
||||
// ...need to populate them to array.
|
||||
if (empty($uninstalllang)) {
|
||||
$uninstalllang = explode('-', $confirmtounistall);
|
||||
$uninstalllang = explode('/', $confirmtounistall);
|
||||
}
|
||||
|
||||
if (in_array('en', $uninstalllang)) {
|
||||
@ -84,8 +84,10 @@ if ($mode == DELETION_OF_SELECTED_LANG and (!empty($uninstalllang) or !empty($co
|
||||
} else if (empty($confirmtounistall) and confirm_sesskey()) { // User chose langs to be deleted, show confirmation.
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->confirm(get_string('uninstallconfirm', 'tool_langimport', implode(', ', $uninstalllang)),
|
||||
'index.php?mode='.DELETION_OF_SELECTED_LANG.'&confirmtouninstall='.implode('-', $uninstalllang),
|
||||
'index.php');
|
||||
new moodle_url($PAGE->url, array(
|
||||
'mode' => DELETION_OF_SELECTED_LANG,
|
||||
'confirmtouninstall' => implode('/', $uninstalllang),
|
||||
)), $PAGE->url);
|
||||
echo $OUTPUT->footer();
|
||||
die;
|
||||
|
||||
|
@ -84,8 +84,7 @@ abstract class restore_tool_log_logstore_subplugin extends restore_subplugin {
|
||||
}
|
||||
}
|
||||
|
||||
// Roll dates.
|
||||
$data->timecreated = $this->apply_date_offset($data->timecreated);
|
||||
// There is no need to roll dates. Logs are supposed to be immutable. See MDL-44961.
|
||||
|
||||
// Revert other to its original php way.
|
||||
$data->other = unserialize(base64_decode($data->other));
|
||||
|
@ -81,10 +81,11 @@
|
||||
{{/config}}
|
||||
</div>
|
||||
|
||||
<div data-region="footer" class="pull-xs-right">
|
||||
<div data-region="footer" class="pull-xs-right m-t-1">
|
||||
{{#config}}
|
||||
<input type="button" class="btn btn-primary" data-action="save" value="{{#str}}savechanges{{/str}}"/>
|
||||
{{/config}}
|
||||
<input type="button" class="btn btn-secondary" data-action="cancel" value="{{#str}}cancel{{/str}}"/>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
|
@ -3089,7 +3089,8 @@ class restore_course_logs_structure_step extends restore_structure_step {
|
||||
|
||||
$data = (object)($data);
|
||||
|
||||
$data->time = $this->apply_date_offset($data->time);
|
||||
// There is no need to roll dates. Logs are supposed to be immutable. See MDL-44961.
|
||||
|
||||
$data->userid = $this->get_mappingid('user', $data->userid);
|
||||
$data->course = $this->get_courseid();
|
||||
$data->cmid = 0;
|
||||
@ -3136,7 +3137,8 @@ class restore_activity_logs_structure_step extends restore_course_logs_structure
|
||||
|
||||
$data = (object)($data);
|
||||
|
||||
$data->time = $this->apply_date_offset($data->time);
|
||||
// There is no need to roll dates. Logs are supposed to be immutable. See MDL-44961.
|
||||
|
||||
$data->userid = $this->get_mappingid('user', $data->userid);
|
||||
$data->course = $this->get_courseid();
|
||||
$data->cmid = $this->task->get_moduleid();
|
||||
|
@ -1 +1 @@
|
||||
define(["jquery","core/ajax","core/custom_interaction_events"],function(a,b,c){var d=function(d){c.define(d,[c.events.activate]),d.on(c.events.activate,"[data-toggle='tab']",function(c){var d=a(c.currentTarget).data("tabname");"function"==typeof window.history.pushState&&window.history.pushState(null,null,"?myoverviewtab="+d);var e={methodname:"core_user_update_user_preferences",args:{preferences:[{type:"block_myoverview_last_tab",value:d}]}};b.call([e])[0].fail(Notification.exception)})};return{registerEventListeners:d}});
|
||||
define(["jquery","core/ajax","core/custom_interaction_events","core/notification"],function(a,b,c,d){var e=function(e){c.define(e,[c.events.activate]),e.on(c.events.activate,"[data-toggle='tab']",function(c){var e=a(c.currentTarget).data("tabname");"function"==typeof window.history.pushState&&window.history.pushState(null,null,"?myoverviewtab="+e);var f={methodname:"core_user_update_user_preferences",args:{preferences:[{type:"block_myoverview_last_tab",value:e}]}};b.call([f])[0].fail(d.exception)})};return{registerEventListeners:e}});
|
@ -21,7 +21,8 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery', 'core/ajax', 'core/custom_interaction_events'], function($, Ajax, CustomEvents) {
|
||||
define(['jquery', 'core/ajax', 'core/custom_interaction_events',
|
||||
'core/notification'], function($, Ajax, CustomEvents, Notification) {
|
||||
|
||||
/**
|
||||
* Registers an event that saves the user's tab preference when switching between them.
|
||||
|
@ -1 +1 @@
|
||||
define(["jquery"],function(a){function b(d,e){var f=a("<ul></ul>");f.attr("role","group"),f.attr("aria-hidden",!0),a.each(e,function(d,e){if("object"==typeof e){var g=a("<li></li>"),h=a("<p></p>"),i=e.id||e.key+"_tree_item",j=null,k=!(!e.expandable&&!e.haschildren);if(h.addClass("tree_item"),h.attr("id",i),h.attr("role","treeitem"),h.attr("tabindex","-1"),e.requiresajaxloading&&(h.attr("data-requires-ajax",!0),h.attr("data-node-id",e.id),h.attr("data-node-key",e.key),h.attr("data-node-type",e.type)),k&&(g.addClass("collapsed contains_branch"),h.attr("aria-expanded",!1),h.addClass("branch")),!e.icon||k&&e.type!==c.ACTIVITY&&e.type!==c.RESOURCE||(g.addClass("item_with_icon"),h.addClass("hasicon"),j=a("<img/>"),j.attr("alt",e.icon.alt),j.attr("title",e.icon.title),j.attr("src",M.util.image_url(e.icon.pix,e.icon.component)),a.each(e.icon.classes,function(a,b){j.addClass(b)})),e.link){var l=a('<a title="'+e.title+'" href="'+e.link+'"></a>');j?(l.append(j),l.append('<span class="item-content-wrap">'+e.name+"</span>")):l.append(e.name),e.hidden&&l.addClass("dimmed"),h.append(l)}else{var m=a("<span></span>");j?(m.append(j),m.append('<span class="item-content-wrap">'+e.name+"</span>")):m.append(e.name),e.hidden&&m.addClass("dimmed"),h.append(m)}g.append(h),f.append(g),e.children&&e.children.length?b(h,e.children):k&&!e.requiresajaxloading&&(g.removeClass("contains_branch"),h.addClass("emptybranch"))}}),d.parent().append(f);var g=d.attr("id")+"_group";f.attr("id",g),d.attr("aria-owns",g),d.attr("role","treeitem")}var c={ACTIVITY:40,RESOURCE:50};return{render:function(a,c){if(c.children&&c.children.length){b(a,c.children);var d=a.children("[role='treeitem']").first(),e=a.find("#"+d.attr("aria-owns"));d.attr("aria-expanded",!0),e.attr("aria-hidden",!1)}else a.parent().hasClass("contains_branch")&&(a.parent().removeClass("contains_branch"),a.addClass("emptybranch"))}}});
|
||||
define(["jquery","core/templates","core/notification","core/url"],function(a,b,c,d){function e(g,h){var i=a("<ul></ul>");i.attr("role","group"),i.attr("aria-hidden",!0),a.each(h,function(g,h){if("object"==typeof h){var j=a("<li></li>"),k=a("<p></p>"),l=h.id||h.key+"_tree_item",m=null,n=!(!h.expandable&&!h.haschildren);k.addClass("tree_item"),k.attr("id",l),k.attr("role","treeitem"),k.attr("tabindex","-1"),h.requiresajaxloading&&(k.attr("data-requires-ajax",!0),k.attr("data-node-id",h.id),k.attr("data-node-key",h.key),k.attr("data-node-type",h.type)),n&&(j.addClass("collapsed contains_branch"),k.attr("aria-expanded",!1),k.addClass("branch"));var o=null;if(h.link){var p=a('<a title="'+h.title+'" href="'+h.link+'"></a>');o=p,p.append('<span class="item-content-wrap">'+h.name+"</span>"),h.hidden&&p.addClass("dimmed"),k.append(p)}else{var q=a("<span></span>");o=q,q.append('<span class="item-content-wrap">'+h.name+"</span>"),h.hidden&&q.addClass("dimmed"),k.append(q)}!h.icon||n&&h.type!==f.ACTIVITY&&h.type!==f.RESOURCE||(j.addClass("item_with_icon"),k.addClass("hasicon"),h.type===f.ACTIVITY||h.type===f.RESOURCE?(m=a("<img/>"),m.attr("alt",h.icon.alt),m.attr("title",h.icon.title),m.attr("src",d.imageUrl(h.icon.pix,h.icon.component)),a.each(h.icon.classes,function(a,b){m.addClass(b)}),o.prepend(m)):("moodle"==h.icon.component&&(h.icon.component="core"),b.renderPix(h.icon.pix,h.icon.component,h.icon.title).then(function(a){o.prepend(a)})["catch"](c.exception))),j.append(k),i.append(j),h.children&&h.children.length?e(k,h.children):n&&!h.requiresajaxloading&&(j.removeClass("contains_branch"),k.addClass("emptybranch"))}}),g.parent().append(i);var j=g.attr("id")+"_group";i.attr("id",j),g.attr("aria-owns",j),g.attr("role","treeitem")}var f={ACTIVITY:40,RESOURCE:50};return{render:function(a,b){if(b.children&&b.children.length){e(a,b.children);var c=a.children("[role='treeitem']").first(),d=a.find("#"+c.attr("aria-owns"));c.attr("aria-expanded",!0),d.attr("aria-hidden",!1)}else a.parent().hasClass("contains_branch")&&(a.parent().removeClass("contains_branch"),a.addClass("emptybranch"))}}});
|
@ -22,7 +22,7 @@
|
||||
* @copyright 2015 John Okely <john@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery'], function($) {
|
||||
define(['jquery', 'core/templates', 'core/notification', 'core/url'], function($, Templates, Notification, Url) {
|
||||
|
||||
// Mappings for the different types of nodes coming from the navigation.
|
||||
// Copied from lib/navigationlib.php navigation_node constants.
|
||||
@ -75,28 +75,12 @@ define(['jquery'], function($) {
|
||||
p.addClass('branch');
|
||||
}
|
||||
|
||||
if (node.icon && (!isBranch || node.type === NODETYPE.ACTIVITY || node.type === NODETYPE.RESOURCE)) {
|
||||
li.addClass('item_with_icon');
|
||||
p.addClass('hasicon');
|
||||
|
||||
icon = $('<img/>');
|
||||
icon.attr('alt', node.icon.alt);
|
||||
icon.attr('title', node.icon.title);
|
||||
icon.attr('src', M.util.image_url(node.icon.pix, node.icon.component));
|
||||
$.each(node.icon.classes, function(index, className) {
|
||||
icon.addClass(className);
|
||||
});
|
||||
}
|
||||
|
||||
var eleToAddIcon = null;
|
||||
if (node.link) {
|
||||
var link = $('<a title="' + node.title + '" href="' + node.link + '"></a>');
|
||||
|
||||
if (icon) {
|
||||
link.append(icon);
|
||||
link.append('<span class="item-content-wrap">' + node.name + '</span>');
|
||||
} else {
|
||||
link.append(node.name);
|
||||
}
|
||||
eleToAddIcon = link;
|
||||
link.append('<span class="item-content-wrap">' + node.name + '</span>');
|
||||
|
||||
if (node.hidden) {
|
||||
link.addClass('dimmed');
|
||||
@ -106,12 +90,8 @@ define(['jquery'], function($) {
|
||||
} else {
|
||||
var span = $('<span></span>');
|
||||
|
||||
if (icon) {
|
||||
span.append(icon);
|
||||
span.append('<span class="item-content-wrap">' + node.name + '</span>');
|
||||
} else {
|
||||
span.append(node.name);
|
||||
}
|
||||
eleToAddIcon = span;
|
||||
span.append('<span class="item-content-wrap">' + node.name + '</span>');
|
||||
|
||||
if (node.hidden) {
|
||||
span.addClass('dimmed');
|
||||
@ -120,6 +100,31 @@ define(['jquery'], function($) {
|
||||
p.append(span);
|
||||
}
|
||||
|
||||
if (node.icon && (!isBranch || node.type === NODETYPE.ACTIVITY || node.type === NODETYPE.RESOURCE)) {
|
||||
li.addClass('item_with_icon');
|
||||
p.addClass('hasicon');
|
||||
|
||||
if (node.type === NODETYPE.ACTIVITY || node.type === NODETYPE.RESOURCE) {
|
||||
icon = $('<img/>');
|
||||
icon.attr('alt', node.icon.alt);
|
||||
icon.attr('title', node.icon.title);
|
||||
icon.attr('src', Url.imageUrl(node.icon.pix, node.icon.component));
|
||||
$.each(node.icon.classes, function(index, className) {
|
||||
icon.addClass(className);
|
||||
});
|
||||
eleToAddIcon.prepend(icon);
|
||||
} else {
|
||||
if (node.icon.component == 'moodle') {
|
||||
node.icon.component = 'core';
|
||||
}
|
||||
Templates.renderPix(node.icon.pix, node.icon.component, node.icon.title).then(function(html) {
|
||||
// Prepend.
|
||||
eleToAddIcon.prepend(html);
|
||||
return;
|
||||
}).catch(Notification.exception);
|
||||
}
|
||||
}
|
||||
|
||||
li.append(p);
|
||||
ul.append(li);
|
||||
|
||||
|
@ -71,6 +71,6 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.block_navigation .block_tree [aria-hidden="true"] {
|
||||
.block_navigation .block_tree [aria-hidden="true"]:not(.icon) {
|
||||
display: none;
|
||||
}
|
||||
|
@ -58,7 +58,7 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.block_settings .block_tree [aria-hidden="true"] {
|
||||
.block_settings .block_tree [aria-hidden="true"]:not(.icon) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -71,11 +71,6 @@ class container {
|
||||
*/
|
||||
protected static $eventretrievalstrategy;
|
||||
|
||||
/**
|
||||
* @var array A list of callbacks to use.
|
||||
*/
|
||||
protected static $callbacks = array();
|
||||
|
||||
/**
|
||||
* @var \stdClass[] An array of cached courses to use with the event factory.
|
||||
*/
|
||||
@ -91,16 +86,6 @@ class container {
|
||||
*/
|
||||
private static function init() {
|
||||
if (empty(self::$eventfactory)) {
|
||||
// When testing the container's components, we need to make sure
|
||||
// the callback implementations in modules are not executed, since
|
||||
// we cannot control their output from PHPUnit. To do this we have
|
||||
// a set of 'testing' callbacks that the factory can use. This way
|
||||
// we know exactly how the factory behaves when being tested.
|
||||
$getcallback = function($which) {
|
||||
return self::$callbacks[PHPUNIT_TEST ? 'testing' : 'production'][$which];
|
||||
};
|
||||
|
||||
self::initcallbacks();
|
||||
self::$actionfactory = new action_factory();
|
||||
self::$eventmapper = new event_mapper(
|
||||
// The event mapper we return from here needs to know how to
|
||||
@ -129,8 +114,8 @@ class container {
|
||||
);
|
||||
|
||||
self::$eventfactory = new event_factory(
|
||||
$getcallback('action'),
|
||||
$getcallback('visibility'),
|
||||
[self::class, 'apply_component_provide_event_action'],
|
||||
[self::class, 'apply_component_is_event_visible'],
|
||||
function ($dbrow) {
|
||||
// At present we only have a bail-out check for events in course modules.
|
||||
if (empty($dbrow->modulename)) {
|
||||
@ -183,6 +168,19 @@ class container {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all static caches, called between tests.
|
||||
*/
|
||||
public static function reset_caches() {
|
||||
self::$eventfactory = null;
|
||||
self::$eventmapper = null;
|
||||
self::$eventvault = null;
|
||||
self::$actionfactory = null;
|
||||
self::$eventretrievalstrategy = null;
|
||||
self::$coursecache = [];
|
||||
self::$modulecache = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the event factory.
|
||||
*
|
||||
@ -214,88 +212,74 @@ class container {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the callbacks.
|
||||
* Calls callback 'core_calendar_provide_event_action' from the component responsible for the event
|
||||
*
|
||||
* There are two sets here, one is used during PHPUnit runs.
|
||||
* See the comment at the start of the init method for more
|
||||
* detail.
|
||||
* If no callback is present or callback returns null, there is no action on the event
|
||||
* and it will not be displayed on the dashboard.
|
||||
*
|
||||
* @param event_interface $event
|
||||
* @return action_event|event_interface
|
||||
*/
|
||||
private static function initcallbacks() {
|
||||
self::$callbacks = array(
|
||||
'testing' => array(
|
||||
// Always return an action event.
|
||||
'action' => function (event_interface $event) {
|
||||
return new action_event(
|
||||
$event,
|
||||
new \core_calendar\local\event\value_objects\action(
|
||||
'test',
|
||||
new \moodle_url('http://example.com'),
|
||||
420,
|
||||
true
|
||||
));
|
||||
},
|
||||
// Always be visible.
|
||||
'visibility' => function (event_interface $event) {
|
||||
return true;
|
||||
}
|
||||
),
|
||||
'production' => array(
|
||||
// This function has type event_interface -> event_interface.
|
||||
// This is enforced by the event_factory.
|
||||
'action' => function (event_interface $event) {
|
||||
// Callbacks will get supplied a "legacy" version
|
||||
// of the event class.
|
||||
$mapper = self::$eventmapper;
|
||||
$action = null;
|
||||
if ($event->get_course_module()) {
|
||||
// TODO MDL-58866 Only activity modules currently support this callback.
|
||||
// Any other event will not be displayed on the dashboard.
|
||||
$action = component_callback(
|
||||
'mod_' . $event->get_course_module()->get('modname'),
|
||||
'core_calendar_provide_event_action',
|
||||
[
|
||||
$mapper->from_event_to_legacy_event($event),
|
||||
self::$actionfactory
|
||||
]
|
||||
);
|
||||
}
|
||||
public static function apply_component_provide_event_action(event_interface $event) {
|
||||
// Callbacks will get supplied a "legacy" version
|
||||
// of the event class.
|
||||
$mapper = self::$eventmapper;
|
||||
$action = null;
|
||||
if ($event->get_course_module()) {
|
||||
// TODO MDL-58866 Only activity modules currently support this callback.
|
||||
// Any other event will not be displayed on the dashboard.
|
||||
$action = component_callback(
|
||||
'mod_' . $event->get_course_module()->get('modname'),
|
||||
'core_calendar_provide_event_action',
|
||||
[
|
||||
$mapper->from_event_to_legacy_event($event),
|
||||
self::$actionfactory
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// If we get an action back, return an action event, otherwise
|
||||
// continue piping through the original event.
|
||||
//
|
||||
// If a module does not implement the callback, component_callback
|
||||
// returns null.
|
||||
return $action ? new action_event($event, $action) : $event;
|
||||
},
|
||||
// This function has type event_interface -> bool.
|
||||
// This is enforced by the event_factory.
|
||||
'visibility' => function (event_interface $event) {
|
||||
$mapper = self::$eventmapper;
|
||||
$eventvisible = null;
|
||||
if ($event->get_course_module()) {
|
||||
// TODO MDL-58866 Only activity modules currently support this callback.
|
||||
$eventvisible = component_callback(
|
||||
'mod_' . $event->get_course_module()->get('modname'),
|
||||
'core_calendar_is_event_visible',
|
||||
[
|
||||
$mapper->from_event_to_legacy_event($event)
|
||||
]
|
||||
);
|
||||
}
|
||||
// If we get an action back, return an action event, otherwise
|
||||
// continue piping through the original event.
|
||||
//
|
||||
// If a module does not implement the callback, component_callback
|
||||
// returns null.
|
||||
return $action ? new action_event($event, $action) : $event;
|
||||
}
|
||||
|
||||
// Do not display the event if there is nothing to action.
|
||||
if ($event instanceof action_event_interface && $event->get_action()->get_item_count() === 0) {
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Calls callback 'core_calendar_is_event_visible' from the component responsible for the event
|
||||
*
|
||||
* The visibility callback is optional, if not present it is assumed as visible.
|
||||
* If it is an actionable event but the get_item_count() returns 0 the visibility
|
||||
* is set to false.
|
||||
*
|
||||
* @param event_interface $event
|
||||
* @return bool
|
||||
*/
|
||||
public static function apply_component_is_event_visible(event_interface $event) {
|
||||
$mapper = self::$eventmapper;
|
||||
$eventvisible = null;
|
||||
if ($event->get_course_module()) {
|
||||
// TODO MDL-58866 Only activity modules currently support this callback.
|
||||
$eventvisible = component_callback(
|
||||
'mod_' . $event->get_course_module()->get('modname'),
|
||||
'core_calendar_is_event_visible',
|
||||
[
|
||||
$mapper->from_event_to_legacy_event($event)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Module does not implement the callback, event should be visible.
|
||||
if (is_null($eventvisible)) {
|
||||
return true;
|
||||
}
|
||||
// Do not display the event if there is nothing to action.
|
||||
if ($event instanceof action_event_interface && $event->get_action()->get_item_count() === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $eventvisible ? true : false;
|
||||
}
|
||||
),
|
||||
);
|
||||
// Module does not implement the callback, event should be visible.
|
||||
if (is_null($eventvisible)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $eventvisible ? true : false;
|
||||
}
|
||||
}
|
||||
|
@ -674,13 +674,15 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
|
||||
*/
|
||||
public function test_get_calendar_events_override() {
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$teacher = $this->getDataGenerator()->create_user();
|
||||
$anotheruser = $this->getDataGenerator()->create_user();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
|
||||
$moduleinstance = $generator->create_instance(['course' => $course->id]);
|
||||
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id);
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
|
||||
$this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
|
||||
$this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
@ -692,11 +694,12 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
|
||||
];
|
||||
|
||||
$now = time();
|
||||
// Create two events - one for everybody in the course and one only for the first student.
|
||||
$event1 = $this->create_calendar_event('Base event', 0, 'due', 0, $now + DAYSECS, $params + ['courseid' => $course->id]);
|
||||
$event2 = $this->create_calendar_event('User event', $user->id, 'due', 0, $now + 2*DAYSECS, $params + ['courseid' => 0]);
|
||||
|
||||
// Retrieve course events for teacher - only one "Base event" is returned.
|
||||
$this->setUser($teacher);
|
||||
// Retrieve course events for the second student - only one "Base event" is returned.
|
||||
$this->setUser($user2);
|
||||
$paramevents = array('courseids' => array($course->id));
|
||||
$options = array ('siteevents' => true, 'userevents' => true);
|
||||
$events = core_calendar_external::get_calendar_events($paramevents, $options);
|
||||
@ -705,7 +708,7 @@ class core_calendar_externallib_testcase extends externallib_advanced_testcase {
|
||||
$this->assertEquals(0, count($events['warnings']));
|
||||
$this->assertEquals('Base event', $events['events'][0]['name']);
|
||||
|
||||
// Retrieve events for user - both events are returned.
|
||||
// Retrieve events for the first student - both events are returned.
|
||||
$this->setUser($user);
|
||||
$events = core_calendar_external::get_calendar_events($paramevents, $options);
|
||||
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
|
||||
|
@ -559,7 +559,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
// so there is definitely something to print.
|
||||
$formattedinfo = \core_availability\info::format_info(
|
||||
$section->availableinfo, $section->course);
|
||||
$o .= $this->courserenderer->availability_info($formattedinfo);
|
||||
$o .= $this->courserenderer->availability_info($formattedinfo, 'isrestricted');
|
||||
}
|
||||
} else if ($canviewhidden && !empty($CFG->enableavailability)) {
|
||||
// Check if there is an availability restriction.
|
||||
@ -568,7 +568,7 @@ abstract class format_section_renderer_base extends plugin_renderer_base {
|
||||
if ($fullinfo) {
|
||||
$formattedinfo = \core_availability\info::format_info(
|
||||
$fullinfo, $section->course);
|
||||
$o .= $this->courserenderer->availability_info($formattedinfo);
|
||||
$o .= $this->courserenderer->availability_info($formattedinfo, 'isrestricted isfullinfo');
|
||||
}
|
||||
}
|
||||
return $o;
|
||||
|
@ -728,7 +728,24 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
* @return string
|
||||
*/
|
||||
public function availability_info($text, $additionalclasses = '') {
|
||||
|
||||
$data = ['text' => $text, 'classes' => $additionalclasses];
|
||||
$additionalclasses = array_filter(explode(' ', $additionalclasses));
|
||||
|
||||
if (in_array('ishidden', $additionalclasses)) {
|
||||
$data['ishidden'] = 1;
|
||||
|
||||
} else if (in_array('isstealth', $additionalclasses)) {
|
||||
$data['isstealth'] = 1;
|
||||
|
||||
} else if (in_array('isrestricted', $additionalclasses)) {
|
||||
$data['isrestricted'] = 1;
|
||||
|
||||
if (in_array('isfullinfo', $additionalclasses)) {
|
||||
$data['isfullinfo'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render_from_template('core/availability_info', $data);
|
||||
}
|
||||
|
||||
@ -752,7 +769,7 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
if (!empty($mod->availableinfo)) {
|
||||
$formattedinfo = \core_availability\info::format_info(
|
||||
$mod->availableinfo, $mod->get_course());
|
||||
$output = $this->availability_info($formattedinfo);
|
||||
$output = $this->availability_info($formattedinfo, 'isrestricted');
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
@ -775,9 +792,9 @@ class core_course_renderer extends plugin_renderer_base {
|
||||
// Display information about conditional availability.
|
||||
// Don't add availability information if user is not editing and activity is hidden.
|
||||
if ($mod->visible || $this->page->user_is_editing()) {
|
||||
$hidinfoclass = '';
|
||||
$hidinfoclass = 'isrestricted isfullinfo';
|
||||
if (!$mod->visible) {
|
||||
$hidinfoclass = 'hide';
|
||||
$hidinfoclass .= ' hide';
|
||||
}
|
||||
$ci = new \core_availability\info_module($mod);
|
||||
$fullinfo = $ci->get_full_information();
|
||||
|
@ -73,8 +73,8 @@ class filter_urltolink extends moodle_text_filter {
|
||||
//<a href="blah">
|
||||
//<a href="blah">
|
||||
//<a href="blah">
|
||||
$filterignoretagsopen = array('<a\s[^>]+?>');
|
||||
$filterignoretagsclose = array('</a>');
|
||||
$filterignoretagsopen = array('<a\s[^>]+?>', '<span[^>]+?class="nolink"[^>]*?>');
|
||||
$filterignoretagsclose = array('</a>', '</span>');
|
||||
filter_save_ignore_tags($text,$filterignoretagsopen,$filterignoretagsclose,$ignoretags);
|
||||
|
||||
// Check if we support unicode modifiers in regular expressions. Cache it.
|
||||
@ -174,4 +174,3 @@ function filter_urltolink_img_callback($link) {
|
||||
}
|
||||
return '<img class="filter_urltolink_image" alt="" src="'.$link[1].'" />';
|
||||
}
|
||||
|
||||
|
@ -174,6 +174,9 @@ class filter_urltolink_filter_testcase extends basic_testcase {
|
||||
'<link rel="search" type="application/opensearchdescription+xml" href="/osd.jsp" title="Peer review - Moodle Tracker"/>' => '<link rel="search" type="application/opensearchdescription+xml" href="/osd.jsp" title="Peer review - Moodle Tracker"/>',
|
||||
'<a href="https://docs.moodle.org/dev/Main_Page"></a><span>www.google.com</span><span class="placeholder"></span>' => '<a href="https://docs.moodle.org/dev/Main_Page"></a><span><a href="http://www.google.com" class="_blanktarget">www.google.com</a></span><span class="placeholder"></span>',
|
||||
'http://nolandforzombies.com <a href="zombiesFTW.com">Zombies FTW</a> http://aliens.org' => '<a href="http://nolandforzombies.com" class="_blanktarget">http://nolandforzombies.com</a> <a href="zombiesFTW.com">Zombies FTW</a> <a href="http://aliens.org" class="_blanktarget">http://aliens.org</a>',
|
||||
// Test 'nolink' class.
|
||||
'URL: <span class="nolink">http://moodle.org</span>' => 'URL: <span class="nolink">http://moodle.org</span>',
|
||||
'<span class="nolink">URL: http://moodle.org</span>' => '<span class="nolink">URL: http://moodle.org</span>',
|
||||
//URLs in Javascript. Commented out as part of MDL-21183
|
||||
//'var url="http://moodle.org";'=>'var url="http://moodle.org";',
|
||||
//'var url = "http://moodle.org";'=>'var url = "http://moodle.org";',
|
||||
|
111
group/classes/output/index_page.php
Normal file
111
group/classes/output/index_page.php
Normal file
@ -0,0 +1,111 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Group index page.
|
||||
*
|
||||
* @package core_group
|
||||
* @copyright 2017 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_group\output;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* Group index page class.
|
||||
*
|
||||
* @package core_group
|
||||
* @copyright 2017 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class index_page implements renderable, templatable {
|
||||
|
||||
/** @var int $courseid The course ID. */
|
||||
public $courseid;
|
||||
|
||||
/** @var array The array of groups to be rendered. */
|
||||
public $groups;
|
||||
|
||||
/** @var string The name of the currently selected group. */
|
||||
public $selectedgroupname;
|
||||
|
||||
/** @var array The array of group members to be rendered, if a group is selected. */
|
||||
public $selectedgroupmembers;
|
||||
|
||||
/** @var bool Whether to disable the add members/edit group buttons. */
|
||||
public $disableaddedit;
|
||||
|
||||
/** @var bool Whether to disable the delete group button. */
|
||||
public $disabledelete;
|
||||
|
||||
/** @var array Groups that can't be deleted by the user. */
|
||||
public $undeletablegroups;
|
||||
|
||||
/**
|
||||
* index_page constructor.
|
||||
*
|
||||
* @param int $courseid The course ID.
|
||||
* @param array $groups The array of groups to be rendered.
|
||||
* @param string $selectedgroupname The name of the currently selected group.
|
||||
* @param array $selectedgroupmembers The array of group members to be rendered, if a group is selected.
|
||||
* @param bool $disableaddedit Whether to disable the add members/edit group buttons.
|
||||
* @param bool $disabledelete Whether to disable the delete group button.
|
||||
* @param array $undeletablegroups Groups that can't be deleted by the user.
|
||||
*/
|
||||
public function __construct($courseid, $groups, $selectedgroupname, $selectedgroupmembers, $disableaddedit, $disabledelete,
|
||||
$undeletablegroups) {
|
||||
$this->courseid = $courseid;
|
||||
$this->groups = $groups;
|
||||
$this->selectedgroupname = $selectedgroupname;
|
||||
$this->selectedgroupmembers = $selectedgroupmembers;
|
||||
$this->disableaddedit = $disableaddedit;
|
||||
$this->disabledelete = $disabledelete;
|
||||
$this->undeletablegroups = $undeletablegroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the data.
|
||||
*
|
||||
* @param renderer_base $output
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output) {
|
||||
global $CFG;
|
||||
|
||||
$data = new stdClass();
|
||||
|
||||
// Variables that will be passed to the JS helper.
|
||||
$data->courseid = $this->courseid;
|
||||
$data->wwwroot = $CFG->wwwroot;
|
||||
// To be passed to the JS init script in the template. Encode as a JSON string.
|
||||
$data->undeletablegroups = json_encode($this->undeletablegroups);
|
||||
|
||||
// Some buttons are enabled if single group selected.
|
||||
$data->addmembersdisabled = $this->disableaddedit;
|
||||
$data->editgroupsettingsdisabled = $this->disableaddedit;
|
||||
$data->deletegroupdisabled = $this->disabledelete;
|
||||
$data->groups = $this->groups;
|
||||
$data->members = $this->selectedgroupmembers;
|
||||
$data->selectedgroup = $this->selectedgroupname;
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
50
group/classes/output/renderer.php
Normal file
50
group/classes/output/renderer.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Renderers.
|
||||
*
|
||||
* @package core_group
|
||||
* @copyright 2017 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_group\output;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use plugin_renderer_base;
|
||||
|
||||
/**
|
||||
* Renderer class.
|
||||
*
|
||||
* @package core_group
|
||||
* @copyright 2017 Jun Pataleta
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class renderer extends plugin_renderer_base {
|
||||
|
||||
/**
|
||||
* Defer to template.
|
||||
*
|
||||
* @param index_page $page
|
||||
* @return string
|
||||
*/
|
||||
public function render_index_page(index_page $page) {
|
||||
$data = $page->export_for_template($this);
|
||||
return parent::render_from_template('core_group/index', $data);
|
||||
}
|
||||
}
|
@ -1386,6 +1386,7 @@ class core_group_external extends external_api {
|
||||
|
||||
$results = array(
|
||||
'groups' => $usergroups,
|
||||
'canaccessallgroups' => has_capability('moodle/site:accessallgroups', $context, $user),
|
||||
'warnings' => $warnings
|
||||
);
|
||||
return $results;
|
||||
@ -1401,6 +1402,8 @@ class core_group_external extends external_api {
|
||||
return new external_single_structure(
|
||||
array(
|
||||
'groups' => new external_multiple_structure(self::group_description()),
|
||||
'canaccessallgroups' => new external_value(PARAM_BOOL,
|
||||
'Whether the user will be able to access all the activity groups.', VALUE_OPTIONAL),
|
||||
'warnings' => new external_warnings(),
|
||||
)
|
||||
);
|
||||
|
124
group/index.php
124
group/index.php
@ -61,6 +61,7 @@ $context = context_course::instance($course->id);
|
||||
require_capability('moodle/course:managegroups', $context);
|
||||
|
||||
$PAGE->requires->js('/group/clientlib.js');
|
||||
$PAGE->requires->js('/group/module.js');
|
||||
|
||||
// Check for multiple/no group errors
|
||||
if (!$singlegroup) {
|
||||
@ -152,41 +153,23 @@ echo $OUTPUT->header();
|
||||
$currenttab = 'groups';
|
||||
require('tabs.php');
|
||||
|
||||
$disabled = 'disabled="disabled"';
|
||||
|
||||
// Some buttons are enabled if single group selected.
|
||||
$showaddmembersform_disabled = $singlegroup ? '' : $disabled;
|
||||
$showeditgroupsettingsform_disabled = $singlegroup ? '' : $disabled;
|
||||
$deletegroup_disabled = count($groupids) > 0 ? '' : $disabled;
|
||||
|
||||
echo $OUTPUT->heading(format_string($course->shortname, true, array('context' => $context)) .' '.$strgroups, 3);
|
||||
echo '<form id="groupeditform" action="index.php" method="post">'."\n";
|
||||
echo '<div>'."\n";
|
||||
echo '<input type="hidden" name="id" value="' . $courseid . '" />'."\n";
|
||||
|
||||
echo html_writer::start_tag('div', array('class' => 'groupmanagementtable boxaligncenter'));
|
||||
echo html_writer::start_tag('div', array('class' => 'groups'));
|
||||
|
||||
echo '<p><label for="groups"><span id="groupslabel">'.get_string('groups').':</span><span id="thegrouping"> </span></label></p>'."\n";
|
||||
|
||||
$onchange = 'M.core_group.membersCombo.refreshMembers();';
|
||||
|
||||
echo '<select name="groups[]" multiple="multiple" id="groups" size="15" class="select" onchange="'.$onchange.'">'."\n";
|
||||
|
||||
$groups = groups_get_all_groups($courseid);
|
||||
$selectedname = ' ';
|
||||
$selectedname = null;
|
||||
$preventgroupremoval = array();
|
||||
|
||||
// Get list of groups to render.
|
||||
$groupoptions = array();
|
||||
if ($groups) {
|
||||
// Print out the HTML
|
||||
foreach ($groups as $group) {
|
||||
$select = '';
|
||||
$usercount = $DB->count_records('groups_members', array('groupid'=>$group->id));
|
||||
$groupname = format_string($group->name).' ('.$usercount.')';
|
||||
if (in_array($group->id,$groupids)) {
|
||||
$select = ' selected="selected"';
|
||||
$selected = false;
|
||||
$usercount = $DB->count_records('groups_members', array('groupid' => $group->id));
|
||||
$groupname = format_string($group->name) . ' (' . $usercount . ')';
|
||||
if (in_array($group->id, $groupids)) {
|
||||
$selected = true;
|
||||
if ($singlegroup) {
|
||||
// Only keep selected name if there is one group selected
|
||||
// Only keep selected name if there is one group selected.
|
||||
$selectedname = $groupname;
|
||||
}
|
||||
}
|
||||
@ -194,76 +177,41 @@ if ($groups) {
|
||||
$preventgroupremoval[$group->id] = true;
|
||||
}
|
||||
|
||||
echo "<option value=\"{$group->id}\"$select title=\"$groupname\">$groupname</option>\n";
|
||||
$groupoptions[] = (object) [
|
||||
'value' => $group->id,
|
||||
'selected' => $selected,
|
||||
'text' => $groupname
|
||||
];
|
||||
}
|
||||
} else {
|
||||
// Print an empty option to avoid the XHTML error of having an empty select element
|
||||
echo '<option> </option>';
|
||||
}
|
||||
|
||||
echo '</select>'."\n";
|
||||
echo '<p><input class="btn btn-secondary" type="submit" name="act_updatemembers" id="updatemembers" value="'
|
||||
. get_string('showmembersforgroup', 'group') . '" /></p>'."\n";
|
||||
echo '<p><input class="btn btn-secondary" type="submit" '. $showeditgroupsettingsform_disabled .
|
||||
' name="act_showgroupsettingsform" id="showeditgroupsettingsform" value="'
|
||||
. get_string('editgroupsettings', 'group') . '" /></p>'."\n";
|
||||
echo '<p><input class="btn btn-secondary" type="submit" '. $deletegroup_disabled .
|
||||
' name="act_deletegroup" id="deletegroup" value="'
|
||||
. get_string('deleteselectedgroup', 'group') . '" /></p>'."\n";
|
||||
|
||||
echo '<p><input class="btn btn-secondary" type="submit" name="act_showcreateorphangroupform" id="showcreateorphangroupform" value="'
|
||||
. get_string('creategroup', 'group') . '" /></p>'."\n";
|
||||
|
||||
echo '<p><input class="btn btn-secondary" type="submit" name="act_showautocreategroupsform" id="showautocreategroupsform" value="'
|
||||
. get_string('autocreategroups', 'group') . '" /></p>'."\n";
|
||||
|
||||
echo '<p><input class="btn btn-secondary" type="submit" name="act_showimportgroups" id="showimportgroups" value="'
|
||||
. get_string('importgroups', 'core_group') . '" /></p>'."\n";
|
||||
|
||||
echo html_writer::end_tag('div');
|
||||
echo html_writer::start_tag('div', array('class' => 'members'));
|
||||
|
||||
echo '<p><label for="members"><span id="memberslabel">'.
|
||||
get_string('membersofselectedgroup', 'group').
|
||||
' </span><span id="thegroup">'.$selectedname.'</span></label></p>'."\n";
|
||||
//NOTE: the SELECT was, multiple="multiple" name="user[]" - not used and breaks onclick.
|
||||
echo '<select name="user" id="members" size="15" class="select"'."\n";
|
||||
echo ' onclick="window.status=this.options[this.selectedIndex].title;" onmouseout="window.status=\'\';">'."\n";
|
||||
|
||||
$member_names = array();
|
||||
|
||||
$atleastonemember = false;
|
||||
// Get list of group members to render if there is a single selected group.
|
||||
$members = array();
|
||||
if ($singlegroup) {
|
||||
if ($groupmemberroles = groups_get_members_by_role($groupids[0], $courseid, 'u.id, ' . get_all_user_name_fields(true, 'u'))) {
|
||||
foreach($groupmemberroles as $roleid=>$roledata) {
|
||||
echo '<optgroup label="'.s($roledata->name).'">';
|
||||
foreach($roledata->users as $member) {
|
||||
echo '<option value="'.$member->id.'">'.fullname($member, true).'</option>';
|
||||
$atleastonemember = true;
|
||||
$usernamefields = get_all_user_name_fields(true, 'u');
|
||||
if ($groupmemberroles = groups_get_members_by_role(reset($groupids), $courseid, 'u.id, ' . $usernamefields)) {
|
||||
foreach ($groupmemberroles as $roleid => $roledata) {
|
||||
$users = array();
|
||||
foreach ($roledata->users as $member) {
|
||||
$users[] = (object)[
|
||||
'value' => $member->id,
|
||||
'text' => fullname($member, true)
|
||||
];
|
||||
}
|
||||
echo '</optgroup>';
|
||||
$members[] = (object)[
|
||||
'role' => s($roledata->name),
|
||||
'rolemembers' => $users
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$atleastonemember) {
|
||||
// Print an empty option to avoid the XHTML error of having an empty select element
|
||||
echo '<option> </option>';
|
||||
}
|
||||
|
||||
echo '</select>'."\n";
|
||||
|
||||
echo '<p><input class="btn btn-secondary" type="submit" ' . $showaddmembersform_disabled . ' name="act_showaddmembersform" '
|
||||
. 'id="showaddmembersform" value="' . get_string('adduserstogroup', 'group'). '" /></p>'."\n";
|
||||
echo html_writer::end_tag('div');
|
||||
echo html_writer::end_tag('div');
|
||||
|
||||
//<input type="hidden" name="rand" value="om" />
|
||||
echo '</div>'."\n";
|
||||
echo '</form>'."\n";
|
||||
|
||||
$PAGE->requires->js_init_call('M.core_group.init_index', array($CFG->wwwroot, $courseid));
|
||||
$PAGE->requires->js_init_call('M.core_group.groupslist', array($preventgroupremoval));
|
||||
$disableaddedit = !$singlegroup;
|
||||
$disabledelete = !empty($groupids);
|
||||
$renderable = new \core_group\output\index_page($courseid, $groupoptions, $selectedname, $members, $disableaddedit, $disabledelete,
|
||||
$preventgroupremoval);
|
||||
$output = $PAGE->get_renderer('core_group');
|
||||
echo $output->render($renderable);
|
||||
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
|
145
group/templates/index.mustache
Normal file
145
group/templates/index.mustache
Normal file
@ -0,0 +1,145 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_group/index
|
||||
|
||||
Template for the Groups page.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* courseid int The course ID.
|
||||
* selectedgroup string The initially selected group.
|
||||
* editgroupsettingsdisabled bool Whether to disable the "Edit group settings" button on load.
|
||||
* deletegroupdisabled bool Whether to disable the "Delete selected group" button on load.
|
||||
* addmembersdisabled bool Whether to disable the "Add/remove users" button on load.
|
||||
* groups array The list of groups.
|
||||
* members array The list of members, grouped based on roles.
|
||||
* undeletablegroups string A JSON string containing an array of group IDs that a user cannot delete.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"courseid": "1",
|
||||
"selectedgroup": "Group 1 (3)",
|
||||
"editgroupsettingsdisabled": false,
|
||||
"deletegroupdisabled": false,
|
||||
"addmembersdisabled": false,
|
||||
"groups": [
|
||||
{
|
||||
"value": "1",
|
||||
"text": "Group 1 (3)",
|
||||
"selected": true
|
||||
},
|
||||
{
|
||||
"value": "2",
|
||||
"text": "Group 2 (2)"
|
||||
}
|
||||
],
|
||||
"members": [
|
||||
{
|
||||
"role": "Student",
|
||||
"rolemembers": [
|
||||
{
|
||||
"value": "1",
|
||||
"text": "John Doe"
|
||||
},
|
||||
{
|
||||
"value": "2",
|
||||
"text": "Jane Doe"
|
||||
},
|
||||
{
|
||||
"value": "3",
|
||||
"text": "John Smith"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"undeletablegroups": "[1: true, 3: true]"
|
||||
}
|
||||
}}
|
||||
<form id="groupeditform" action="index.php" method="post">
|
||||
<div class="container-fluid groupmanagementtable">
|
||||
<div class="row row-fluid rtl-compatible">
|
||||
<div class="col-md-6 span6 m-b-1">
|
||||
<input type="hidden" name="id" value="{{courseid}}">
|
||||
<div class="form-group">
|
||||
<label for="groups">
|
||||
<span id="groupslabel">{{#str}}groups{{/str}}</span>
|
||||
<span id="thegrouping"> </span>
|
||||
</label>
|
||||
<select name="groups[]" multiple="multiple" id="groups" size="15" class="form-control input-block-level">
|
||||
{{#groups}}
|
||||
<option value="{{value}}" {{#selected}}selected="selected"{{/selected}} title="{{{text}}}">{{{text}}}</option>
|
||||
{{/groups}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" name="act_updatemembers" id="updatemembers" value="{{#str}}showmembersforgroup, group{{/str}}" class="btn btn-default" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" name="act_showgroupsettingsform" id="showeditgroupsettingsform" value="{{#str}}editgroupsettings, group{{/str}}" {{#editgroupsettingsdisabled}}disabled="disabled"{{/editgroupsettingsdisabled}} class="btn btn-default" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" name="act_deletegroup" id="deletegroup" value="{{#str}}deleteselectedgroup, group{{/str}}" {{#deletegroupdisabled}}disabled="disabled"{{/deletegroupdisabled}} class="btn btn-default" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" name="act_showcreateorphangroupform" id="showcreateorphangroupform" value="{{#str}}creategroup, group{{/str}}" class="btn btn-default" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" name="act_showautocreategroupsform" id="showautocreategroupsform" value="{{#str}}autocreategroups, group{{/str}}" class="btn btn-default" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" name="act_showimportgroups" id="showimportgroups" value="{{#str}}importgroups, group{{/str}}" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 span6 m-b-1">
|
||||
<div class="form-group">
|
||||
<label for="members">
|
||||
<span id="memberslabel">{{#str}}membersofselectedgroup, group{{/str}}</span>
|
||||
<span id="thegroup">{{{selectedgroup}}}</span>
|
||||
</label>
|
||||
<select size="15" multiple="multiple" class="form-control input-block-level" id="members" name="user">
|
||||
{{#members}}
|
||||
<optgroup label="{{role}}">
|
||||
{{#rolemembers}}
|
||||
<option value="{{value}}">{{{text}}}</option>
|
||||
{{/rolemembers}}
|
||||
</optgroup>
|
||||
{{/members}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="submit" value="{{#str}}adduserstogroup, group{{/str}}" class="btn btn-default" {{#addmembersdisabled}}disabled="disabled"{{/addmembersdisabled}} name="act_showaddmembersform" id="showaddmembersform"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{{#js}}
|
||||
require(['jquery', 'core/yui'], function($) {
|
||||
$("#groups").change(function() {
|
||||
M.core_group.membersCombo.refreshMembers();
|
||||
});
|
||||
M.core_group.init_index(Y, "{{wwwroot}}", {{courseid}});
|
||||
var undeletableGroups = JSON.parse('{{{undeletablegroups}}}');
|
||||
M.core_group.groupslist(Y, undeletableGroups);
|
||||
});
|
||||
{{/js}}
|
@ -523,6 +523,7 @@ class core_group_externallib_testcase extends externallib_advanced_testcase {
|
||||
$groups = core_group_external::get_activity_allowed_groups($cm1->id);
|
||||
$groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups);
|
||||
$this->assertCount(2, $groups['groups']);
|
||||
$this->assertFalse($groups['canaccessallgroups']);
|
||||
|
||||
foreach ($groups['groups'] as $group) {
|
||||
if ($group['name'] == $group1data['name']) {
|
||||
@ -539,12 +540,21 @@ class core_group_externallib_testcase extends externallib_advanced_testcase {
|
||||
$groups = core_group_external::get_activity_allowed_groups($cm1->id, $student->id);
|
||||
$groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups);
|
||||
$this->assertCount(2, $groups['groups']);
|
||||
// We are checking the $student passed as parameter so this will return false.
|
||||
$this->assertFalse($groups['canaccessallgroups']);
|
||||
|
||||
// Check warnings. Trying to get groups for a user not enrolled in course.
|
||||
$groups = core_group_external::get_activity_allowed_groups($cm1->id, $otherstudent->id);
|
||||
$groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups);
|
||||
$this->assertCount(1, $groups['warnings']);
|
||||
$this->assertFalse($groups['canaccessallgroups']);
|
||||
|
||||
// Checking teacher groups.
|
||||
$groups = core_group_external::get_activity_allowed_groups($cm1->id);
|
||||
$groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups);
|
||||
$this->assertCount(2, $groups['groups']);
|
||||
// Teachers by default can access all groups.
|
||||
$this->assertTrue($groups['canaccessallgroups']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1505,6 +1505,7 @@ $string['publicsitefileswarning3'] = 'Note: Files placed here can be accessed by
|
||||
$string['publish'] = 'Publish';
|
||||
$string['question'] = 'Question';
|
||||
$string['questionsinthequestionbank'] = 'Questions in the question bank';
|
||||
$string['quotausage'] = 'You have currently used {$a->used} of your {$a->total} limit.';
|
||||
$string['readinginfofrombackup'] = 'Reading info from backup';
|
||||
$string['readme'] = 'README';
|
||||
$string['recentactivity'] = 'Recent activity';
|
||||
|
2
lib/amd/build/ajax.min.js
vendored
2
lib/amd/build/ajax.min.js
vendored
@ -1 +1 @@
|
||||
define(["jquery","core/config"],function(a,b){var c=function(a){var b,c,d=this,e=null,f=0;for(f=0;f<d.length;f++){if(b=d[f],c=a[f],"undefined"==typeof c){e=new Error("missing response");break}if(c.error!==!1){e=c.exception;break}b.deferred.resolve(c.data)}if(null!==e)for(;f<d.length;f++)b=d[f],b.deferred.reject(e)},d=function(a,b){var c=this,d=0;for(d=0;d<c.length;d++){var e=c[d];e.deferred.reject(b)}};return{call:function(e,f,g){var h,i=[],j=[],k=[],l="";for("undefined"==typeof g&&(g=!0),"undefined"==typeof f&&(f=!0),h=0;h<e.length;h++){var m=e[h];i.push({index:h,methodname:m.methodname,args:m.args}),m.deferred=a.Deferred(),j.push(m.deferred.promise()),"undefined"!=typeof m.done&&m.deferred.done(m.done),"undefined"!=typeof m.fail&&m.deferred.fail(m.fail),m.index=h,k.push(m.methodname)}l=k.length<=5?k.sort().join():k.length+"-method-calls",i=JSON.stringify(i);var n={type:"POST",data:i,context:e,dataType:"json",processData:!1,async:f,contentType:"application/json"},o="service.php";g||(o="service-nologin.php");var p=b.wwwroot+"/lib/ajax/"+o+"?sesskey="+b.sesskey+"&info="+l;return f?a.ajax(p,n).done(c).fail(d):(n.success=c,n.error=d,a.ajax(p,n)),j}}});
|
||||
define(["jquery","core/config","core/log"],function(a,b,c){var d=!1,e=function(a){var b,c,d=this,e=null,f=0;for(f=0;f<d.length;f++){if(b=d[f],c=a[f],"undefined"==typeof c){e=new Error("missing response");break}if(c.error!==!1){e=c.exception;break}b.deferred.resolve(c.data)}if(null!==e)for(;f<d.length;f++)b=d[f],b.deferred.reject(e)},f=function(a,b){var e=this,f=0;for(f=0;f<e.length;f++){var g=e[f];d?c.error("Page unload: "+b):g.deferred.reject(b)}};return{call:function(c,g,h){a(window).bind("beforeunload",function(){d=!0});var i,j=[],k=[],l=[],m="";for("undefined"==typeof h&&(h=!0),"undefined"==typeof g&&(g=!0),i=0;i<c.length;i++){var n=c[i];j.push({index:i,methodname:n.methodname,args:n.args}),n.deferred=a.Deferred(),k.push(n.deferred.promise()),"undefined"!=typeof n.done&&n.deferred.done(n.done),"undefined"!=typeof n.fail&&n.deferred.fail(n.fail),n.index=i,l.push(n.methodname)}m=l.length<=5?l.sort().join():l.length+"-method-calls",j=JSON.stringify(j);var o={type:"POST",data:j,context:c,dataType:"json",processData:!1,async:g,contentType:"application/json"},p="service.php";h||(p="service-nologin.php");var q=b.wwwroot+"/lib/ajax/"+p+"?sesskey="+b.sesskey+"&info="+m;return g?a.ajax(q,o).done(e).fail(f):(o.success=e,o.error=f,a.ajax(q,o)),k}}});
|
@ -25,7 +25,10 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @since 2.9
|
||||
*/
|
||||
define(['jquery', 'core/config'], function($, config) {
|
||||
define(['jquery', 'core/config', 'core/log'], function($, config, Log) {
|
||||
|
||||
// Keeps track of when the user leaves the page so we know not to show an error.
|
||||
var unloading = false;
|
||||
|
||||
/**
|
||||
* Success handler. Called when the ajax call succeeds. Checks each response and
|
||||
@ -87,7 +90,12 @@ define(['jquery', 'core/config'], function($, config) {
|
||||
for (i = 0; i < requests.length; i++) {
|
||||
var request = requests[i];
|
||||
|
||||
request.deferred.reject(textStatus);
|
||||
if (unloading) {
|
||||
// No need to trigger an error because we are already navigating.
|
||||
Log.error("Page unload: " + textStatus);
|
||||
} else {
|
||||
request.deferred.reject(textStatus);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -109,6 +117,9 @@ define(['jquery', 'core/config'], function($, config) {
|
||||
* @return {Promise[]} Array of promises that will be resolved when the ajax call returns.
|
||||
*/
|
||||
call: function(requests, async, loginrequired) {
|
||||
$(window).bind('beforeunload', function() {
|
||||
unloading = true;
|
||||
});
|
||||
var ajaxRequestData = [],
|
||||
i,
|
||||
promises = [],
|
||||
|
@ -247,7 +247,7 @@ class icon_system_fontawesome extends icon_system_font {
|
||||
'core:i/mnethost' => 'fa-external-link',
|
||||
'core:i/moodle_host' => 'fa-graduation-cap',
|
||||
'core:i/move_2d' => 'fa-arrows',
|
||||
'core:i/navigationitem' => 'fa-angle-right',
|
||||
'core:i/navigationitem' => 'fa-fw',
|
||||
'core:i/ne_red_mark' => 'fa-remove',
|
||||
'core:i/new' => 'fa-plus',
|
||||
'core:i/news' => 'fa-newspaper-o',
|
||||
@ -280,7 +280,7 @@ class icon_system_fontawesome extends icon_system_font {
|
||||
'core:i/scales' => 'fa-balance-scale',
|
||||
'core:i/scheduled' => 'fa-calendar-check-o',
|
||||
'core:i/search' => 'fa-search',
|
||||
'core:i/settings' => 'fa-cogs',
|
||||
'core:i/settings' => 'fa-cog',
|
||||
'core:i/show' => 'fa-eye-slash',
|
||||
'core:i/siteevent' => 'fa-share-alt',
|
||||
'core:i/star-rating' => 'fa-star',
|
||||
@ -309,7 +309,7 @@ class icon_system_fontawesome extends icon_system_font {
|
||||
'core:t/block' => 'fa-ban',
|
||||
'core:t/block_to_dock_rtl' => 'fa-chevron-right',
|
||||
'core:t/block_to_dock' => 'fa-chevron-left',
|
||||
'core:t/calc_off' => 'fa-times fa-cross',
|
||||
'core:t/calc_off' => 'fa-calculator', // TODO: Change to better icon once we have stacked icon support or more icons.
|
||||
'core:t/calc' => 'fa-calculator',
|
||||
'core:t/check' => 'fa-check',
|
||||
'core:t/cohort' => 'fa-users',
|
||||
|
@ -37,7 +37,7 @@ class repository extends base {
|
||||
*/
|
||||
public static function get_enabled_plugins() {
|
||||
global $DB;
|
||||
return $DB->get_records_menu('repository', array('visible'=>1), 'type ASC', 'type, type AS val');
|
||||
return $DB->get_records_menu('repository', null, 'type ASC', 'type, type AS val');
|
||||
}
|
||||
|
||||
public function get_settings_section_name() {
|
||||
|
@ -461,7 +461,7 @@ class core_user {
|
||||
'choices' => array_merge(array('' => ''), get_string_manager()->get_list_of_countries(true, true)));
|
||||
$fields['lang'] = array('type' => PARAM_LANG, 'null' => NULL_NOT_ALLOWED, 'default' => $CFG->lang,
|
||||
'choices' => array_merge(array('' => ''), get_string_manager()->get_list_of_translations(false)));
|
||||
$fields['calendartype'] = array('type' => PARAM_NOTAGS, 'null' => NULL_NOT_ALLOWED, 'default' => $CFG->calendartype,
|
||||
$fields['calendartype'] = array('type' => PARAM_PLUGIN, 'null' => NULL_NOT_ALLOWED, 'default' => $CFG->calendartype,
|
||||
'choices' => array_merge(array('' => ''), \core_calendar\type_factory::get_list_of_calendar_types()));
|
||||
$fields['theme'] = array('type' => PARAM_THEME, 'null' => NULL_NOT_ALLOWED,
|
||||
'default' => theme_config::DEFAULT_THEME, 'choices' => array_merge(array('' => ''), get_list_of_themes()));
|
||||
|
@ -423,6 +423,21 @@ class completion_info {
|
||||
// Load criteria from database
|
||||
$records = (array)$DB->get_records('course_completion_criteria', $params);
|
||||
|
||||
// Order records so activities are in the same order as they appear on the course view page.
|
||||
if ($records) {
|
||||
$activitiesorder = array_keys(get_fast_modinfo($this->course)->get_cms());
|
||||
usort($records, function ($a, $b) use ($activitiesorder) {
|
||||
$aidx = ($a->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) ?
|
||||
array_search($a->moduleinstance, $activitiesorder) : false;
|
||||
$bidx = ($b->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) ?
|
||||
array_search($b->moduleinstance, $activitiesorder) : false;
|
||||
if ($aidx === false || $bidx === false || $aidx == $bidx) {
|
||||
return 0;
|
||||
}
|
||||
return ($aidx < $bidx) ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
// Build array of criteria objects
|
||||
$this->criteria = array();
|
||||
foreach ($records as $record) {
|
||||
|
@ -1206,6 +1206,15 @@ $functions = array(
|
||||
'type' => 'write',
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
|
||||
),
|
||||
'core_user_get_private_files_info' => array(
|
||||
'classname' => 'core_user_external',
|
||||
'methodname' => 'get_private_files_info',
|
||||
'classpath' => 'user/externallib.php',
|
||||
'description' => 'Returns general information about files in the user private files area.',
|
||||
'type' => 'read',
|
||||
'capabilities' => 'moodle/user:manageownfiles',
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
|
||||
),
|
||||
|
||||
// Competencies functions.
|
||||
'core_competency_create_competency_framework' => array(
|
||||
|
@ -503,9 +503,29 @@ function file_rewrite_pluginfile_urls($text, $file, $contextid, $component, $fil
|
||||
* (more information will be added as needed).
|
||||
*/
|
||||
function file_get_draft_area_info($draftitemid, $filepath = '/') {
|
||||
global $CFG, $USER;
|
||||
global $USER;
|
||||
|
||||
$usercontext = context_user::instance($USER->id);
|
||||
return file_get_file_area_info($usercontext->id, 'user', 'draft', $draftitemid, $filepath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about files in an area.
|
||||
*
|
||||
* @param int $contextid context id
|
||||
* @param string $component component
|
||||
* @param string $filearea file area name
|
||||
* @param int $itemid item id or all files if not specified
|
||||
* @param string $filepath path to the directory from which the information have to be retrieved.
|
||||
* @return array with the following entries:
|
||||
* 'filecount' => number of files in the area.
|
||||
* 'filesize' => total size of the files in the area.
|
||||
* 'foldercount' => number of folders in the area.
|
||||
* 'filesize_without_references' => total size of the area excluding file references.
|
||||
* @since Moodle 3.4
|
||||
*/
|
||||
function file_get_file_area_info($contextid, $component, $filearea, $itemid = 0, $filepath = '/') {
|
||||
|
||||
$fs = get_file_storage();
|
||||
|
||||
$results = array(
|
||||
@ -515,11 +535,8 @@ function file_get_draft_area_info($draftitemid, $filepath = '/') {
|
||||
'filesize_without_references' => 0
|
||||
);
|
||||
|
||||
if ($filepath != '/') {
|
||||
$draftfiles = $fs->get_directory_files($usercontext->id, 'user', 'draft', $draftitemid, $filepath, true, true);
|
||||
} else {
|
||||
$draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id', true);
|
||||
}
|
||||
$draftfiles = $fs->get_directory_files($contextid, $component, $filearea, $itemid, $filepath, true, true);
|
||||
|
||||
foreach ($draftfiles as $file) {
|
||||
if ($file->is_directory()) {
|
||||
$results['foldercount'] += 1;
|
||||
|
@ -662,6 +662,7 @@ class zip_archive extends file_archive {
|
||||
case 'ISO-8859-6': $encoding = 'CP720'; break;
|
||||
case 'ISO-8859-7': $encoding = 'CP737'; break;
|
||||
case 'ISO-8859-8': $encoding = 'CP862'; break;
|
||||
case 'WINDOWS-1251': $encoding = 'CP866'; break;
|
||||
case 'EUC-JP':
|
||||
case 'UTF-8':
|
||||
if ($winchar = get_string('localewincharset', 'langconfig')) {
|
||||
|
@ -229,6 +229,11 @@ class phpunit_util extends testing_util {
|
||||
// Reset internal users.
|
||||
core_user::reset_internal_users();
|
||||
|
||||
// Clear static caches in calendar container.
|
||||
if (class_exists('\core_calendar\local\event\container', false)) {
|
||||
core_calendar\local\event\container::reset_caches();
|
||||
}
|
||||
|
||||
//TODO MDL-25290: add more resets here and probably refactor them to new core function
|
||||
|
||||
// Reset course and module caches.
|
||||
|
@ -1545,7 +1545,7 @@ class table_sql extends flexible_table {
|
||||
* Of course you can use sub-queries, JOINS etc. by putting them in the
|
||||
* appropriate clause of the query.
|
||||
*/
|
||||
function set_sql($fields, $from, $where, array $params = NULL) {
|
||||
function set_sql($fields, $from, $where, array $params = array()) {
|
||||
$this->sql = new stdClass();
|
||||
$this->sql->fields = $fields;
|
||||
$this->sql->from = $from;
|
||||
|
@ -1229,6 +1229,117 @@ EOF;
|
||||
$file = array_shift($files);
|
||||
$this->assertTrue($file->is_directory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test file_get_draft_area_info.
|
||||
*/
|
||||
public function test_file_get_draft_area_info() {
|
||||
global $USER;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
$fs = get_file_storage();
|
||||
|
||||
$filerecord = array(
|
||||
'filename' => 'one.txt',
|
||||
);
|
||||
$file = self::create_draft_file($filerecord);
|
||||
$size = $file->get_filesize();
|
||||
$draftitemid = $file->get_itemid();
|
||||
// Add another file.
|
||||
$filerecord = array(
|
||||
'itemid' => $draftitemid,
|
||||
'filename' => 'second.txt',
|
||||
);
|
||||
$file = self::create_draft_file($filerecord);
|
||||
$size += $file->get_filesize();
|
||||
|
||||
// Create directory.
|
||||
$usercontext = context_user::instance($USER->id);
|
||||
$dir = $fs->create_directory($usercontext->id, 'user', 'draft', $draftitemid, '/testsubdir/');
|
||||
// Add file to directory.
|
||||
$filerecord = array(
|
||||
'itemid' => $draftitemid,
|
||||
'filename' => 'third.txt',
|
||||
'filepath' => '/testsubdir/',
|
||||
);
|
||||
$file = self::create_draft_file($filerecord);
|
||||
$size += $file->get_filesize();
|
||||
|
||||
$fileinfo = file_get_draft_area_info($draftitemid);
|
||||
$this->assertEquals(3, $fileinfo['filecount']);
|
||||
$this->assertEquals($size, $fileinfo['filesize']);
|
||||
$this->assertEquals(1, $fileinfo['foldercount']); // Directory created.
|
||||
$this->assertEquals($size, $fileinfo['filesize_without_references']);
|
||||
|
||||
// Now get files from just one folder.
|
||||
$fileinfo = file_get_draft_area_info($draftitemid, '/testsubdir/');
|
||||
$this->assertEquals(1, $fileinfo['filecount']);
|
||||
$this->assertEquals($file->get_filesize(), $fileinfo['filesize']);
|
||||
$this->assertEquals(0, $fileinfo['foldercount']); // No subdirectories inside the directory.
|
||||
$this->assertEquals($file->get_filesize(), $fileinfo['filesize_without_references']);
|
||||
|
||||
// Check we get the same results if we call file_get_file_area_info.
|
||||
$fileinfo = file_get_file_area_info($usercontext->id, 'user', 'draft', $draftitemid);
|
||||
$this->assertEquals(3, $fileinfo['filecount']);
|
||||
$this->assertEquals($size, $fileinfo['filesize']);
|
||||
$this->assertEquals(1, $fileinfo['foldercount']); // Directory created.
|
||||
$this->assertEquals($size, $fileinfo['filesize_without_references']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test file_get_file_area_info.
|
||||
*/
|
||||
public function test_file_get_file_area_info() {
|
||||
global $USER;
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
$fs = get_file_storage();
|
||||
|
||||
$filerecord = array(
|
||||
'filename' => 'one.txt',
|
||||
);
|
||||
$file = self::create_draft_file($filerecord);
|
||||
$size = $file->get_filesize();
|
||||
$draftitemid = $file->get_itemid();
|
||||
// Add another file.
|
||||
$filerecord = array(
|
||||
'itemid' => $draftitemid,
|
||||
'filename' => 'second.txt',
|
||||
);
|
||||
$file = self::create_draft_file($filerecord);
|
||||
$size += $file->get_filesize();
|
||||
|
||||
// Create directory.
|
||||
$usercontext = context_user::instance($USER->id);
|
||||
$dir = $fs->create_directory($usercontext->id, 'user', 'draft', $draftitemid, '/testsubdir/');
|
||||
// Add file to directory.
|
||||
$filerecord = array(
|
||||
'itemid' => $draftitemid,
|
||||
'filename' => 'third.txt',
|
||||
'filepath' => '/testsubdir/',
|
||||
);
|
||||
$file = self::create_draft_file($filerecord);
|
||||
$size += $file->get_filesize();
|
||||
|
||||
// Add files to user private file area.
|
||||
$options = array('subdirs' => 1, 'maxfiles' => 3);
|
||||
file_merge_files_from_draft_area_into_filearea($draftitemid, $file->get_contextid(), 'user', 'private', 0, $options);
|
||||
|
||||
$fileinfo = file_get_file_area_info($usercontext->id, 'user', 'private');
|
||||
$this->assertEquals(3, $fileinfo['filecount']);
|
||||
$this->assertEquals($size, $fileinfo['filesize']);
|
||||
$this->assertEquals(1, $fileinfo['foldercount']); // Directory created.
|
||||
$this->assertEquals($size, $fileinfo['filesize_without_references']);
|
||||
|
||||
// Now get files from just one folder.
|
||||
$fileinfo = file_get_file_area_info($usercontext->id, 'user', 'private', 0, '/testsubdir/');
|
||||
$this->assertEquals(1, $fileinfo['filecount']);
|
||||
$this->assertEquals($file->get_filesize(), $fileinfo['filesize']);
|
||||
$this->assertEquals(0, $fileinfo['foldercount']); // No subdirectories inside the directory.
|
||||
$this->assertEquals($file->get_filesize(), $fileinfo['filesize_without_references']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -10,6 +10,9 @@ information provided here is intended especially for developers.
|
||||
* Removed accesslib private functions: load_course_context(), load_role_access_by_context(), dedupe_user_access() (MDL-49398).
|
||||
* Internal "accessdata" structure format has changed to improve ability to perform role definition caching (MDL-49398).
|
||||
* Role definitions are no longer cached in user session (MDL-49398).
|
||||
* External function core_group_external::get_activity_allowed_groups now returns an additional field: canaccessallgroups.
|
||||
It indicates whether the user will be able to access all the activity groups.
|
||||
* file_get_draft_area_info does not sum the root folder anymore when calculating the foldercount.
|
||||
|
||||
=== 3.3.1 ===
|
||||
|
||||
|
@ -98,7 +98,7 @@ Y.extend(Confirmation, Y.Base, {
|
||||
_uninstall: function(e, langCodes) {
|
||||
Y.config.win.location.href = this.get('uninstallUrl') + '?mode=4' +
|
||||
'&sesskey=' + M.cfg.sesskey +
|
||||
'&confirmtouninstall=' + langCodes.join('-');
|
||||
'&confirmtouninstall=' + langCodes.join('/');
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -1 +1 @@
|
||||
YUI.add("moodle-core-languninstallconfirm",function(e,t){function n(){n.superclass.constructor.apply(this,arguments)}var r={UNINSTALLBUTTON:"#languninstallbutton",UNINSTALLSELECT:"#menuuninstalllang option",ENGLISHOPTION:"#menuuninstalllang option[value='en']"};n.NAME=t,n.ATTRS={uninstallUrl:{validator:e.Lang.isString}},e.extend(n,e.Base,{initializer:function(){e.one(r.UNINSTALLBUTTON).on("click",this._confirm,this)},_confirm:function(t){t.preventDefault();var n=[],i=[];e.all(r.UNINSTALLSELECT).each(function(e){e.get("selected")&&(n.push(e.getAttribute("value")),i.push(e.get("text")))});if(n.length===0){(new M.core.alert({message:M.util.get_string("selectlangs","tool_langimport")})).show();return}if(n.indexOf("en")>-1){e.one(r.ENGLISHOPTION).set("selected",!1),(new M.core.alert({message:M.util.get_string("noenglishuninstall","tool_langimport")})).show();return}var s={modal:!0,visible:!1,centered:!0,title:M.util.get_string("uninstall","tool_langimport"),question:M.util.get_string("uninstallconfirm","tool_langimport",i.join(", "))};(new M.core.confirm(s)).show().on("complete-yes",this._uninstall,this,n)},_uninstall:function(t,n){e.config.win.location.href=this.get("uninstallUrl")+"?mode=4"+"&sesskey="+M.cfg.sesskey+"&confirmtouninstall="+n.join("-")}}),e.namespace("M.core.languninstallconfirm").Confirmation=n,e.namespace("M.core.languninstallconfirm").init=function(e){return new n(e)}},"@VERSION@",{requires:["base","node","moodle-core-notification-confirm","moodle-core-notification-alert"]});
|
||||
YUI.add("moodle-core-languninstallconfirm",function(e,t){function n(){n.superclass.constructor.apply(this,arguments)}var r={UNINSTALLBUTTON:"#languninstallbutton",UNINSTALLSELECT:"#menuuninstalllang option",ENGLISHOPTION:"#menuuninstalllang option[value='en']"};n.NAME=t,n.ATTRS={uninstallUrl:{validator:e.Lang.isString}},e.extend(n,e.Base,{initializer:function(){e.one(r.UNINSTALLBUTTON).on("click",this._confirm,this)},_confirm:function(t){t.preventDefault();var n=[],i=[];e.all(r.UNINSTALLSELECT).each(function(e){e.get("selected")&&(n.push(e.getAttribute("value")),i.push(e.get("text")))});if(n.length===0){(new M.core.alert({message:M.util.get_string("selectlangs","tool_langimport")})).show();return}if(n.indexOf("en")>-1){e.one(r.ENGLISHOPTION).set("selected",!1),(new M.core.alert({message:M.util.get_string("noenglishuninstall","tool_langimport")})).show();return}var s={modal:!0,visible:!1,centered:!0,title:M.util.get_string("uninstall","tool_langimport"),question:M.util.get_string("uninstallconfirm","tool_langimport",i.join(", "))};(new M.core.confirm(s)).show().on("complete-yes",this._uninstall,this,n)},_uninstall:function(t,n){e.config.win.location.href=this.get("uninstallUrl")+"?mode=4"+"&sesskey="+M.cfg.sesskey+"&confirmtouninstall="+n.join("/")}}),e.namespace("M.core.languninstallconfirm").Confirmation=n,e.namespace("M.core.languninstallconfirm").init=function(e){return new n(e)}},"@VERSION@",{requires:["base","node","moodle-core-notification-confirm","moodle-core-notification-alert"]});
|
||||
|
@ -98,7 +98,7 @@ Y.extend(Confirmation, Y.Base, {
|
||||
_uninstall: function(e, langCodes) {
|
||||
Y.config.win.location.href = this.get('uninstallUrl') + '?mode=4' +
|
||||
'&sesskey=' + M.cfg.sesskey +
|
||||
'&confirmtouninstall=' + langCodes.join('-');
|
||||
'&confirmtouninstall=' + langCodes.join('/');
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -96,7 +96,7 @@ Y.extend(Confirmation, Y.Base, {
|
||||
_uninstall: function(e, langCodes) {
|
||||
Y.config.win.location.href = this.get('uninstallUrl') + '?mode=4' +
|
||||
'&sesskey=' + M.cfg.sesskey +
|
||||
'&confirmtouninstall=' + langCodes.join('-');
|
||||
'&confirmtouninstall=' + langCodes.join('/');
|
||||
}
|
||||
|
||||
});
|
||||
|
2
media/player/videojs/amd/build/loader.min.js
vendored
2
media/player/videojs/amd/build/loader.min.js
vendored
@ -1 +1 @@
|
||||
define(["jquery","core/event"],function(a,b){var c,d=function(d){c=d,e(null,a("body")),b.getLegacyEvents().done(function(b){a(document).on(b.FILTER_CONTENT_UPDATED,e)})},e=function(b,d){var e=".mediaplugin_videojs";d.find(e).addBack(e).find("audio, video").each(function(){var b=a(this).attr("id"),d=a(this).data("setup"),e=["media_videojs/video-lazy"];d.techOrder&&d.techOrder.indexOf("youtube")!==-1&&e.push("media_videojs/Youtube-lazy"),require(e,function(a){c&&(c(a),c=null),a(b,d)})})};return{setUp:d}});
|
||||
define(["jquery","core/event"],function(a,b){var c,d=function(d){c=d,e(null,a("body")),b.getLegacyEvents().done(function(b){a(document).on(b.FILTER_CONTENT_UPDATED,e)})},e=function(b,d){var e=".mediaplugin_videojs";d.find(e).addBack(e).find("audio, video").each(function(){var b=a(this).attr("id"),d=a(this).data("setup-lazy"),e=["media_videojs/video-lazy"];d.techOrder&&d.techOrder.indexOf("youtube")!==-1&&e.push("media_videojs/Youtube-lazy"),require(e,function(a){c&&(c(a),c=null),a(b,d)})})};return{setUp:d}});
|
@ -62,7 +62,7 @@ define(['jquery', 'core/event'], function($, Event) {
|
||||
.addBack(selector)
|
||||
.find('audio, video').each(function() {
|
||||
var id = $(this).attr('id'),
|
||||
config = $(this).data('setup'),
|
||||
config = $(this).data('setup-lazy'),
|
||||
modules = ['media_videojs/video-lazy'];
|
||||
|
||||
if (config.techOrder && config.techOrder.indexOf('youtube') !== -1) {
|
||||
|
@ -129,8 +129,13 @@ class media_videojs_plugin extends core_media_player_native {
|
||||
}
|
||||
|
||||
// Attributes for the video/audio tag.
|
||||
// We use data-setup-lazy as the attribute name for the config instead of
|
||||
// data-setup because data-setup will cause video.js to load the player as soon as the library is loaded,
|
||||
// which is BEFORE we have a chance to load any additional libraries (youtube).
|
||||
// The data-setup-lazy is just a tag name that video.js does not recognise so we can manually initialise
|
||||
// it when we are sure the dependencies are loaded.
|
||||
$attributes = [
|
||||
'data-setup' => '{' . join(', ', $datasetup) . '}',
|
||||
'data-setup-lazy' => '{' . join(', ', $datasetup) . '}',
|
||||
'id' => 'id_videojs_' . uniqid(),
|
||||
'class' => get_config('media_videojs', $isaudio ? 'audiocssclass' : 'videocssclass')
|
||||
];
|
||||
|
@ -230,7 +230,7 @@ class media_videojs_testcase extends advanced_testcase {
|
||||
|
||||
protected function youtube_plugin_engaged($t) {
|
||||
$this->assertContains('mediaplugin_videojs', $t);
|
||||
$this->assertContains('data-setup="{"techOrder": ["youtube"]', $t);
|
||||
$this->assertContains('data-setup-lazy="{"techOrder": ["youtube"]', $t);
|
||||
}
|
||||
|
||||
/**
|
||||
|
2
message/amd/build/message_repository.min.js
vendored
2
message/amd/build/message_repository.min.js
vendored
@ -1 +1 @@
|
||||
define(["jquery","core/ajax","core/notification","core/log"],function(a,b,c,d){var e=function(a){"undefined"==typeof a.limit&&(a.limit=0),"undefined"==typeof a.offset&&(a.offset=0),a.limitfrom=a.offset,a.limitnum=a.limit,delete a.limit,delete a.offset;var d={methodname:"core_message_data_for_messagearea_conversations",args:a},e=b.call([d])[0];return e.fail(c.exception),e},f=function(a){var c={methodname:"core_message_get_unread_conversations_count",args:a},e=b.call([c])[0];return e.fail(function(a){d.error("Could not retrieve unread message count: "+a.message)}),e},g=function(a){var d={methodname:"core_message_mark_all_messages_as_read",args:a},e=b.call([d])[0];return e.fail(c.exception),e};return{query:e,countUnreadConversations:f,markAllAsRead:g}});
|
||||
define(["jquery","core/ajax","core/notification"],function(a,b,c){var d=function(a){"undefined"==typeof a.limit&&(a.limit=0),"undefined"==typeof a.offset&&(a.offset=0),a.limitfrom=a.offset,a.limitnum=a.limit,delete a.limit,delete a.offset;var d={methodname:"core_message_data_for_messagearea_conversations",args:a},e=b.call([d])[0];return e.fail(c.exception),e},e=function(a){var d={methodname:"core_message_get_unread_conversations_count",args:a},e=b.call([d])[0];return e.fail(c.exception),e},f=function(a){var d={methodname:"core_message_mark_all_messages_as_read",args:a},e=b.call([d])[0];return e.fail(c.exception),e};return{query:d,countUnreadConversations:e,markAllAsRead:f}});
|
@ -22,7 +22,7 @@
|
||||
* @copyright 2016 Ryan Wyllie <ryan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/ajax', 'core/notification', 'core/log'], function($, Ajax, Notification, Log) {
|
||||
define(['jquery', 'core/ajax', 'core/notification'], function($, Ajax, Notification) {
|
||||
/**
|
||||
* Retrieve a list of messages from the server.
|
||||
*
|
||||
@ -72,9 +72,7 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/log'], function($, Aja
|
||||
|
||||
var promise = Ajax.call([request])[0];
|
||||
|
||||
promise.fail(function(e) {
|
||||
Log.error('Could not retrieve unread message count: ' + e.message);
|
||||
});
|
||||
promise.fail(Notification.exception);
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
@ -1 +1 @@
|
||||
define(["core/ajax","core/notification","core/log"],function(a,b,c){var d=function(c){"undefined"==typeof c.limit&&(c.limit=20),"undefined"==typeof c.offset&&(c.offset=0);var d={methodname:"message_popup_get_popup_notifications",args:c},e=a.call([d])[0];return e.fail(b.exception),e},e=function(b){var d={methodname:"message_popup_get_unread_popup_notification_count",args:b},e=a.call([d])[0];return e.fail(function(a){c.error("Could not retrieve notifications count: "+a.message)}),e},f=function(c){var d={methodname:"core_message_mark_all_notifications_as_read",args:c},e=a.call([d])[0];return e.fail(b.exception),e},g=function(c,d){var e={messageid:c};d&&(e.timeread=d);var f={methodname:"core_message_mark_message_read",args:e},g=a.call([f])[0];return g.fail(b.exception),g};return{query:d,countUnread:e,markAllAsRead:f,markAsRead:g}});
|
||||
define(["core/ajax","core/notification"],function(a,b){var c=function(c){"undefined"==typeof c.limit&&(c.limit=20),"undefined"==typeof c.offset&&(c.offset=0);var d={methodname:"message_popup_get_popup_notifications",args:c},e=a.call([d])[0];return e.fail(b.exception),e},d=function(c){var d={methodname:"message_popup_get_unread_popup_notification_count",args:c},e=a.call([d])[0];return e.fail(b.exception),e},e=function(c){var d={methodname:"core_message_mark_all_notifications_as_read",args:c},e=a.call([d])[0];return e.fail(b.exception),e},f=function(c,d){var e={messageid:c};d&&(e.timeread=d);var f={methodname:"core_message_mark_message_read",args:e},g=a.call([f])[0];return g.fail(b.exception),g};return{query:c,countUnread:d,markAllAsRead:e,markAsRead:f}});
|
@ -22,7 +22,7 @@
|
||||
* @copyright 2016 Ryan Wyllie <ryan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['core/ajax', 'core/notification', 'core/log'], function(Ajax, Notification, Log) {
|
||||
define(['core/ajax', 'core/notification'], function(Ajax, Notification) {
|
||||
/**
|
||||
* Retrieve a list of notifications from the server.
|
||||
*
|
||||
@ -64,9 +64,7 @@ define(['core/ajax', 'core/notification', 'core/log'], function(Ajax, Notificati
|
||||
|
||||
var promise = Ajax.call([request])[0];
|
||||
|
||||
promise.fail(function(e) {
|
||||
Log.error('Could not retrieve notifications count: ' + e.message);
|
||||
});
|
||||
promise.fail(Notification.exception);
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
@ -29,6 +29,7 @@ $string['configmaxbytes'] = 'Maximum file size';
|
||||
$string['countfiles'] = '{$a} files';
|
||||
$string['default'] = 'Enabled by default';
|
||||
$string['default_help'] = 'If set, this submission method will be enabled by default for all new assignments.';
|
||||
$string['defaultacceptedfiletypes'] = 'Default accepted file types';
|
||||
$string['enabled'] = 'File submissions';
|
||||
$string['enabled_help'] = 'If enabled, students are able to upload one or more files as their submission.';
|
||||
$string['eventassessableuploaded'] = 'A file has been uploaded.';
|
||||
|
@ -71,7 +71,12 @@ class assign_submission_file extends assign_submission_plugin {
|
||||
|
||||
$defaultmaxfilesubmissions = $this->get_config('maxfilesubmissions');
|
||||
$defaultmaxsubmissionsizebytes = $this->get_config('maxsubmissionsizebytes');
|
||||
$defaultfiletypes = (string)$this->get_config('filetypeslist');
|
||||
if ($this->assignment->has_instance()) {
|
||||
$defaultfiletypes = $this->get_config('filetypeslist');
|
||||
} else {
|
||||
$defaultfiletypes = get_config('assignsubmission_file', 'filetypes');
|
||||
}
|
||||
$defaultfiletypes = (string)$defaultfiletypes;
|
||||
|
||||
$settings = array();
|
||||
$options = array();
|
||||
@ -108,7 +113,7 @@ class assign_submission_file extends assign_submission_plugin {
|
||||
'notchecked');
|
||||
|
||||
$name = get_string('acceptedfiletypes', 'assignsubmission_file');
|
||||
$mform->addElement('text', 'assignsubmission_file_filetypes', $name);
|
||||
$mform->addElement('text', 'assignsubmission_file_filetypes', $name, array('size' => '60'));
|
||||
$mform->addHelpButton('assignsubmission_file_filetypes', 'acceptedfiletypes', 'assignsubmission_file');
|
||||
$mform->setType('assignsubmission_file_filetypes', PARAM_RAW);
|
||||
$mform->setDefault('assignsubmission_file_filetypes', $defaultfiletypes);
|
||||
|
@ -32,6 +32,10 @@ $settings->add(new admin_setting_configtext('assignsubmission_file/maxfiles',
|
||||
new lang_string('maxfiles', 'assignsubmission_file'),
|
||||
new lang_string('maxfiles_help', 'assignsubmission_file'), 20, PARAM_INT));
|
||||
|
||||
$settings->add(new admin_setting_configtext('assignsubmission_file/filetypes',
|
||||
new lang_string('defaultacceptedfiletypes', 'assignsubmission_file'),
|
||||
new lang_string('acceptedfiletypes_help', 'assignsubmission_file'), '', PARAM_RAW, 60));
|
||||
|
||||
if (isset($CFG->maxbytes)) {
|
||||
|
||||
$name = new lang_string('maximumsubmissionsize', 'assignsubmission_file');
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 324 B |
BIN
pix/i/mahara_host.png
Normal file
BIN
pix/i/mahara_host.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 516 B |
15
pix/i/mahara_host.svg
Normal file
15
pix/i/mahara_host.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="full_color" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="16" width="16" x="0px" y="0px"
|
||||
viewBox="0 0 65.2 124.7" style="enable-background:new 0 0 65.2 124.7;" xml:space="preserve" preserveAspectRatio="xMinYMid meet">
|
||||
<style type="text/css">
|
||||
.st0{fill:#566D31;}
|
||||
</style>
|
||||
<path class="st0" d="M63.2,92c0-17.3-14.7-36.7-16-35.8c-3.5,2.2,10.1,13.4,10.1,34.3c0,11.7-6.9,23.2-22.1,23.2
|
||||
c-14.7,0-24.9-9.4-24.9-23.6c0-13.5,9.3-26.7,14.9-33.5c2.2,3.2,7.4,11.2,8.5,13c1.4,2.3,3.3,7.3-0.2,7.8c-5.1,0.8-5.3-7.3-5.5-9.7
|
||||
c-0.1-0.8-1.3-0.9-1.7-0.2c-1.6,2.9-2.8,11.6,2,15.7c3.4,2.9,9.4,3,11.8-3.6c3.8,6.6,4.8,12.7,5.4,16.8c0.1,0.7,1.1,0.7,1.2,0
|
||||
c2.3-13.5-4-22.9-6.1-27.1c-2.1-4.2-10.8-16.5-12.3-19c-1.5-2.5-5.6-9.7-5.6-17.4c0-11.7,9.6-22.9,11.4-22.9c1.8,0,12,9.3,12,23.3
|
||||
c0,5.3-1.2,9.2-2.6,12c-0.5,0.9-1.7,1-2.3,0.1c-5.6-8.7-5.6-10.5-2.1-13c2.3-1.7-9-6.4-8.6,3.3c0.2,5.1,7.4,15.5,9.2,17.5
|
||||
s12-2.2,12-20.7c0-18.5-15.9-30.8-17.9-30.8C31.8,1.7,16,16.7,16,32.5c0,7.8,2.8,14.5,6.1,19.7C21.7,52.5,1.8,68.3,2,92
|
||||
c0.2,18.1,12.3,31.1,32.1,31.1C52,123.1,63.2,111.3,63.2,92z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@ -206,3 +206,45 @@ $doughnut-fill-colour: $brand-warning;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show expand collapse with font-awesome.
|
||||
.block_settings .block_tree [aria-expanded="true"],
|
||||
.block_settings .block_tree [aria-expanded="true"].emptybranch,
|
||||
.block_settings .block_tree [aria-expanded="false"],
|
||||
.block_navigation .block_tree [aria-expanded="true"],
|
||||
.block_navigation .block_tree [aria-expanded="true"].emptybranch,
|
||||
.block_navigation .block_tree [aria-expanded="false"] {
|
||||
background-image: none;
|
||||
}
|
||||
.block_settings .block_tree [aria-expanded="true"]:before,
|
||||
.block_navigation .block_tree [aria-expanded="true"]:before {
|
||||
content: $fa-var-angle-down;
|
||||
margin-right: 0;
|
||||
font-size: 16px;
|
||||
@extend .fa;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.block_settings .block_tree [aria-expanded="false"]:before,
|
||||
.block_navigation .block_tree [aria-expanded="false"]:before {
|
||||
content: $fa-var-angle-right;
|
||||
font-size: 16px;
|
||||
margin-right: 0;
|
||||
@extend .fa;
|
||||
width: 16px;
|
||||
}
|
||||
.dir-rtl {
|
||||
.block_settings .block_tree [aria-expanded="false"]:before,
|
||||
.block_navigation .block_tree [aria-expanded="false"]:before {
|
||||
content: $fa-var-angle-left;
|
||||
}
|
||||
}
|
||||
|
||||
.block_navigation .block_tree p.hasicon,
|
||||
.block_settings .block_tree p.hasicon {
|
||||
text-indent: -3px;
|
||||
|
||||
.icon {
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
@ -346,9 +346,6 @@ a.skip:active {
|
||||
margin-left: 43px;
|
||||
}
|
||||
// Group
|
||||
#page-group-index #groupeditform {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#doc-contents h1 {
|
||||
margin: 1em 0 0 0;
|
||||
|
@ -277,21 +277,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
#groupeditform {
|
||||
.groups,
|
||||
.members {
|
||||
min-width: 175px;
|
||||
width: 49%;
|
||||
float: left;
|
||||
text-align: left;
|
||||
|
||||
select {
|
||||
min-width: 175px;
|
||||
max-width: 90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the little cog from participants page because we are putting a cog menu there.
|
||||
.userlist h3 .action-icon {
|
||||
display: none;
|
||||
|
@ -15,13 +15,47 @@
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
Availability info.
|
||||
@template core/availability_info
|
||||
|
||||
Renders the availability info on the course outline page.
|
||||
|
||||
Availability info can be displayed for activity modules or whole course
|
||||
sections. Activity modules can be either hidden from students, or available
|
||||
but not shown on course page (stealth), or the access can be restricted by
|
||||
configured conditions. Sections can be hidden.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* classes String list of CSS classes for the wrapping element
|
||||
* text HTML formatted text with the actual availability information
|
||||
* ishidden Boolean flag indiciating that the item is hidden from students
|
||||
* isstealth Boolean flag indicating that the item is in stealth mode
|
||||
* isrestricted Boolean flag indicating that restricted access conditions apply
|
||||
* isfullinfo Boolean flag indicating that the full list of restricted
|
||||
access conditions is displayed (aka teacher's view).
|
||||
|
||||
Example context (json):
|
||||
{ "classes": "", "text": "This activity is not available" }
|
||||
{
|
||||
"classes": "",
|
||||
"text": "Not available unless: <ul><li>It is on or after <strong>8 June 2012</strong></li></ul>",
|
||||
"ishidden": 0,
|
||||
"isstealth": 0,
|
||||
"isrestricted": 1,
|
||||
"isfullinfo": 1
|
||||
}
|
||||
}}
|
||||
{{#text}}
|
||||
<div class="availabilityinfo {{classes}}">
|
||||
{{^isrestricted}}
|
||||
<span class="tag tag-info">{{{text}}}</span>
|
||||
{{/isrestricted}}
|
||||
{{#isrestricted}}
|
||||
<span class="tag tag-info">{{#str}}restricted, core{{/str}}</span> {{{text}}}
|
||||
{{/isrestricted}}
|
||||
</div>
|
||||
{{/text}}
|
||||
|
@ -53,6 +53,15 @@
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
p {
|
||||
&.hasicon {
|
||||
img {
|
||||
&.icon {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.footer {
|
||||
margin-bottom: 4px;
|
||||
|
@ -429,9 +429,6 @@ a.skip:active {
|
||||
margin-left: 43px;
|
||||
}
|
||||
// Group
|
||||
#page-group-index #groupeditform {
|
||||
text-align: center;
|
||||
}
|
||||
#doc-contents h1 {
|
||||
margin: 1em 0 0 0;
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ div[data-flexitour="step-background-fader"],
|
||||
span[data-flexitour="container"] {
|
||||
div[data-role="flexitour-step"] {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
border-radius: 6px;
|
||||
border: 1px solid rgba(0, 0, 0, .2);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
|
||||
|
@ -264,21 +264,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
#groupeditform {
|
||||
.groups,
|
||||
.members {
|
||||
min-width: 175px;
|
||||
width: 49%;
|
||||
float: left;
|
||||
text-align: left;
|
||||
|
||||
select {
|
||||
min-width: 175px;
|
||||
max-width: 90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Preferences page */
|
||||
.preferences-group {
|
||||
ul {
|
||||
|
@ -2723,9 +2723,6 @@ a.skip:active {
|
||||
.blog_entry .content {
|
||||
margin-left: 43px;
|
||||
}
|
||||
#page-group-index #groupeditform {
|
||||
text-align: center;
|
||||
}
|
||||
#doc-contents h1 {
|
||||
margin: 1em 0 0 0;
|
||||
}
|
||||
@ -9891,18 +9888,6 @@ body.path-question-type .mform fieldset.hidden {
|
||||
.profileeditor > .singlebutton input {
|
||||
margin: 0;
|
||||
}
|
||||
#groupeditform .groups,
|
||||
#groupeditform .members {
|
||||
min-width: 175px;
|
||||
width: 49%;
|
||||
float: left;
|
||||
text-align: left;
|
||||
}
|
||||
#groupeditform .groups select,
|
||||
#groupeditform .members select {
|
||||
min-width: 175px;
|
||||
max-width: 90%;
|
||||
}
|
||||
/** Preferences page */
|
||||
.preferences-group ul {
|
||||
list-style: none;
|
||||
@ -15873,6 +15858,9 @@ body {
|
||||
margin-left: 5px;
|
||||
display: inline;
|
||||
}
|
||||
.block .content p.hasicon img.icon {
|
||||
padding-right: 0;
|
||||
}
|
||||
.block .footer {
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
@ -19339,6 +19327,7 @@ div[data-flexitour="step-background-fader"],
|
||||
}
|
||||
span[data-flexitour="container"] div[data-role="flexitour-step"] {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
border-radius: 6px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
|
@ -24,27 +24,33 @@
|
||||
}}
|
||||
|
||||
<div id="block-myoverview-{{uniqid}}" class="block-myoverview" data-region="myoverview">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item active">
|
||||
<a class="nav-link" href="#myoverview_timeline_view" role="tab" data-toggle="tab">
|
||||
<ul id="block-myoverview-view-choices-{{uniqid}}" class="nav nav-tabs" role="tablist">
|
||||
<li class="nav-item {{#viewingtimeline}}active{{/viewingtimeline}}">
|
||||
<a class="nav-link" href="#myoverview_timeline_view" role="tab" data-toggle="tab" data-tabname="timeline">
|
||||
{{#str}} timeline, block_myoverview {{/str}}
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#myoverview_courses_view" role="tab" data-toggle="tab">
|
||||
<li class="nav-item {{#viewingcourses}}active{{/viewingcourses}}">
|
||||
<a class="nav-link" href="#myoverview_courses_view" role="tab" data-toggle="tab" data-tabname="courses">
|
||||
{{#str}} courses {{/str}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane fade in active" id="myoverview_timeline_view">
|
||||
<div role="tabpanel" class="tab-pane fade {{#viewingtimeline}}in active{{/viewingtimeline}}" id="myoverview_timeline_view">
|
||||
{{> block_myoverview/timeline-view }}
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane fade" id="myoverview_courses_view">
|
||||
<div role="tabpanel" class="tab-pane fade {{#viewingcourses}}in active{{/viewingcourses}}" id="myoverview_courses_view">
|
||||
{{#coursesview}}
|
||||
{{> block_myoverview/courses-view }}
|
||||
{{/coursesview}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{#js}}
|
||||
require(['jquery', 'block_myoverview/tab_preferences'], function($, TabPreferences) {
|
||||
var root = $('#block-myoverview-view-choices-{{uniqid}}');
|
||||
TabPreferences.registerEventListeners(root);
|
||||
});
|
||||
{{/js}}
|
||||
|
@ -15,13 +15,47 @@
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
Availability info.
|
||||
@template core/availability_info
|
||||
|
||||
Renders the availability info on the course outline page.
|
||||
|
||||
Availability info can be displayed for activity modules or whole course
|
||||
sections. Activity modules can be either hidden from students, or available
|
||||
but not shown on course page (stealth), or the access can be restricted by
|
||||
configured conditions. Sections can be hidden.
|
||||
|
||||
Classes required for JS:
|
||||
* none
|
||||
|
||||
Data attributes required for JS:
|
||||
* none
|
||||
|
||||
Context variables required for this template:
|
||||
* classes String list of CSS classes for the wrapping element
|
||||
* text HTML formatted text with the actual availability information
|
||||
* ishidden Boolean flag indiciating that the item is hidden from students
|
||||
* isstealth Boolean flag indicating that the item is in stealth mode
|
||||
* isrestricted Boolean flag indicating that restricted access conditions apply
|
||||
* isfullinfo Boolean flag indicating that the full list of restricted
|
||||
access conditions is displayed (aka teacher's view).
|
||||
|
||||
Example context (json):
|
||||
{ "classes": "", "text": "This activity is not available" }
|
||||
{
|
||||
"classes": "",
|
||||
"text": "Not available unless: <ul><li>It is on or after <strong>8 June 2012</strong></li></ul>",
|
||||
"ishidden": 0,
|
||||
"isstealth": 0,
|
||||
"isrestricted": 1,
|
||||
"isfullinfo": 1
|
||||
}
|
||||
}}
|
||||
{{#text}}
|
||||
<div class="availabilityinfo {{classes}}">
|
||||
{{^isrestricted}}
|
||||
<span class="label label-info">{{{text}}}</span>
|
||||
{{/isrestricted}}
|
||||
{{#isrestricted}}
|
||||
<span class="label label-info">{{#str}}restricted, core{{/str}}</span> {{{text}}}
|
||||
{{/isrestricted}}
|
||||
</div>
|
||||
{{/text}}
|
||||
|
@ -1860,4 +1860,76 @@ class core_user_external extends external_api {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method parameters.
|
||||
*
|
||||
* @return external_function_parameters
|
||||
* @since Moodle 3.4
|
||||
*/
|
||||
public static function get_private_files_info_parameters() {
|
||||
return new external_function_parameters(
|
||||
array(
|
||||
'userid' => new external_value(PARAM_INT, 'Id of the user, default to current user.', VALUE_DEFAULT, 0)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns general information about files in the user private files area.
|
||||
*
|
||||
* @param int $userid Id of the user, default to current user.
|
||||
* @return array of warnings and file area information
|
||||
* @since Moodle 3.4
|
||||
* @throws moodle_exception
|
||||
*/
|
||||
public static function get_private_files_info($userid = 0) {
|
||||
global $CFG, $USER;
|
||||
require_once($CFG->libdir . '/filelib.php');
|
||||
|
||||
$params = self::validate_parameters(self::get_private_files_info_parameters(), array('userid' => $userid));
|
||||
$warnings = array();
|
||||
|
||||
$context = context_system::instance();
|
||||
self::validate_context($context);
|
||||
|
||||
if (empty($params['userid']) || $params['userid'] == $USER->id) {
|
||||
$usercontext = context_user::instance($USER->id);
|
||||
require_capability('moodle/user:manageownfiles', $usercontext);
|
||||
} else {
|
||||
$user = core_user::get_user($params['userid'], '*', MUST_EXIST);
|
||||
core_user::require_active_user($user);
|
||||
// Only admins can retrieve other users information.
|
||||
require_capability('moodle/site:config', $context);
|
||||
$usercontext = context_user::instance($user->id);
|
||||
}
|
||||
|
||||
$fileareainfo = file_get_file_area_info($usercontext->id, 'user', 'private');
|
||||
|
||||
$result = array();
|
||||
$result['filecount'] = $fileareainfo['filecount'];
|
||||
$result['foldercount'] = $fileareainfo['foldercount'];
|
||||
$result['filesize'] = $fileareainfo['filesize'];
|
||||
$result['filesizewithoutreferences'] = $fileareainfo['filesize_without_references'];
|
||||
$result['warnings'] = $warnings;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns description of method result value.
|
||||
*
|
||||
* @return external_description
|
||||
* @since Moodle 3.4
|
||||
*/
|
||||
public static function get_private_files_info_returns() {
|
||||
return new external_single_structure(
|
||||
array(
|
||||
'filecount' => new external_value(PARAM_INT, 'Number of files in the area.'),
|
||||
'foldercount' => new external_value(PARAM_INT, 'Number of folders in the area.'),
|
||||
'filesize' => new external_value(PARAM_INT, 'Total size of the files in the area.'),
|
||||
'filesizewithoutreferences' => new external_value(PARAM_INT, 'Total size of the area excluding file references'),
|
||||
'warnings' => new external_warnings()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,20 @@ if ($mform->is_cancelled()) {
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->box_start('generalbox');
|
||||
|
||||
// Show file area space usage.
|
||||
if ($maxareabytes != FILE_AREA_MAX_BYTES_UNLIMITED) {
|
||||
$fileareainfo = file_get_file_area_info($context->id, 'user', 'private');
|
||||
// Display message only if we have files.
|
||||
if ($fileareainfo['filecount']) {
|
||||
$a = (object) [
|
||||
'used' => display_size($fileareainfo['filesize_without_references']),
|
||||
'total' => display_size($maxareabytes)
|
||||
];
|
||||
$quotamsg = get_string('quotausage', 'moodle', $a);
|
||||
$notification = new \core\output\notification($quotamsg, \core\output\notification::NOTIFY_INFO);
|
||||
echo $OUTPUT->render($notification);
|
||||
}
|
||||
}
|
||||
|
||||
$mform->display();
|
||||
echo $OUTPUT->box_end();
|
||||
|
@ -1148,6 +1148,60 @@ class core_user_externallib_testcase extends externallib_advanced_testcase {
|
||||
} catch (Exception $e) {
|
||||
$this->fail('Expecting \'usernotfullysetup\' moodle_exception to be thrown.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_private_files_info
|
||||
*/
|
||||
public function test_get_private_files_info() {
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
$user = self::getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
$usercontext = context_user::instance($user->id);
|
||||
|
||||
$filerecord = array(
|
||||
'contextid' => $usercontext->id,
|
||||
'component' => 'user',
|
||||
'filearea' => 'private',
|
||||
'itemid' => 0,
|
||||
'filepath' => '/',
|
||||
'filename' => 'thefile',
|
||||
);
|
||||
|
||||
$fs = get_file_storage();
|
||||
$file = $fs->create_file_from_string($filerecord, 'abc');
|
||||
|
||||
// Get my private files information.
|
||||
$result = core_user_external::get_private_files_info();
|
||||
$result = external_api::clean_returnvalue(core_user_external::get_private_files_info_returns(), $result);
|
||||
$this->assertEquals(1, $result['filecount']);
|
||||
$this->assertEquals($file->get_filesize(), $result['filesize']);
|
||||
$this->assertEquals(0, $result['foldercount']);
|
||||
$this->assertEquals($file->get_filesize(), $result['filesizewithoutreferences']);
|
||||
|
||||
// As admin, get user information.
|
||||
$this->setAdminUser();
|
||||
$result = core_user_external::get_private_files_info($user->id);
|
||||
$result = external_api::clean_returnvalue(core_user_external::get_private_files_info_returns(), $result);
|
||||
$this->assertEquals(1, $result['filecount']);
|
||||
$this->assertEquals($file->get_filesize(), $result['filesize']);
|
||||
$this->assertEquals(0, $result['foldercount']);
|
||||
$this->assertEquals($file->get_filesize(), $result['filesizewithoutreferences']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_private_files_info missing permissions.
|
||||
*/
|
||||
public function test_get_private_files_info_missing_permissions() {
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
$user1 = self::getDataGenerator()->create_user();
|
||||
$user2 = self::getDataGenerator()->create_user();
|
||||
$this->setUser($user1);
|
||||
|
||||
$this->setExpectedException('required_capability_exception');
|
||||
// Try to retrieve other user private files info.
|
||||
core_user_external::get_private_files_info($user2->id);
|
||||
}
|
||||
}
|
||||
|
@ -29,11 +29,11 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2017061600.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2017062200.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
|
||||
$release = '3.4dev (Build: 20170616)'; // Human-friendly version name
|
||||
$release = '3.4dev (Build: 20170622)'; // Human-friendly version name
|
||||
|
||||
$branch = '34'; // This version's branch.
|
||||
$maturity = MATURITY_ALPHA; // This version's maturity level.
|
||||
|
@ -197,6 +197,14 @@ class core_webservice_external extends external_api {
|
||||
// User home page.
|
||||
$siteinfo['userhomepage'] = get_home_page();
|
||||
|
||||
// Calendar.
|
||||
$siteinfo['sitecalendartype'] = $CFG->calendartype;
|
||||
if (empty($USER->calendartype)) {
|
||||
$siteinfo['usercalendartype'] = $CFG->calendartype;
|
||||
} else {
|
||||
$siteinfo['usercalendartype'] = $USER->calendartype;
|
||||
}
|
||||
|
||||
return $siteinfo;
|
||||
}
|
||||
|
||||
@ -259,7 +267,9 @@ class core_webservice_external extends external_api {
|
||||
'userhomepage' => new external_value(PARAM_INT,
|
||||
'the default home page for the user: 0 for the site home, 1 for dashboard',
|
||||
VALUE_OPTIONAL),
|
||||
'siteid' => new external_value(PARAM_INT, 'Site course ID', VALUE_OPTIONAL)
|
||||
'siteid' => new external_value(PARAM_INT, 'Site course ID', VALUE_OPTIONAL),
|
||||
'sitecalendartype' => new external_value(PARAM_PLUGIN, 'Calendar type set in the site.', VALUE_OPTIONAL),
|
||||
'usercalendartype' => new external_value(PARAM_PLUGIN, 'Calendar typed used by the user.', VALUE_OPTIONAL),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -122,6 +122,12 @@ class core_webservice_externallib_testcase extends externallib_advanced_testcase
|
||||
$this->assertEquals(true, $siteinfo['usercanmanageownfiles']);
|
||||
|
||||
$this->assertEquals(HOMEPAGE_MY, $siteinfo['userhomepage']);
|
||||
$this->assertEquals($CFG->calendartype, $siteinfo['sitecalendartype']);
|
||||
if (!empty($USER->calendartype)) {
|
||||
$this->assertEquals($USER->calendartype, $siteinfo['usercalendartype']);
|
||||
} else {
|
||||
$this->assertEquals($CFG->calendartype, $siteinfo['usercalendartype']);
|
||||
}
|
||||
|
||||
// Now as admin.
|
||||
$this->setAdminUser();
|
||||
|
@ -3,6 +3,11 @@ information provided here is intended especially for developers.
|
||||
|
||||
This information is intended for authors of webservices, not people writing webservice clients.
|
||||
|
||||
=== 3.4 ===
|
||||
|
||||
* External function core_webservice_external::get_site_info() now returns the calendar type used in the site and
|
||||
by the user in the sitecalendartype and usercalendartype fields.
|
||||
|
||||
=== 3.2 ===
|
||||
|
||||
* webservice->get_external_functions now returns the external function list ordered by name ASC.
|
||||
|
Loading…
x
Reference in New Issue
Block a user