diff --git a/group/classes/output/user_groups_editable.php b/group/classes/output/user_groups_editable.php new file mode 100644 index 00000000000..c6534ba6dd6 --- /dev/null +++ b/group/classes/output/user_groups_editable.php @@ -0,0 +1,164 @@ +. + +/** + * Contains class core_group\output\user_groups_editable + * + * @package core_group + * @copyright 2017 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core_group\output; + +use context_system; +use context_course; +use core_user; +use core_external; + +/** + * Class to display list of user groups. + * + * @package core_group + * @copyright 2017 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class user_groups_editable extends \core\output\inplace_editable { + + /** @var $coursegroups */ + private $coursegroups = null; + /** @var $context */ + private $context = null; + + /** + * Constructor. + * + * @param \stdClass|core_tag_tag $tag + */ + public function __construct($course, $context, $user, $coursegroups, $value) { + // Check capabilities to get editable value. + $editable = has_capability('moodle/course:managegroups', $context); + + // Invent an itemid. + $itemid = $course->id . ':' . $user->id; + + $value = json_encode($value); + + // Remember these for the display value. + $this->coursegroups = $coursegroups; + $this->context = $context; + + parent::__construct('core_group', 'user_groups', $itemid, $editable, $value, $value); + + // Assignable groups. + $options = []; + + foreach ($coursegroups as $group) { + $options[$group->id] = $group->name; + } + + $attributes = ['multiple' => true]; + $this->set_type_autocomplete($options, $attributes); + } + + /** + * Export this data so it can be used as the context for a mustache template. + * + * @param \renderer_base $output + * @return \stdClass + */ + public function export_for_template(\renderer_base $output) { + // Set edithint and display value. + $this->edithint = get_string('editusersgroups', 'group'); + + $listofgroups = []; + $groupids = json_decode($this->value); + foreach ($groupids as $id) { + $listofgroups[] = format_string($this->coursegroups[$id]->name, true, ['context' => $this->context]); + } + + $this->displayvalue = implode($listofgroups, ', '); + return parent::export_for_template($output); + } + + /** + * Updates the value in database and returns itself, called from inplace_editable callback + * + * @param int $itemid + * @param mixed $newvalue + * @return \self + */ + public static function update($itemid, $newvalue) { + // Check caps. + // Do the thing. + // Return one of me. + // Validate the inputs. + list($courseid, $userid) = explode(':', $itemid, 2); + + $courseid = clean_param($courseid, PARAM_INT); + $userid = clean_param($userid, PARAM_INT); + $groupids = json_decode($newvalue); + foreach ($groupids as $index => $groupid) { + $groupids[$index] = clean_param($groupid, PARAM_INT); + } + + // Check user is enrolled in the course. + $context = context_course::instance($courseid); + core_external::validate_context($context); + + if (!is_enrolled($context, $userid)) { + throw new coding_exception('User does not belong to the course'); + } + + // Check that all the groups belong to the course. + $coursegroups = groups_get_all_groups($courseid, 0, 0, 'g.*', true); + + $byid = []; + foreach ($groupids as $groupid) { + if (!isset($coursegroups[$groupid])) { + throw new coding_exception('Group does not belong to the course'); + } + $byid[$groupid] = $groupid; + } + $groupids = $byid; + // Check permissions. + require_capability('moodle/course:managegroups', $context); + + // Process adds. + foreach ($groupids as $groupid) { + if (!isset($coursegroups[$groupid]->members[$userid])) { + // Add them. + groups_add_member($groupid, $userid); + // Keep this variable in sync. + $coursegroups[$groupid]->members[$userid] = $userid; + } + } + + // Process removals. + foreach ($coursegroups as $groupid => $group) { + if (isset($group->members[$userid]) && !isset($groupids[$groupid])) { + if (groups_remove_member_allowed($groupid, $userid)) { + groups_remove_member($groupid, $userid); + unset($coursegroups[$groupid]->members[$userid]); + } + } + } + + $course = get_course($courseid); + $user = core_user::get_user($userid); + return new self($course, $context, $user, $coursegroups, $groupids); + } +} diff --git a/group/lib.php b/group/lib.php index 3f8de4e6804..67d04ddfbf9 100644 --- a/group/lib.php +++ b/group/lib.php @@ -1096,3 +1096,9 @@ function groups_sync_with_enrolment($enrolname, $courseid = 0, $gidfield = 'cust return $affectedusers; } + +function core_group_inplace_editable($itemtype, $itemid, $newvalue) { + if ($itemtype === 'user_groups') { + return \core_group\output\user_groups_editable::update($itemid, $newvalue); + } +} diff --git a/lang/en/group.php b/lang/en/group.php index bd2a6058492..3132a6436ff 100644 --- a/lang/en/group.php +++ b/lang/en/group.php @@ -56,6 +56,7 @@ $string['deletegroupsconfirm'] = 'Are you sure you want to delete the following $string['deleteselectedgroup'] = 'Delete selected group'; $string['editgroupingsettings'] = 'Edit grouping settings'; $string['editgroupsettings'] = 'Edit group settings'; +$string['editusersgroups'] = 'Edit users groups'; $string['enrolmentkey'] = 'Enrolment key'; $string['enrolmentkey_help'] = 'An enrolment key enables access to the course to be restricted to only those who know the key. If a group enrolment key is specified, then not only will entering that key let the user into the course, but it will also automatically make them a member of this group. diff --git a/lib/amd/src/inplace_editable.js b/lib/amd/src/inplace_editable.js index bc79fda29d3..43a89098b79 100644 --- a/lib/amd/src/inplace_editable.js +++ b/lib/amd/src/inplace_editable.js @@ -28,8 +28,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since 3.1 */ -define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str', 'core/config', 'core/url'], - function($, ajax, templates, notification, str, cfg, url) { +define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str', 'core/config', 'core/url', 'core/form-autocomplete'], + function($, ajax, templates, notification, str, cfg, url, autocomplete) { $('body').on('click keypress', '[data-inplaceeditable] [data-inplaceeditablelink]', function(e) { if (e.type === 'keypress' && e.keyCode !== 13) { @@ -176,6 +176,7 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str' .html(options[i].value)); } inputelement.val(el.attr('data-value')); + el.html('') .append(lbl) .append(inputelement); @@ -199,6 +200,63 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str' }); }; + var turnEditingOnAutocomplete = function(el, args) { + var i, + inputelement = $(''). + attr('id', uniqueId('id_inplacevalue_', 20)). + addClass('custom-select'), + lbl = $('') + .attr('for', inputelement.attr('id')), + options = args.options, + attributes = args.attributes, + saveelement = $(''); + + for (i in options) { + inputelement + .append($('