MDL-59368 groups: Peer review fixes

* Fix coalesce on postgres.
* The edit icon's alt shows the HTML entities causing
* enrol/tests/behat/add_to_group.feature Contains a '@doit' tag which I assume is there from testing.
* group/classes/output/user_groups_editable.php
** Missing MOODLE_INTERNAL check.
** Unused variables context_system and moodle_exception.
** The PHPDocs for the constructors are wrong.
** export_for_template() returns an array, not stdClass (according to parent docs).
** Change moodle_exception to coding_exception at the beginning.
* group/lib.php
** Missing docs for core_group_inplace_editable().
* user/classes/participants_table.php
** The docs for the class variables $groups, $course and $context need a '\' beforehand as they are in a namespace.
** I would prefer $this->context = $context; and $this->groups = ... to be done at the end of the constructor with the other class variable assignments.
** You could get rid of the class variable courseid if we are setting course and use $this->course->id instead.
** The function col_groups has @param \stdclass $row but it should be $data
* lib/amd/src/form-autocomplete.js and lib/amd/src/inplace_editable.js
** Some issues here CiBot has pointed out.
* lib/classes/output/inplace_editable.php
** @see should be on a new line.
This commit is contained in:
Damyon Wiese 2017-07-07 10:08:18 +08:00
parent f746a078cb
commit f3ecea3af4
10 changed files with 39 additions and 25 deletions

View File

@ -1,4 +1,4 @@
@core_enrol @core_group @doit
@core_enrol @core_group
Feature: Users can be added to multiple groups at once
In order to manage group membership effectively
As a user

View File

