diff --git a/admin/tool/cohortroles/classes/api.php b/admin/tool/cohortroles/classes/api.php new file mode 100644 index 00000000000..36b0d1dbf62 --- /dev/null +++ b/admin/tool/cohortroles/classes/api.php @@ -0,0 +1,229 @@ +. + +/** + * Class exposing the api for the cohortroles tool. + * + * @package tool_cohortroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace tool_cohortroles; + +use stdClass; +use context_system; +use tool_lp\invalid_persistent_exception; + +/** + * Class for doing things with cohort roles. + * + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class api { + + /** + * Create a cohort role assignment from a record containing all the data for the class. + * + * Requires moodle/role:manage capability at the system context. + * + * @param stdClass $record Record containing all the data for an instance of the class. + * @return competency + */ + public static function create_cohort_role_assignment(stdClass $record) { + $cohortroleassignment = new cohort_role_assignment(0, $record); + $context = context_system::instance(); + + // First we do a permissions check. + require_capability('moodle/role:manage', $context); + + // Validate before we check for existing records. + if (!$cohortroleassignment->is_valid()) { + throw new invalid_persistent_exception($cohortroleassignment->get_errors()); + } + + $existing = cohort_role_assignment::get_record((array) $record); + if (!empty($existing)) { + return false; + } else { + // OK - all set. + $cohortroleassignment->create(); + } + return $cohortroleassignment; + } + + /** + * Delete a cohort role assignment by id. + * + * Requires moodle/role:manage capability at the system context. + * + * @param int $id The record to delete. This will also remove this role from the user for all users in the system. + * @return boolean + */ + public static function delete_cohort_role_assignment($id) { + $cohortroleassignment = new cohort_role_assignment($id); + $context = context_system::instance(); + + // First we do a permissions check. + require_capability('moodle/role:manage', $context); + + // OK - all set. + return $cohortroleassignment->delete(); + } + + /** + * Perform a search based on the provided filters and return a paginated list of records. + * + * Requires moodle/role:manage capability at the system context. + * + * @param string $sort The column to sort on + * @param string $order ('ASC' or 'DESC') + * @param int $skip Number of records to skip (pagination) + * @param int $limit Max of records to return (pagination) + * @return array of cohort_role_assignment + */ + public static function list_cohort_role_assignments($sort = '', $order = 'ASC', $skip = 0, $limit = 0) { + $context = context_system::instance(); + + // First we do a permissions check. + require_capability('moodle/role:manage', $context); + + // OK - all set. + return cohort_role_assignment::get_records(array(), $sort, $order, $skip, $limit); + } + + /** + * Perform a search based on the provided filters and return a paginated list of records. + * + * Requires moodle/role:manage capability at system context. + * + * @param array $filters A list of filters to apply to the list. + * @return int + */ + public static function count_cohort_role_assignments() { + $context = context_system::instance(); + + // First we do a permissions check. + require_capability('moodle/role:manage', $context); + + // OK - all set. + return cohort_role_assignment::count_records(); + } + + /** + * Sync all roles - adding and deleting role assignments as required. + * + * Slow. Should only be called from a background task. + * + * Requires moodle/role:manage capability at the system context. + * + * @return array('rolesadded' => array of (useridassignedto, useridassignedover, roleid), + * 'rolesremoved' => array of (useridassignedto, useridassignedover, roleid)) + */ + public static function sync_all_cohort_roles() { + global $DB; + + $context = context_system::instance(); + + // First we do a permissions check. + require_capability('moodle/role:manage', $context); + + // Ok ready to go. + $rolesadded = array(); + $rolesremoved = array(); + + // Get all cohort role assignments and group them by user and role. + $all = cohort_role_assignment::get_records(array(), 'userid, roleid'); + // We build an better structure to loop on. + $info = array(); + foreach ($all as $cra) { + if (!isset($info[$cra->get_userid()])) { + $info[$cra->get_userid()] = array(); + } + if (!isset($info[$cra->get_userid()][$cra->get_roleid()])) { + $info[$cra->get_userid()][$cra->get_roleid()] = array(); + } + array_push($info[$cra->get_userid()][$cra->get_roleid()], $cra->get_cohortid()); + } + // Then for each user+role combo - find user context in the cohort without a role assigned. + + foreach ($info as $userid => $roles) { + foreach ($roles as $roleid => $cohorts) { + list($cohortsql, $params) = $DB->get_in_or_equal($cohorts, SQL_PARAMS_NAMED); + + $params['usercontext'] = CONTEXT_USER; + $params['roleid'] = $roleid; + $params['userid'] = $userid; + + $sql = 'SELECT u.id AS userid, ra.id, ctx.id AS contextid + FROM {user} u + JOIN {cohort_members} cm ON u.id = cm.userid + JOIN {context} ctx ON u.id = ctx.instanceid AND ctx.contextlevel = :usercontext + LEFT JOIN {role_assignments} ra ON ra.contextid = ctx.id + AND ra.roleid = :roleid + AND ra.userid = :userid + WHERE cm.cohortid ' . $cohortsql . ' + AND ra.id IS NULL'; + + $toadd = $DB->get_records_sql($sql, $params); + + foreach ($toadd as $add) { + role_assign($roleid, $userid, $add->contextid, 'tool_cohortroles'); + $rolesadded[] = array( + 'useridassignedto' => $userid, + 'useridassignedover' => $add->userid, + 'roleid' => $roleid + ); + } + } + } + + // And for each user+role combo - find user context not in the cohort with a role assigned. + // If the role was assigned by this component, unassign the role. + foreach ($info as $userid => $roles) { + foreach ($roles as $roleid => $cohorts) { + // Now we are looking for entries NOT in the cohort. + list($cohortsql, $params) = $DB->get_in_or_equal($cohorts, SQL_PARAMS_NAMED); + + $params['usercontext'] = CONTEXT_USER; + $params['roleid'] = $roleid; + $params['userid'] = $userid; + $params['component'] = 'tool_cohortroles'; + + $sql = 'SELECT u.id as userid, ra.id, ctx.id AS contextid + FROM {user} u + JOIN {context} ctx ON u.id = ctx.instanceid AND ctx.contextlevel = :usercontext + JOIN {role_assignments} ra ON ra.contextid = ctx.id AND ra.roleid = :roleid AND ra.userid = :userid + LEFT JOIN {cohort_members} cm ON u.id = cm.userid + AND cm.cohortid ' . $cohortsql . ' + WHERE ra.component = :component AND cm.cohortid IS NULL'; + + $toremove = $DB->get_records_sql($sql, $params); + foreach ($toremove as $remove) { + role_unassign($roleid, $userid, $remove->contextid, 'tool_cohortroles'); + $rolesremoved[] = array( + 'useridassignedto' => $userid, + 'useridassignedover' => $remove->userid, + 'roleid' => $roleid + ); + } + } + } + + return array('rolesadded' => $rolesadded, 'rolesremoved' => $rolesremoved); + } + +} diff --git a/admin/tool/cohortroles/classes/cohort_role_assignment.php b/admin/tool/cohortroles/classes/cohort_role_assignment.php new file mode 100644 index 00000000000..6c1e4a10973 --- /dev/null +++ b/admin/tool/cohortroles/classes/cohort_role_assignment.php @@ -0,0 +1,107 @@ +. + +/** + * Class for cohort_role_assignment persistence. + * + * @package tool_cohortroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace tool_cohortroles; + +use lang_string; +use tool_lp\persistent; + +/** + * Class for loading/storing cohort_role_assignments from the DB. + * + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class cohort_role_assignment extends persistent { + + /** Table name for user_competency persistency */ + const TABLE = 'tool_cohortroles'; + + /** + * Return the definition of the properties of this model. + * + * @return array + */ + protected static function define_properties() { + return array( + 'userid' => array( + 'type' => PARAM_INT, + ), + 'roleid' => array( + 'type' => PARAM_INT, + ), + 'cohortid' => array( + 'type' => PARAM_INT, + ) + ); + } + + /** + * Validate the user ID. + * + * @param int $value The value. + * @return true|lang_string + */ + protected function validate_userid($value) { + global $DB; + + if (!$DB->record_exists('user', array('id' => $value))) { + return new lang_string('invaliduserid', 'error'); + } + + return true; + } + + /** + * Validate the role ID. + * + * @param int $value The value. + * @return true|lang_string + */ + protected function validate_roleid($value) { + global $DB; + + if (!$DB->record_exists('role', array('id' => $value))) { + return new lang_string('invalidroleid', 'error'); + } + + return true; + } + + /** + * Validate the cohort ID. + * + * @param int $value The value. + * @return true|lang_string + */ + protected function validate_cohortid($value) { + global $DB; + + if (!$DB->record_exists('cohort', array('id' => $value))) { + return new lang_string('invalidcohortid', 'error'); + } + + return true; + } + +} diff --git a/admin/tool/cohortroles/classes/form/assign_role_cohort.php b/admin/tool/cohortroles/classes/form/assign_role_cohort.php new file mode 100644 index 00000000000..9292b9beff1 --- /dev/null +++ b/admin/tool/cohortroles/classes/form/assign_role_cohort.php @@ -0,0 +1,82 @@ +. + +/** + * Assign role to cohort form. + * + * @package tool_cohortroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_cohortroles\form; +defined('MOODLE_INTERNAL') || die(); + +use moodleform; +use context_system; + +require_once($CFG->libdir . '/formslib.php'); + +/** + * Assign role to cohort form. + * + * @package tool_cohortroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class assign_role_cohort extends moodleform { + + public function definition() { + global $OUTPUT; + + $mform = $this->_form; + $roles = get_roles_for_contextlevels(CONTEXT_USER); + + if (empty($roles)) { + $warning = $OUTPUT->notify_problem(get_string('noassignableroles', 'tool_cohortroles')); + $mform->addElement('html', $warning); + return; + } + + $options = array( + 'ajax' => 'tool_lp/form-user-selector', + 'multiple' => true + ); + $mform->addElement('autocomplete', 'userids', get_string('selectusers', 'tool_cohortroles'), array(), $options); + $mform->addRule('userids', null, 'required'); + + $names = role_get_names(); + $options = array(); + foreach ($roles as $idx => $roleid) { + $options[$roleid] = $names[$roleid]->localname; + } + + $mform->addElement('select', 'roleid', get_string('selectrole', 'tool_cohortroles'), $options); + $mform->addRule('roleid', null, 'required'); + + $context = context_system::instance(); + $options = array( + 'ajax' => 'tool_lp/form-cohort-selector', + 'multiple' => true, + 'data-contextid' => $context->id, + 'data-includes' => 'all' + ); + $mform->addElement('autocomplete', 'cohortids', get_string('selectcohorts', 'tool_cohortroles'), array(), $options); + $mform->addRule('cohortids', null, 'required'); + $mform->addElement('submit', 'submit', get_string('assign', 'tool_cohortroles')); + } + +} diff --git a/admin/tool/cohortroles/classes/output/cohort_role_assignments_table.php b/admin/tool/cohortroles/classes/output/cohort_role_assignments_table.php new file mode 100644 index 00000000000..7d1e72030cc --- /dev/null +++ b/admin/tool/cohortroles/classes/output/cohort_role_assignments_table.php @@ -0,0 +1,213 @@ +. + +/** + * Cohort role assignments table + * + * @package tool_cohortroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_cohortroles\output; +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir . '/tablelib.php'); + +use context_helper; +use context_system; +use html_writer; +use moodle_url; +use table_sql; + +/** + * Cohort role assignments table. + * + * @package tool_cohortroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class cohort_role_assignments_table extends table_sql { + + /** + * Sets up the table. + * + * @param string $uniqueid Unique id of table. + * @param \tool_lp\template $template The template. + */ + public function __construct($uniqueid, $url) { + global $CFG; + parent::__construct($uniqueid); + $context = context_system::instance(); + + $this->context = $context; + + $this->rolenames = role_get_names(); + + // This object should not be used without the right permissions. + require_capability('moodle/role:manage', $context); + + $this->useridfield = 'userid'; + + // Define columns in the table. + $this->define_table_columns(); + + $this->define_baseurl($url); + // Define configs. + $this->define_table_configs(); + } + + protected function col_rolename($data) { + return $this->rolenames[$data->roleid]->localname; + } + + protected function col_cohortname($data) { + global $OUTPUT; + + $record = (object) array( + 'id' => $data->cohortid, + 'idnumber' => $data->cohortidnumber, + 'description' => $data->cohortdescription, + 'visible' => $data->cohortvisible, + 'name' => $data->cohortname + ); + $context = context_helper::instance_by_id($data->cohortcontextid); + + $exporter = new \tool_lp\external\cohort_summary_exporter($record, array('context' => $context)); + $cohort = $exporter->export($OUTPUT); + + $html = $OUTPUT->render_from_template('tool_cohortroles/cohort-in-list', $cohort); + return $html; + } + + protected function col_actions($data) { + global $OUTPUT; + + $action = new \confirm_action(get_string('removecohortroleassignmentconfirm', 'tool_cohortroles')); + $url = new moodle_url($this->baseurl); + $url->params(array('removecohortroleassignment' => $data->id, 'sesskey' => sesskey())); + $pix = new \pix_icon('t/delete', get_string('removecohortroleassignment', 'tool_cohortroles')); + return $OUTPUT->action_link($url, '', $action, null, $pix); + } + + /** + * Setup the headers for the table. + */ + protected function define_table_columns() { + $extrafields = get_extra_user_fields($this->context); + + // Define headers and columns. + $cols = array( + 'cohortname' => get_string('cohort', 'cohort'), + 'rolename' => get_string('role'), + 'fullname' => get_string('name'), + 'actions' => get_string('actions') + ); + + // Add headers for extra user fields. + foreach ($extrafields as $field) { + if (get_string_manager()->string_exists($field, 'moodle')) { + $cols[$field] = get_string($field); + } else { + $cols[$field] = $field; + } + } + + // Add remaining headers. + $cols = array_merge($cols, array()); + + $this->define_columns(array_keys($cols)); + $this->define_headers(array_values($cols)); + } + + /** + * Define table configs. + */ + protected function define_table_configs() { + $this->collapsible(false); + $this->sortable(true, 'lastname', SORT_ASC); + $this->pageable(true); + } + + /** + * Builds the SQL query. + * + * @param bool $count When true, return the count SQL. + * @return array containing sql to use and an array of params. + */ + protected function get_sql_and_params($count = false) { + $fields = 'uca.id, uca.cohortid, uca.userid, uca.roleid, '; + $fields .= 'c.name as cohortname, c.idnumber as cohortidnumber, c.contextid as cohortcontextid, '; + $fields .= 'c.visible as cohortvisible, c.description as cohortdescription, '; + + // Add extra user fields that we need for the graded user. + $extrafields = get_extra_user_fields($this->context); + foreach ($extrafields as $field) { + $fields .= 'u.' . $field . ', '; + } + $fields .= get_all_user_name_fields(true, 'u'); + + if ($count) { + $select = "COUNT(1)"; + } else { + $select = "$fields"; + } + + $sql = "SELECT $select + FROM {tool_cohortroles} uca + JOIN {user} u ON u.id = uca.userid + JOIN {cohort} c ON c.id = uca.cohortid"; + $params = array(); + + // Add order by if needed. + if (!$count && $sqlsort = $this->get_sql_sort()) { + $sql .= " ORDER BY " . $sqlsort; + } + + return array($sql, $params); + } + + /** + * Override the default implementation to set a decent heading level. + */ + public function print_nothing_to_display() { + global $OUTPUT; + echo $this->render_reset_button(); + $this->print_initials_bar(); + echo $OUTPUT->heading(get_string('nothingtodisplay'), 4); + } + + /** + * Query the DB. + * + * @param int $pagesize size of page for paginated displayed table. + * @param bool $useinitialsbar do you want to use the initials bar. + */ + public function query_db($pagesize, $useinitialsbar = true) { + global $DB; + + list($countsql, $countparams) = $this->get_sql_and_params(true); + list($sql, $params) = $this->get_sql_and_params(); + $total = $DB->count_records_sql($countsql, $countparams); + $this->pagesize($pagesize, $total); + $this->rawdata = $DB->get_records_sql($sql, $params, $this->get_page_start(), $this->get_page_size()); + + // Set initial bars. + if ($useinitialsbar) { + $this->initialbars($total > $pagesize); + } + } +} diff --git a/admin/tool/cohortroles/classes/task/cohort_role_sync.php b/admin/tool/cohortroles/classes/task/cohort_role_sync.php new file mode 100644 index 00000000000..45a5e9e4035 --- /dev/null +++ b/admin/tool/cohortroles/classes/task/cohort_role_sync.php @@ -0,0 +1,48 @@ +. + +/** + * Scheduled task for syncing cohort roles. + * + * @package tool_cohortroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace tool_cohortroles\task; + +use core\task\scheduled_task; +use tool_cohortroles\api; + +/** + * Scheduled task for syncing cohort roles. + * + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class cohort_role_sync extends scheduled_task { + public function get_name() { + // Shown in admin screens. + return get_string('taskname', 'tool_cohortroles'); + } + + public function execute() { + mtrace('Sync cohort roles...'); + $result = api::sync_all_cohort_roles(); + + mtrace('Added ' . count($result['rolesadded'])); + mtrace('Removed ' . count($result['rolesremoved'])); + } +} diff --git a/admin/tool/cohortroles/db/install.xml b/admin/tool/cohortroles/db/install.xml new file mode 100644 index 00000000000..538ec1fe6a1 --- /dev/null +++ b/admin/tool/cohortroles/db/install.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + +
+
+
diff --git a/admin/tool/cohortroles/db/tasks.php b/admin/tool/cohortroles/db/tasks.php new file mode 100644 index 00000000000..e5f5a627bb9 --- /dev/null +++ b/admin/tool/cohortroles/db/tasks.php @@ -0,0 +1,37 @@ +. + +/** + * Tasks definitions. + * + * @package tool_cohortroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$tasks = array( + array( + 'classname' => 'tool_cohortroles\task\cohort_role_sync', + 'blocking' => 0, + 'minute' => 'R', + 'hour' => '*', + 'day' => '*', + 'dayofweek' => '*', + 'month' => '*' + ), +); diff --git a/admin/tool/cohortroles/index.php b/admin/tool/cohortroles/index.php new file mode 100644 index 00000000000..dfcbb502fca --- /dev/null +++ b/admin/tool/cohortroles/index.php @@ -0,0 +1,98 @@ +. + +/** + * Assign roles for a user. + * + * @package tool_cohortroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(dirname(__FILE__) . '/../../../config.php'); +require_once($CFG->libdir.'/adminlib.php'); +require_once($CFG->libdir.'/tablelib.php'); + +$removeid = optional_param('removecohortroleassignment', 0, PARAM_INT); + +admin_externalpage_setup('toolcohortroles'); +$context = context_system::instance(); + +$pageurl = new moodle_url('/admin/tool/cohortroles/index.php'); + +$renderer = $PAGE->get_renderer('core'); + +echo $OUTPUT->header(); +$title = get_string('assignroletocohort', 'tool_cohortroles'); +echo $OUTPUT->heading($title); + +$form = new tool_cohortroles\form\assign_role_cohort(); + +if ($removeid) { + require_sesskey(); + + $result = \tool_cohortroles\api::delete_cohort_role_assignment($removeid); + if ($result) { + $notification = get_string('cohortroleassignmentremoved', 'tool_cohortroles'); + echo $OUTPUT->notify_success($notification); + } else { + $notification = get_string('cohortroleassignmentnotremoved', 'tool_cohortroles'); + echo $OUTPUT->notify_problem($notification); + } + echo $OUTPUT->continue_button(new moodle_url($pageurl)); +} else if ($data = $form->get_data()) { + require_sesskey(); + // We must create them all or none. + $saved = 0; + foreach ($data->userids as $userid) { + if (empty($data->cohortids)) { + $data->cohortids = array(); + } + foreach ($data->cohortids as $cohortid) { + $params = (object) array('userid' => $userid, 'cohortid' => $cohortid, 'roleid' => $data->roleid); + $result = \tool_cohortroles\api::create_cohort_role_assignment($params); + if ($result) { + $saved++; + } + } + } + if ($saved == 0) { + $notification = get_string('nocohortroleassignmentssaved', 'tool_cohortroles'); + echo $OUTPUT->notify_problem($notification); + } else if ($saved == 1) { + $notification = get_string('onecohortroleassignmentsaved', 'tool_cohortroles'); + echo $OUTPUT->notify_success($notification); + } else { + $notification = get_string('acohortroleassignmentssaved', 'tool_cohortroles', $saved); + echo $OUTPUT->notify_success($notification); + } + + echo $OUTPUT->continue_button(new moodle_url($pageurl)); +} else { + $form->display(); + + $title = get_string('existingcohortroles', 'tool_cohortroles'); + echo $OUTPUT->heading($title); + $url = new moodle_url('/admin/tool/cohortroles/index.php'); + $table = new tool_cohortroles\output\cohort_role_assignments_table(uniqid(), $url); + echo $table->out(50, true); + + echo $OUTPUT->spacer(); + echo $OUTPUT->notify_message(get_string('backgroundsync', 'tool_cohortroles')); +} + +echo $OUTPUT->footer(); + diff --git a/admin/tool/cohortroles/lang/en/tool_cohortroles.php b/admin/tool/cohortroles/lang/en/tool_cohortroles.php new file mode 100644 index 00000000000..40569192962 --- /dev/null +++ b/admin/tool/cohortroles/lang/en/tool_cohortroles.php @@ -0,0 +1,44 @@ +. + +/** + * Strings for component 'tool_userroles', language 'en' + * + * @package tool_userroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['acohortroleassignmentssaved'] = '{$a} cohort role assignments were saved.'; +$string['assign'] = 'Assign'; +$string['assignroletocohort'] = 'Assign role to users in the context of all members of these cohorts'; +$string['backgroundsync'] = 'Note: Roles assigned on this page will not take affect immediately, the changes to role assignments will be made by a background task.'; +$string['cohortroleassignmentremoved'] = 'The cohort role assignment was removed.'; +$string['cohortroleassignmentnotremoved'] = 'The cohort role assignment was not removed.'; +$string['cohortroles'] = 'Cohort roles'; +$string['existingcohortroles'] = 'Existing cohort role assignments'; +$string['managecohortroles'] = 'Assign user roles for entire cohort'; +$string['noassignableroles'] = 'No roles can be assigned at a user context level. Manage roles'; +$string['nocohortroleassignmentssaved'] = 'No cohort role assignments were saved.'; +$string['onecohortroleassignmentsaved'] = 'One cohort role assignment was saved.'; +$string['pluginname'] = 'Cohort roles management'; +$string['removecohortroleassignment'] = 'Remove cohort role assignment'; +$string['removecohortroleassignmentconfirm'] = 'Are you sure you want to remove this cohort role assignment? This role will be removed for this user in all other user contexts.'; +$string['selectcohorts'] = 'Select cohorts'; +$string['selectrole'] = 'Select role'; +$string['selectusers'] = 'Select users to assign role'; +$string['taskname'] = 'Sync cohort role assignments'; +$string['thisuserroles'] = 'Roles assigned relative to this user'; diff --git a/admin/tool/cohortroles/settings.php b/admin/tool/cohortroles/settings.php new file mode 100644 index 00000000000..98613c15042 --- /dev/null +++ b/admin/tool/cohortroles/settings.php @@ -0,0 +1,27 @@ +. + +/** + * Link to user roles management. + * + * @package tool_cohortroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; +$str = get_string('managecohortroles', 'tool_cohortroles'); +$ADMIN->add('roles', new admin_externalpage('toolcohortroles', $str, '/admin/tool/cohortroles/index.php', 'moodle/role:manage')); diff --git a/admin/tool/cohortroles/templates/cohort-in-list.mustache b/admin/tool/cohortroles/templates/cohort-in-list.mustache new file mode 100644 index 00000000000..971065a0da1 --- /dev/null +++ b/admin/tool/cohortroles/templates/cohort-in-list.mustache @@ -0,0 +1,43 @@ +{{! + 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 . +}} +{{! + @template tool_cohortroles/cohort-in-list + + Moodle template for displaying a cohort in a list. + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Context variables required for this template: + * id cohort id field + * name cohort name field + * idnumber cohort idnumber field + * description cohort description field + * visible cohort visible field + + Example context (json): + { "id": "1", + "name": "Cohort 1", + "visible": true, + "idnumber": "014", + "description": "Some users" + } +}} +{{> tool_lp/form-cohort-selector-suggestion }} diff --git a/admin/tool/cohortroles/tests/api_test.php b/admin/tool/cohortroles/tests/api_test.php new file mode 100644 index 00000000000..ab08e5ec732 --- /dev/null +++ b/admin/tool/cohortroles/tests/api_test.php @@ -0,0 +1,243 @@ +. + +/** + * API tests. + * + * @package tool_cohortroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +use tool_cohortroles\api; + +/** + * API tests. + * + * @package tool_cohortroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class tool_cohortroles_api_testcase extends advanced_testcase { + /** @var stdClass $cohort */ + protected $cohort = null; + + /** @var stdClass $userassignto */ + protected $userassignto = null; + + /** @var stdClass $userassignover */ + protected $userassignover = null; + + /** @var stdClass $role */ + protected $role = null; + + /** + * Setup function- we will create a course and add an assign instance to it. + */ + protected function setUp() { + global $DB; + + $this->resetAfterTest(true); + + // Create some users. + $this->cohort = $this->getDataGenerator()->create_cohort(); + $this->userassignto = $this->getDataGenerator()->create_user(); + $this->userassignover = $this->getDataGenerator()->create_user(); + $this->roleid = create_role('Sausage Roll', 'sausageroll', 'mmmm'); + cohort_add_member($this->cohort->id, $this->userassignover->id); + } + + + public function test_create_cohort_role_assignment_without_permission() { + $this->setExpectedException('required_capability_exception'); + $this->setUser($this->userassignto); + $params = (object) array( + 'userid' => $this->userassignto->id, + 'roleid' => $this->roleid, + 'cohortid' => $this->cohort->id + ); + api::create_cohort_role_assignment($params); + } + + public function test_create_cohort_role_assignment_with_invalid_data() { + $this->setExpectedException('tool_lp\invalid_persistent_exception'); + $this->setAdminUser(); + $params = (object) array( + 'userid' => $this->userassignto->id, + 'roleid' => -8, + 'cohortid' => $this->cohort->id + ); + api::create_cohort_role_assignment($params); + } + + public function test_create_cohort_role_assignment() { + $this->setAdminUser(); + $params = (object) array( + 'userid' => $this->userassignto->id, + 'roleid' => $this->roleid, + 'cohortid' => $this->cohort->id + ); + $result = api::create_cohort_role_assignment($params); + $this->assertNotEmpty($result->get_id()); + $this->assertEquals($result->get_userid(), $this->userassignto->id); + $this->assertEquals($result->get_roleid(), $this->roleid); + $this->assertEquals($result->get_cohortid(), $this->cohort->id); + } + + public function test_delete_cohort_role_assignment_without_permission() { + $this->setAdminUser(); + $params = (object) array( + 'userid' => $this->userassignto->id, + 'roleid' => $this->roleid, + 'cohortid' => $this->cohort->id + ); + $result = api::create_cohort_role_assignment($params); + $this->setExpectedException('required_capability_exception'); + $this->setUser($this->userassignto); + api::delete_cohort_role_assignment($result->get_id()); + } + + public function test_delete_cohort_role_assignment_with_invalid_data() { + $this->setAdminUser(); + $params = (object) array( + 'userid' => $this->userassignto->id, + 'roleid' => $this->roleid, + 'cohortid' => $this->cohort->id + ); + $result = api::create_cohort_role_assignment($params); + $this->setExpectedException('dml_missing_record_exception'); + api::delete_cohort_role_assignment($result->get_id() + 1); + } + + public function test_delete_cohort_role_assignment() { + $this->setAdminUser(); + $params = (object) array( + 'userid' => $this->userassignto->id, + 'roleid' => $this->roleid, + 'cohortid' => $this->cohort->id + ); + $result = api::create_cohort_role_assignment($params); + $worked = api::delete_cohort_role_assignment($result->get_id()); + $this->assertTrue($worked); + } + + public function test_list_cohort_role_assignments() { + $this->setAdminUser(); + $params = (object) array( + 'userid' => $this->userassignto->id, + 'roleid' => $this->roleid, + 'cohortid' => $this->cohort->id + ); + $result = api::create_cohort_role_assignment($params); + + $list = api::list_cohort_role_assignments(); + $list[0]->is_valid(); + $this->assertEquals($list[0], $result); + } + + public function test_count_cohort_role_assignments() { + $this->setAdminUser(); + $params = (object) array( + 'userid' => $this->userassignto->id, + 'roleid' => $this->roleid, + 'cohortid' => $this->cohort->id + ); + $result = api::create_cohort_role_assignment($params); + + $count = api::count_cohort_role_assignments(); + $this->assertEquals($count, 1); + } + + public function test_sync_all_cohort_roles() { + $this->setAdminUser(); + $params = (object) array( + 'userid' => $this->userassignto->id, + 'roleid' => $this->roleid, + 'cohortid' => $this->cohort->id + ); + $result = api::create_cohort_role_assignment($params); + + // Verify roles are assigned when users enter the cohort. + $sync = api::sync_all_cohort_roles(); + + $rolesadded = array(array( + 'useridassignedto' => $this->userassignto->id, + 'useridassignedover' => $this->userassignover->id, + 'roleid' => $this->roleid + )); + $rolesremoved = array(); + $expected = array('rolesadded' => $rolesadded, + 'rolesremoved' => $rolesremoved); + $this->assertEquals($sync, $expected); + + // Verify roles are removed when users leave the cohort. + cohort_remove_member($this->cohort->id, $this->userassignover->id); + $sync = api::sync_all_cohort_roles(); + + $rolesadded = array(); + $rolesremoved = array(array( + 'useridassignedto' => $this->userassignto->id, + 'useridassignedover' => $this->userassignover->id, + 'roleid' => $this->roleid + )); + $expected = array('rolesadded' => $rolesadded, + 'rolesremoved' => $rolesremoved); + $this->assertEquals($sync, $expected); + + // Verify roles assigned by any other component are not removed. + $usercontext = context_user::instance($this->userassignover->id); + role_assign($this->roleid, $this->userassignto->id, $usercontext->id); + $sync = api::sync_all_cohort_roles(); + + $rolesadded = array(); + $rolesremoved = array(); + $expected = array('rolesadded' => $rolesadded, + 'rolesremoved' => $rolesremoved); + $this->assertEquals($sync, $expected); + + // Remove manual role assignment. + role_unassign($this->roleid, $this->userassignto->id, $usercontext->id); + // Add someone to the cohort again... + cohort_add_member($this->cohort->id, $this->userassignover->id); + $sync = api::sync_all_cohort_roles(); + $rolesadded = array(array( + 'useridassignedto' => $this->userassignto->id, + 'useridassignedover' => $this->userassignover->id, + 'roleid' => $this->roleid + )); + $rolesremoved = array(); + $expected = array('rolesadded' => $rolesadded, + 'rolesremoved' => $rolesremoved); + $this->assertEquals($sync, $expected); + + // Verify no fatal errors when a cohort is deleted. + cohort_delete_cohort($this->cohort); + $sync = api::sync_all_cohort_roles(); + + $rolesadded = array(); + $rolesremoved = array(array( + 'useridassignedto' => $this->userassignto->id, + 'useridassignedover' => $this->userassignover->id, + 'roleid' => $this->roleid + )); + $expected = array('rolesadded' => $rolesadded, + 'rolesremoved' => $rolesremoved); + $this->assertEquals($sync, $expected); + } + +} diff --git a/admin/tool/cohortroles/version.php b/admin/tool/cohortroles/version.php new file mode 100644 index 00000000000..bf9fb21f408 --- /dev/null +++ b/admin/tool/cohortroles/version.php @@ -0,0 +1,34 @@ +. + +/** + * Plugin version info + * + * @package tool_cohortroles + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + + +$plugin->version = 2015111018; // The current plugin version (Date: YYYYMMDDXX). +$plugin->requires = 2014110400; // Requires this Moodle version. +$plugin->component = 'tool_cohortroles'; // Full name of the plugin (used for diagnostics). + +$plugin->dependencies = array( + 'tool_lp' => ANY_VERSION +); diff --git a/admin/tool/lp/templates/form-cohort-selector-suggestion.mustache b/admin/tool/lp/templates/form-cohort-selector-suggestion.mustache index 881a56bdb29..fafe24f89c6 100644 --- a/admin/tool/lp/templates/form-cohort-selector-suggestion.mustache +++ b/admin/tool/lp/templates/form-cohort-selector-suggestion.mustache @@ -45,5 +45,5 @@ {{#idnumber}} {{idnumber}} {{/idnumber}} - {{contextname}} + ( {{contextname}} )