Differences Between: [Versions 311 and 402] [Versions 311 and 403]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body