2009-05-22 08:41:00 +00:00
< ? php
2009-11-01 11:31:16 +00:00
// This file is part of Moodle - http://moodle.org/
//
2009-05-22 08:41:00 +00:00
// 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.
2009-11-01 11:31:16 +00:00
//
2009-05-22 08:41:00 +00:00
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
2008-04-15 21:46:04 +00:00
2004-09-23 02:48:41 +00:00
/**
* Library of functions for database manipulation .
2007-08-02 23:39:28 +00:00
*
2004-09-23 02:48:41 +00:00
* Other main libraries :
* - weblib . php - functions that produce web output
* - moodlelib . php - general - purpose Moodle functions
2009-05-22 08:41:00 +00:00
*
2010-07-25 13:35:05 +00:00
* @ package core
* @ copyright 1999 onwards Martin Dougiamas { @ link http :// moodle . com }
* @ license http :// www . gnu . org / copyleft / gpl . html GNU GPL v3 or later
2004-09-23 02:48:41 +00:00
*/
2010-07-25 13:35:05 +00:00
defined ( 'MOODLE_INTERNAL' ) || die ();
2010-03-31 07:41:31 +00:00
/**
* The maximum courses in a category
* MAX_COURSES_IN_CATEGORY * MAX_COURSE_CATEGORIES must not be more than max integer !
*/
2009-05-22 08:41:00 +00:00
define ( 'MAX_COURSES_IN_CATEGORY' , 10000 );
2010-03-31 07:41:31 +00:00
2009-11-01 11:31:16 +00:00
/**
2009-05-22 08:41:00 +00:00
* The maximum number of course categories
2009-11-01 11:31:16 +00:00
* MAX_COURSES_IN_CATEGORY * MAX_COURSE_CATEGORIES must not be more than max integer !
2009-05-22 08:41:00 +00:00
*/
2008-06-16 14:25:53 +00:00
define ( 'MAX_COURSE_CATEGORIES' , 10000 );
2010-03-31 07:41:31 +00:00
/**
* Number of seconds to wait before updating lastaccess information in DB .
*/
define ( 'LASTACCESS_UPDATE_SECS' , 60 );
2002-12-17 04:41:18 +00:00
2003-09-14 04:04:15 +00:00
/**
2004-09-24 21:28:22 +00:00
* Returns $user object of the main admin user
2009-05-22 08:41:00 +00:00
*
2010-12-16 08:29:23 +01:00
* @ static stdClass $mainadmin
2010-12-21 19:19:35 +01:00
* @ return stdClass { @ link $USER } record from DB , false if not found
2004-09-24 21:28:22 +00:00
*/
2010-03-31 07:41:31 +00:00
function get_admin () {
2011-12-07 11:18:10 +01:00
global $CFG , $DB ;
2010-03-31 07:41:31 +00:00
static $mainadmin = null ;
2012-10-09 19:13:29 +02:00
static $prevadmins = null ;
2007-09-19 07:22:01 +00:00
2012-10-26 10:43:39 +08:00
if ( empty ( $CFG -> siteadmins )) {
// Should not happen on an ordinary site.
// It does however happen during unit tests.
2012-10-09 19:13:29 +02:00
return false ;
2011-12-07 11:18:10 +01:00
}
2012-10-09 19:13:29 +02:00
if ( isset ( $mainadmin ) and $prevadmins === $CFG -> siteadmins ) {
return clone ( $mainadmin );
2011-12-09 09:04:43 +01:00
}
2012-10-09 19:13:29 +02:00
$mainadmin = null ;
2011-12-07 11:18:10 +01:00
foreach ( explode ( ',' , $CFG -> siteadmins ) as $id ) {
if ( $user = $DB -> get_record ( 'user' , array ( 'id' => $id , 'deleted' => 0 ))) {
$mainadmin = $user ;
break ;
2002-12-17 04:41:18 +00:00
}
}
2011-12-07 11:18:10 +01:00
if ( $mainadmin ) {
2012-10-09 19:13:29 +02:00
$prevadmins = $CFG -> siteadmins ;
2011-12-07 11:18:10 +01:00
return clone ( $mainadmin );
} else {
// this should not happen
return false ;
}
2002-12-17 04:41:18 +00:00
}
2003-09-14 04:04:15 +00:00
/**
2010-03-31 07:41:31 +00:00
* Returns list of all admins , using 1 DB query
2004-09-24 21:28:22 +00:00
*
2009-05-22 08:41:00 +00:00
* @ return array
2004-09-24 21:28:22 +00:00
*/
2002-12-17 04:41:18 +00:00
function get_admins () {
2010-03-31 07:41:31 +00:00
global $DB , $CFG ;
2007-08-02 23:39:28 +00:00
2010-05-17 05:17:12 +00:00
if ( empty ( $CFG -> siteadmins )) { // Should not happen on an ordinary site
2010-05-18 16:59:08 +00:00
return array ();
2010-05-17 05:17:12 +00:00
}
2010-03-31 07:41:31 +00:00
$sql = " SELECT u.*
2008-05-25 09:39:02 +00:00
FROM { user } u
2010-03-31 07:41:31 +00:00
WHERE u . deleted = 0 AND u . id IN ( $CFG -> siteadmins ) " ;
2007-08-02 23:39:28 +00:00
2012-10-09 19:13:29 +02:00
// We want the same order as in $CFG->siteadmins.
$records = $DB -> get_records_sql ( $sql );
$admins = array ();
foreach ( explode ( ',' , $CFG -> siteadmins ) as $id ) {
$id = ( int ) $id ;
if ( ! isset ( $records [ $id ])) {
// User does not exist, this should not happen.
continue ;
}
$admins [ $records [ $id ] -> id ] = $records [ $id ];
}
return $admins ;
2002-12-17 04:41:18 +00:00
}
2004-08-21 12:41:40 +00:00
/**
2004-09-24 21:28:22 +00:00
* Search through course users
*
2007-08-02 23:39:28 +00:00
* If $coursid specifies the site course then this function searches
2004-09-24 21:28:22 +00:00
* through all undeleted and confirmed users
*
2009-05-22 08:41:00 +00:00
* @ global object
* @ uses SITEID
* @ uses SQL_PARAMS_NAMED
* @ uses CONTEXT_COURSE
2004-09-24 21:28:22 +00:00
* @ param int $courseid The course in question .
* @ param int $groupid The group in question .
2009-05-22 08:41:00 +00:00
* @ param string $searchtext The string to search for
* @ param string $sort A field to sort by
* @ param array $exceptions A list of IDs to ignore , eg 2 , 4 , 5 , 8 , 9 , 10
* @ return array
2004-09-24 21:28:22 +00:00
*/
2008-05-25 09:39:02 +00:00
function search_users ( $courseid , $groupid , $searchtext , $sort = '' , array $exceptions = null ) {
global $DB ;
2004-01-31 03:30:31 +00:00
2008-06-09 19:48:24 +00:00
$fullname = $DB -> sql_fullname ( 'u.firstname' , 'u.lastname' );
2004-09-21 11:41:58 +00:00
2004-08-21 12:41:40 +00:00
if ( ! empty ( $exceptions )) {
2011-04-14 15:13:28 +02:00
list ( $exceptions , $params ) = $DB -> get_in_or_equal ( $exceptions , SQL_PARAMS_NAMED , 'ex' , false );
2008-05-25 09:39:02 +00:00
$except = " AND u.id $exceptions " ;
2004-08-21 12:41:40 +00:00
} else {
2008-05-25 09:39:02 +00:00
$except = " " ;
$params = array ();
2004-08-21 12:41:40 +00:00
}
2004-08-30 17:27:00 +00:00
2004-08-21 12:41:40 +00:00
if ( ! empty ( $sort )) {
2008-05-25 09:39:02 +00:00
$order = " ORDER BY $sort " ;
2004-08-21 12:41:40 +00:00
} else {
2008-05-25 09:39:02 +00:00
$order = " " ;
2004-08-21 12:41:40 +00:00
}
2004-09-21 11:41:58 +00:00
2010-09-04 12:45:47 +00:00
$select = " u.deleted = 0 AND u.confirmed = 1 AND ( " . $DB -> sql_like ( $fullname , ':search1' , false ) . " OR " . $DB -> sql_like ( 'u.email' , ':search2' , false ) . " ) " ;
2008-05-25 09:39:02 +00:00
$params [ 'search1' ] = " % $searchtext % " ;
$params [ 'search2' ] = " % $searchtext % " ;
2004-08-30 17:27:00 +00:00
2004-08-29 14:15:40 +00:00
if ( ! $courseid or $courseid == SITEID ) {
2008-05-25 09:39:02 +00:00
$sql = " SELECT u.id, u.firstname, u.lastname, u.email
FROM { user } u
WHERE $select
$except
$order " ;
return $DB -> get_records_sql ( $sql , $params );
2004-08-30 17:27:00 +00:00
2008-05-25 09:39:02 +00:00
} else {
2004-08-21 12:41:40 +00:00
if ( $groupid ) {
2008-05-25 09:39:02 +00:00
$sql = " SELECT u.id, u.firstname, u.lastname, u.email
FROM { user } u
JOIN { groups_members } gm ON gm . userid = u . id
WHERE $select AND gm . groupid = : groupid
$except
$order " ;
$params [ 'groupid' ] = $groupid ;
return $DB -> get_records_sql ( $sql , $params );
2004-08-21 12:41:40 +00:00
} else {
2012-07-25 16:25:55 +08:00
$context = context_course :: instance ( $courseid );
2006-09-14 09:08:07 +00:00
$contextlists = get_related_contexts_string ( $context );
2008-05-25 09:39:02 +00:00
$sql = " SELECT u.id, u.firstname, u.lastname, u.email
FROM { user } u
JOIN { role_assignments } ra ON ra . userid = u . id
WHERE $select AND ra . contextid $contextlists
$except
$order " ;
return $DB -> get_records_sql ( $sql , $params );
2004-08-21 12:41:40 +00:00
}
}
2002-12-17 04:41:18 +00:00
}
2012-08-08 13:53:28 +01:00
/**
* This function generates the standard ORDER BY clause for use when generating
* lists of users . If you don ' t have a reason to use a different order , then
* you should use this method to generate the order when displaying lists of users .
*
* If the optional $search parameter is passed , then exact matches to the search
* will be sorted first . For example , suppose you have two users 'Al Zebra' and
* 'Alan Aardvark' . The default sort is Alan , then Al . If , however , you search for
* 'Al' , then Al will be listed first . ( With two users , this is not a big deal ,
* but with thousands of users , it is essential . )
*
* The list of fields scanned for exact matches are :
* - firstname
* - lastname
* - $DB -> sql_fullname
* - those returned by get_extra_user_fields
*
* If named parameters are used ( which is the default , and highly recommended ),
* then the parameter names are like : usersortexactN , where N is an int .
*
* The simplest possible example use is :
* list ( $sort , $params ) = users_order_by_sql ();
* $sql = 'SELECT * FROM {users} ORDER BY ' . $sort ;
*
* A more complex example , showing that this sort can be combined with other sorts :
* list ( $sort , $sortparams ) = users_order_by_sql ( 'u' );
* $sql = " SELECT g.id AS groupid, gg.groupingid, u.id AS userid, u.firstname, u.lastname, u.idnumber, u.username
* FROM { groups } g
* LEFT JOIN { groupings_groups } gg ON g . id = gg . groupid
* LEFT JOIN { groups_members } gm ON g . id = gm . groupid
* LEFT JOIN { user } u ON gm . userid = u . id
* WHERE g . courseid = : courseid $groupwhere $groupingwhere
* ORDER BY g . name , $sort " ;
* $params += $sortparams ;
*
* An example showing the use of $search :
* list ( $sort , $sortparams ) = users_order_by_sql ( 'u' , $search , $this -> get_context ());
* $order = ' ORDER BY ' . $sort ;
* $params += $sortparams ;
* $availableusers = $DB -> get_records_sql ( $fields . $sql . $order , $params , $page * $perpage , $perpage );
*
* @ param string $usertablealias ( optional ) any table prefix for the { users } table . E . g . 'u' .
* @ param string $search ( optional ) a current search string . If given ,
* any exact matches to this string will be sorted first .
* @ param context $context the context we are in . Use by get_extra_user_fields .
* Defaults to $PAGE -> context .
* @ return array with two elements :
* string SQL fragment to use in the ORDER BY clause . For example , " firstname, lastname " .
* array of parameters used in the SQL fragment .
*/
function users_order_by_sql ( $usertablealias = '' , $search = null , context $context = null ) {
global $DB , $PAGE ;
if ( $usertablealias ) {
$tableprefix = $usertablealias . '.' ;
} else {
$tableprefix = '' ;
}
$sort = " { $tableprefix } lastname, { $tableprefix } firstname, { $tableprefix } id " ;
$params = array ();
if ( ! $search ) {
return array ( $sort , $params );
}
if ( ! $context ) {
$context = $PAGE -> context ;
}
$exactconditions = array ();
$paramkey = 'usersortexact1' ;
$exactconditions [] = $DB -> sql_fullname ( $tableprefix . 'firstname' , $tableprefix . 'lastname' ) .
' = :' . $paramkey ;
$params [ $paramkey ] = $search ;
$paramkey ++ ;
$fieldstocheck = array_merge ( array ( 'firstname' , 'lastname' ), get_extra_user_fields ( $context ));
foreach ( $fieldstocheck as $key => $field ) {
2012-10-04 11:43:33 +01:00
$exactconditions [] = 'LOWER(' . $tableprefix . $field . ') = LOWER(:' . $paramkey . ')' ;
2012-08-08 13:53:28 +01:00
$params [ $paramkey ] = $search ;
$paramkey ++ ;
}
$sort = 'CASE WHEN ' . implode ( ' OR ' , $exactconditions ) .
' THEN 0 ELSE 1 END, ' . $sort ;
return array ( $sort , $params );
}
2003-09-14 04:04:15 +00:00
/**
2004-09-24 21:28:22 +00:00
* Returns a subset of users
*
2009-05-22 08:41:00 +00:00
* @ global object
* @ uses DEBUG_DEVELOPER
* @ uses SQL_PARAMS_NAMED
2005-07-12 02:23:58 +00:00
* @ param bool $get If false then only a count of the records is returned
2004-09-24 21:28:22 +00:00
* @ param string $search A simple string to search for
2005-07-12 02:23:58 +00:00
* @ param bool $confirmed A switch to allow / disallow unconfirmed users
2009-05-22 08:41:00 +00:00
* @ param array $exceptions A list of IDs to ignore , eg 2 , 4 , 5 , 8 , 9 , 10
2004-09-24 21:28:22 +00:00
* @ param string $sort A SQL snippet for the sorting criteria to use
2009-05-22 08:41:00 +00:00
* @ param string $firstinitial Users whose first name starts with $firstinitial
* @ param string $lastinitial Users whose last name starts with $lastinitial
* @ param string $page The page or records to return
* @ param string $recordsperpage The number of records to return per page
2004-09-24 21:28:22 +00:00
* @ param string $fields A comma separated list of fields to be returned from the chosen table .
2009-11-01 11:31:16 +00:00
* @ return array | int | bool { @ link $USER } records unless get is false in which case the integer count of the records found is returned .
2012-08-08 13:53:28 +01:00
* False is returned if an error is encountered .
2004-09-24 21:28:22 +00:00
*/
2008-05-25 09:39:02 +00:00
function get_users ( $get = true , $search = '' , $confirmed = false , array $exceptions = null , $sort = 'firstname ASC' ,
$firstinitial = '' , $lastinitial = '' , $page = '' , $recordsperpage = '' , $fields = '*' , $extraselect = '' , array $extraparams = null ) {
2010-08-25 08:56:07 +00:00
global $DB , $CFG ;
2007-08-02 23:39:28 +00:00
2006-09-15 14:32:35 +00:00
if ( $get && ! $recordsperpage ) {
debugging ( 'Call to get_users with $get = true no $recordsperpage limit. ' .
'On large installations, this will probably cause an out of memory error. ' .
'Please think again and change your code so that it does not try to ' .
2006-10-01 06:39:20 +00:00
'load so much data into memory.' , DEBUG_DEVELOPER );
2006-09-15 14:32:35 +00:00
}
2003-09-14 04:04:15 +00:00
2008-06-09 19:48:24 +00:00
$fullname = $DB -> sql_fullname ();
2003-03-21 10:10:21 +00:00
2010-08-25 08:56:07 +00:00
$select = " id <> :guestid AND deleted = 0 " ;
$params = array ( 'guestid' => $CFG -> siteguest );
2004-03-20 06:58:52 +00:00
2004-12-22 23:11:27 +00:00
if ( ! empty ( $search )){
$search = trim ( $search );
2010-09-04 12:45:47 +00:00
$select .= " AND ( " . $DB -> sql_like ( $fullname , ':search1' , false ) . " OR " . $DB -> sql_like ( 'email' , ':search2' , false ) . " OR username = :search3) " ;
2008-05-25 09:39:02 +00:00
$params [ 'search1' ] = " % $search % " ;
$params [ 'search2' ] = " % $search % " ;
$params [ 'search3' ] = " $search " ;
2003-03-21 10:10:21 +00:00
}
2003-05-14 15:19:04 +00:00
if ( $confirmed ) {
2008-05-25 09:39:02 +00:00
$select .= " AND confirmed = 1 " ;
2003-05-14 15:19:04 +00:00
}
if ( $exceptions ) {
2011-04-14 15:13:28 +02:00
list ( $exceptions , $eparams ) = $DB -> get_in_or_equal ( $exceptions , SQL_PARAMS_NAMED , 'ex' , false );
2008-05-25 09:39:02 +00:00
$params = $params + $eparams ;
2012-03-05 13:48:55 +00:00
$select .= " AND id $exceptions " ;
2003-05-14 15:19:04 +00:00
}
2004-03-20 06:58:52 +00:00
if ( $firstinitial ) {
2010-09-04 12:45:47 +00:00
$select .= " AND " . $DB -> sql_like ( 'firstname' , ':fni' , false , false );
2008-05-25 09:39:02 +00:00
$params [ 'fni' ] = " $firstinitial % " ;
2004-09-21 11:41:58 +00:00
}
2004-03-20 06:58:52 +00:00
if ( $lastinitial ) {
2010-09-04 12:45:47 +00:00
$select .= " AND " . $DB -> sql_like ( 'lastname' , ':lni' , false , false );
2008-05-25 09:39:02 +00:00
$params [ 'lni' ] = " $lastinitial % " ;
2004-09-21 11:41:58 +00:00
}
2004-03-20 06:58:52 +00:00
2007-11-13 08:43:20 +00:00
if ( $extraselect ) {
2008-05-25 09:39:02 +00:00
$select .= " AND $extraselect " ;
$params = $params + ( array ) $extraparams ;
2007-11-13 08:43:20 +00:00
}
2003-05-14 15:19:04 +00:00
if ( $get ) {
2008-05-25 09:39:02 +00:00
return $DB -> get_records_select ( 'user' , $select , $params , $sort , $fields , $page , $recordsperpage );
2003-05-14 15:19:04 +00:00
} else {
2008-05-25 09:39:02 +00:00
return $DB -> count_records_select ( 'user' , $select , $params );
2003-05-14 15:19:04 +00:00
}
2002-12-20 14:44:14 +00:00
}
2003-05-14 15:19:04 +00:00
2003-09-14 04:04:15 +00:00
/**
2004-09-24 21:28:22 +00:00
* @ todo Finish documenting this function
2009-05-22 08:41:00 +00:00
*
* @ param string $sort An SQL field to sort by
* @ param string $dir The sort direction ASC | DESC
* @ param int $page The page or records to return
* @ param int $recordsperpage The number of records to return per page
* @ param string $search A simple string to search for
* @ param string $firstinitial Users whose first name starts with $firstinitial
* @ param string $lastinitial Users whose last name starts with $lastinitial
* @ param string $extraselect An additional SQL select statement to append to the query
* @ param array $extraparams Additional parameters to use for the above $extraselect
2011-04-13 15:44:42 +01:00
* @ param object $extracontext If specified , will include user 'extra fields'
* as appropriate for current user and given context
2009-05-22 08:41:00 +00:00
* @ return array Array of { @ link $USER } records
2004-09-24 21:28:22 +00:00
*/
2006-09-15 14:32:35 +00:00
function get_users_listing ( $sort = 'lastaccess' , $dir = 'ASC' , $page = 0 , $recordsperpage = 0 ,
2011-04-13 15:44:42 +01:00
$search = '' , $firstinitial = '' , $lastinitial = '' , $extraselect = '' ,
array $extraparams = null , $extracontext = null ) {
2008-05-25 09:39:02 +00:00
global $DB ;
2002-12-23 13:48:31 +00:00
2008-06-09 19:48:24 +00:00
$fullname = $DB -> sql_fullname ();
2003-02-18 03:16:07 +00:00
2008-05-25 09:39:02 +00:00
$select = " deleted <> 1 " ;
$params = array ();
2004-03-20 06:58:52 +00:00
2004-12-22 23:11:27 +00:00
if ( ! empty ( $search )) {
$search = trim ( $search );
2010-09-04 11:46:00 +00:00
$select .= " AND ( " . $DB -> sql_like ( $fullname , ':search1' , false , false ) .
" OR " . $DB -> sql_like ( 'email' , ':search2' , false , false ) .
" OR username = :search3) " ;
2008-05-25 09:39:02 +00:00
$params [ 'search1' ] = " % $search % " ;
$params [ 'search2' ] = " % $search % " ;
$params [ 'search3' ] = " $search " ;
2004-03-20 06:58:52 +00:00
}
if ( $firstinitial ) {
2010-09-04 11:46:00 +00:00
$select .= " AND " . $DB -> sql_like ( 'firstname' , ':fni' , false , false );
2008-05-25 09:39:02 +00:00
$params [ 'fni' ] = " $firstinitial % " ;
2004-03-20 06:58:52 +00:00
}
if ( $lastinitial ) {
2010-09-04 11:46:00 +00:00
$select .= " AND " . $DB -> sql_like ( 'lastname' , ':lni' , false , false );
2008-05-25 09:39:02 +00:00
$params [ 'lni' ] = " $lastinitial % " ;
2003-03-21 09:42:42 +00:00
}
2007-11-13 08:43:20 +00:00
if ( $extraselect ) {
2008-05-25 09:39:02 +00:00
$select .= " AND $extraselect " ;
$params = $params + ( array ) $extraparams ;
2007-11-13 08:43:20 +00:00
}
2007-01-04 02:52:44 +00:00
2004-03-20 06:58:52 +00:00
if ( $sort ) {
2008-05-25 09:39:02 +00:00
$sort = " ORDER BY $sort $dir " ;
2004-03-20 06:58:52 +00:00
}
2011-04-13 15:44:42 +01:00
// If a context is specified, get extra user fields that the current user
// is supposed to see.
$extrafields = '' ;
if ( $extracontext ) {
$extrafields = get_extra_user_fields_sql ( $extracontext , '' , '' ,
array ( 'id' , 'username' , 'email' , 'firstname' , 'lastname' , 'city' , 'country' ,
'lastaccess' , 'confirmed' , 'mnethostid' ));
}
// warning: will return UNCONFIRMED USERS
return $DB -> get_records_sql ( " SELECT id, username, email, firstname, lastname, city, country,
2012-01-03 13:18:06 +01:00
lastaccess , confirmed , mnethostid , suspended $extrafields
2008-05-25 09:39:02 +00:00
FROM { user }
WHERE $select
$sort " , $params , $page , $recordsperpage );
2002-12-20 14:44:14 +00:00
}
2004-03-20 06:58:52 +00:00
2003-09-14 04:04:15 +00:00
/**
2005-07-12 02:23:58 +00:00
* Full list of users that have confirmed their accounts .
2004-09-24 21:28:22 +00:00
*
2009-05-22 08:41:00 +00:00
* @ global object
2008-05-25 09:39:02 +00:00
* @ return array of unconfirmed users
2004-09-24 21:28:22 +00:00
*/
2002-12-20 14:44:14 +00:00
function get_users_confirmed () {
2010-08-25 08:56:07 +00:00
global $DB , $CFG ;
2008-05-25 09:39:02 +00:00
return $DB -> get_records_sql ( " SELECT *
FROM { user }
2010-08-25 08:56:07 +00:00
WHERE confirmed = 1 AND deleted = 0 AND id <> ? " , array( $CFG->siteguest ));
2002-12-20 14:44:14 +00:00
}
2003-08-15 13:59:24 +00:00
/// OTHER SITE AND COURSE FUNCTIONS /////////////////////////////////////////////
2003-09-14 04:04:15 +00:00
/**
2004-09-24 21:28:22 +00:00
* Returns $course object of the top - level site .
*
2009-11-01 09:10:09 +00:00
* @ return object A { @ link $COURSE } object for the site , exception if not found
2004-09-24 21:28:22 +00:00
*/
2005-03-03 04:41:46 +00:00
function get_site () {
2008-05-25 09:39:02 +00:00
global $SITE , $DB ;
2005-03-03 04:41:46 +00:00
if ( ! empty ( $SITE -> id )) { // We already have a global to use, so return that
return $SITE ;
}
2003-08-15 13:59:24 +00:00
2008-05-25 09:39:02 +00:00
if ( $course = $DB -> get_record ( 'course' , array ( 'category' => 0 ))) {
2003-08-15 13:59:24 +00:00
return $course ;
} else {
2009-11-01 09:10:09 +00:00
// course table exists, but the site is not there,
// unfortunately there is no automatic way to recover
throw new moodle_exception ( 'nosite' , 'error' );
2003-08-15 13:59:24 +00:00
}
}
2003-09-14 04:04:15 +00:00
/**
2006-09-10 07:07:52 +00:00
* Returns list of courses , for whole site , or category
*
* Returns list of courses , for whole site , or category
2007-09-20 13:15:26 +00:00
* Important : Using c .* for fields is extremely expensive because
2006-09-10 07:07:52 +00:00
* we are using distinct . You almost _NEVER_ need all the fields
* in such a large SELECT
*
2009-05-22 08:41:00 +00:00
* @ global object
* @ global object
* @ global object
* @ uses CONTEXT_COURSE
* @ param string | int $categoryid Either a category id or 'all' for everything
* @ param string $sort A field and direction to sort by
* @ param string $fields The additional fields to return
* @ return array Array of courses
2006-09-10 07:07:52 +00:00
*/
2004-11-18 02:31:53 +00:00
function get_courses ( $categoryid = " all " , $sort = " c.sortorder ASC " , $fields = " c.* " ) {
2003-08-15 13:59:24 +00:00
2008-05-25 15:16:17 +00:00
global $USER , $CFG , $DB ;
2007-08-02 23:39:28 +00:00
2008-05-25 15:16:17 +00:00
$params = array ();
if ( $categoryid !== " all " && is_numeric ( $categoryid )) {
$categoryselect = " WHERE c.category = :catid " ;
$params [ 'catid' ] = $categoryid ;
2006-09-15 09:08:48 +00:00
} else {
2007-08-02 23:39:28 +00:00
$categoryselect = " " ;
2006-09-17 18:07:35 +00:00
}
if ( empty ( $sort )) {
$sortstatement = " " ;
} else {
$sortstatement = " ORDER BY $sort " ;
}
$visiblecourses = array ();
2007-08-02 23:39:28 +00:00
2010-03-31 07:41:31 +00:00
list ( $ccselect , $ccjoin ) = context_instance_preload_sql ( 'c.id' , CONTEXT_COURSE , 'ctx' );
$sql = " SELECT $fields $ccselect
2008-05-25 15:16:17 +00:00
FROM { course } c
2010-03-31 07:41:31 +00:00
$ccjoin
2008-05-25 15:16:17 +00:00
$categoryselect
$sortstatement " ;
2006-09-15 09:08:48 +00:00
// pull out all course matching the cat
2008-05-25 15:16:17 +00:00
if ( $courses = $DB -> get_records_sql ( $sql , $params )) {
2006-09-17 18:07:35 +00:00
// loop throught them
foreach ( $courses as $course ) {
2010-03-31 07:41:31 +00:00
context_instance_preload ( $course );
2006-09-19 08:16:26 +00:00
if ( isset ( $course -> visible ) && $course -> visible <= 0 ) {
2006-09-17 18:07:35 +00:00
// for hidden courses, require visibility check
2012-07-25 16:25:55 +08:00
if ( has_capability ( 'moodle/course:viewhiddencourses' , context_course :: instance ( $course -> id ))) {
2008-05-25 15:16:17 +00:00
$visiblecourses [ $course -> id ] = $course ;
2006-09-17 18:07:35 +00:00
}
} else {
2008-05-25 15:16:17 +00:00
$visiblecourses [ $course -> id ] = $course ;
2007-08-02 23:39:28 +00:00
}
2006-09-17 18:07:35 +00:00
}
2004-11-18 02:31:53 +00:00
}
2006-09-15 09:08:48 +00:00
return $visiblecourses ;
2003-08-21 14:04:04 +00:00
}
2004-11-18 02:31:53 +00:00
/**
2006-09-10 07:07:52 +00:00
* Returns list of courses , for whole site , or category
*
* Similar to get_courses , but allows paging
2007-08-02 23:39:28 +00:00
* Important : Using c .* for fields is extremely expensive because
2006-09-10 07:07:52 +00:00
* we are using distinct . You almost _NEVER_ need all the fields
* in such a large SELECT
*
2009-05-22 08:41:00 +00:00
* @ global object
* @ global object
* @ global object
* @ uses CONTEXT_COURSE
* @ param string | int $categoryid Either a category id or 'all' for everything
* @ param string $sort A field and direction to sort by
* @ param string $fields The additional fields to return
* @ param int $totalcount Reference for the number of courses
* @ param string $limitfrom The course to start from
* @ param string $limitnum The number of courses to limit to
2009-11-01 11:31:16 +00:00
* @ return array Array of courses
2006-09-10 07:07:52 +00:00
*/
2004-11-18 02:31:53 +00:00
function get_courses_page ( $categoryid = " all " , $sort = " c.sortorder ASC " , $fields = " c.* " ,
& $totalcount , $limitfrom = " " , $limitnum = " " ) {
2008-05-25 15:16:17 +00:00
global $USER , $CFG , $DB ;
2004-09-23 07:46:41 +00:00
2008-05-25 15:16:17 +00:00
$params = array ();
2007-08-02 23:39:28 +00:00
2006-09-15 09:08:48 +00:00
$categoryselect = " " ;
2013-01-29 15:06:37 +11:00
if ( $categoryid !== " all " && is_numeric ( $categoryid )) {
2008-05-25 15:16:17 +00:00
$categoryselect = " WHERE c.category = :catid " ;
$params [ 'catid' ] = $categoryid ;
2006-09-15 09:08:48 +00:00
} else {
2007-08-02 23:39:28 +00:00
$categoryselect = " " ;
}
2010-03-31 07:41:31 +00:00
list ( $ccselect , $ccjoin ) = context_instance_preload_sql ( 'c.id' , CONTEXT_COURSE , 'ctx' );
2010-12-30 01:48:43 +01:00
$totalcount = 0 ;
if ( ! $limitfrom ) {
$limitfrom = 0 ;
}
$visiblecourses = array ();
2010-03-31 07:41:31 +00:00
$sql = " SELECT $fields $ccselect
2008-05-25 15:16:17 +00:00
FROM { course } c
2010-03-31 07:41:31 +00:00
$ccjoin
2008-05-25 15:16:17 +00:00
$categoryselect
ORDER BY $sort " ;
2006-09-15 09:08:48 +00:00
// pull out all course matching the cat
2010-12-30 01:48:43 +01:00
$rs = $DB -> get_recordset_sql ( $sql , $params );
2006-09-15 09:08:48 +00:00
// iteration will have to be done inside loop to keep track of the limitfrom and limitnum
2008-05-25 15:16:17 +00:00
foreach ( $rs as $course ) {
2010-03-31 07:41:31 +00:00
context_instance_preload ( $course );
2007-10-10 12:19:27 +00:00
if ( $course -> visible <= 0 ) {
// for hidden courses, require visibility check
2012-07-25 16:25:55 +08:00
if ( has_capability ( 'moodle/course:viewhiddencourses' , context_course :: instance ( $course -> id ))) {
2006-09-15 09:08:48 +00:00
$totalcount ++ ;
2007-10-10 12:19:27 +00:00
if ( $totalcount > $limitfrom && ( ! $limitnum or count ( $visiblecourses ) < $limitnum )) {
2008-05-25 15:16:17 +00:00
$visiblecourses [ $course -> id ] = $course ;
2006-09-15 09:08:48 +00:00
}
}
2007-10-10 12:19:27 +00:00
} else {
$totalcount ++ ;
if ( $totalcount > $limitfrom && ( ! $limitnum or count ( $visiblecourses ) < $limitnum )) {
2008-05-25 15:16:17 +00:00
$visiblecourses [ $course -> id ] = $course ;
2007-10-10 12:19:27 +00:00
}
2007-08-02 23:39:28 +00:00
}
2006-09-15 09:08:48 +00:00
}
2008-05-25 15:16:17 +00:00
$rs -> close ();
2006-09-15 09:08:48 +00:00
return $visiblecourses ;
2003-08-15 13:59:24 +00:00
}
2008-05-25 09:39:02 +00:00
/**
2007-09-19 07:26:42 +00:00
* Retrieve course records with the course managers and other related records
* that we need for print_course () . This allows print_courses () to do its job
* in a constant number of DB queries , regardless of the number of courses ,
* role assignments , etc .
2007-09-20 13:15:26 +00:00
*
2007-09-19 07:26:42 +00:00
* The returned array is indexed on c . id , and each course will have
* - $course -> managers - array containing RA objects that include a $user obj
* with the minimal fields needed for fullname ()
*
2009-05-22 08:41:00 +00:00
* @ global object
* @ global object
* @ global object
* @ uses CONTEXT_COURSE
* @ uses CONTEXT_SYSTEM
* @ uses CONTEXT_COURSECAT
* @ uses SITEID
* @ param int | string $categoryid Either the categoryid for the courses or 'all'
* @ param string $sort A SQL sort field and direction
* @ param array $fields An array of additional fields to fetch
* @ return array
2007-09-19 07:26:42 +00:00
*/
function get_courses_wmanagers ( $categoryid = 0 , $sort = " c.sortorder ASC " , $fields = array ()) {
/*
2007-09-20 13:15:26 +00:00
* The plan is to
2007-09-19 07:26:42 +00:00
*
* - Grab the courses JOINed w / context
*
* - Grab the interesting course - manager RAs
* JOINed with a base user obj and add them to each course
*
* So as to do all the work in 2 DB queries . The RA + user JOIN
* ends up being pretty expensive if it happens over _all_
* courses on a large site . ( Are we surprised ! ? )
*
* So this should _never_ get called with 'all' on a large site .
*
*/
2008-05-25 15:16:17 +00:00
global $USER , $CFG , $DB ;
2007-09-19 07:26:42 +00:00
2008-05-25 15:16:17 +00:00
$params = array ();
2007-09-19 07:26:42 +00:00
$allcats = false ; // bool flag
if ( $categoryid === 'all' ) {
$categoryclause = '' ;
$allcats = true ;
} elseif ( is_numeric ( $categoryid )) {
2008-05-25 15:16:17 +00:00
$categoryclause = " c.category = :catid " ;
$params [ 'catid' ] = $categoryid ;
2007-09-19 07:26:42 +00:00
} else {
debugging ( " Could not recognise categoryid = $categoryid " );
$categoryclause = '' ;
}
$basefields = array ( 'id' , 'category' , 'sortorder' ,
'shortname' , 'fullname' , 'idnumber' ,
MDL-21782 reworked enrolment framework, the core infrastructure is in place, the basic plugins are all implemented; see the tracker issue for list of unfinished bits, expect more changes and improvements during the next week
AMOS START
MOV [sendcoursewelcomemessage,core_admin],[sendcoursewelcomemessage,enrol_self]
MOV [configsendcoursewelcomemessage,core_admin],[sendcoursewelcomemessage_desc,enrol_self]
MOV [enrolstartdate,core],[enrolstartdate,enrol_self]
MOV [enrolenddate,core],[enrolenddate,enrol_self]
CPY [welcometocourse,core],[welcometocourse,enrol_self]
CPY [welcometocoursetext,core],[welcometocoursetext,enrol_self]
MOV [notenrollable,core],[notenrollable,core_enrol]
MOV [enrolenddaterror,core],[enrolenddaterror,enrol_self]
MOV [enrolmentkeyhint,core],[passwordinvalidhint,enrol_self]
MOV [coursemanager,core_admin],[coursecontact,core_admin]
MOV [configcoursemanager,core_admin],[coursecontact_desc,core_admin]
MOV [enrolledincourserole,core],[enrolledincourserole,enrol_manual]
MOV [enrolme,core],[enrolme,core_enrol]
MOV [unenrol,core],[unenrol,core_enrol]
MOV [unenrolme,core],[unenrolme,core_enrol]
MOV [enrolmentnew,core],[enrolmentnew,core_enrol]
MOV [enrolmentnewuser,core],[enrolmentnewuser,core_enrol]
MOV [enrolments,core],[enrolments,core_enrol]
MOV [enrolperiod,core],[enrolperiod,core_enrol]
MOV [unenrolroleusers,core],[unenrolroleusers,core_enrol]
AMOS END
2010-06-21 15:30:49 +00:00
'startdate' , 'visible' ,
'newsitems' , 'groupmode' , 'groupmodeforce' );
2007-09-19 07:26:42 +00:00
if ( ! is_null ( $fields ) && is_string ( $fields )) {
if ( empty ( $fields )) {
$fields = $basefields ;
} else {
2007-09-20 13:15:26 +00:00
// turn the fields from a string to an array that
2007-09-19 07:26:42 +00:00
// get_user_courses_bycap() will like...
$fields = explode ( ',' , $fields );
$fields = array_map ( 'trim' , $fields );
$fields = array_unique ( array_merge ( $basefields , $fields ));
}
} elseif ( is_array ( $fields )) {
$fields = array_merge ( $basefields , $fields );
}
$coursefields = 'c.' . join ( ',c.' , $fields );
if ( empty ( $sort )) {
$sortstatement = " " ;
} else {
$sortstatement = " ORDER BY $sort " ;
}
2007-09-19 07:49:10 +00:00
$where = 'WHERE c.id != ' . SITEID ;
2007-09-19 07:26:42 +00:00
if ( $categoryclause !== '' ){
2007-09-19 07:49:10 +00:00
$where = " $where AND $categoryclause " ;
2007-09-19 07:26:42 +00:00
}
// pull out all courses matching the cat
2010-03-31 07:41:31 +00:00
list ( $ccselect , $ccjoin ) = context_instance_preload_sql ( 'c.id' , CONTEXT_COURSE , 'ctx' );
$sql = " SELECT $coursefields $ccselect
2008-05-25 15:16:17 +00:00
FROM { course } c
2010-03-31 07:41:31 +00:00
$ccjoin
2008-05-25 15:16:17 +00:00
$where
$sortstatement " ;
2007-09-19 07:26:42 +00:00
$catpaths = array ();
$catpath = NULL ;
2008-05-25 15:16:17 +00:00
if ( $courses = $DB -> get_records_sql ( $sql , $params )) {
2007-09-19 07:26:42 +00:00
// loop on courses materialising
2007-09-20 13:15:26 +00:00
// the context, and prepping data to fetch the
2007-09-19 07:26:42 +00:00
// managers efficiently later...
foreach ( $courses as $k => $course ) {
2010-03-31 07:41:31 +00:00
context_instance_preload ( $course );
2012-07-25 16:25:55 +08:00
$coursecontext = context_course :: instance ( $course -> id );
2010-03-31 07:41:31 +00:00
$courses [ $k ] = $course ;
2007-09-19 07:26:42 +00:00
$courses [ $k ] -> managers = array ();
if ( $allcats === false ) {
// single cat, so take just the first one...
if ( $catpath === NULL ) {
2010-03-31 07:41:31 +00:00
$catpath = preg_replace ( ':/\d+$:' , '' , $coursecontext -> path );
2007-09-19 07:26:42 +00:00
}
} else {
// chop off the contextid of the course itself
// like dirname() does...
2010-03-31 07:41:31 +00:00
$catpaths [] = preg_replace ( ':/\d+$:' , '' , $coursecontext -> path );
2007-09-19 07:26:42 +00:00
}
}
} else {
return array (); // no courses!
}
MDL-21782 reworked enrolment framework, the core infrastructure is in place, the basic plugins are all implemented; see the tracker issue for list of unfinished bits, expect more changes and improvements during the next week
AMOS START
MOV [sendcoursewelcomemessage,core_admin],[sendcoursewelcomemessage,enrol_self]
MOV [configsendcoursewelcomemessage,core_admin],[sendcoursewelcomemessage_desc,enrol_self]
MOV [enrolstartdate,core],[enrolstartdate,enrol_self]
MOV [enrolenddate,core],[enrolenddate,enrol_self]
CPY [welcometocourse,core],[welcometocourse,enrol_self]
CPY [welcometocoursetext,core],[welcometocoursetext,enrol_self]
MOV [notenrollable,core],[notenrollable,core_enrol]
MOV [enrolenddaterror,core],[enrolenddaterror,enrol_self]
MOV [enrolmentkeyhint,core],[passwordinvalidhint,enrol_self]
MOV [coursemanager,core_admin],[coursecontact,core_admin]
MOV [configcoursemanager,core_admin],[coursecontact_desc,core_admin]
MOV [enrolledincourserole,core],[enrolledincourserole,enrol_manual]
MOV [enrolme,core],[enrolme,core_enrol]
MOV [unenrol,core],[unenrol,core_enrol]
MOV [unenrolme,core],[unenrolme,core_enrol]
MOV [enrolmentnew,core],[enrolmentnew,core_enrol]
MOV [enrolmentnewuser,core],[enrolmentnewuser,core_enrol]
MOV [enrolments,core],[enrolments,core_enrol]
MOV [enrolperiod,core],[enrolperiod,core_enrol]
MOV [unenrolroleusers,core],[unenrolroleusers,core_enrol]
AMOS END
2010-06-21 15:30:49 +00:00
$CFG -> coursecontact = trim ( $CFG -> coursecontact );
if ( empty ( $CFG -> coursecontact )) {
2007-09-19 07:54:23 +00:00
return $courses ;
}
2010-07-20 01:38:54 +00:00
$managerroles = explode ( ',' , $CFG -> coursecontact );
2007-09-19 07:26:42 +00:00
$catctxids = '' ;
if ( count ( $managerroles )) {
if ( $allcats === true ) {
$catpaths = array_unique ( $catpaths );
$ctxids = array ();
foreach ( $catpaths as $cpath ) {
$ctxids = array_merge ( $ctxids , explode ( '/' , substr ( $cpath , 1 )));
}
$ctxids = array_unique ( $ctxids );
$catctxids = implode ( ',' , $ctxids );
2008-03-20 07:20:00 +00:00
unset ( $catpaths );
unset ( $cpath );
2007-09-19 07:26:42 +00:00
} else {
// take the ctx path from the first course
// as all categories will be the same...
$catpath = substr ( $catpath , 1 );
$catpath = preg_replace ( ':/\d+$:' , '' , $catpath );
$catctxids = str_replace ( '/' , ',' , $catpath );
}
if ( $categoryclause !== '' ) {
$categoryclause = " AND $categoryclause " ;
}
/*
2007-09-20 13:15:26 +00:00
* Note : Here we use a LEFT OUTER JOIN that can
2007-09-19 07:26:42 +00:00
* " optionally " match to avoid passing a ton of context
* ids in an IN () clause . Perhaps a subselect is faster .
*
* In any case , this SQL is not - so - nice over large sets of
* courses with no $categoryclause .
*
*/
$sql = " SELECT ctx.path, ctx.instanceid, ctx.contextlevel,
2012-05-18 11:40:35 +02:00
r . id AS roleid , r . name AS rolename , r . shortname AS roleshortname ,
rn . name AS rolecoursealias , u . id AS userid , u . firstname , u . lastname
2008-05-25 15:16:17 +00:00
FROM { role_assignments } ra
JOIN { context } ctx ON ra . contextid = ctx . id
JOIN { user } u ON ra . userid = u . id
JOIN { role } r ON ra . roleid = r . id
2012-05-18 11:40:35 +02:00
LEFT JOIN { role_names } rn ON ( rn . contextid = ctx . id AND rn . roleid = r . id )
2008-05-25 15:16:17 +00:00
LEFT OUTER JOIN { course } c
ON ( ctx . instanceid = c . id AND ctx . contextlevel = " .CONTEXT_COURSE. " )
2008-03-20 07:20:00 +00:00
WHERE ( c . id IS NOT NULL " ;
// under certain conditions, $catctxids is NULL
if ( $catctxids == NULL ){
$sql .= " ) " ;
} else {
$sql .= " OR ra.contextid IN ( $catctxids ) ) " ;
}
MDL-21782 reworked enrolment framework, the core infrastructure is in place, the basic plugins are all implemented; see the tracker issue for list of unfinished bits, expect more changes and improvements during the next week
AMOS START
MOV [sendcoursewelcomemessage,core_admin],[sendcoursewelcomemessage,enrol_self]
MOV [configsendcoursewelcomemessage,core_admin],[sendcoursewelcomemessage_desc,enrol_self]
MOV [enrolstartdate,core],[enrolstartdate,enrol_self]
MOV [enrolenddate,core],[enrolenddate,enrol_self]
CPY [welcometocourse,core],[welcometocourse,enrol_self]
CPY [welcometocoursetext,core],[welcometocoursetext,enrol_self]
MOV [notenrollable,core],[notenrollable,core_enrol]
MOV [enrolenddaterror,core],[enrolenddaterror,enrol_self]
MOV [enrolmentkeyhint,core],[passwordinvalidhint,enrol_self]
MOV [coursemanager,core_admin],[coursecontact,core_admin]
MOV [configcoursemanager,core_admin],[coursecontact_desc,core_admin]
MOV [enrolledincourserole,core],[enrolledincourserole,enrol_manual]
MOV [enrolme,core],[enrolme,core_enrol]
MOV [unenrol,core],[unenrol,core_enrol]
MOV [unenrolme,core],[unenrolme,core_enrol]
MOV [enrolmentnew,core],[enrolmentnew,core_enrol]
MOV [enrolmentnewuser,core],[enrolmentnewuser,core_enrol]
MOV [enrolments,core],[enrolments,core_enrol]
MOV [enrolperiod,core],[enrolperiod,core_enrol]
MOV [unenrolroleusers,core],[unenrolroleusers,core_enrol]
AMOS END
2010-06-21 15:30:49 +00:00
$sql .= " AND ra.roleid IN ( { $CFG -> coursecontact } )
2007-09-19 07:26:42 +00:00
$categoryclause
ORDER BY r . sortorder ASC , ctx . contextlevel ASC , ra . sortorder ASC " ;
2008-05-25 15:16:17 +00:00
$rs = $DB -> get_recordset_sql ( $sql , $params );
2007-09-20 13:15:26 +00:00
2007-09-19 07:26:42 +00:00
// This loop is fairly stupid as it stands - might get better
// results doing an initial pass clustering RAs by path.
2008-05-25 15:16:17 +00:00
foreach ( $rs as $ra ) {
2010-03-31 07:41:31 +00:00
$user = new stdClass ;
2007-10-10 12:19:27 +00:00
$user -> id = $ra -> userid ; unset ( $ra -> userid );
$user -> firstname = $ra -> firstname ; unset ( $ra -> firstname );
$user -> lastname = $ra -> lastname ; unset ( $ra -> lastname );
$ra -> user = $user ;
if ( $ra -> contextlevel == CONTEXT_SYSTEM ) {
foreach ( $courses as $k => $course ) {
$courses [ $k ] -> managers [] = $ra ;
}
2010-03-31 07:41:31 +00:00
} else if ( $ra -> contextlevel == CONTEXT_COURSECAT ) {
2007-10-10 12:19:27 +00:00
if ( $allcats === false ) {
// It always applies
2007-09-19 07:26:42 +00:00
foreach ( $courses as $k => $course ) {
$courses [ $k ] -> managers [] = $ra ;
}
2007-10-10 12:19:27 +00:00
} else {
foreach ( $courses as $k => $course ) {
2012-07-25 16:25:55 +08:00
$coursecontext = context_course :: instance ( $course -> id );
2007-10-10 12:19:27 +00:00
// Note that strpos() returns 0 as "matched at pos 0"
2010-03-31 07:41:31 +00:00
if ( strpos ( $coursecontext -> path , $ra -> path . '/' ) === 0 ) {
2007-10-10 12:19:27 +00:00
// Only add it to subpaths
2007-09-19 07:26:42 +00:00
$courses [ $k ] -> managers [] = $ra ;
}
}
}
2007-10-10 12:19:27 +00:00
} else { // course-level
2010-03-31 07:41:31 +00:00
if ( ! array_key_exists ( $ra -> instanceid , $courses )) {
2007-10-10 12:19:27 +00:00
//this course is not in a list, probably a frontpage course
continue ;
}
$courses [ $ra -> instanceid ] -> managers [] = $ra ;
2007-09-19 07:26:42 +00:00
}
}
2008-05-25 15:16:17 +00:00
$rs -> close ();
2007-09-19 07:26:42 +00:00
}
return $courses ;
}
2003-08-15 13:59:24 +00:00
2003-09-14 04:04:15 +00:00
/**
2005-07-12 02:23:58 +00:00
* A list of courses that match a search
2004-09-24 21:28:22 +00:00
*
2009-05-22 08:41:00 +00:00
* @ global object
* @ global object
* @ param array $searchterms An array of search criteria
* @ param string $sort A field and direction to sort by
* @ param int $page The page number to get
* @ param int $recordsperpage The number of records per page
* @ param int $totalcount Passed in by reference .
2005-07-12 02:23:58 +00:00
* @ return object { @ link $COURSE } records
2004-09-24 21:28:22 +00:00
*/
2004-09-22 16:15:23 +00:00
function get_courses_search ( $searchterms , $sort = 'fullname ASC' , $page = 0 , $recordsperpage = 50 , & $totalcount ) {
2008-05-25 15:16:17 +00:00
global $CFG , $DB ;
2003-08-15 13:59:24 +00:00
2008-05-25 20:43:46 +00:00
if ( $DB -> sql_regex_supported ()) {
$REGEXP = $DB -> sql_regex ( true );
$NOTREGEXP = $DB -> sql_regex ( false );
2003-08-15 13:59:24 +00:00
}
2008-05-25 20:43:46 +00:00
$searchcond = array ();
$params = array ();
$i = 0 ;
2003-08-15 13:59:24 +00:00
2011-10-26 19:04:31 +02:00
// Thanks Oracle for your non-ansi concat and type limits in coalesce. MDL-29912
if ( $DB -> get_dbfamily () == 'oracle' ) {
$concat = $DB -> sql_concat ( 'c.summary' , " ' ' " , 'c.fullname' , " ' ' " , 'c.idnumber' , " ' ' " , 'c.shortname' );
} else {
$concat = $DB -> sql_concat ( " COALESCE(c.summary, ' " . $DB -> sql_empty () . " ') " , " ' ' " , 'c.fullname' , " ' ' " , 'c.idnumber' , " ' ' " , 'c.shortname' );
}
2008-05-25 15:16:17 +00:00
2003-08-15 13:59:24 +00:00
foreach ( $searchterms as $searchterm ) {
2008-05-25 20:43:46 +00:00
$i ++ ;
2006-10-31 19:54:46 +00:00
2010-09-04 12:45:47 +00:00
$NOT = false ; /// Initially we aren't going to perform NOT LIKE searches, only MSSQL and Oracle
2008-01-01 12:03:00 +00:00
/// will use it to simulate the "-" operator with LIKE clause
2006-10-31 19:54:46 +00:00
/// Under Oracle and MSSQL, trim the + and - operators and perform
2008-01-01 12:03:00 +00:00
/// simpler LIKE (or NOT LIKE) queries
2008-05-25 20:43:46 +00:00
if ( ! $DB -> sql_regex_supported ()) {
2008-01-01 12:03:00 +00:00
if ( substr ( $searchterm , 0 , 1 ) == '-' ) {
2010-09-04 12:45:47 +00:00
$NOT = true ;
2008-01-01 12:03:00 +00:00
}
2006-10-31 19:54:46 +00:00
$searchterm = trim ( $searchterm , '+-' );
}
2008-05-25 20:43:46 +00:00
// TODO: +- may not work for non latin languages
2008-05-25 15:16:17 +00:00
2004-09-22 16:15:23 +00:00
if ( substr ( $searchterm , 0 , 1 ) == '+' ) {
2008-05-25 20:43:46 +00:00
$searchterm = trim ( $searchterm , '+-' );
$searchterm = preg_quote ( $searchterm , '|' );
$searchcond [] = " $concat $REGEXP :ss $i " ;
$params [ 'ss' . $i ] = " (^|[^a-zA-Z0-9]) $searchterm ([^a-zA-Z0-9]| $ ) " ;
2003-08-19 05:32:20 +00:00
} else if ( substr ( $searchterm , 0 , 1 ) == " - " ) {
2008-05-25 20:43:46 +00:00
$searchterm = trim ( $searchterm , '+-' );
$searchterm = preg_quote ( $searchterm , '|' );
$searchcond [] = " $concat $NOTREGEXP :ss $i " ;
$params [ 'ss' . $i ] = " (^|[^a-zA-Z0-9]) $searchterm ([^a-zA-Z0-9]| $ ) " ;
2003-08-19 05:32:20 +00:00
} else {
2010-09-04 14:52:47 +00:00
$searchcond [] = $DB -> sql_like ( $concat , " :ss $i " , false , true , $NOT );
2008-05-25 20:43:46 +00:00
$params [ 'ss' . $i ] = " % $searchterm % " ;
2003-08-19 05:32:20 +00:00
}
2003-08-15 13:59:24 +00:00
}
2008-05-25 20:43:46 +00:00
if ( empty ( $searchcond )) {
$totalcount = 0 ;
return array ();
}
$searchcond = implode ( " AND " , $searchcond );
2010-12-30 01:48:43 +01:00
$courses = array ();
$c = 0 ; // counts how many visible courses we've seen
// Tiki pagination
$limitfrom = $page * $recordsperpage ;
$limitto = $limitfrom + $recordsperpage ;
2010-03-31 07:41:31 +00:00
list ( $ccselect , $ccjoin ) = context_instance_preload_sql ( 'c.id' , CONTEXT_COURSE , 'ctx' );
$sql = " SELECT c.* $ccselect
2008-05-25 15:16:17 +00:00
FROM { course } c
2010-03-31 07:41:31 +00:00
$ccjoin
2008-05-25 20:43:46 +00:00
WHERE $searchcond AND c . id <> " .SITEID. "
ORDER BY $sort " ;
2007-09-19 07:08:37 +00:00
2010-12-30 01:48:43 +01:00
$rs = $DB -> get_recordset_sql ( $sql , $params );
foreach ( $rs as $course ) {
context_instance_preload ( $course );
2012-07-25 16:25:55 +08:00
$coursecontext = context_course :: instance ( $course -> id );
2010-12-30 01:48:43 +01:00
if ( $course -> visible || has_capability ( 'moodle/course:viewhiddencourses' , $coursecontext )) {
// Don't exit this loop till the end
// we need to count all the visible courses
// to update $totalcount
if ( $c >= $limitfrom && $c < $limitto ) {
$courses [ $course -> id ] = $course ;
2003-08-15 13:59:24 +00:00
}
2010-12-30 01:48:43 +01:00
$c ++ ;
2003-08-15 13:59:24 +00:00
}
}
2010-12-30 01:48:43 +01:00
$rs -> close ();
2003-08-15 13:59:24 +00:00
2007-09-19 07:08:37 +00:00
// our caller expects 2 bits of data - our return
// array, and an updated $totalcount
$totalcount = $c ;
2003-08-15 13:59:24 +00:00
return $courses ;
}
2003-09-14 04:04:15 +00:00
/**
2007-09-19 07:27:20 +00:00
* Returns a sorted list of categories . Each category object has a context
* property that is a context object .
2007-09-20 13:15:26 +00:00
*
2007-09-19 07:27:20 +00:00
* When asking for $parent = 'none' it will return all the categories , regardless
* of depth . Wheen asking for a specific parent , the default is to return
* a " shallow " resultset . Pass false to $shallow and it will return all
2007-09-20 13:15:26 +00:00
* the child categories as well .
*
2009-05-22 08:41:00 +00:00
* @ global object
* @ uses CONTEXT_COURSECAT
2006-09-10 07:07:52 +00:00
* @ param string $parent The parent category if any
* @ param string $sort the sortorder
2007-09-19 07:27:20 +00:00
* @ param bool $shallow - set to false to get the children too
2006-09-10 07:07:52 +00:00
* @ return array of categories
2004-09-24 21:28:22 +00:00
*/
2007-09-19 07:27:20 +00:00
function get_categories ( $parent = 'none' , $sort = NULL , $shallow = true ) {
2008-05-25 15:16:17 +00:00
global $DB ;
2007-09-19 07:27:20 +00:00
if ( $sort === NULL ) {
$sort = 'ORDER BY cc.sortorder ASC' ;
} elseif ( $sort === '' ) {
// leave it as empty
} else {
$sort = " ORDER BY $sort " ;
}
2003-08-15 13:59:24 +00:00
2010-03-31 07:41:31 +00:00
list ( $ccselect , $ccjoin ) = context_instance_preload_sql ( 'cc.id' , CONTEXT_COURSECAT , 'ctx' );
2005-02-23 01:49:22 +00:00
if ( $parent === 'none' ) {
2010-03-31 07:41:31 +00:00
$sql = " SELECT cc.* $ccselect
2008-05-25 15:16:17 +00:00
FROM { course_categories } cc
2010-03-31 07:41:31 +00:00
$ccjoin
2007-09-19 07:27:20 +00:00
$sort " ;
2008-05-25 15:16:17 +00:00
$params = array ();
2007-09-19 07:27:20 +00:00
} elseif ( $shallow ) {
2010-03-31 07:41:31 +00:00
$sql = " SELECT cc.* $ccselect
2008-05-25 15:16:17 +00:00
FROM { course_categories } cc
2010-03-31 07:41:31 +00:00
$ccjoin
2008-05-25 15:16:17 +00:00
WHERE cc . parent = ?
2007-09-19 07:27:20 +00:00
$sort " ;
2008-05-25 15:16:17 +00:00
$params = array ( $parent );
2003-08-15 13:59:24 +00:00
} else {
2010-03-31 07:41:31 +00:00
$sql = " SELECT cc.* $ccselect
2008-05-25 15:16:17 +00:00
FROM { course_categories } cc
2010-03-31 07:41:31 +00:00
$ccjoin
2008-05-25 15:16:17 +00:00
JOIN { course_categories } ccp
2011-08-05 20:13:12 +02:00
ON (( cc . parent = ccp . id ) OR ( cc . path LIKE " . $DB->sql_concat ('ccp.path', " '/%' " ). " ))
2008-05-25 15:16:17 +00:00
WHERE ccp . id = ?
2007-09-19 07:27:20 +00:00
$sort " ;
2008-05-25 15:16:17 +00:00
$params = array ( $parent );
2003-08-15 13:59:24 +00:00
}
2007-09-19 07:27:20 +00:00
$categories = array ();
2010-12-30 01:48:43 +01:00
$rs = $DB -> get_recordset_sql ( $sql , $params );
foreach ( $rs as $cat ) {
context_instance_preload ( $cat );
2012-07-25 16:25:55 +08:00
$catcontext = context_coursecat :: instance ( $cat -> id );
2010-12-30 01:48:43 +01:00
if ( $cat -> visible || has_capability ( 'moodle/category:viewhiddencategories' , $catcontext )) {
$categories [ $cat -> id ] = $cat ;
2003-08-15 13:59:24 +00:00
}
}
2010-12-30 01:48:43 +01:00
$rs -> close ();
2003-08-15 13:59:24 +00:00
return $categories ;
}
2007-02-08 08:06:08 +00:00
/**
* Returns an array of category ids of all the subcategories for a given
* category .
2009-05-22 08:41:00 +00:00
*
* @ global object
* @ param int $catid - The id of the category whose subcategories we want to find .
2007-02-08 08:06:08 +00:00
* @ return array of category ids .
*/
function get_all_subcategories ( $catid ) {
2008-05-25 15:16:17 +00:00
global $DB ;
2007-02-08 08:06:08 +00:00
$subcats = array ();
2008-05-25 15:16:17 +00:00
if ( $categories = $DB -> get_records ( 'course_categories' , array ( 'parent' => $catid ))) {
2007-02-08 08:06:08 +00:00
foreach ( $categories as $cat ) {
array_push ( $subcats , $cat -> id );
$subcats = array_merge ( $subcats , get_all_subcategories ( $cat -> id ));
}
}
return $subcats ;
}
2003-09-14 04:04:15 +00:00
/**
2008-06-16 14:25:53 +00:00
* Return specified category , default if given does not exist
2009-11-01 11:31:16 +00:00
*
2009-05-22 08:41:00 +00:00
* @ global object
* @ uses MAX_COURSES_IN_CATEGORY
* @ uses CONTEXT_COURSECAT
* @ uses SYSCONTEXTID
2008-06-16 14:25:53 +00:00
* @ param int $catid course category id
* @ return object caregory
*/
function get_course_category ( $catid = 0 ) {
global $DB ;
$category = false ;
if ( ! empty ( $catid )) {
$category = $DB -> get_record ( 'course_categories' , array ( 'id' => $catid ));
}
2004-09-21 11:41:58 +00:00
2008-06-16 14:25:53 +00:00
if ( ! $category ) {
// the first category is considered default for now
if ( $category = $DB -> get_records ( 'course_categories' , null , 'sortorder' , '*' , 0 , 1 )) {
$category = reset ( $category );
} else {
2010-09-21 08:07:44 +00:00
$cat = new stdClass ();
2008-06-16 14:25:53 +00:00
$cat -> name = get_string ( 'miscellaneous' );
$cat -> depth = 1 ;
$cat -> sortorder = MAX_COURSES_IN_CATEGORY ;
$cat -> timemodified = time ();
2009-06-03 20:16:20 +00:00
$catid = $DB -> insert_record ( 'course_categories' , $cat );
2008-06-16 14:25:53 +00:00
// make sure category context exists
2012-07-25 16:25:55 +08:00
context_coursecat :: instance ( $catid );
2008-06-16 14:25:53 +00:00
mark_context_dirty ( '/' . SYSCONTEXTID );
2009-04-20 05:59:11 +00:00
fix_course_sortorder (); // Required to build course_categories.depth and .path.
2008-06-16 14:25:53 +00:00
$category = $DB -> get_record ( 'course_categories' , array ( 'id' => $catid ));
2005-08-16 23:15:58 +00:00
}
2008-06-16 14:25:53 +00:00
}
2004-11-17 06:57:28 +00:00
2008-06-16 14:25:53 +00:00
return $category ;
}
/**
* Fixes course category and course sortorder , also verifies category and course parents and paths .
2008-08-27 11:31:37 +00:00
* ( circular references are not fixed )
2009-05-22 08:41:00 +00:00
*
* @ global object
* @ global object
* @ uses MAX_COURSES_IN_CATEGORY
* @ uses MAX_COURSE_CATEGORIES
* @ uses SITEID
* @ uses CONTEXT_COURSE
* @ return void
2008-06-16 14:25:53 +00:00
*/
function fix_course_sortorder () {
global $DB , $SITE ;
//WARNING: this is PHP5 only code!
if ( $unsorted = $DB -> get_records ( 'course_categories' , array ( 'sortorder' => 0 ))) {
//move all categories that are not sorted yet to the end
$DB -> set_field ( 'course_categories' , 'sortorder' , MAX_COURSES_IN_CATEGORY * MAX_COURSE_CATEGORIES , array ( 'sortorder' => 0 ));
}
$allcats = $DB -> get_records ( 'course_categories' , null , 'sortorder, id' , 'id, sortorder, parent, depth, path' );
$topcats = array ();
$brokencats = array ();
foreach ( $allcats as $cat ) {
$sortorder = ( int ) $cat -> sortorder ;
if ( ! $cat -> parent ) {
while ( isset ( $topcats [ $sortorder ])) {
$sortorder ++ ;
}
$topcats [ $sortorder ] = $cat ;
continue ;
}
if ( ! isset ( $allcats [ $cat -> parent ])) {
$brokencats [] = $cat ;
continue ;
2007-09-19 07:27:08 +00:00
}
2008-06-16 14:25:53 +00:00
if ( ! isset ( $allcats [ $cat -> parent ] -> children )) {
$allcats [ $cat -> parent ] -> children = array ();
2007-09-19 07:27:08 +00:00
}
2008-06-16 14:25:53 +00:00
while ( isset ( $allcats [ $cat -> parent ] -> children [ $sortorder ])) {
$sortorder ++ ;
}
$allcats [ $cat -> parent ] -> children [ $sortorder ] = $cat ;
2005-08-16 23:15:58 +00:00
}
2008-06-16 14:25:53 +00:00
unset ( $allcats );
2005-01-13 02:34:45 +00:00
2008-06-16 14:25:53 +00:00
// add broken cats to category tree
if ( $brokencats ) {
$defaultcat = reset ( $topcats );
foreach ( $brokencats as $cat ) {
$topcats [] = $cat ;
2008-08-27 11:31:37 +00:00
}
2004-11-17 06:57:28 +00:00
}
2008-06-16 14:25:53 +00:00
// now walk recursively the tree and fix any problems found
$sortorder = 0 ;
$fixcontexts = array ();
_fix_course_cats ( $topcats , $sortorder , 0 , 0 , '' , $fixcontexts );
// detect if there are "multiple" frontpage courses and fix them if needed
$frontcourses = $DB -> get_records ( 'course' , array ( 'category' => 0 ), 'id' );
if ( count ( $frontcourses ) > 1 ) {
if ( isset ( $frontcourses [ SITEID ])) {
$frontcourse = $frontcourses [ SITEID ];
unset ( $frontcourses [ SITEID ]);
} else {
$frontcourse = array_shift ( $frontcourses );
}
$defaultcat = reset ( $topcats );
foreach ( $frontcourses as $course ) {
$DB -> set_field ( 'course' , 'category' , $defaultcat -> id , array ( 'id' => $course -> id ));
2012-07-25 16:25:55 +08:00
$context = context_course :: instance ( $course -> id );
2008-06-16 14:25:53 +00:00
$fixcontexts [ $context -> id ] = $context ;
}
unset ( $frontcourses );
} else {
$frontcourse = reset ( $frontcourses );
2005-02-23 01:49:22 +00:00
}
2008-06-16 14:25:53 +00:00
// now fix the paths and depths in context table if needed
if ( $fixcontexts ) {
2011-10-14 12:48:00 +02:00
foreach ( $fixcontexts as $fixcontext ) {
$fixcontext -> reset_paths ( false );
}
context_helper :: build_all_paths ( false );
unset ( $fixcontexts );
2005-01-13 02:34:45 +00:00
}
2007-08-02 23:39:28 +00:00
2008-06-16 14:25:53 +00:00
// release memory
unset ( $topcats );
unset ( $brokencats );
unset ( $fixcontexts );
// fix frontpage course sortorder
if ( $frontcourse -> sortorder != 1 ) {
$DB -> set_field ( 'course' , 'sortorder' , 1 , array ( 'id' => $frontcourse -> id ));
2005-01-13 02:34:45 +00:00
}
2008-06-16 14:25:53 +00:00
// now fix the course counts in category records if needed
$sql = " SELECT cc.id, cc.coursecount, COUNT(c.id) AS newcount
FROM { course_categories } cc
LEFT JOIN { course } c ON c . category = cc . id
GROUP BY cc . id , cc . coursecount
HAVING cc . coursecount <> COUNT ( c . id ) " ;
2004-11-17 06:57:28 +00:00
2008-06-16 14:25:53 +00:00
if ( $updatecounts = $DB -> get_records_sql ( $sql )) {
2011-01-07 11:54:41 +08:00
// categories with more courses than MAX_COURSES_IN_CATEGORY
$categories = array ();
2008-06-16 14:25:53 +00:00
foreach ( $updatecounts as $cat ) {
$cat -> coursecount = $cat -> newcount ;
2011-01-07 11:54:41 +08:00
if ( $cat -> coursecount >= MAX_COURSES_IN_CATEGORY ) {
$categories [] = $cat -> id ;
}
2008-06-16 14:25:53 +00:00
unset ( $cat -> newcount );
$DB -> update_record_raw ( 'course_categories' , $cat , true );
2008-08-27 11:31:37 +00:00
}
2011-01-07 11:54:41 +08:00
if ( ! empty ( $categories )) {
$str = implode ( ', ' , $categories );
debugging ( " The number of courses (category id: $str ) has reached MAX_COURSES_IN_CATEGORY ( " . MAX_COURSES_IN_CATEGORY . " ), it will cause a sorting performance issue, please increase the value of MAX_COURSES_IN_CATEGORY in lib/datalib.php file. See tracker issue: MDL-25669 " , DEBUG_DEVELOPER );
}
2003-08-15 13:59:24 +00:00
}
2004-09-21 11:41:58 +00:00
2008-06-16 14:25:53 +00:00
// now make sure that sortorders in course table are withing the category sortorder ranges
course categories: Fix many bugs with category editing and permissions. Clean up code.
Bugs: MDL-17479, MDL-16426, MDL-16063, MDL-16013, MDL-15658, MDL-15556, MDL-15161, MDL-14925, MDL-13742, MDL-11557.
* Simplify category editing permissions to just moodle/category:manage and moodle/category:seehiddencategories.
* Enforce those correctly. (Note MDL 17502 is still outstanding.)
* Don't screw up category sort order when you just edit name or description.
* Niceties like where redirects go when you cancel or submit forms.
* Make sure a global course creator can see the site admin block.
* Don't allow a category to be made the child of one of its children!
* General code cleanup to bring key files more in line with best pracitice.
Apologies for the fact it is one big patch, rather than a series of smaller patches. However, categoryedit.php, category.php and index.php where in pretty bad shape and needed significant cleaning up. categoryedit.php, in particular, was almost completely rewritten.
Merged from MOODLE_19_STABLE.
2008-12-04 08:53:10 +00:00
$sql = " SELECT DISTINCT cc.id, cc.sortorder
2008-06-16 14:25:53 +00:00
FROM { course_categories } cc
JOIN { course } c ON c . category = cc . id
WHERE c . sortorder < cc . sortorder OR c . sortorder > cc . sortorder + " .MAX_COURSES_IN_CATEGORY;
if ( $fixcategories = $DB -> get_records_sql ( $sql )) {
//fix the course sortorder ranges
foreach ( $fixcategories as $cat ) {
$sql = " UPDATE { course}
2008-11-26 08:51:08 +00:00
SET sortorder = " . $DB->sql_modulo ('sortorder', MAX_COURSES_IN_CATEGORY). " + ?
2008-06-16 14:25:53 +00:00
WHERE category = ? " ;
$DB -> execute ( $sql , array ( $cat -> sortorder , $cat -> id ));
}
2005-02-23 01:49:22 +00:00
}
2008-06-16 14:25:53 +00:00
unset ( $fixcategories );
// categories having courses with sortorder duplicates or having gaps in sortorder
$sql = " SELECT DISTINCT c1.category AS id , cc.sortorder
FROM { course } c1
JOIN { course } c2 ON c1 . sortorder = c2 . sortorder
JOIN { course_categories } cc ON ( c1 . category = cc . id )
WHERE c1 . id <> c2 . id " ;
$fixcategories = $DB -> get_records_sql ( $sql );
$sql = " SELECT cc.id, cc.sortorder, cc.coursecount, MAX(c.sortorder) AS maxsort, MIN(c.sortorder) AS minsort
FROM { course_categories } cc
JOIN { course } c ON c . category = cc . id
GROUP BY cc . id , cc . sortorder , cc . coursecount
HAVING ( MAX ( c . sortorder ) <> cc . sortorder + cc . coursecount ) OR ( MIN ( c . sortorder ) <> cc . sortorder + 1 ) " ;
$gapcategories = $DB -> get_records_sql ( $sql );
foreach ( $gapcategories as $cat ) {
if ( isset ( $fixcategories [ $cat -> id ])) {
// duplicates detected already
} else if ( $cat -> minsort == $cat -> sortorder and $cat -> maxsort == $cat -> sortorder + $cat -> coursecount - 1 ) {
// easy - new course inserted with sortorder 0, the rest is ok
$sql = " UPDATE { course}
SET sortorder = sortorder + 1
WHERE category = ? " ;
$DB -> execute ( $sql , array ( $cat -> id ));
2005-01-25 05:27:41 +00:00
2008-06-16 14:25:53 +00:00
} else {
// it needs full resorting
$fixcategories [ $cat -> id ] = $cat ;
2004-05-30 00:33:45 +00:00
}
}
2008-06-16 14:25:53 +00:00
unset ( $gapcategories );
2004-09-21 11:41:58 +00:00
2008-06-16 14:25:53 +00:00
// fix course sortorders in problematic categories only
foreach ( $fixcategories as $cat ) {
$i = 1 ;
$courses = $DB -> get_records ( 'course' , array ( 'category' => $cat -> id ), 'sortorder ASC, id DESC' , 'id, sortorder' );
foreach ( $courses as $course ) {
if ( $course -> sortorder != $cat -> sortorder + $i ) {
$course -> sortorder = $cat -> sortorder + $i ;
2008-08-27 11:31:37 +00:00
$DB -> update_record_raw ( 'course' , $course , true );
2008-06-16 14:25:53 +00:00
}
$i ++ ;
}
}
2003-08-15 13:59:24 +00:00
}
2007-09-12 02:56:36 +00:00
/**
2008-06-16 14:25:53 +00:00
* Internal recursive category verification function , do not use directly !
2009-05-22 08:41:00 +00:00
*
* @ todo Document the arguments of this function better
*
* @ global object
* @ uses MAX_COURSES_IN_CATEGORY
* @ uses CONTEXT_COURSECAT
* @ param array $children
* @ param int $sortorder
* @ param string $parent
* @ param int $depth
* @ param string $path
* @ param array $fixcontexts
* @ return void
2008-06-16 14:25:53 +00:00
*/
function _fix_course_cats ( $children , & $sortorder , $parent , $depth , $path , & $fixcontexts ) {
2008-05-30 16:47:21 +00:00
global $DB ;
2007-09-12 02:56:36 +00:00
2008-06-16 14:25:53 +00:00
$depth ++ ;
2008-05-30 16:47:21 +00:00
2008-06-16 14:25:53 +00:00
foreach ( $children as $cat ) {
$sortorder = $sortorder + MAX_COURSES_IN_CATEGORY ;
$update = false ;
if ( $parent != $cat -> parent or $depth != $cat -> depth or $path . '/' . $cat -> id != $cat -> path ) {
$cat -> parent = $parent ;
$cat -> depth = $depth ;
$cat -> path = $path . '/' . $cat -> id ;
$update = true ;
2008-05-30 16:47:21 +00:00
2008-06-16 14:25:53 +00:00
// make sure context caches are rebuild and dirty contexts marked
2012-07-25 16:25:55 +08:00
$context = context_coursecat :: instance ( $cat -> id );
2008-06-16 14:25:53 +00:00
$fixcontexts [ $context -> id ] = $context ;
}
if ( $cat -> sortorder != $sortorder ) {
$cat -> sortorder = $sortorder ;
$update = true ;
}
if ( $update ) {
$DB -> update_record ( 'course_categories' , $cat , true );
}
if ( isset ( $cat -> children )) {
_fix_course_cats ( $cat -> children , $sortorder , $cat -> id , $cat -> depth , $cat -> path , $fixcontexts );
2007-09-12 02:56:36 +00:00
}
}
}
2007-01-19 08:57:13 +00:00
/**
* List of remote courses that a user has access to via MNET .
* Works only on the IDP
*
2009-05-22 08:41:00 +00:00
* @ global object
* @ global object
* @ param int @ userid The user id to get remote courses for
* @ return array Array of { @ link $COURSE } of course objects
2007-01-19 08:57:13 +00:00
*/
function get_my_remotecourses ( $userid = 0 ) {
2008-05-30 16:47:21 +00:00
global $DB , $USER ;
2007-01-19 08:57:13 +00:00
if ( empty ( $userid )) {
$userid = $USER -> id ;
}
2010-07-20 09:02:23 +00:00
// we can not use SELECT DISTINCT + text field (summary) because of MS SQL and Oracle, subselect used therefore
$sql = " SELECT c.id, c.remoteid, c.shortname, c.fullname,
2010-07-17 22:34:12 +00:00
c . hostid , c . summary , c . summaryformat , c . categoryname AS cat_name ,
2007-01-19 09:23:47 +00:00
h . name AS hostname
2010-07-17 22:34:12 +00:00
FROM { mnetservice_enrol_courses } c
2010-07-20 09:02:23 +00:00
JOIN ( SELECT DISTINCT hostid , remotecourseid
FROM { mnetservice_enrol_enrolments }
WHERE userid = ?
) e ON ( e . hostid = c . hostid AND e . remotecourseid = c . remoteid )
JOIN { mnet_host } h ON h . id = c . hostid " ;
2007-01-19 08:57:13 +00:00
2008-05-30 16:47:21 +00:00
return $DB -> get_records_sql ( $sql , array ( $userid ));
2007-01-19 08:57:13 +00:00
}
/**
* List of remote hosts that a user has access to via MNET .
* Works on the SP
*
2009-05-22 08:41:00 +00:00
* @ global object
* @ global object
* @ return array | bool Array of host objects or false
2007-01-19 08:57:13 +00:00
*/
function get_my_remotehosts () {
global $CFG , $USER ;
if ( $USER -> mnethostid == $CFG -> mnet_localhost_id ) {
return false ; // Return nothing on the IDP
}
if ( ! empty ( $USER -> mnet_foreign_host_array ) && is_array ( $USER -> mnet_foreign_host_array )) {
return $USER -> mnet_foreign_host_array ;
}
return false ;
}
2004-09-24 21:28:22 +00:00
2003-09-14 04:04:15 +00:00
/**
2004-09-24 21:28:22 +00:00
* This function creates a default separated / connected scale
*
* This function creates a default separated / connected scale
* so there ' s something in the database . The locations of
* strings and files is a bit odd , but this is because we
* need to maintain backward compatibility with many different
* existing language translations and older sites .
2009-05-22 08:41:00 +00:00
*
* @ global object
* @ return void
2004-09-24 21:28:22 +00:00
*/
2003-08-15 13:59:24 +00:00
function make_default_scale () {
2010-04-27 23:50:06 +00:00
global $DB ;
2003-08-15 13:59:24 +00:00
2012-01-15 13:37:53 +01:00
$defaultscale = new stdClass ();
2003-08-15 13:59:24 +00:00
$defaultscale -> courseid = 0 ;
$defaultscale -> userid = 0 ;
2004-09-22 16:15:23 +00:00
$defaultscale -> name = get_string ( 'separateandconnected' );
2010-04-27 23:50:06 +00:00
$defaultscale -> description = get_string ( 'separateandconnectedinfo' );
2004-09-22 16:15:23 +00:00
$defaultscale -> scale = get_string ( 'postrating1' , 'forum' ) . ',' .
get_string ( 'postrating2' , 'forum' ) . ',' .
get_string ( 'postrating3' , 'forum' );
2003-08-15 13:59:24 +00:00
$defaultscale -> timemodified = time ();
2010-09-03 17:47:41 +00:00
$defaultscale -> id = $DB -> insert_record ( 'scale' , $defaultscale );
$DB -> execute ( " UPDATE { forum} SET scale = ? " , array ( $defaultscale -> id ));
2003-08-15 13:59:24 +00:00
}
2004-09-24 21:28:22 +00:00
2003-09-14 04:04:15 +00:00
/**
2004-09-24 21:28:22 +00:00
* Returns a menu of all available scales from the site as well as the given course
*
2009-05-22 08:41:00 +00:00
* @ global object
2004-09-24 21:28:22 +00:00
* @ param int $courseid The id of the course as found in the 'course' table .
2009-05-22 08:41:00 +00:00
* @ return array
2004-09-24 21:28:22 +00:00
*/
2003-08-15 13:59:24 +00:00
function get_scales_menu ( $courseid = 0 ) {
2008-05-30 16:47:21 +00:00
global $DB ;
2003-08-15 13:59:24 +00:00
2008-05-30 16:47:21 +00:00
$sql = " SELECT id, name
FROM { scale }
WHERE courseid = 0 or courseid = ?
2003-08-15 13:59:24 +00:00
ORDER BY courseid ASC , name ASC " ;
2008-05-30 16:47:21 +00:00
$params = array ( $courseid );
2003-08-15 13:59:24 +00:00
2008-05-30 16:47:21 +00:00
if ( $scales = $DB -> get_records_sql_menu ( $sql , $params )) {
2003-08-15 13:59:24 +00:00
return $scales ;
}
make_default_scale ();
2008-05-30 16:47:21 +00:00
return $DB -> get_records_sql_menu ( $sql , $params );
2003-08-15 13:59:24 +00:00
}
2005-04-10 09:15:15 +00:00
/**
* Given a set of timezone records , put them in the database , replacing what is there
*
2009-05-22 08:41:00 +00:00
* @ global object
2005-04-10 09:15:15 +00:00
* @ param array $timezones An array of timezone records
2009-05-22 08:41:00 +00:00
* @ return void
2005-04-10 09:15:15 +00:00
*/
function update_timezone_records ( $timezones ) {
2008-05-30 16:47:21 +00:00
global $DB ;
2005-04-10 09:15:15 +00:00
/// Clear out all the old stuff
2008-05-31 15:32:28 +00:00
$DB -> delete_records ( 'timezone' );
2005-04-10 09:15:15 +00:00
/// Insert all the new stuff
foreach ( $timezones as $timezone ) {
2007-12-31 15:20:02 +00:00
if ( is_array ( $timezone )) {
$timezone = ( object ) $timezone ;
}
2008-05-30 16:47:21 +00:00
$DB -> insert_record ( 'timezone' , $timezone );
2005-04-10 09:15:15 +00:00
}
}
2002-12-17 04:41:18 +00:00
/// MODULE FUNCTIONS /////////////////////////////////////////////////
2003-09-14 04:04:15 +00:00
/**
2004-09-24 21:28:22 +00:00
* Just gets a raw list of all modules in a course
*
2009-05-22 08:41:00 +00:00
* @ global object
2004-09-24 21:28:22 +00:00
* @ param int $courseid The id of the course as found in the 'course' table .
2009-05-22 08:41:00 +00:00
* @ return array
2004-09-24 21:28:22 +00:00
*/
2002-12-20 14:44:14 +00:00
function get_course_mods ( $courseid ) {
2008-05-30 16:47:21 +00:00
global $DB ;
2002-12-20 14:44:14 +00:00
2006-02-14 02:01:31 +00:00
if ( empty ( $courseid )) {
return false ; // avoid warnings
}
2008-05-30 16:47:21 +00:00
return $DB -> get_records_sql ( " SELECT cm.*, m.name as modname
FROM { modules } m , { course_modules } cm
WHERE cm . course = ? AND cm . module = m . id AND m . visible = 1 " ,
array ( $courseid )); // no disabled mods
2002-12-20 14:44:14 +00:00
}
2004-09-24 21:28:22 +00:00
2003-09-14 04:04:15 +00:00
/**
2006-08-08 22:09:55 +00:00
* Given an id of a course module , finds the coursemodule description
2004-09-24 21:28:22 +00:00
*
2009-05-22 08:41:00 +00:00
* @ global object
2008-08-27 11:31:37 +00:00
* @ param string $modulename name of module type , eg . resource , assignment , ... ( optional , slower and less safe if not specified )
2006-08-08 22:09:55 +00:00
* @ param int $cmid course module id ( id in course_modules table )
* @ param int $courseid optional course id for extra validation
2008-08-27 11:31:37 +00:00
* @ param bool $sectionnum include relative section number ( 0 , 1 , 2 ... )
2009-07-04 11:15:16 +00:00
* @ param int $strictness IGNORE_MISSING means compatible mode , false returned if record not found , debug message if more found ;
* IGNORE_MULTIPLE means return first , ignore multiple records found ( not recommended );
* MUST_EXIST means throw exception if no record or multiple records found
2010-09-18 13:59:39 +00:00
* @ return stdClass
2006-08-08 22:09:55 +00:00
*/
2009-07-04 11:15:16 +00:00
function get_coursemodule_from_id ( $modulename , $cmid , $courseid = 0 , $sectionnum = false , $strictness = IGNORE_MISSING ) {
2008-05-30 16:47:21 +00:00
global $DB ;
2006-08-08 22:09:55 +00:00
2008-08-27 11:31:37 +00:00
$params = array ( 'cmid' => $cmid );
if ( ! $modulename ) {
if ( ! $modulename = $DB -> get_field_sql ( " SELECT md.name
FROM { modules } md
JOIN { course_modules } cm ON cm . module = md . id
2009-07-04 11:15:16 +00:00
WHERE cm . id = : cmid " , $params , $strictness )) {
2008-08-27 11:31:37 +00:00
return false ;
}
}
$params [ 'modulename' ] = $modulename ;
2008-05-30 16:51:01 +00:00
$courseselect = " " ;
2008-08-27 11:31:37 +00:00
$sectionfield = " " ;
$sectionjoin = " " ;
2006-08-08 22:09:55 +00:00
2008-05-30 16:47:21 +00:00
if ( $courseid ) {
2008-08-27 11:31:37 +00:00
$courseselect = " AND cm.course = :courseid " ;
2008-05-30 16:47:21 +00:00
$params [ 'courseid' ] = $courseid ;
2008-05-30 16:51:01 +00:00
}
2008-05-30 16:47:21 +00:00
2008-08-27 11:31:37 +00:00
if ( $sectionnum ) {
$sectionfield = " , cw.section AS sectionnum " ;
$sectionjoin = " LEFT JOIN { course_sections} cw ON cw.id = cm.section " ;
}
$sql = " SELECT cm.*, m.name, md.name AS modname $sectionfield
FROM { course_modules } cm
JOIN { modules } md ON md . id = cm . module
JOIN { " . $modulename . " } m ON m . id = cm . instance
$sectionjoin
WHERE cm . id = : cmid AND md . name = : modulename
$courseselect " ;
2009-07-04 11:15:16 +00:00
return $DB -> get_record_sql ( $sql , $params , $strictness );
2006-08-08 22:09:55 +00:00
}
/**
* Given an instance number of a module , finds the coursemodule description
*
2009-05-22 08:41:00 +00:00
* @ global object
2006-08-08 22:09:55 +00:00
* @ param string $modulename name of module type , eg . resource , assignment , ...
* @ param int $instance module instance number ( id in resource , assignment etc . table )
* @ param int $courseid optional course id for extra validation
2008-08-27 11:31:37 +00:00
* @ param bool $sectionnum include relative section number ( 0 , 1 , 2 ... )
2009-07-04 11:15:16 +00:00
* @ param int $strictness IGNORE_MISSING means compatible mode , false returned if record not found , debug message if more found ;
* IGNORE_MULTIPLE means return first , ignore multiple records found ( not recommended );
* MUST_EXIST means throw exception if no record or multiple records found
2010-09-18 13:59:39 +00:00
* @ return stdClass
2004-09-24 21:28:22 +00:00
*/
2009-07-04 11:15:16 +00:00
function get_coursemodule_from_instance ( $modulename , $instance , $courseid = 0 , $sectionnum = false , $strictness = IGNORE_MISSING ) {
2008-05-30 16:47:21 +00:00
global $DB ;
2002-12-17 04:41:18 +00:00
2008-08-27 11:31:37 +00:00
$params = array ( 'instance' => $instance , 'modulename' => $modulename );
2008-05-30 16:51:01 +00:00
$courseselect = " " ;
2008-08-27 11:31:37 +00:00
$sectionfield = " " ;
$sectionjoin = " " ;
2002-12-17 04:41:18 +00:00
2008-05-30 16:47:21 +00:00
if ( $courseid ) {
2008-08-27 11:31:37 +00:00
$courseselect = " AND cm.course = :courseid " ;
2008-05-30 16:47:21 +00:00
$params [ 'courseid' ] = $courseid ;
2008-05-30 16:51:01 +00:00
}
2008-05-30 16:47:21 +00:00
2008-08-27 11:31:37 +00:00
if ( $sectionnum ) {
$sectionfield = " , cw.section AS sectionnum " ;
$sectionjoin = " LEFT JOIN { course_sections} cw ON cw.id = cm.section " ;
}
$sql = " SELECT cm.*, m.name, md.name AS modname $sectionfield
FROM { course_modules } cm
JOIN { modules } md ON md . id = cm . module
JOIN { " . $modulename . " } m ON m . id = cm . instance
$sectionjoin
WHERE m . id = : instance AND md . name = : modulename
$courseselect " ;
2002-12-17 04:41:18 +00:00
2009-07-04 11:15:16 +00:00
return $DB -> get_record_sql ( $sql , $params , $strictness );
2002-12-17 04:41:18 +00:00
}
2008-01-24 20:33:50 +00:00
/**
* Returns all course modules of given activity in course
2009-05-22 08:41:00 +00:00
*
* @ param string $modulename The module name ( forum , quiz , etc . )
* @ param int $courseid The course id to get modules for
2008-01-24 20:33:50 +00:00
* @ param string $extrafields extra fields starting with m .
2009-05-22 08:41:00 +00:00
* @ return array Array of results
2008-01-24 20:33:50 +00:00
*/
function get_coursemodules_in_course ( $modulename , $courseid , $extrafields = '' ) {
2008-05-30 16:47:21 +00:00
global $DB ;
2008-01-24 20:33:50 +00:00
if ( ! empty ( $extrafields )) {
$extrafields = " , $extrafields " ;
}
2008-05-30 16:47:21 +00:00
$params = array ();
$params [ 'courseid' ] = $courseid ;
$params [ 'modulename' ] = $modulename ;
return $DB -> get_records_sql ( " SELECT cm.*, m.name, md.name as modname $extrafields
FROM { course_modules } cm , { modules } md , { " . $modulename . " } m
WHERE cm . course = : courseid AND
cm . instance = m . id AND
md . name = : modulename AND
2008-06-02 10:41:59 +00:00
md . id = cm . module " , $params );
2008-01-24 20:33:50 +00:00
}
2008-02-05 21:34:58 +00:00
2006-01-17 20:49:43 +00:00
/**
* Returns an array of all the active instances of a particular module in given courses , sorted in the order they are defined
*
* Returns an array of all the active instances of a particular
* module in given courses , sorted in the order they are defined
2008-02-05 21:34:58 +00:00
* in the course . Returns an empty array on any errors .
2006-01-17 20:49:43 +00:00
*
2008-02-05 21:34:58 +00:00
* The returned objects includle the columns cw . section , cm . visible ,
* cm . groupmode and cm . groupingid , cm . groupmembersonly , and are indexed by cm . id .
*
2009-05-22 08:41:00 +00:00
* @ global object
* @ global object
2008-02-05 21:34:58 +00:00
* @ param string $modulename The name of the module to get instances for
* @ param array $courses an array of course objects .
2009-05-22 08:41:00 +00:00
* @ param int $userid
* @ param int $includeinvisible
2008-02-05 21:34:58 +00:00
* @ return array of module instance objects , including some extra fields from the course_modules
* and course_sections tables , or an empty array if an error occurred .
2006-01-17 20:49:43 +00:00
*/
2006-10-24 20:22:30 +00:00
function get_all_instances_in_courses ( $modulename , $courses , $userid = NULL , $includeinvisible = false ) {
2008-05-30 16:47:21 +00:00
global $CFG , $DB ;
2008-02-05 21:34:58 +00:00
$outputarray = array ();
2006-01-17 20:49:43 +00:00
if ( empty ( $courses ) || ! is_array ( $courses ) || count ( $courses ) == 0 ) {
2008-02-05 21:34:58 +00:00
return $outputarray ;
2006-01-17 20:49:43 +00:00
}
2008-02-05 21:34:58 +00:00
2008-05-30 16:47:21 +00:00
list ( $coursessql , $params ) = $DB -> get_in_or_equal ( array_keys ( $courses ), SQL_PARAMS_NAMED , 'c0' );
$params [ 'modulename' ] = $modulename ;
if ( ! $rawmods = $DB -> get_records_sql ( " SELECT cm.id AS coursemodule, m.*, cw.section, cm.visible AS visible,
cm . groupmode , cm . groupingid , cm . groupmembersonly
FROM { course_modules } cm , { course_sections } cw , { modules } md ,
{ " . $modulename . " } m
WHERE cm . course $coursessql AND
cm . instance = m . id AND
cm . section = cw . id AND
md . name = : modulename AND
md . id = cm . module " , $params )) {
2008-02-05 21:34:58 +00:00
return $outputarray ;
2006-01-17 20:49:43 +00:00
}
foreach ( $courses as $course ) {
2008-02-05 21:34:58 +00:00
$modinfo = get_fast_modinfo ( $course , $userid );
2006-09-04 21:12:37 +00:00
2008-02-05 21:34:58 +00:00
if ( empty ( $modinfo -> instances [ $modulename ])) {
2006-01-17 20:49:43 +00:00
continue ;
}
2008-02-05 21:34:58 +00:00
foreach ( $modinfo -> instances [ $modulename ] as $cm ) {
if ( ! $includeinvisible and ! $cm -> uservisible ) {
continue ;
}
if ( ! isset ( $rawmods [ $cm -> id ])) {
continue ;
2006-01-17 20:49:43 +00:00
}
2008-02-05 21:34:58 +00:00
$instance = $rawmods [ $cm -> id ];
if ( ! empty ( $cm -> extra )) {
2010-02-14 20:18:10 +00:00
$instance -> extra = $cm -> extra ;
2008-02-05 21:34:58 +00:00
}
$outputarray [] = $instance ;
2006-01-17 20:49:43 +00:00
}
}
return $outputarray ;
}
2004-09-24 21:28:22 +00:00
2003-09-14 04:04:15 +00:00
/**
2007-12-12 17:10:48 +00:00
* Returns an array of all the active instances of a particular module in a given course ,
* sorted in the order they are defined .
2004-09-24 21:28:22 +00:00
*
* Returns an array of all the active instances of a particular
* module in a given course , sorted in the order they are defined
2007-12-12 17:10:48 +00:00
* in the course . Returns an empty array on any errors .
*
* The returned objects includle the columns cw . section , cm . visible ,
2008-02-05 21:34:58 +00:00
* cm . groupmode and cm . groupingid , cm . groupmembersonly , and are indexed by cm . id .
2004-09-24 21:28:22 +00:00
*
2009-05-22 08:41:00 +00:00
* Simply calls { @ link all_instances_in_courses ()} with a single provided course
*
2007-12-12 17:10:48 +00:00
* @ param string $modulename The name of the module to get instances for
2008-02-05 21:34:58 +00:00
* @ param object $course The course obect .
2007-12-12 17:10:48 +00:00
* @ return array of module instance objects , including some extra fields from the course_modules
* and course_sections tables , or an empty array if an error occurred .
2009-05-22 08:41:00 +00:00
* @ param int $userid
* @ param int $includeinvisible
2004-09-24 21:28:22 +00:00
*/
2006-10-24 20:22:30 +00:00
function get_all_instances_in_course ( $modulename , $course , $userid = NULL , $includeinvisible = false ) {
2008-02-05 21:34:58 +00:00
return get_all_instances_in_courses ( $modulename , array ( $course -> id => $course ), $userid , $includeinvisible );
2002-12-17 04:41:18 +00:00
}
2002-12-20 14:44:14 +00:00
2003-09-14 04:04:15 +00:00
/**
2004-09-24 21:28:22 +00:00
* Determine whether a module instance is visible within a course
*
* Given a valid module object with info about the id and course ,
* and the module ' s type ( eg " forum " ) returns whether the object
2008-01-24 20:33:50 +00:00
* is visible or not , groupmembersonly visibility not tested
2004-09-24 21:28:22 +00:00
*
2009-05-22 08:41:00 +00:00
* @ global object
2009-11-01 11:31:16 +00:00
2006-09-10 07:07:52 +00:00
* @ param $moduletype Name of the module eg 'forum'
* @ param $module Object which is the instance of the module
2009-05-22 08:41:00 +00:00
* @ return bool Success
2004-09-24 21:28:22 +00:00
*/
2003-04-25 05:24:29 +00:00
function instance_is_visible ( $moduletype , $module ) {
2008-05-30 16:47:21 +00:00
global $DB ;
2003-04-25 05:24:29 +00:00
2004-11-18 02:37:52 +00:00
if ( ! empty ( $module -> id )) {
2008-05-30 16:51:01 +00:00
$params = array ( 'courseid' => $module -> course , 'moduletype' => $moduletype , 'moduleid' => $module -> id );
2008-05-30 16:47:21 +00:00
if ( $records = $DB -> get_records_sql ( " SELECT cm.instance, cm.visible, cm.groupingid, cm.id, cm.groupmembersonly, cm.course
FROM { course_modules } cm , { modules } m
WHERE cm . course = : courseid AND
cm . module = m . id AND
m . name = : moduletype AND
2008-06-04 07:54:58 +00:00
cm . instance = : moduleid " , $params )) {
2007-08-02 23:39:28 +00:00
2004-11-18 02:37:52 +00:00
foreach ( $records as $record ) { // there should only be one - use the first one
2008-01-24 20:33:50 +00:00
return $record -> visible ;
2004-11-18 02:37:52 +00:00
}
2003-04-25 05:24:29 +00:00
}
}
return true ; // visible by default!
}
2008-01-24 20:33:50 +00:00
/**
* Determine whether a course module is visible within a course ,
* this is different from instance_is_visible () - faster and visibility for user
*
2009-05-22 08:41:00 +00:00
* @ global object
* @ global object
* @ uses DEBUG_DEVELOPER
* @ uses CONTEXT_MODULE
* @ uses CONDITION_MISSING_EXTRATABLE
2008-01-24 20:33:50 +00:00
* @ param object $cm object
* @ param int $userid empty means current user
2009-05-22 08:41:00 +00:00
* @ return bool Success
2008-01-24 20:33:50 +00:00
*/
function coursemodule_visible_for_user ( $cm , $userid = 0 ) {
2008-12-17 16:37:35 +00:00
global $USER , $CFG ;
2008-01-24 20:33:50 +00:00
if ( empty ( $cm -> id )) {
debugging ( " Incorrect course module parameter! " , DEBUG_DEVELOPER );
return false ;
}
if ( empty ( $userid )) {
$userid = $USER -> id ;
}
2012-07-25 16:25:55 +08:00
if ( ! $cm -> visible and ! has_capability ( 'moodle/course:viewhiddenactivities' , context_module :: instance ( $cm -> id ), $userid )) {
2008-01-24 20:33:50 +00:00
return false ;
}
2008-12-17 16:37:35 +00:00
if ( $CFG -> enableavailability ) {
require_once ( $CFG -> libdir . '/conditionlib.php' );
$ci = new condition_info ( $cm , CONDITION_MISSING_EXTRATABLE );
2009-11-01 11:31:16 +00:00
if ( ! $ci -> is_available ( $cm -> availableinfo , false , $userid ) and
! has_capability ( 'moodle/course:viewhiddenactivities' ,
2012-07-25 16:25:55 +08:00
context_module :: instance ( $cm -> id ), $userid )) {
2008-12-17 16:37:35 +00:00
return false ;
}
}
2008-01-24 20:33:50 +00:00
return groups_course_module_visible ( $cm , $userid );
}
2003-01-03 15:31:30 +00:00
2002-12-20 14:44:14 +00:00
/// LOG FUNCTIONS /////////////////////////////////////////////////////
2003-09-14 04:04:15 +00:00
/**
2004-09-24 21:28:22 +00:00
* Add an entry to the log table .
*
* Add an entry to the log table . These are " action " focussed rather
* than web server hits , and provide a way to easily reconstruct what
* any particular student has been doing .
*
2012-01-06 10:30:24 +05:30
* @ package core
* @ category log
* @ global moodle_database $DB
* @ global stdClass $CFG
* @ global stdClass $USER
2004-09-24 21:28:22 +00:00
* @ uses SITEID
2009-05-22 08:41:00 +00:00
* @ uses DEBUG_DEVELOPER
* @ uses DEBUG_ALL
2004-09-25 05:29:21 +00:00
* @ param int $courseid The course id
2012-01-05 07:55:47 +05:30
* @ param string $module The module name e . g . forum , journal , resource , course , user etc
2006-03-21 14:09:55 +00:00
* @ param string $action 'view' , 'update' , 'add' or 'delete' , possibly followed by another word to clarify .
2004-09-24 21:28:22 +00:00
* @ param string $url The file and parameters used to see the results of the action
* @ param string $info Additional description information
* @ param string $cm The course_module -> id if there is one
* @ param string $user If log regards $user other than $USER
2009-05-22 08:41:00 +00:00
* @ return void
2004-09-24 21:28:22 +00:00
*/
2004-09-22 16:15:23 +00:00
function add_to_log ( $courseid , $module , $action , $url = '' , $info = '' , $cm = 0 , $user = 0 ) {
2006-03-13 10:37:21 +00:00
// Note that this function intentionally does not follow the normal Moodle DB access idioms.
// This is for a good reason: it is the most frequently used DB update function,
// so it has been optimised for speed.
2008-05-15 21:40:00 +00:00
global $DB , $CFG , $USER ;
2002-12-20 14:44:14 +00:00
2005-07-18 22:21:56 +00:00
if ( $cm === '' || is_null ( $cm )) { // postgres won't translate empty string to its default
2005-03-23 07:07:47 +00:00
$cm = 0 ;
}
2004-01-30 18:21:56 +00:00
if ( $user ) {
$userid = $user ;
} else {
2009-01-02 20:32:05 +00:00
if ( session_is_loggedinas ()) { // Don't log
2004-01-30 18:21:56 +00:00
return ;
}
2004-09-22 16:15:23 +00:00
$userid = empty ( $USER -> id ) ? '0' : $USER -> id ;
2002-12-20 14:44:14 +00:00
}
2010-11-11 06:11:13 +00:00
if ( isset ( $CFG -> logguests ) and ! $CFG -> logguests ) {
if ( ! $userid or isguestuser ( $userid )) {
return ;
}
}
2005-04-01 07:40:07 +00:00
$REMOTE_ADDR = getremoteaddr ();
2002-12-20 14:44:14 +00:00
$timenow = time ();
2008-05-30 17:00:25 +00:00
$info = $info ;
2005-06-16 02:58:24 +00:00
if ( ! empty ( $url )) { // could break doing html_entity_decode on an empty var.
2012-12-09 18:21:38 +01:00
$url = html_entity_decode ( $url , ENT_QUOTES , 'UTF-8' );
2011-07-24 14:07:30 +02:00
} else {
$url = '' ;
2005-06-16 02:58:24 +00:00
}
2005-04-07 00:11:28 +00:00
2008-03-13 15:32:08 +00:00
// Restrict length of log lines to the space actually available in the
// database so that it doesn't cause a DB error. Log a warning so that
// developers can avoid doing things which are likely to cause this on a
// routine basis.
2012-01-21 13:28:20 +01:00
if ( ! empty ( $info ) && textlib :: strlen ( $info ) > 255 ) {
$info = textlib :: substr ( $info , 0 , 252 ) . '...' ;
2008-03-13 15:32:08 +00:00
debugging ( 'Warning: logged very long info' , DEBUG_DEVELOPER );
}
2008-05-30 17:00:25 +00:00
2008-03-13 15:32:08 +00:00
// If the 100 field size is changed, also need to alter print_log in course/lib.php
2012-01-21 13:28:20 +01:00
if ( ! empty ( $url ) && textlib :: strlen ( $url ) > 100 ) {
$url = textlib :: substr ( $url , 0 , 97 ) . '...' ;
2008-03-13 15:32:08 +00:00
debugging ( 'Warning: logged very long URL' , DEBUG_DEVELOPER );
}
2008-06-02 14:18:18 +00:00
2008-05-15 21:40:00 +00:00
if ( defined ( 'MDL_PERFDB' )) { global $PERF ; $PERF -> logwrites ++ ;};
2005-04-07 00:11:28 +00:00
2008-05-15 21:40:00 +00:00
$log = array ( 'time' => $timenow , 'userid' => $userid , 'course' => $courseid , 'ip' => $REMOTE_ADDR , 'module' => $module ,
'cmid' => $cm , 'action' => $action , 'url' => $url , 'info' => $info );
2007-10-25 04:02:41 +00:00
2010-11-15 07:38:02 +00:00
try {
$DB -> insert_record_raw ( 'log' , $log , false );
2011-11-12 17:39:19 +01:00
} catch ( dml_exception $e ) {
2012-02-09 13:56:05 +08:00
debugging ( 'Error: Could not insert a new entry to the Moodle log. ' . $e -> error , DEBUG_ALL );
2010-11-15 07:38:02 +00:00
// MDL-11893, alert $CFG->supportemail if insert into log failed
if ( $CFG -> supportemail and empty ( $CFG -> noemailever )) {
// email_to_user is not usable because email_to_user tries to write to the logs table,
// and this will get caught in an infinite loop, if disk is full
$site = get_site ();
$subject = 'Insert into log failed at your moodle site ' . $site -> fullname ;
$message = " Insert into log table failed at " . date ( 'l dS \of F Y h:i:s A' ) . " . \n It is possible that your disk is full. \n \n " ;
$message .= " The failed query parameters are: \n \n " . var_export ( $log , true );
$lasttime = get_config ( 'admin' , 'lastloginserterrormail' );
if ( empty ( $lasttime ) || time () - $lasttime > 60 * 60 * 24 ) { // limit to 1 email per day
2010-12-22 12:05:49 +08:00
//using email directly rather than messaging as they may not be able to log in to access a message
2010-11-15 07:38:02 +00:00
mail ( $CFG -> supportemail , $subject , $message );
set_config ( 'lastloginserterrormail' , time (), 'admin' );
}
}
2004-09-21 11:41:58 +00:00
}
2008-04-15 21:46:04 +00:00
}
/**
* Store user last access times - called when use enters a course or site
*
2012-01-06 10:30:24 +05:30
* @ package core
* @ category log
* @ global stdClass $USER
* @ global stdClass $CFG
* @ global moodle_database $DB
2009-05-22 08:41:00 +00:00
* @ uses LASTACCESS_UPDATE_SECS
* @ uses SITEID
2012-01-05 07:55:47 +05:30
* @ param int $courseid empty courseid means site
2008-04-15 21:46:04 +00:00
* @ return void
*/
function user_accesstime_log ( $courseid = 0 ) {
2008-05-15 21:40:00 +00:00
global $USER , $CFG , $DB ;
2008-04-15 21:46:04 +00:00
2009-01-02 20:32:05 +00:00
if ( ! isloggedin () or session_is_loggedinas ()) {
2008-04-15 21:46:04 +00:00
// no access tracking
return ;
}
2012-09-05 10:34:51 +08:00
if ( isguestuser ()) {
// Do not update guest access times/ips for performance.
return ;
}
2008-04-15 21:46:04 +00:00
if ( empty ( $courseid )) {
$courseid = SITEID ;
}
$timenow = time ();
/// Store site lastaccess time for the current user
if ( $timenow - $USER -> lastaccess > LASTACCESS_UPDATE_SECS ) {
/// Update $USER->lastaccess for next checks
$USER -> lastaccess = $timenow ;
2010-09-21 08:07:44 +00:00
$last = new stdClass ();
2008-05-15 21:40:00 +00:00
$last -> id = $USER -> id ;
$last -> lastip = getremoteaddr ();
$last -> lastaccess = $timenow ;
2009-02-17 17:15:47 +00:00
$DB -> update_record_raw ( 'user' , $last );
2008-04-15 21:46:04 +00:00
}
if ( $courseid == SITEID ) {
/// no user_lastaccess for frontpage
return ;
}
2008-04-08 23:17:20 +00:00
2008-04-15 21:46:04 +00:00
/// Store course lastaccess times for the current user
if ( empty ( $USER -> currentcourseaccess [ $courseid ]) or ( $timenow - $USER -> currentcourseaccess [ $courseid ] > LASTACCESS_UPDATE_SECS )) {
2008-05-15 21:40:00 +00:00
$lastaccess = $DB -> get_field ( 'user_lastaccess' , 'timeaccess' , array ( 'userid' => $USER -> id , 'courseid' => $courseid ));
2008-04-15 21:46:04 +00:00
2008-05-15 21:40:00 +00:00
if ( $lastaccess === false ) {
// Update course lastaccess for next checks
$USER -> currentcourseaccess [ $courseid ] = $timenow ;
2010-09-21 08:07:44 +00:00
$last = new stdClass ();
2008-05-15 21:40:00 +00:00
$last -> userid = $USER -> id ;
$last -> courseid = $courseid ;
$last -> timeaccess = $timenow ;
2009-02-17 17:15:47 +00:00
$DB -> insert_record_raw ( 'user_lastaccess' , $last , false );
2008-05-30 16:51:01 +00:00
2008-05-15 21:40:00 +00:00
} else if ( $timenow - $lastaccess < LASTACCESS_UPDATE_SECS ) {
// no need to update now, it was updated recently in concurrent login ;-)
2008-04-15 21:46:04 +00:00
2008-05-15 21:40:00 +00:00
} else {
// Update course lastaccess for next checks
$USER -> currentcourseaccess [ $courseid ] = $timenow ;
2009-02-17 17:15:47 +00:00
$DB -> set_field ( 'user_lastaccess' , 'timeaccess' , $timenow , array ( 'userid' => $USER -> id , 'courseid' => $courseid ));
2004-01-30 18:21:56 +00:00
}
2004-09-21 11:41:58 +00:00
}
2002-12-20 14:44:14 +00:00
}
2003-09-14 04:04:15 +00:00
/**
2004-09-24 21:28:22 +00:00
* Select all log records based on SQL criteria
*
2012-01-06 10:30:24 +05:30
* @ package core
* @ category log
* @ global moodle_database $DB
2004-09-24 21:28:22 +00:00
* @ param string $select SQL select criteria
2008-05-30 16:47:21 +00:00
* @ param array $params named sql type params
2004-09-24 21:28:22 +00:00
* @ param string $order SQL order by clause to sort the records returned
2012-01-05 07:55:47 +05:30
* @ param string $limitfrom return a subset of records , starting at this point ( optional , required if $limitnum is set )
* @ param int $limitnum return a subset comprising this many records ( optional , required if $limitfrom is set )
2004-09-24 21:28:22 +00:00
* @ param int $totalcount Passed in by reference .
2012-01-06 10:30:24 +05:30
* @ return array
2004-09-24 21:28:22 +00:00
*/
2008-05-30 16:47:21 +00:00
function get_logs ( $select , array $params = null , $order = 'l.time DESC' , $limitfrom = '' , $limitnum = '' , & $totalcount ) {
global $DB ;
2002-12-20 14:44:14 +00:00
2003-08-22 06:07:18 +00:00
if ( $order ) {
2008-05-30 16:47:21 +00:00
$order = " ORDER BY $order " ;
}
$selectsql = " " ;
$countsql = " " ;
if ( $select ) {
$select = " WHERE $select " ;
2003-08-22 06:07:18 +00:00
}
2008-05-30 16:47:21 +00:00
$sql = " SELECT COUNT(*)
FROM { log } l
$select " ;
$totalcount = $DB -> count_records_sql ( $sql , $params );
2004-11-25 21:56:32 +00:00
2008-05-30 16:47:21 +00:00
$sql = " SELECT l.*, u.firstname, u.lastname, u.picture
2008-05-30 16:51:01 +00:00
FROM { log } l
2008-05-30 16:47:21 +00:00
LEFT JOIN { user } u ON l . userid = u . id
2008-05-30 16:51:01 +00:00
$select
2008-05-30 16:47:21 +00:00
$order " ;
2003-08-22 06:07:18 +00:00
2008-05-30 16:47:21 +00:00
return $DB -> get_records_sql ( $sql , $params , $limitfrom , $limitnum ) ;
2002-12-20 14:44:14 +00:00
}
2003-08-22 06:07:18 +00:00
2003-09-14 04:04:15 +00:00
/**
2004-09-24 21:28:22 +00:00
* Select all log records for a given course and user
*
2012-01-06 10:30:24 +05:30
* @ package core
* @ category log
* @ global moodle_database $DB
2004-09-29 18:56:50 +00:00
* @ uses DAYSECS
2004-09-24 21:28:22 +00:00
* @ param int $userid The id of the user as found in the 'user' table .
* @ param int $courseid The id of the course as found in the 'course' table .
2012-01-05 07:55:47 +05:30
* @ param string $coursestart unix timestamp representing course start date and time .
2012-01-06 10:30:24 +05:30
* @ return array
2004-09-24 21:28:22 +00:00
*/
2002-12-20 14:44:14 +00:00
function get_logs_usercourse ( $userid , $courseid , $coursestart ) {
2008-05-30 16:47:21 +00:00
global $DB ;
2002-12-20 14:44:14 +00:00
2008-05-30 16:47:21 +00:00
$params = array ();
$courseselect = '' ;
2003-07-25 13:23:28 +00:00
if ( $courseid ) {
2008-05-30 16:47:21 +00:00
$courseselect = " AND course = :courseid " ;
2008-05-30 16:51:01 +00:00
$params [ 'courseid' ] = $courseid ;
2003-07-25 13:23:28 +00:00
}
2008-05-30 16:47:21 +00:00
$params [ 'userid' ] = $userid ;
2011-07-19 10:53:20 +02:00
$$coursestart = ( int ) $coursestart ; // note: unfortunately pg complains if you use name parameter or column alias in GROUP BY
2003-07-25 13:23:28 +00:00
2011-07-19 10:53:20 +02:00
return $DB -> get_records_sql ( " SELECT FLOOR((time - $coursestart )/ " . DAYSECS . " ) AS day, COUNT(*) AS num
2008-05-30 16:47:21 +00:00
FROM { log }
WHERE userid = : userid
2011-07-19 10:53:20 +02:00
AND time > $coursestart $courseselect
GROUP BY FLOOR (( time - $coursestart ) / " . DAYSECS . " ) " , $params );
2002-12-20 14:44:14 +00:00
}
2003-09-14 04:04:15 +00:00
/**
2004-09-24 21:28:22 +00:00
* Select all log records for a given course , user , and day
*
2012-01-06 10:30:24 +05:30
* @ package core
* @ category log
* @ global moodle_database $DB
2004-09-29 18:56:50 +00:00
* @ uses HOURSECS
2004-09-24 21:28:22 +00:00
* @ param int $userid The id of the user as found in the 'user' table .
* @ param int $courseid The id of the course as found in the 'course' table .
2012-01-05 07:55:47 +05:30
* @ param string $daystart unix timestamp of the start of the day for which the logs needs to be retrived
2012-01-06 10:30:24 +05:30
* @ return array
2004-09-24 21:28:22 +00:00
*/
2002-12-20 14:44:14 +00:00
function get_logs_userday ( $userid , $courseid , $daystart ) {
2008-05-30 16:47:21 +00:00
global $DB ;
2011-07-19 10:53:20 +02:00
$params = array ( 'userid' => $userid );
2002-12-20 14:44:14 +00:00
2008-05-30 16:47:21 +00:00
$courseselect = '' ;
2003-07-24 01:54:06 +00:00
if ( $courseid ) {
2011-07-19 10:53:20 +02:00
$courseselect = " AND course = :courseid " ;
$params [ 'courseid' ] = $courseid ;
2003-07-24 01:54:06 +00:00
}
2011-07-19 10:53:20 +02:00
$daystart = ( int ) $daystart ; // note: unfortunately pg complains if you use name parameter or column alias in GROUP BY
2003-07-24 01:54:06 +00:00
2011-07-19 10:53:20 +02:00
return $DB -> get_records_sql ( " SELECT FLOOR((time - $daystart )/ " . HOURSECS . " ) AS hour, COUNT(*) AS num
2008-05-30 16:47:21 +00:00
FROM { log }
2011-07-19 10:53:20 +02:00
WHERE userid = : userid
AND time > $daystart $courseselect
GROUP BY FLOOR (( time - $daystart ) / " . HOURSECS . " ) " , $params );
2002-12-20 14:44:14 +00:00
}
2004-07-25 13:47:38 +00:00
/**
* Returns an object with counts of failed login attempts
*
2004-09-21 11:41:58 +00:00
* Returns information about failed login attempts . If the current user is
* an admin , then two numbers are returned : the number of attempts and the
2004-07-25 13:47:38 +00:00
* number of accounts . For non - admins , only the attempts on the given user
* are shown .
*
2012-01-06 10:30:24 +05:30
* @ global moodle_database $DB
2009-05-22 08:41:00 +00:00
* @ uses CONTEXT_SYSTEM
2010-03-31 07:41:31 +00:00
* @ param string $mode Either 'admin' or 'everybody'
2004-09-24 21:28:22 +00:00
* @ param string $username The username we are searching for
* @ param string $lastlogin The date from which we are searching
* @ return int
2004-07-25 13:47:38 +00:00
*/
function count_login_failures ( $mode , $username , $lastlogin ) {
2008-05-30 16:47:21 +00:00
global $DB ;
2004-07-25 13:47:38 +00:00
2008-05-30 16:47:21 +00:00
$params = array ( 'mode' => $mode , 'username' => $username , 'lastlogin' => $lastlogin );
$select = " module='login' AND action='error' AND time > :lastlogin " ;
2010-09-21 08:07:44 +00:00
$count = new stdClass ();
2004-07-25 13:47:38 +00:00
2010-03-31 07:41:31 +00:00
if ( is_siteadmin ()) {
2008-05-30 16:47:21 +00:00
if ( $count -> attempts = $DB -> count_records_select ( 'log' , $select , $params )) {
$count -> accounts = $DB -> count_records_select ( 'log' , $select , $params , 'COUNT(DISTINCT info)' );
2004-07-25 13:47:38 +00:00
return $count ;
}
2010-03-31 07:41:31 +00:00
} else if ( $mode == 'everybody' ) {
2008-05-30 16:47:21 +00:00
if ( $count -> attempts = $DB -> count_records_select ( 'log' , " $select AND info = :username " , $params )) {
2004-07-25 13:47:38 +00:00
return $count ;
}
}
return NULL ;
}
2003-01-03 15:31:30 +00:00
/// GENERAL HELPFUL THINGS ///////////////////////////////////
2003-09-14 04:04:15 +00:00
/**
2012-01-03 11:29:10 +01:00
* Dumps a given object ' s information for debugging purposes
2004-09-24 21:28:22 +00:00
*
2012-01-03 11:29:10 +01:00
* When used in a CLI script , the object ' s information is written to the standard
* error output stream . When used in a web script , the object is dumped to a
* pre - formatted block with the " notifytiny " CSS class .
2004-09-24 21:28:22 +00:00
*
* @ param mixed $object The data to be printed
2012-01-03 11:29:10 +01:00
* @ return void output is echo ' d
2004-09-24 21:28:22 +00:00
*/
2003-01-03 15:31:30 +00:00
function print_object ( $object ) {
2012-01-03 11:29:10 +01:00
2011-11-07 15:18:32 +08:00
// we may need a lot of memory here
raise_memory_limit ( MEMORY_EXTRA );
2012-01-03 11:29:10 +01:00
if ( CLI_SCRIPT ) {
fwrite ( STDERR , print_r ( $object , true ));
fwrite ( STDERR , PHP_EOL );
} else {
echo html_writer :: tag ( 'pre' , s ( print_r ( $object , true )), array ( 'class' => 'notifytiny' ));
}
2003-01-03 15:31:30 +00:00
}
2006-10-28 15:20:14 +00:00
/**
2007-08-02 23:39:28 +00:00
* This function is the official hook inside XMLDB stuff to delegate its debug to one
2006-10-28 15:20:14 +00:00
* external function .
*
* Any script can avoid calls to this function by defining XMLDB_SKIP_DEBUG_HOOK before
* using XMLDB classes . Obviously , also , if this function doesn 't exist, it isn' t invoked ; - )
*
2009-05-22 08:41:00 +00:00
* @ uses DEBUG_DEVELOPER
* @ param string $message string contains the error message
* @ param object $object object XMLDB object that fired the debug
2006-10-28 15:20:14 +00:00
*/
function xmldb_debug ( $message , $object ) {
2007-08-12 15:58:08 +00:00
debugging ( $message , DEBUG_DEVELOPER );
2006-10-28 15:20:14 +00:00
}
2007-04-03 09:19:09 +00:00
/**
2009-05-22 08:41:00 +00:00
* @ global object
* @ uses CONTEXT_COURSECAT
2008-12-01 06:55:11 +00:00
* @ return boolean Whether the user can create courses in any category in the system .
2007-04-03 09:19:09 +00:00
*/
function user_can_create_courses () {
2008-05-30 16:47:21 +00:00
global $DB ;
2008-12-01 06:55:11 +00:00
$catsrs = $DB -> get_recordset ( 'course_categories' );
2009-01-11 17:16:50 +00:00
foreach ( $catsrs as $cat ) {
2012-07-25 16:25:55 +08:00
if ( has_capability ( 'moodle/course:create' , context_coursecat :: instance ( $cat -> id ))) {
2008-12-01 06:55:11 +00:00
$catsrs -> close ();
return true ;
2007-04-03 09:19:09 +00:00
}
}
2008-12-01 06:55:11 +00:00
$catsrs -> close ();
return false ;
2007-04-03 09:19:09 +00:00
}