@ -24,11 +24,12 @@
namespace core_group\output;
use context_system;
use context_course;
use core_user;
use core_external;
use moodle_exception;
use coding_exception;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/group/lib.php');
@ -49,7 +50,11 @@ class user_groups_editable extends \core\output\inplace_editable {
/**
* Constructor.
*
* @param \stdClass|core_tag_tag $tag
* @param \stdClass $course The current course
* @param \context $context The course context
* @param \stdClass $user The current user
* @param \stdClass[] $coursegroups The list of course groups from groups_get_all_groups with membership.
* @param string $value JSON Encoded list of group ids.
*/
public function __construct($course, $context, $user, $coursegroups, $value) {
// Check capabilities to get editable value.
@ -83,7 +88,7 @@ class user_groups_editable extends \core\output\inplace_editable {
* Export this data so it can be used as the context for a mustache template.
*
* @param \renderer_base $output
* @return \stdClass
* @return array
*/
public function export_for_template(\renderer_base $output) {
$listofgroups = [];

View File

@ -1097,6 +1097,14 @@ function groups_sync_with_enrolment($enrolname, $courseid = 0, $gidfield = 'cust
return $affectedusers;
}
/**
* Callback for inplace editable API.
*
* @param string $itemtype - Only user_groups is supported.
* @param string $itemid - Userid and groupid separated by a :
* @param string $newvalue - json encoded list of groupids.
* @return \core\output\inplace_editable
*/
function core_group_inplace_editable($itemtype, $itemid, $newvalue) {
if ($itemtype === 'user_groups') {
return \core_group\output\user_groups_editable::update($itemid, $newvalue);

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
define(["jquery","core/ajax","core/templates","core/notification","core/str","core/config","core/url","core/form-autocomplete"],function(a,b,c,d,e,f,g,h){return a("body").on("click keypress","[data-inplaceeditable] [data-inplaceeditablelink]",function(i){if("keypress"!==i.type||13===i.keyCode){i.stopImmediatePropagation(),i.preventDefault();var j=a(this),k=j.closest("[data-inplaceeditable]"),l=function(b){b.addClass("updating");var c=b.find("img.spinner");c.length?c.show():(c=a("<img/>").attr("src",g.imageUrl("i/loading_small")).addClass("spinner").addClass("smallicon"),b.append(c))},m=function(a){a.removeClass("updating"),a.find("img.spinner").hide()},n=function(e,f){l(e),b.call([{methodname:"core_update_inplace_editable",args:{itemid:e.attr("data-itemid"),component:e.attr("data-component"),itemtype:e.attr("data-itemtype"),value:f},done:function(b){var d=e.attr("data-value");c.render("core/inplace_editable",b).done(function(f,g){var h=a(f);c.replaceNode(e,h,g),h.find("[data-inplaceeditablelink]").focus(),h.trigger({type:"updated",ajaxreturn:b,oldvalue:d})})},fail:function(b){var c=a.Event("updatefailed",{exception:b,newvalue:f});m(e),e.trigger(c),c.isDefaultPrevented()||d.exception(b)}}],!0)},o=function(a){a.find("input").off(),a.find("select").off(),a.html(a.attr("data-oldcontent")),a.removeAttr("data-oldcontent"),a.removeClass("inplaceeditingon"),a.find("[data-inplaceeditablelink]").focus()},p=function(){a("span.inplaceeditable.inplaceeditingon").each(function(){o(a(this))})},q=function(b,c){var d,e=b;for(d=0;d<c;d++)e+=String(Math.floor(10*Math.random()));return 0===a("#"+e).length?e:q(b,c)},r=function(b){e.get_string("edittitleinstructions").done(function(c){var d=a('<span class="editinstructions">'+c+"</span>").attr("id",q("id_editinstructions_",20)),e=a('<input type="text"/>').attr("id",q("id_inplacevalue_",20)).attr("value",b.attr("data-value")).attr("aria-describedby",d.attr("id")).addClass("ignoredirty").addClass("form-control"),g=a('<label class="accesshide">'+k.attr("data-editlabel")+"</label>").attr("for",e.attr("id"));b.html("").append(d).append(g).append(e),e.focus(),e.select(),e.on("keyup keypress focusout",function(a){if(!f.behatsiterunning||"focusout"!==a.type){if("keypress"===a.type&&13===a.keyCode){var c=e.val();o(b),n(b,c)}("keyup"===a.type&&27===a.keyCode||"focusout"===a.type)&&o(b)}})})},s=function(a,b){o(a),n(a,b)},t=function(b,c){var d,e=a("<select></select>").attr("id",q("id_inplacevalue_",20)).addClass("custom-select"),g=a('<label class="accesshide">'+k.attr("data-editlabel")+"</label>").attr("for",e.attr("id"));for(d in c)e.append(a("<option>").attr("value",c[d].key).html(c[d].value));e.val(b.attr("data-value")),b.html("").append(g).append(e),e.focus(),e.select(),e.on("keyup change focusout",function(a){if(!f.behatsiterunning||"focusout"!==a.type){if("change"===a.type){var c=e.val();o(b),n(b,c)}("keyup"===a.type&&27===a.keyCode||"focusout"===a.type)&&o(b)}})},u=function(b,f){var g,i=a("<select></select>").attr("id",q("id_inplacevalue_",20)).addClass("custom-select"),j=a('<label class="accesshide">'+k.attr("data-editlabel")+"</label>").attr("for",i.attr("id")),l=f.options,m=f.attributes,p=a('<a href="#"></a>'),r=a('<a href="#"></a>');for(g in l)i.append(a("<option>").attr("value",l[g].key).html(l[g].value));m.multiple&&i.attr("multiple","true"),i.val(JSON.parse(b.attr("data-value"))),e.get_string("savechanges","core").then(function(a){return c.renderPix("e/save","core",a)}).then(function(a){p.append(a)}).fail(d.exception),e.get_string("cancel","core").then(function(a){return c.renderPix("e/cancel","core",a)}).then(function(a){r.append(a)}).fail(d.exception),b.html("").append(j).append(i).append(p).append(r),i.focus(),i.select(),h.enhance(i,m.tags,m.ajax,m.placeholder,m.caseSensitive,m.showSuggestions,m.noSelectionString).then(function(){b.find("[role=combobox]").focus()}),i.on("keyup",function(a){("keyup"===a.type&&27===a.keyCode||"focusout"===a.type)&&o(b)}),p.on("click",function(a){var c=JSON.stringify(i.val());o(b),n(b,c),a.preventDefault()}),r.on("click",function(a){o(b),a.preventDefault()})},v=function(b){b.addClass("inplaceeditingon"),b.attr("data-oldcontent",b.html());var c=b.attr("data-type"),d=b.attr("data-options");"toggle"===c?s(b,d):"select"===c?t(b,a.parseJSON(d)):"autocomplete"===c?u(b,a.parseJSON(d)):r(b)};p(),v(k)}}),{}});
define(["jquery","core/ajax","core/templates","core/notification","core/str","core/config","core/url","core/form-autocomplete"],function(a,b,c,d,e,f,g,h){return a("body").on("click keypress","[data-inplaceeditable] [data-inplaceeditablelink]",function(i){if("keypress"!==i.type||13===i.keyCode){i.stopImmediatePropagation(),i.preventDefault();var j=a(this),k=j.closest("[data-inplaceeditable]"),l=function(b){b.addClass("updating");var c=b.find("img.spinner");c.length?c.show():(c=a("<img/>").attr("src",g.imageUrl("i/loading_small")).addClass("spinner").addClass("smallicon"),b.append(c))},m=function(a){a.removeClass("updating"),a.find("img.spinner").hide()},n=function(e,f){l(e),b.call([{methodname:"core_update_inplace_editable",args:{itemid:e.attr("data-itemid"),component:e.attr("data-component"),itemtype:e.attr("data-itemtype"),value:f},done:function(b){var d=e.attr("data-value");c.render("core/inplace_editable",b).done(function(f,g){var h=a(f);c.replaceNode(e,h,g),h.find("[data-inplaceeditablelink]").focus(),h.trigger({type:"updated",ajaxreturn:b,oldvalue:d})})},fail:function(b){var c=a.Event("updatefailed",{exception:b,newvalue:f});m(e),e.trigger(c),c.isDefaultPrevented()||d.exception(b)}}],!0)},o=function(a){a.find("input").off(),a.find("select").off(),a.html(a.attr("data-oldcontent")),a.removeAttr("data-oldcontent"),a.removeClass("inplaceeditingon"),a.find("[data-inplaceeditablelink]").focus()},p=function(){a("span.inplaceeditable.inplaceeditingon").each(function(){o(a(this))})},q=function(b,c){var d,e=b;for(d=0;d<c;d++)e+=String(Math.floor(10*Math.random()));return 0===a("#"+e).length?e:q(b,c)},r=function(b){e.get_string("edittitleinstructions").done(function(c){var d=a('<span class="editinstructions">'+c+"</span>").attr("id",q("id_editinstructions_",20)),e=a('<input type="text"/>').attr("id",q("id_inplacevalue_",20)).attr("value",b.attr("data-value")).attr("aria-describedby",d.attr("id")).addClass("ignoredirty").addClass("form-control"),g=a('<label class="accesshide">'+k.attr("data-editlabel")+"</label>").attr("for",e.attr("id"));b.html("").append(d).append(g).append(e),e.focus(),e.select(),e.on("keyup keypress focusout",function(a){if(!f.behatsiterunning||"focusout"!==a.type){if("keypress"===a.type&&13===a.keyCode){var c=e.val();o(b),n(b,c)}("keyup"===a.type&&27===a.keyCode||"focusout"===a.type)&&o(b)}})})},s=function(a,b){o(a),n(a,b)},t=function(b,c){var d,e=a("<select></select>").attr("id",q("id_inplacevalue_",20)).addClass("custom-select"),g=a('<label class="accesshide">'+k.attr("data-editlabel")+"</label>").attr("for",e.attr("id"));for(d in c)e.append(a("<option>").attr("value",c[d].key).html(c[d].value));e.val(b.attr("data-value")),b.html("").append(g).append(e),e.focus(),e.select(),e.on("keyup change focusout",function(a){if(!f.behatsiterunning||"focusout"!==a.type){if("change"===a.type){var c=e.val();o(b),n(b,c)}("keyup"===a.type&&27===a.keyCode||"focusout"===a.type)&&o(b)}})},u=function(b,f){var g,i=a("<select></select>").attr("id",q("id_inplacevalue_",20)).addClass("custom-select"),j=a('<label class="accesshide">'+k.attr("data-editlabel")+"</label>").attr("for",i.attr("id")),l=f.options,m=f.attributes,p=a('<a href="#"></a>'),r=a('<a href="#"></a>');for(g in l)i.append(a("<option>").attr("value",l[g].key).html(l[g].value));m.multiple&&i.attr("multiple","true"),i.val(JSON.parse(b.attr("data-value"))),e.get_string("savechanges","core").then(function(a){return c.renderPix("e/save","core",a)}).then(function(a){p.append(a)}).fail(d.exception),e.get_string("cancel","core").then(function(a){return c.renderPix("e/cancel","core",a)}).then(function(a){r.append(a)}).fail(d.exception),b.html("").append(j).append(i).append(p).append(r),i.focus(),i.select(),h.enhance(i,m.tags,m.ajax,m.placeholder,m.caseSensitive,m.showSuggestions,m.noSelectionString).then(function(){b.find("[role=combobox]").focus()}).fail(d.exception),i.on("keyup",function(a){("keyup"===a.type&&27===a.keyCode||"focusout"===a.type)&&o(b)}),p.on("click",function(a){var c=JSON.stringify(i.val());o(b),n(b,c),a.preventDefault()}),r.on("click",function(a){o(b),a.preventDefault()})},v=function(b){b.addClass("inplaceeditingon"),b.attr("data-oldcontent",b.html());var c=b.attr("data-type"),d=b.attr("data-options");"toggle"===c?s(b,d):"select"===c?t(b,a.parseJSON(d)):"autocomplete"===c?u(b,a.parseJSON(d)):r(b)};p(),v(k)}}),{}});

View File

@ -738,7 +738,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
var originalSelect = $(selector);
if (!originalSelect) {
log.debug('Selector not found: ' + selector);
return;
return false;
}
// Hide the original select.
@ -770,7 +770,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
var renderDatalist = templates.render('core/form_autocomplete_suggestions', context);
var renderSelection = templates.render('core/form_autocomplete_selection', context);
return $.when(renderInput, renderDatalist, renderSelection).done(function(input, suggestions, selection) {
return $.when(renderInput, renderDatalist, renderSelection).then(function(input, suggestions, selection) {
// Add our new UI elements to the page.
originalSelect.after(suggestions);
originalSelect.after(input);

View File

@ -262,7 +262,9 @@ define(['jquery',
.then(function() {
// Focus on the enhanced combobox.
el.find('[role=combobox]').focus();
});
// Stop eslint nagging.
return;
}).fail(notification.exception);
inputelement.on('keyup', function(e) {
if ((e.type === 'keyup' && e.keyCode === 27) || e.type === 'focusout') {

View File

@ -197,7 +197,7 @@ class inplace_editable implements templatable, renderable {
* Sets the element type to be an autocomplete field
*
* @param array $options associative array with dropdown options
* @param array $attributes associative array with attributes for autoselect field. @see AMD module core/form-autocomplete
* @param array $attributes associative array with attributes for autoselect field. See AMD module core/form-autocomplete.
* @return self
*/
public function set_type_autocomplete($options, $attributes) {

View File

@ -56,7 +56,7 @@
{{^ linkeverything }}{{{displayvalue}}}{{/ linkeverything }}
<a href="#" class="quickeditlink" data-inplaceeditablelink="1" title="{{edithint}}">
{{# linkeverything }}{{{displayvalue}}}{{/ linkeverything }}
<span class="quickediticon visibleifjs">{{#pix}}t/editstring,core,{{edithint}}{{/pix}}</span>
<span class="quickediticon visibleifjs">{{#pix}}t/editstring,core,{{{edithint}}}{{/pix}}</span>
</a>
</span>
{{#js}}

View File

@ -76,7 +76,7 @@ class participants_table extends \table_sql {
protected $countries;
/**
* @var stdClass[] The list of groups with membership info for the course.
* @var \stdClass[] The list of groups with membership info for the course.
*/
protected $groups;
@ -86,12 +86,12 @@ class participants_table extends \table_sql {
protected $extrafields;
/**
* @var stdClass The course details.
* @var \stdClass The course details.
*/
protected $course;
/**
* @var context The course context.
* @var \context The course context.
*/
protected $context;
@ -115,7 +115,6 @@ class participants_table extends \table_sql {
// Get the context.
$this->course = get_course($courseid);
$context = \context_course::instance($courseid, MUST_EXIST);
$this->context = $context;
// Define the headers and columns.
$headers = [];
@ -136,7 +135,6 @@ class participants_table extends \table_sql {
}
// Load and cache the course groupinfo.
$this->groups = groups_get_all_groups($courseid, 0, 0, 'g.*', true);
// Add column for groups.
$headers[] = get_string('groups');
$columns[] = 'groups';
@ -173,7 +171,6 @@ class participants_table extends \table_sql {
$this->set_attribute('id', 'participants');
// Set the variables we need to use later.
$this->courseid = $courseid;
$this->currentgroup = $currentgroup;
$this->accesssince = $accesssince;
$this->roleid = $roleid;
@ -181,6 +178,8 @@ class participants_table extends \table_sql {
$this->selectall = $selectall;
$this->countries = get_string_manager()->get_list_of_countries();
$this->extrafields = $extrafields;
$this->context = $context;
$this->groups = groups_get_all_groups($courseid, 0, 0, 'g.*', true);
}
/**
@ -207,25 +206,25 @@ class participants_table extends \table_sql {
public function col_fullname($data) {
global $OUTPUT;
return $OUTPUT->user_picture($data, array('size' => 35, 'courseid' => $this->courseid)) . ' ' . fullname($data);
return $OUTPUT->user_picture($data, array('size' => 35, 'courseid' => $this->course->id)) . ' ' . fullname($user);
}
/**
* Generate the groups column.
*
* @param \stdClass $row
* @param \stdClass $data
* @return string
*/
public function col_groups($user) {
public function col_groups($data) {
global $OUTPUT;
$usergroups = [];
foreach ($this->groups as $coursegroup) {
if (isset($coursegroup->members[$user->id])) {
if (isset($coursegroup->members[$data->id])) {
$usergroups[] = $coursegroup->id;
}
}
$editable = new \core_group\output\user_groups_editable($this->course, $this->context, $user, $this->groups, $usergroups);
$editable = new \core_group\output\user_groups_editable($this->course, $this->context, $data, $this->groups, $usergroups);
return $OUTPUT->render_from_template('core/inplace_editable', $editable->export_for_template($OUTPUT));
}
@ -295,7 +294,7 @@ class participants_table extends \table_sql {
public function query_db($pagesize, $useinitialsbar = true) {
list($twhere, $tparams) = $this->get_sql_where();
$total = user_get_total_participants($this->courseid, $this->currentgroup, $this->accesssince,
$total = user_get_total_participants($this->course->id, $this->currentgroup, $this->accesssince,
$this->roleid, $this->search, $twhere, $tparams);
$this->pagesize($pagesize, $total);
@ -305,7 +304,7 @@ class participants_table extends \table_sql {
$sort = 'ORDER BY ' . $sort;
}
$this->rawdata = user_get_participants($this->courseid, $this->currentgroup, $this->accesssince,
$this->rawdata = user_get_participants($this->course->id, $this->currentgroup, $this->accesssince,
$this->roleid, $this->search, $twhere, $tparams, $sort, $this->get_page_start(),
$this->get_page_size());