Search moodle.org's
Developer Documentation

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.
  • /cohort/ -> lib.php (source)

    Differences Between: [Versions 310 and 34] [Versions 310 and 35] [Versions 34 and 310] [Versions 35 and 310]

       1  <?php
       2  // This file is part of Moodle - http://moodle.org/
       3  //
       4  // Moodle is free software: you can redistribute it and/or modify
       5  // it under the terms of the GNU General Public License as published by
       6  // the Free Software Foundation, either version 3 of the License, or
       7  // (at your option) any later version.
       8  //
       9  // Moodle is distributed in the hope that it will be useful,
      10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12  // GNU General Public License for more details.
      13  //
      14  // You should have received a copy of the GNU General Public License
      15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
      16  
      17  /**
      18   * Cohort related management functions, this file needs to be included manually.
      19   *
      20   * @package    core_cohort
      21   * @copyright  2010 Petr Skoda  {@link http://skodak.org}
      22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      23   */
      24  
      25  defined('MOODLE_INTERNAL') || die();
      26  
      27  define('COHORT_ALL', 0);
      28  define('COHORT_COUNT_MEMBERS', 1);
      29  define('COHORT_COUNT_ENROLLED_MEMBERS', 3);
      30  define('COHORT_WITH_MEMBERS_ONLY', 5);
      31  define('COHORT_WITH_ENROLLED_MEMBERS_ONLY', 17);
      32  define('COHORT_WITH_NOTENROLLED_MEMBERS_ONLY', 23);
      33  
      34  /**
      35   * Add new cohort.
      36   *
      37   * @param  stdClass $cohort
      38   * @return int new cohort id
      39   */
      40  function cohort_add_cohort($cohort) {
      41      global $DB, $CFG;
      42  
      43      if (!isset($cohort->name)) {
      44          throw new coding_exception('Missing cohort name in cohort_add_cohort().');
      45      }
      46      if (!isset($cohort->idnumber)) {
      47          $cohort->idnumber = NULL;
      48      }
      49      if (!isset($cohort->description)) {
      50          $cohort->description = '';
      51      }
      52      if (!isset($cohort->descriptionformat)) {
      53          $cohort->descriptionformat = FORMAT_HTML;
      54      }
      55      if (!isset($cohort->visible)) {
      56          $cohort->visible = 1;
      57      }
      58      if (empty($cohort->component)) {
      59          $cohort->component = '';
      60      }
      61      if (empty($CFG->allowcohortthemes) && isset($cohort->theme)) {
      62          unset($cohort->theme);
      63      }
      64      if (empty($cohort->theme) || empty($CFG->allowcohortthemes)) {
      65          $cohort->theme = '';
      66      }
      67      if (!isset($cohort->timecreated)) {
      68          $cohort->timecreated = time();
      69      }
      70      if (!isset($cohort->timemodified)) {
      71          $cohort->timemodified = $cohort->timecreated;
      72      }
      73  
      74      $cohort->id = $DB->insert_record('cohort', $cohort);
      75  
      76      $event = \core\event\cohort_created::create(array(
      77          'context' => context::instance_by_id($cohort->contextid),
      78          'objectid' => $cohort->id,
      79      ));
      80      $event->add_record_snapshot('cohort', $cohort);
      81      $event->trigger();
      82  
      83      return $cohort->id;
      84  }
      85  
      86  /**
      87   * Update existing cohort.
      88   * @param  stdClass $cohort
      89   * @return void
      90   */
      91  function cohort_update_cohort($cohort) {
      92      global $DB, $CFG;
      93      if (property_exists($cohort, 'component') and empty($cohort->component)) {
      94          // prevent NULLs
      95          $cohort->component = '';
      96      }
      97      // Only unset the cohort theme if allowcohortthemes is enabled to prevent the value from being overwritten.
      98      if (empty($CFG->allowcohortthemes) && isset($cohort->theme)) {
      99          unset($cohort->theme);
     100      }
     101      $cohort->timemodified = time();
     102      $DB->update_record('cohort', $cohort);
     103  
     104      $event = \core\event\cohort_updated::create(array(
     105          'context' => context::instance_by_id($cohort->contextid),
     106          'objectid' => $cohort->id,
     107      ));
     108      $event->trigger();
     109  }
     110  
     111  /**
     112   * Delete cohort.
     113   * @param  stdClass $cohort
     114   * @return void
     115   */
     116  function cohort_delete_cohort($cohort) {
     117      global $DB;
     118  
     119      if ($cohort->component) {
     120          // TODO: add component delete callback
     121      }
     122  
     123      $DB->delete_records('cohort_members', array('cohortid'=>$cohort->id));
     124      $DB->delete_records('cohort', array('id'=>$cohort->id));
     125  
     126      // Notify the competency subsystem.
     127      \core_competency\api::hook_cohort_deleted($cohort);
     128  
     129      $event = \core\event\cohort_deleted::create(array(
     130          'context' => context::instance_by_id($cohort->contextid),
     131          'objectid' => $cohort->id,
     132      ));
     133      $event->add_record_snapshot('cohort', $cohort);
     134      $event->trigger();
     135  }
     136  
     137  /**
     138   * Somehow deal with cohorts when deleting course category,
     139   * we can not just delete them because they might be used in enrol
     140   * plugins or referenced in external systems.
     141   * @param  stdClass|core_course_category $category
     142   * @return void
     143   */
     144  function cohort_delete_category($category) {
     145      global $DB;
     146      // TODO: make sure that cohorts are really, really not used anywhere and delete, for now just move to parent or system context
     147  
     148      $oldcontext = context_coursecat::instance($category->id);
     149  
     150      if ($category->parent and $parent = $DB->get_record('course_categories', array('id'=>$category->parent))) {
     151          $parentcontext = context_coursecat::instance($parent->id);
     152          $sql = "UPDATE {cohort} SET contextid = :newcontext WHERE contextid = :oldcontext";
     153          $params = array('oldcontext'=>$oldcontext->id, 'newcontext'=>$parentcontext->id);
     154      } else {
     155          $syscontext = context_system::instance();
     156          $sql = "UPDATE {cohort} SET contextid = :newcontext WHERE contextid = :oldcontext";
     157          $params = array('oldcontext'=>$oldcontext->id, 'newcontext'=>$syscontext->id);
     158      }
     159  
     160      $DB->execute($sql, $params);
     161  }
     162  
     163  /**
     164   * Add cohort member
     165   * @param  int $cohortid
     166   * @param  int $userid
     167   * @return void
     168   */
     169  function cohort_add_member($cohortid, $userid) {
     170      global $DB;
     171      if ($DB->record_exists('cohort_members', array('cohortid'=>$cohortid, 'userid'=>$userid))) {
     172          // No duplicates!
     173          return;
     174      }
     175      $record = new stdClass();
     176      $record->cohortid  = $cohortid;
     177      $record->userid    = $userid;
     178      $record->timeadded = time();
     179      $DB->insert_record('cohort_members', $record);
     180  
     181      $cohort = $DB->get_record('cohort', array('id' => $cohortid), '*', MUST_EXIST);
     182  
     183      $event = \core\event\cohort_member_added::create(array(
     184          'context' => context::instance_by_id($cohort->contextid),
     185          'objectid' => $cohortid,
     186          'relateduserid' => $userid,
     187      ));
     188      $event->add_record_snapshot('cohort', $cohort);
     189      $event->trigger();
     190  }
     191  
     192  /**
     193   * Remove cohort member
     194   * @param  int $cohortid
     195   * @param  int $userid
     196   * @return void
     197   */
     198  function cohort_remove_member($cohortid, $userid) {
     199      global $DB;
     200      $DB->delete_records('cohort_members', array('cohortid'=>$cohortid, 'userid'=>$userid));
     201  
     202      $cohort = $DB->get_record('cohort', array('id' => $cohortid), '*', MUST_EXIST);
     203  
     204      $event = \core\event\cohort_member_removed::create(array(
     205          'context' => context::instance_by_id($cohort->contextid),
     206          'objectid' => $cohortid,
     207          'relateduserid' => $userid,
     208      ));
     209      $event->add_record_snapshot('cohort', $cohort);
     210      $event->trigger();
     211  }
     212  
     213  /**
     214   * Is this user a cohort member?
     215   * @param int $cohortid
     216   * @param int $userid
     217   * @return bool
     218   */
     219  function cohort_is_member($cohortid, $userid) {
     220      global $DB;
     221  
     222      return $DB->record_exists('cohort_members', array('cohortid'=>$cohortid, 'userid'=>$userid));
     223  }
     224  
     225  /**
     226   * Returns the list of cohorts visible to the current user in the given course.
     227   *
     228   * The following fields are returned in each record: id, name, contextid, idnumber, visible
     229   * Fields memberscnt and enrolledcnt will be also returned if requested
     230   *
     231   * @param context $currentcontext
     232   * @param int $withmembers one of the COHORT_XXX constants that allows to return non empty cohorts only
     233   *      or cohorts with enroled/not enroled users, or just return members count
     234   * @param int $offset
     235   * @param int $limit
     236   * @param string $search
     237   * @return array
     238   */
     239  function cohort_get_available_cohorts($currentcontext, $withmembers = 0, $offset = 0, $limit = 25, $search = '') {
     240      global $DB;
     241  
     242      $params = array();
     243  
     244      // Build context subquery. Find the list of parent context where user is able to see any or visible-only cohorts.
     245      // Since this method is normally called for the current course all parent contexts are already preloaded.
     246      $contextsany = array_filter($currentcontext->get_parent_context_ids(),
     247          function($a) {
     248              return has_capability("moodle/cohort:view", context::instance_by_id($a));
     249          });
     250      $contextsvisible = array_diff($currentcontext->get_parent_context_ids(), $contextsany);
     251      if (empty($contextsany) && empty($contextsvisible)) {
     252          // User does not have any permissions to view cohorts.
     253          return array();
     254      }
     255      $subqueries = array();
     256      if (!empty($contextsany)) {
     257          list($parentsql, $params1) = $DB->get_in_or_equal($contextsany, SQL_PARAMS_NAMED, 'ctxa');
     258          $subqueries[] = 'c.contextid ' . $parentsql;
     259          $params = array_merge($params, $params1);
     260      }
     261      if (!empty($contextsvisible)) {
     262          list($parentsql, $params1) = $DB->get_in_or_equal($contextsvisible, SQL_PARAMS_NAMED, 'ctxv');
     263          $subqueries[] = '(c.visible = 1 AND c.contextid ' . $parentsql. ')';
     264          $params = array_merge($params, $params1);
     265      }
     266      $wheresql = '(' . implode(' OR ', $subqueries) . ')';
     267  
     268      // Build the rest of the query.
     269      $fromsql = "";
     270      $fieldssql = 'c.id, c.name, c.contextid, c.idnumber, c.visible';
     271      $groupbysql = '';
     272      $havingsql = '';
     273      if ($withmembers) {
     274          $fieldssql .= ', s.memberscnt';
     275          $subfields = "c.id, COUNT(DISTINCT cm.userid) AS memberscnt";
     276          $groupbysql = " GROUP BY c.id";
     277          $fromsql = " LEFT JOIN {cohort_members} cm ON cm.cohortid = c.id ";
     278          if (in_array($withmembers,
     279                  array(COHORT_COUNT_ENROLLED_MEMBERS, COHORT_WITH_ENROLLED_MEMBERS_ONLY, COHORT_WITH_NOTENROLLED_MEMBERS_ONLY))) {
     280              list($esql, $params2) = get_enrolled_sql($currentcontext);
     281              $fromsql .= " LEFT JOIN ($esql) u ON u.id = cm.userid ";
     282              $params = array_merge($params2, $params);
     283              $fieldssql .= ', s.enrolledcnt';
     284              $subfields .= ', COUNT(DISTINCT u.id) AS enrolledcnt';
     285          }
     286          if ($withmembers == COHORT_WITH_MEMBERS_ONLY) {
     287              $havingsql = " HAVING COUNT(DISTINCT cm.userid) > 0";
     288          } else if ($withmembers == COHORT_WITH_ENROLLED_MEMBERS_ONLY) {
     289              $havingsql = " HAVING COUNT(DISTINCT u.id) > 0";
     290          } else if ($withmembers == COHORT_WITH_NOTENROLLED_MEMBERS_ONLY) {
     291              $havingsql = " HAVING COUNT(DISTINCT cm.userid) > COUNT(DISTINCT u.id)";
     292          }
     293      }
     294      if ($search) {
     295          list($searchsql, $searchparams) = cohort_get_search_query($search);
     296          $wheresql .= ' AND ' . $searchsql;
     297          $params = array_merge($params, $searchparams);
     298      }
     299  
     300      if ($withmembers) {
     301          $sql = "SELECT " . str_replace('c.', 'cohort.', $fieldssql) . "
     302                    FROM {cohort} cohort
     303                    JOIN (SELECT $subfields
     304                            FROM {cohort} c $fromsql
     305                           WHERE $wheresql $groupbysql $havingsql
     306                          ) s ON cohort.id = s.id
     307                ORDER BY cohort.name, cohort.idnumber";
     308      } else {
     309          $sql = "SELECT $fieldssql
     310                    FROM {cohort} c $fromsql
     311                   WHERE $wheresql
     312                ORDER BY c.name, c.idnumber";
     313      }
     314  
     315      return $DB->get_records_sql($sql, $params, $offset, $limit);
     316  }
     317  
     318  /**
     319   * Check if cohort exists and user is allowed to access it from the given context.
     320   *
     321   * @param stdClass|int $cohortorid cohort object or id
     322   * @param context $currentcontext current context (course) where visibility is checked
     323   * @return boolean
     324   */
     325  function cohort_can_view_cohort($cohortorid, $currentcontext) {
     326      global $DB;
     327      if (is_numeric($cohortorid)) {
     328          $cohort = $DB->get_record('cohort', array('id' => $cohortorid), 'id, contextid, visible');
     329      } else {
     330          $cohort = $cohortorid;
     331      }
     332  
     333      if ($cohort && in_array($cohort->contextid, $currentcontext->get_parent_context_ids())) {
     334          if ($cohort->visible) {
     335              return true;
     336          }
     337          $cohortcontext = context::instance_by_id($cohort->contextid);
     338          if (has_capability('moodle/cohort:view', $cohortcontext)) {
     339              return true;
     340          }
     341      }
     342      return false;
     343  }
     344  
     345  /**
     346   * Get a cohort by id. Also does a visibility check and returns false if the user cannot see this cohort.
     347   *
     348   * @param stdClass|int $cohortorid cohort object or id
     349   * @param context $currentcontext current context (course) where visibility is checked
     350   * @return stdClass|boolean
     351   */
     352  function cohort_get_cohort($cohortorid, $currentcontext) {
     353      global $DB;
     354      if (is_numeric($cohortorid)) {
     355          $cohort = $DB->get_record('cohort', array('id' => $cohortorid), 'id, contextid, visible');
     356      } else {
     357          $cohort = $cohortorid;
     358      }
     359  
     360      if ($cohort && in_array($cohort->contextid, $currentcontext->get_parent_context_ids())) {
     361          if ($cohort->visible) {
     362              return $cohort;
     363          }
     364          $cohortcontext = context::instance_by_id($cohort->contextid);
     365          if (has_capability('moodle/cohort:view', $cohortcontext)) {
     366              return $cohort;
     367          }
     368      }
     369      return false;
     370  }
     371  
     372  /**
     373   * Produces a part of SQL query to filter cohorts by the search string
     374   *
     375   * Called from {@link cohort_get_cohorts()}, {@link cohort_get_all_cohorts()} and {@link cohort_get_available_cohorts()}
     376   *
     377   * @access private
     378   *
     379   * @param string $search search string
     380   * @param string $tablealias alias of cohort table in the SQL query (highly recommended if other tables are used in query)
     381   * @return array of two elements - SQL condition and array of named parameters
     382   */
     383  function cohort_get_search_query($search, $tablealias = '') {
     384      global $DB;
     385      $params = array();
     386      if (empty($search)) {
     387          // This function should not be called if there is no search string, just in case return dummy query.
     388          return array('1=1', $params);
     389      }
     390      if ($tablealias && substr($tablealias, -1) !== '.') {
     391          $tablealias .= '.';
     392      }
     393      $searchparam = '%' . $DB->sql_like_escape($search) . '%';
     394      $conditions = array();
     395      $fields = array('name', 'idnumber', 'description');
     396      $cnt = 0;
     397      foreach ($fields as $field) {
     398          $conditions[] = $DB->sql_like($tablealias . $field, ':csearch' . $cnt, false);
     399          $params['csearch' . $cnt] = $searchparam;
     400          $cnt++;
     401      }
     402      $sql = '(' . implode(' OR ', $conditions) . ')';
     403      return array($sql, $params);
     404  }
     405  
     406  /**
     407   * Get all the cohorts defined in given context.
     408   *
     409   * The function does not check user capability to view/manage cohorts in the given context
     410   * assuming that it has been already verified.
     411   *
     412   * @param int $contextid
     413   * @param int $page number of the current page
     414   * @param int $perpage items per page
     415   * @param string $search search string
     416   * @return array    Array(totalcohorts => int, cohorts => array, allcohorts => int)
     417   */
     418  function cohort_get_cohorts($contextid, $page = 0, $perpage = 25, $search = '') {
     419      global $DB;
     420  
     421      $fields = "SELECT *";
     422      $countfields = "SELECT COUNT(1)";
     423      $sql = " FROM {cohort}
     424               WHERE contextid = :contextid";
     425      $params = array('contextid' => $contextid);
     426      $order = " ORDER BY name ASC, idnumber ASC";
     427  
     428      if (!empty($search)) {
     429          list($searchcondition, $searchparams) = cohort_get_search_query($search);
     430          $sql .= ' AND ' . $searchcondition;
     431          $params = array_merge($params, $searchparams);
     432      }
     433  
     434      $totalcohorts = $allcohorts = $DB->count_records('cohort', array('contextid' => $contextid));
     435      if (!empty($search)) {
     436          $totalcohorts = $DB->count_records_sql($countfields . $sql, $params);
     437      }
     438      $cohorts = $DB->get_records_sql($fields . $sql . $order, $params, $page*$perpage, $perpage);
     439  
     440      return array('totalcohorts' => $totalcohorts, 'cohorts' => $cohorts, 'allcohorts' => $allcohorts);
     441  }
     442  
     443  /**
     444   * Get all the cohorts defined anywhere in system.
     445   *
     446   * The function assumes that user capability to view/manage cohorts on system level
     447   * has already been verified. This function only checks if such capabilities have been
     448   * revoked in child (categories) contexts.
     449   *
     450   * @param int $page number of the current page
     451   * @param int $perpage items per page
     452   * @param string $search search string
     453   * @return array    Array(totalcohorts => int, cohorts => array, allcohorts => int)
     454   */
     455  function cohort_get_all_cohorts($page = 0, $perpage = 25, $search = '') {
     456      global $DB;
     457  
     458      $fields = "SELECT c.*, ".context_helper::get_preload_record_columns_sql('ctx');
     459      $countfields = "SELECT COUNT(*)";
     460      $sql = " FROM {cohort} c
     461               JOIN {context} ctx ON ctx.id = c.contextid ";
     462      $params = array();
     463      $wheresql = '';
     464  
     465      if ($excludedcontexts = cohort_get_invisible_contexts()) {
     466          list($excludedsql, $excludedparams) = $DB->get_in_or_equal($excludedcontexts, SQL_PARAMS_NAMED, 'excl', false);
     467          $wheresql = ' WHERE c.contextid '.$excludedsql;
     468          $params = array_merge($params, $excludedparams);
     469      }
     470  
     471      $totalcohorts = $allcohorts = $DB->count_records_sql($countfields . $sql . $wheresql, $params);
     472  
     473      if (!empty($search)) {
     474          list($searchcondition, $searchparams) = cohort_get_search_query($search, 'c');
     475          $wheresql .= ($wheresql ? ' AND ' : ' WHERE ') . $searchcondition;
     476          $params = array_merge($params, $searchparams);
     477          $totalcohorts = $DB->count_records_sql($countfields . $sql . $wheresql, $params);
     478      }
     479  
     480      $order = " ORDER BY c.name ASC, c.idnumber ASC";
     481      $cohorts = $DB->get_records_sql($fields . $sql . $wheresql . $order, $params, $page*$perpage, $perpage);
     482  
     483      // Preload used contexts, they will be used to check view/manage/assign capabilities and display categories names.
     484      foreach (array_keys($cohorts) as $key) {
     485          context_helper::preload_from_record($cohorts[$key]);
     486      }
     487  
     488      return array('totalcohorts' => $totalcohorts, 'cohorts' => $cohorts, 'allcohorts' => $allcohorts);
     489  }
     490  
     491  /**
     492   * Get all the cohorts where the given user is member of.
     493   *
     494   * @param int $userid
     495   * @return array Array
     496   */
     497  function cohort_get_user_cohorts($userid) {
     498      global $DB;
     499  
     500      $sql = 'SELECT c.*
     501                FROM {cohort} c
     502                JOIN {cohort_members} cm ON c.id = cm.cohortid
     503               WHERE cm.userid = ? AND c.visible = 1';
     504      return $DB->get_records_sql($sql, array($userid));
     505  }
     506  
     507  /**
     508   * Get the user cohort theme.
     509   *
     510   * If the user is member of one cohort, will return this cohort theme (if defined).
     511   * If the user is member of 2 or more cohorts, will return the theme if all them have the same
     512   * theme (null themes are ignored).
     513   *
     514   * @param int $userid
     515   * @return string|null
     516   */
     517  function cohort_get_user_cohort_theme($userid) {
     518      $cohorts = cohort_get_user_cohorts($userid);
     519      $theme = null;
     520      foreach ($cohorts as $cohort) {
     521          if (!empty($cohort->theme)) {
     522              if (null === $theme) {
     523                  $theme = $cohort->theme;
     524              } else if ($theme != $cohort->theme) {
     525                  return null;
     526              }
     527          }
     528      }
     529      return $theme;
     530  }
     531  
     532  /**
     533   * Returns list of contexts where cohorts are present but current user does not have capability to view/manage them.
     534   *
     535   * This function is called from {@link cohort_get_all_cohorts()} to ensure correct pagination in rare cases when user
     536   * is revoked capability in child contexts. It assumes that user's capability to view/manage cohorts on system
     537   * level has already been verified.
     538   *
     539   * @access private
     540   *
     541   * @return array array of context ids
     542   */
     543  function cohort_get_invisible_contexts() {
     544      global $DB;
     545      if (is_siteadmin()) {
     546          // Shortcut, admin can do anything and can not be prohibited from any context.
     547          return array();
     548      }
     549      $records = $DB->get_recordset_sql("SELECT DISTINCT ctx.id, ".context_helper::get_preload_record_columns_sql('ctx')." ".
     550          "FROM {context} ctx JOIN {cohort} c ON ctx.id = c.contextid ");
     551      $excludedcontexts = array();
     552      foreach ($records as $ctx) {
     553          context_helper::preload_from_record($ctx);
     554          if (context::instance_by_id($ctx->id) == context_system::instance()) {
     555              continue; // System context cohorts should be available and permissions already checked.
     556          }
     557          if (!has_any_capability(array('moodle/cohort:manage', 'moodle/cohort:view'), context::instance_by_id($ctx->id))) {
     558              $excludedcontexts[] = $ctx->id;
     559          }
     560      }
     561      $records->close();
     562      return $excludedcontexts;
     563  }
     564  
     565  /**
     566   * Returns navigation controls (tabtree) to be displayed on cohort management pages
     567   *
     568   * @param context $context system or category context where cohorts controls are about to be displayed
     569   * @param moodle_url $currenturl
     570   * @return null|renderable
     571   */
     572  function cohort_edit_controls(context $context, moodle_url $currenturl) {
     573      $tabs = array();
     574      $currenttab = 'view';
     575      $viewurl = new moodle_url('/cohort/index.php', array('contextid' => $context->id));
     576      if (($searchquery = $currenturl->get_param('search'))) {
     577          $viewurl->param('search', $searchquery);
     578      }
     579      if ($context->contextlevel == CONTEXT_SYSTEM) {
     580          $tabs[] = new tabobject('view', new moodle_url($viewurl, array('showall' => 0)), get_string('systemcohorts', 'cohort'));
     581          $tabs[] = new tabobject('viewall', new moodle_url($viewurl, array('showall' => 1)), get_string('allcohorts', 'cohort'));
     582          if ($currenturl->get_param('showall')) {
     583              $currenttab = 'viewall';
     584          }
     585      } else {
     586          $tabs[] = new tabobject('view', $viewurl, get_string('cohorts', 'cohort'));
     587      }
     588      if (has_capability('moodle/cohort:manage', $context)) {
     589          $addurl = new moodle_url('/cohort/edit.php', array('contextid' => $context->id));
     590          $tabs[] = new tabobject('addcohort', $addurl, get_string('addcohort', 'cohort'));
     591          if ($currenturl->get_path() === $addurl->get_path() && !$currenturl->param('id')) {
     592              $currenttab = 'addcohort';
     593          }
     594  
     595          $uploadurl = new moodle_url('/cohort/upload.php', array('contextid' => $context->id));
     596          $tabs[] = new tabobject('uploadcohorts', $uploadurl, get_string('uploadcohorts', 'cohort'));
     597          if ($currenturl->get_path() === $uploadurl->get_path()) {
     598              $currenttab = 'uploadcohorts';
     599          }
     600      }
     601      if (count($tabs) > 1) {
     602          return new tabtree($tabs, $currenttab);
     603      }
     604      return null;
     605  }
     606  
     607  /**
     608   * Implements callback inplace_editable() allowing to edit values in-place
     609   *
     610   * @param string $itemtype
     611   * @param int $itemid
     612   * @param mixed $newvalue
     613   * @return \core\output\inplace_editable
     614   */
     615  function core_cohort_inplace_editable($itemtype, $itemid, $newvalue) {
     616      if ($itemtype === 'cohortname') {
     617          return \core_cohort\output\cohortname::update($itemid, $newvalue);
     618      } else if ($itemtype === 'cohortidnumber') {
     619          return \core_cohort\output\cohortidnumber::update($itemid, $newvalue);
     620      }
     621  }
     622  
     623  /**
     624   * Returns a list of valid themes which can be displayed in a selector.
     625   *
     626   * @return array as (string)themename => (string)get_string_theme
     627   */
     628  function cohort_get_list_of_themes() {
     629      $themes = array();
     630      $allthemes = get_list_of_themes();
     631      foreach ($allthemes as $key => $theme) {
     632          if (empty($theme->hidefromselector)) {
     633              $themes[$key] = get_string('pluginname', 'theme_'.$theme->name);
     634          }
     635      }
     636      return $themes;
     637  }