MDL-29695 enrol: cohort: Add pagination and search to quickenrolment interface
authorRuslan Kabalin <ruslan.kabalin@luns.net.uk>
Tue, 30 Aug 2011 16:12:14 +0000 (17:12 +0100)
committerSam Hemelryk <sam@moodle.com>
Mon, 17 Oct 2011 21:54:51 +0000 (10:54 +1300)
enrol/cohort/ajax.php
enrol/cohort/lang/en/enrol_cohort.php
enrol/cohort/lib.php
enrol/cohort/locallib.php
enrol/cohort/yui/quickenrolment/assets/skins/sam/quickenrolment.css
enrol/cohort/yui/quickenrolment/quickenrolment.js

index ccb8412..87d319b 100644 (file)
@@ -70,7 +70,9 @@ switch ($action) {
         break;
     case 'getcohorts':
         require_capability('moodle/course:enrolconfig', $context);
-        $outcome->response = enrol_cohort_get_cohorts($manager);
+        $offset = optional_param('offset', 0, PARAM_INT);
+        $search  = optional_param('search', '', PARAM_RAW);
+        $outcome->response = enrol_cohort_search_cohorts($manager, $offset, 25, $search);
         break;
     case 'enrolcohort':
         require_capability('moodle/course:enrolconfig', $context);
@@ -79,8 +81,7 @@ switch ($action) {
         $cohortid = required_param('cohortid', PARAM_INT);
         
         $roles = $manager->get_assignable_roles();
-        $cohorts = enrol_cohort_get_cohorts($manager);
-        if (!array_key_exists($cohortid, $cohorts) || !array_key_exists($roleid, $roles)) {
+        if (!enrol_cohort_can_view_cohort($cohortid) || !array_key_exists($roleid, $roles)) {
             throw new enrol_ajax_exception('errorenrolcohort');
         }
         $enrol = enrol_get_plugin('cohort');
@@ -94,8 +95,7 @@ switch ($action) {
         $result = enrol_cohort_enrol_all_users($manager, $cohortid, $roleid);
 
         $roles = $manager->get_assignable_roles();
-        $cohorts = enrol_cohort_get_cohorts($manager);
-        if (!array_key_exists($cohortid, $cohorts) || !array_key_exists($roleid, $roles)) {
+        if (!enrol_cohort_can_view_cohort($cohortid) || !array_key_exists($roleid, $roles)) {
             throw new enrol_ajax_exception('errorenrolcohort');
         }
         if ($result === false) {
index 740834c..a2dcbf4 100644 (file)
@@ -24,6 +24,8 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+$string['ajaxmore'] = 'More...';
+$string['cohortsearch'] = 'Search';
 $string['cohort:config'] = 'Configure cohort instances';
 $string['pluginname'] = 'Cohort sync';
 $string['pluginname_desc'] = 'Cohort enrolment plugin synchronises cohort members with course participants.';
index d8cd4e1..11c22f7 100644 (file)
@@ -157,7 +157,16 @@ class enrol_cohort_plugin extends enrol_plugin {
         $button = new enrol_user_button($cohorturl, get_string('enrolcohort', 'enrol'), 'get');
         $button->class .= ' enrol_cohort_plugin';
 
-        $button->strings_for_js(array('enrol','synced','enrolcohort','enrolcohortusers'), 'enrol');
+        $button->strings_for_js(array(
+            'enrol',
+            'synced',
+            'enrolcohort',
+            'enrolcohortusers',
+            ), 'enrol');
+        $button->strings_for_js(array(
+            'ajaxmore',
+            'cohortsearch',
+            ), 'enrol_cohort');
         $button->strings_for_js('assignroles', 'role');
         $button->strings_for_js('cohort', 'cohort');
         $button->strings_for_js('users', 'moodle');
index 5a37f44..c19b52a 100644 (file)
@@ -244,6 +244,7 @@ function enrol_cohort_enrol_all_users(course_enrolment_manager $manager, $cohort
  * Gets all the cohorts the user is able to view.
  *
  * @global moodle_database $DB
+ * @param course_enrolment_manager $manager
  * @return array
  */
 function enrol_cohort_get_cohorts(course_enrolment_manager $manager) {
@@ -277,4 +278,101 @@ function enrol_cohort_get_cohorts(course_enrolment_manager $manager) {
     }
     $rs->close();
     return $cohorts;
+}
+
+/**
+ * Check if cohort exists and user is allowed to enrol it
+ *
+ * @global moodle_database $DB
+ * @param int $cohortid Cohort ID
+ * @return boolean
+ */
+function enrol_cohort_can_view_cohort($cohortid) {
+    global $DB;
+    $cohort = $DB->get_record_select('cohort', 'id = ?', array($cohortid));
+
+    if ($cohort) {
+        $context = get_context_instance_by_id($cohort->contextid);
+        if (has_capability('moodle/cohort:view', $context)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/**
+ * Gets cohorts the user is able to view.
+ *
+ * @global moodle_database $DB
+ * @param course_enrolment_manager $manager
+ * @param int $offset limit output from
+ * @param int $limit items to output per load
+ * @param string $search search string
+ * @return array    Array(more => bool, offset => int, cohorts => array)
+ */
+function enrol_cohort_search_cohorts(course_enrolment_manager $manager, $offset = 0, $limit = 25, $search = '') {
+    global $DB;
+    $context = $manager->get_context();
+    $cohorts = array();
+    $instances = $manager->get_enrolment_instances();
+    $enrolled = array();
+    foreach ($instances as $instance) {
+        if ($instance->enrol == 'cohort') {
+            $enrolled[] = $instance->customint1;
+        }
+    }
+
+    list($sqlparents, $params) = $DB->get_in_or_equal(get_parent_contexts($context));
+
+    // Add some additional sensible conditions
+    $tests = array('contextid ' . $sqlparents);
+
+    // Modify the quesry to perform the search if requred
+    if (!empty($search)) {
+        $conditions = array(
+            'name',
+            'idnumber',
+            'description'
+        );
+        $searchparam = '%' . $search . '%';
+        foreach ($conditions as $key=>$condition) {
+            $conditions[$key] = $DB->sql_like($condition,"?", false);
+            $params[] = $searchparam;
+        }
+        $tests[] = '(' . implode(' OR ', $conditions) . ')';
+    }
+    $wherecondition = implode(' AND ', $tests);
+
+    $fields = 'SELECT id, name, contextid, description';
+    $countfields = 'SELECT COUNT(1)';
+    $sql = " FROM {cohort}
+             WHERE $wherecondition";
+    $order = ' ORDER BY name ASC';
+    $rs = $DB->get_recordset_sql($fields . $sql . $order, $params, $offset);
+
+    // Produce the output respecting parameters
+    foreach ($rs as $c) {
+        // Track offset
+        $offset++;
+        // Check capabilities
+        $context = get_context_instance_by_id($c->contextid);
+        if (!has_capability('moodle/cohort:view', $context)) {
+            continue;
+        }
+        if ($limit === 0) {
+            // we have reached the required number of items and know that there are more, exit now
+            $offset--;
+            break;
+        }
+        $cohorts[$c->id] = array(
+            'cohortid'=>$c->id,
+            'name'=>  shorten_text(format_string($c->name), 35),
+            'users'=>$DB->count_records('cohort_members', array('cohortid'=>$c->id)),
+            'enrolled'=>in_array($c->id, $enrolled)
+        );
+        // Count items
+        $limit--;
+    }
+    $rs->close();
+    return array('more' => !(bool)$limit, 'offset' => $offset, 'cohorts' => $cohorts);
 }
\ No newline at end of file
index 916adcb..540ed78 100644 (file)
@@ -1,25 +1,36 @@
-.qce-panel {background-color:#666;border:2px solid #666;border-width:0 2px 2px 0;width:420px;}
+.qce-panel {background-color:#666;border:2px solid #666;border-width:0 2px 2px 0;min-width:420px;}
 .qce-panel .yui3-widget-hd {background:url("sprite.png");background-repeat:repeat-x;background-color:#DDD;background-position: 0 -15px;border-bottom:1px solid #555;border-top:1px solid #fff;}
 .qce-panel .yui3-widget-hd h2 {margin:3px 5px 2px;padding:0;font-size:110%;}
 .qce-panel .yui3-widget-hd .close {width:25px;height:15px;position:absolute;top:3px;right:1em;cursor:pointer;background:url("sprite.png") no-repeat scroll 0 0 transparent;}
 .qce-panel .yui3-overlay-content {background-color:#F6F6F6;border:1px solid #555;margin-top:-2px;margin-left:-2px;}
-.qce-panel .yui3-widget-bd .loading {height:300px;text-align:center;}
-.qce-panel .yui3-widget-bd .loading img {margin-top:130px;}
-.qce-panel .qce-enrollable-cohorts {overflow:auto;margin:5px;}
-.qce-panel .qce-cohorts {border:1px solid #666;width:408px;background-color:#FFF;height:250px;}
-.qce-panel .qce-cohort {width:100%;position:relative;clear:both;height:24px;}
+.qce-panel .qce-enrollable-cohorts {margin:5px;}
+.qce-panel .qce-cohorts {border:1px solid #666;min-width:408px;background-color:#FFF;height:375px;overflow:auto;}
+.qce-panel .qce-cohorts .qce-more-results {background-color:#eee;padding:5px;border-top:1px solid #BBB;text-align:center;}
+
+.qce-panel .qce-panel-content {min-height:405px;}
+.qce-panel .qce-panel-content .qce-loading-lightbox {position:absolute;width:100%;height:100%;top:0;left:0;background-color:#FFF;min-height:264px;text-align:center;}
+.qce-panel .qce-panel-content .qce-loading-lightbox.hidden {display:none;}
+.qce-panel .qce-panel-content .qce-loading-lightbox .loading-icon {margin:auto;vertical-align:middle;margin-top:198px;}
+
+.qce-panel .qce-cohort {width:100%;position:relative;clear:both;height:24px;white-space:nowrap;}
+.qce-panel .qce-cohort div {display:inline-block;overflow:hidden;}
 .qce-panel .qce-cohort.odd {background-color:#f4f4f4;}
-.qce-panel .qce-cohort .qce-cohort-button {float:left;text-align:center;display:inline-block;font-size:80%;height:22px;line-height:22px;overflow:hidden;padding:0 4px;}
+.qce-panel .qce-cohort .qce-cohort-button {text-align:center;display:inline-block;font-size:80%;height:22px;line-height:22px;overflow:hidden;padding:0 4px;}
 .qce-panel .qce-cohort .qce-cohort-button.notenrolled {background-color:#ddd;border:1px outset #CCC;background:url("sprite.png");background-repeat:repeat-x;background-color:#DDD;background-position: 0 -25px;color:inherit;}
 .qce-panel .qce-cohort .qce-cohort-button.notenrolled:hover {background-position:0 -15px;cursor:pointer;}
 .qce-panel .qce-cohort .qce-cohort-button.notenrolled.enrolusers {margin-right: 4px;}
-.qce-panel .qce-cohort .qce-cohort-button.alreadyenrolled {font-weight:bold;margin:2px;}
-.qce-panel .qce-cohort .qce-cohort-name {margin-left:80px;margin-right:40px;line-height:24px;}
-.qce-panel .canenrolusers .qce-cohort .qce-cohort-button.alreadyenrolled {width:140px;}
-.qce-panel .canenrolusers .qce-cohort .qce-cohort-name {margin-left:150px;margin-right:40px;line-height:24px;}
-.qce-panel .qce-cohort .qce-cohort-users {position:absolute;right:5px;top:0;width:30px;text-align:right;height:24px;line-height:24px;}
-.qce-panel .qce-assignable-roles {margin:3px 5px 2px;}
+.qce-panel .qce-cohort .qce-cohort-button.alreadyenrolled {font-weight:bold;}
+.qce-panel .qce-cohort .qce-cohort-name {line-height:24px;width:220px;}
+.qce-panel .canenrolusers .qce-cohort .qce-cohort-button.alreadyenrolled {width:125px;}
+.qce-panel .canenrolusers .qce-cohort .qce-cohort-name {line-height:24px;}
+.qce-panel .qce-cohort .qce-cohort-users {width:30px;text-align:right;height:24px;line-height:24px;margin-right:4px}
+
+.qce-panel .qce-assignable-roles {margin:5px 6px 0 6px;}
+.qce-panel .qce-assignable-roles label {padding-right:8px;}
+
+.qce-panel .qce-footer {margin:0 6px 5px 6px;}
+.qce-panel .qce-search label {padding-right:8px;}
+.qce-panel .qce-search input {width:70%;}
+
 .qce-panel .qce-cohort.headings {font-weight:bold;border-width:0;}
-.qce-panel .qce-cohort.headings .qce-cohort-button {display:none;}
-.qce-panel .performing-action {position:absolute;top:0;left:0;width:100%;height:100%;background-color:#fff;text-align:center;}
-.qce-panel .performing-action img {margin-top:150px;}
\ No newline at end of file
+.qce-panel .qce-cohort.headings .qce-cohort-button {display:none;}
\ No newline at end of file
index 6607566..7f5437e 100644 (file)
@@ -10,7 +10,12 @@ YUI.add('moodle-enrol_cohort-quickenrolment', function(Y) {
         ASSIGNABLEROLES = 'assignableRoles',
         DEFAULTCOHORTROLE = 'defaultCohortRole',
         COHORTS = 'cohorts',
+        MORERESULTS = 'moreresults',
+        FIRSTPAGE = 'firstpage',
+        OFFSET = 'offset',
         PANELID = 'qce-panel-',
+        REQUIREREFRESH = 'requiresRefresh',
+        SEARCH = 'search',
         URL = 'url',
         AJAXURL = 'ajaxurl',
         MANUALENROLMENT = 'manualEnrolment',
@@ -21,12 +26,18 @@ YUI.add('moodle-enrol_cohort-quickenrolment', function(Y) {
             COHORTENROLLED : 'qce-cohort-enrolled',
             COHORTNAME : 'qce-cohort-name',
             COHORTUSERS : 'qce-cohort-users',
+            ENROLUSERS : 'canenrolusers',
+            FOOTER : 'qce-footer',
+            HIDDEN : 'hidden',
+            LIGHTBOX : 'qce-loading-lightbox',
+            LOADINGICON : 'loading-icon',
+            MORERESULTS : 'qce-more-results',
             PANEL : 'qce-panel',
             PANELCONTENT : 'qce-panel-content',
             PANELCOHORTS : 'qce-enrollable-cohorts',
             PANELROLES : 'qce-assignable-roles',
             PANELCONTROLS : 'qce-panel-controls',
-            ENROLUSERS : 'canenrolusers'
+            SEARCH : 'qce-search'
         },
         COUNT = 0;
 
@@ -35,24 +46,47 @@ YUI.add('moodle-enrol_cohort-quickenrolment', function(Y) {
         CONTROLLER.superclass.constructor.apply(this, arguments);
     };
     CONTROLLER.prototype = {
-        _preformingAction : false,
         initializer : function(config) {
             COUNT ++;
             this.publish('assignablerolesloaded', {fireOnce:true});
             this.publish('cohortsloaded');
-            this.publish('performingaction');
-            this.publish('actioncomplete');
             this.publish('defaultcohortroleloaded', {fireOnce:true});
 
+            var base = Y.Node.create('<div class="'+CSS.PANELCONTENT+'"></div>')
+                .append(Y.Node.create('<div class="'+CSS.PANELROLES+'"></div>'))
+                .append(Y.Node.create('<div class="'+CSS.PANELCOHORTS+'"></div>'))
+                .append(Y.Node.create('<div class="'+CSS.FOOTER+'"></div>')
+                    .append(Y.Node.create('<div class="'+CSS.SEARCH+'"><label>'+M.str.enrol_cohort.cohortsearch+':</label></div>')
+                        .append(Y.Node.create('<input type="text" id="enrolcohortsearch" value="" />'))
+                    )
+                )
+                .append(Y.Node.create('<div class="'+CSS.LIGHTBOX+' '+CSS.HIDDEN+'"></div>')
+                    .append(Y.Node.create('<img alt="loading" class="'+CSS.LOADINGICON+'" />')
+                        .setAttribute('src', M.util.image_url('i/loading', 'moodle')))
+                    .setStyle('opacity', 0.5)
+                );
+
             var close = Y.Node.create('<div class="close"></div>');
             var panel = new Y.Overlay({
                 headerContent : Y.Node.create('<div></div>').append(Y.Node.create('<h2>'+M.str.enrol.enrolcohort+'</h2>')).append(close),
-                bodyContent : Y.Node.create('<div class="loading"></div>').append(Y.Node.create('<img alt="loading" />').setAttribute('src', M.cfg.loadingicon)),
+                bodyContent : base,
                 constrain : true,
                 centered : true,
                 id : PANELID+COUNT,
                 visible : false
             });
+
+            // display the wheel on ajax events
+            Y.on('io:start', function() {
+                base.one('.'+CSS.LIGHTBOX).removeClass(CSS.HIDDEN);
+            }, this);
+            Y.on('io:end', function() {
+                base.one('.'+CSS.LIGHTBOX).addClass(CSS.HIDDEN);
+            }, this);
+
+            this.set(SEARCH, base.one('#enrolcohortsearch'));
+            Y.on('key', this.getCohorts, this.get(SEARCH), 'down:13', this, false);
+
             panel.get('boundingBox').addClass(CSS.PANEL);
             panel.render(Y.one(document.body));
             this.on('show', function(){
@@ -60,15 +94,10 @@ YUI.add('moodle-enrol_cohort-quickenrolment', function(Y) {
                 this.show();
             }, panel);
             this.on('hide', panel.hide, panel);
-            this.on('performingaction', function(){
-                this.get('boundingBox').append(Y.Node.create('<div class="performing-action"></div>').append(Y.Node.create('<img alt="loading" />').setAttribute('src', M.cfg.loadingicon)).setStyle('opacity', 0.5));
-            }, panel);
-            this.on('actioncomplete', function(){
-                this.get('boundingBox').one('.performing-action').remove();
-            }, panel);
             this.on('assignablerolesloaded', this.updateContent, this, panel);
             this.on('cohortsloaded', this.updateContent, this, panel);
             this.on('defaultcohortroleloaded', this.updateContent, this, panel);
+            Y.on('key', this.hide, document.body, 'down:27', this);
             close.on('click', this.hide, this);
 
             Y.all('.enrol_cohort_plugin input').each(function(node){
@@ -77,38 +106,51 @@ YUI.add('moodle-enrol_cohort-quickenrolment', function(Y) {
                 }
             }, this);
 
-            var base = panel.get('boundingBox');
+            base = panel.get('boundingBox');
             base.plug(Y.Plugin.Drag);
             base.dd.addHandle('.yui3-widget-hd h2');
             base.one('.yui3-widget-hd h2').setStyle('cursor', 'move');
         },
         show : function(e) {
             e.preventDefault();
-            this.getCohorts();
+            // prepare the data and display the window
+            this.getCohorts(e, false);
             this.getAssignableRoles();
             this.fire('show');
         },
         updateContent : function(e, panel) {
-            if (panel.get('contentBox').one('.loading')) {
-                panel.set('bodyContent', Y.Node.create('<div class="'+CSS.PANELCONTENT+'"></div>')
-                    .append(Y.Node.create('<div class="'+CSS.PANELCOHORTS+'"><div class="'+CSS.COHORT+' headings"><div class="'+CSS.COHORTBUTTON+'"></div><div class="'+CSS.COHORTNAME+'">'+M.str.cohort.cohort+'</div><div class="'+CSS.COHORTUSERS+'">'+M.str.moodle.users+'</div></div></div>'))
-                    .append(Y.Node.create('<div class="'+CSS.PANELROLES+'"></div>')));
-            }
             var content, i, roles, cohorts, count=0, supportmanual = this.get(MANUALENROLMENT), defaultrole;
             switch (e.type.replace(/^[^:]+:/, '')) {
                 case 'cohortsloaded' :
-                    cohorts = this.get(COHORTS);
-                    content = Y.Node.create('<div class="'+CSS.COHORTS+'"></div>');
-                    if (supportmanual) {
-                        content.addClass(CSS.ENROLUSERS);
+                    if (this.get(FIRSTPAGE)) {
+                        // we are on the page 0, create new element for cohorts list
+                        content = Y.Node.create('<div class="'+CSS.COHORTS+'"></div>');
+                        if (supportmanual) {
+                            content.addClass(CSS.ENROLUSERS);
+                        }
+                    } else {
+                        // we are adding cohorts to existing list
+                        content = Y.Node.one('.'+CSS.PANELCOHORTS+' .'+CSS.COHORTS);
+                        content.one('.'+CSS.MORERESULTS).remove();
                     }
+                    // add cohorts items to the content
+                    cohorts = this.get(COHORTS);
                     for (i in cohorts) {
                         count++;
                         cohorts[i].on('enrolchort', this.enrolCohort, this, cohorts[i], panel.get('contentBox'), false);
                         cohorts[i].on('enrolusers', this.enrolCohort, this, cohorts[i], panel.get('contentBox'), true);
                         content.append(cohorts[i].toHTML(supportmanual).addClass((count%2)?'even':'odd'));
                     }
-                    panel.get('contentBox').one('.'+CSS.PANELCOHORTS).setContent(content);
+                    // add the next link if there are more items expected
+                    if (this.get(MORERESULTS)) {
+                        var fetchmore = Y.Node.create('<div class="'+CSS.MORERESULTS+'"><a href="#">'+M.str.enrol_cohort.ajaxmore+'</a></div>');
+                        fetchmore.on('click', this.getCohorts, this, true);
+                        content.append(fetchmore);
+                    }
+                    // finally assing the content to the block
+                    if (this.get(FIRSTPAGE)) {
+                        panel.get('contentBox').one('.'+CSS.PANELCOHORTS).setContent(content);
+                    }
                     break;
                 case 'assignablerolesloaded':
                     roles = this.get(ASSIGNABLEROLES);
@@ -116,7 +158,7 @@ YUI.add('moodle-enrol_cohort-quickenrolment', function(Y) {
                     for (i in roles) {
                         content.append(Y.Node.create('<option value="'+i+'">'+roles[i]+'</option>'));
                     }
-                    panel.get('contentBox').one('.'+CSS.PANELROLES).setContent(Y.Node.create('<div>'+M.str.role.assignroles+': </div>').append(content));
+                    panel.get('contentBox').one('.'+CSS.PANELROLES).setContent(Y.Node.create('<div><label>'+M.str.role.assignroles+':</label></div>').append(content));
 
                     this.getDefaultCohortRole();
                     break;
@@ -127,12 +169,31 @@ YUI.add('moodle-enrol_cohort-quickenrolment', function(Y) {
             }
         },
         hide : function() {
+            if (this.get(REQUIREREFRESH)) {
+                window.location = this.get(URL);
+            }
             this.fire('hide');
         },
-        getCohorts : function() {
+        getCohorts : function(e, append) {
+            if (e) {
+                e.preventDefault();
+            }
+            if (append) {
+                this.set(FIRSTPAGE, false);
+            } else {
+                this.set(FIRSTPAGE, true);
+                this.set(OFFSET, 0);
+            }
+            var params = [];
+            params['id'] = this.get(COURSEID);
+            params['offset'] = this.get(OFFSET);
+            params['search'] = this.get(SEARCH).get('value');
+            params['action'] = 'getcohorts';
+            params['sesskey'] = M.cfg.sesskey;
+
             Y.io(M.cfg.wwwroot+this.get(AJAXURL), {
                 method:'POST',
-                data:'id='+this.get(COURSEID)+'&action=getcohorts&sesskey='+M.cfg.sesskey,
+                data:build_querystring(params),
                 on: {
                     complete: function(tid, outcome, args) {
                         try {
@@ -145,16 +206,16 @@ YUI.add('moodle-enrol_cohort-quickenrolment', function(Y) {
                         } catch (e) {
                             return new M.core.exception(e);
                         }
-                        this.getCohorts = function() {
-                            this.fire('cohortsloaded');
-                        };
-                        this.getCohorts();
+                        this.fire('cohortsloaded');
                     }
                 },
                 context:this
             });
         },
-        setCohorts : function(rawcohorts) {
+        setCohorts : function(response) {
+            this.set(MORERESULTS, response.more);
+            this.set(OFFSET, response.offset);
+            var rawcohorts = response.cohorts;
             var cohorts = [], i=0;
             for (i in rawcohorts) {
                 cohorts[rawcohorts[i].cohortid] = new COHORT(rawcohorts[i]);
@@ -201,11 +262,6 @@ YUI.add('moodle-enrol_cohort-quickenrolment', function(Y) {
             });
         },
         enrolCohort : function(e, cohort, node, usersonly) {
-            if (this._preformingAction) {
-                return true;
-            }
-            this._preformingAction = true;
-            this.fire('performingaction');
             var params = {
                 id : this.get(COURSEID),
                 roleid : node.one('.'+CSS.PANELROLES+' select').get('value'),
@@ -223,24 +279,13 @@ YUI.add('moodle-enrol_cohort-quickenrolment', function(Y) {
                             if (result.error) {
                                 new M.core.ajaxException(result);
                             } else {
-                                var redirecturl = this.get(URL), redirect = function() {
-                                    if (!usersonly || result.response.users) {
-                                        Y.one(document.body).append(
-                                            Y.Node.create('<div class="corelightbox"></div>')
-                                                .setStyle('height', Y.one(document.body).get('docHeight')+'px')
-                                                .setStyle('opacity', '0.4')
-                                                .append(Y.Node.create('<img alt="loading" />').setAttribute('src', M.cfg.loadingicon)));
-                                        window.location.href = redirecturl;
-                                    }
-                                };
                                 if (result.response && result.response.message) {
-                                    new M.core.alert(result.response).on('complete', redirect, this);
-                                } else {
-                                    redirect();
+                                    new M.core.alert(result.response);
                                 }
+                                var enrolled = Y.Node.create('<div class="'+CSS.COHORTBUTTON+' alreadyenrolled">'+M.str.enrol.synced+'</div>');
+                                node.one('.'+CSS.COHORT+' #cohortid_'+cohort.get(COHORTID)).replace(enrolled);
+                                this.set(REQUIREREFRESH, true);
                             }
-                            this._preformingAction = false;
-                            this.fire('actioncomplete');
                         } catch (e) {
                             new M.core.exception(e);
                         }
@@ -275,6 +320,10 @@ YUI.add('moodle-enrol_cohort-quickenrolment', function(Y) {
             },
             defaultCohortRole : {
                 value : null
+            },
+            requiresRefresh : {
+                value : false,
+                validator : Y.Lang.isBool
             }
         }
     });
@@ -290,7 +339,7 @@ YUI.add('moodle-enrol_cohort-quickenrolment', function(Y) {
             if (this.get(ENROLLED)) {
                 button = Y.Node.create('<div class="'+CSS.COHORTBUTTON+' alreadyenrolled">'+M.str.enrol.synced+'</div>');
             } else {
-                button = Y.Node.create('<div></div>');
+                button = Y.Node.create('<div id="cohortid_'+this.get(COHORTID)+'"></div>');
 
                 syncbutton = Y.Node.create('<a class="'+CSS.COHORTBUTTON+' notenrolled enrolcohort">'+M.str.enrol.enrolcohort+'</a>');
                 syncbutton.on('click', function(){this.fire('enrolchort');}, this);
@@ -304,9 +353,6 @@ YUI.add('moodle-enrol_cohort-quickenrolment', function(Y) {
             }
             name = Y.Node.create('<div class="'+CSS.COHORTNAME+'">'+this.get(NAME)+'</div>');
             users = Y.Node.create('<div class="'+CSS.COHORTUSERS+'">'+this.get(USERS)+'</div>');
-            if (this.get(ENROLLED)) {
-                button.one(CSS.COHORTENROLLED);
-            }
             return result.append(button).append(name).append(users);
         }
     }, {