Uploaded image for project: 'Moodle'
  1. Moodle
  2. MDL-4790

Language-specific grammar doesn't exist

XMLWordPrintable

    • Icon: New Feature New Feature
    • Resolution: Won't Do
    • Icon: Trivial Trivial
    • None
    • 1.5.2
    • General
    • None
    • All
    • MOODLE_15_STABLE

      For Slav(ic) and I think Finnish and there are certainly more languages, language-specific grammar is not handled in Moodle code, no matter the version is. For instance, when Admin goes to the Edit User Accounts he can see e.g. 6 Korisnici in Serbian instead of 6 korisnika for 6 Users, because plural form when in no count-related context (Korisnici) differs from the count-related versions, namely: 1 poruka (being 1 message in English), 2, 3, 4 poruke (messages), but then if we count further, we say 5, 6, 7,..., 20 poruka (in English it remains messages), then 21, 31, 41,... are the same as 1, 22-24,32-34,42-44 the same as 2-4, and 25-30, 35-40,... are the same as 5-10 and 11-19, 111-119, 211-219 (poruka). I can suggest a function for handling this:

      /**

      • more specific function to accommodate for plural forms of known words
      • @param int $num numerical value that has to have appropriate plural form
      • @param string $singlr singular form
      • @param string $frm2 word form for $num values ending with a number between 2 and 4
      • @param string $frm5 word form for $num values ending with a number between 5 and 9, 0 and between 11 and 14
      • @return string (hopefully) correct plural form

      */

      function sr_la_utf8_plu_ext($num = 1, $singlrfrm = '', $frm2 = '', $frm5 = '') {

      //determine what plural form to apply according to $num

      if (($num % 10 > 4 and $num % 10 < 10) or ($num % 100 < 15 and $num % 100 > 10))

      { $result = $frm5; } elseif ($num % 10 == 1) { $result = $singlrfrm; } else { $result = $frm5; }

      return $result;

      }

      Declinations are another problem - there are so many languages with them, but no support at all!

      e.g. resource = izvor in Serbian, but

      Adding a resource = dodavanje izvora

      activity=aktivnost, but adding a new activity is dodavanje nove aktivnosti.

      I have also got functions for this, they don't seem to be general enough, but I 'm still working on them. More important is, I think, how to organize the support for this. I myself have put all language-grammar-related functions into langfun.php, residing in language pack folder and then updated functions in lib/moodlelib.php to allow one more code evaluation (eval function in PHP) in get_string for translated expressions between e.g. <eval> and </eval> tags. An example for this comes from moodle.php in language pack:

      $string['addinganewto'] = 'Dodavanje nov<eval>sr_la_utf8_gen_ext(sr_la_utf8_ma_fem(\$a->what),\'og\',\'e\',\'og\')</eval> <eval>sr_la_utf8_gen(\'$a->what\')</eval> <eval>sr_la_utf8_dat(\'$a->to\')</eval>';

      (sr_la_utf8_ma_fem is the gender detection function, the others are for handling declinations, namely the Genitive and Dative Case)

      another example:

      $string['displayingrecords'] = 'Za prikaz: $a zapis<eval>sr_la_utf8_plu_ext($a,\'\',\'a\',\'a\')</eval>';

      Function for accommodating this is as follows (it is eval_get_string, located in moodlelib.php; here is the whole file, the code sequences added are commented with //Bojan Milosavljevic:...); look in lines: 4514, and for the rest of code in 836, 861, 4411, 4420, 4442, 4458:

      <?php // $Id: moodlelib.php,v 1.565.2.21 2005/07/15 11:11:08 stronk7 Exp $

      ///////////////////////////////////////////////////////////////////////////

      // //

      // NOTICE OF COPYRIGHT //

      // //

      // Moodle - Modular Object-Oriented Dynamic Learning Environment //

      // http://moodle.org //

      // //

      // Copyright (C) 1999-2004 Martin Dougiamas http://dougiamas.com //

      // //

      // This program 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 2 of the License, or //

      // (at your option) any later version. //

      // //

      // This program 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: //

      // //

      // http://www.gnu.org/copyleft/gpl.html //

      // //

      ///////////////////////////////////////////////////////////////////////////

      /**

      • moodlelib.php - Moodle main library

      *

      • Main library file of miscellaneous general-purpose Moodle functions.
      • Other main libraries:
      • - weblib.php - functions that produce web output
      • - datalib.php - functions that access the database
      • @author Martin Dougiamas
      • @version $Id: moodlelib.php,v 1.565.2.21 2005/07/15 11:11:08 stronk7 Exp $
      • @package moodlecore

      */

      /// CONSTANTS /////////////////////////////////////////////////////////////

      /**

      • Used by some scripts to check they are being called by Moodle

      */

      define('MOODLE_INTERNAL', true);

      /**

      • No groups used?

      */

      define('NOGROUPS', 0);

      /**

      • Groups used?

      */

      define('SEPARATEGROUPS', 1);

      /**

      • Groups visible?

      */

      define('VISIBLEGROUPS', 2);

      /**

      • Time constant - the number of seconds in a week

      */

      define('WEEKSECS', 604800);

      /**

      • Time constant - the number of seconds in a day

      */

      define('DAYSECS', 86400);

      /**

      • Time constant - the number of seconds in an hour

      */

      define('HOURSECS', 3600);

      /**

      • Time constant - the number of seconds in a minute

      */

      define('MINSECS', 60);

      /**

      • Time constant - the number of minutes in a day

      */

      define('DAYMINS', 1440);

      /**

      • Time constant - the number of minutes in an hour

      */

      define('HOURMINS', 60);

      /**

      • Parameter constants - if set then the parameter is cleaned of scripts etc

      */

      define('PARAM_RAW', 0x0000);

      define('PARAM_CLEAN', 0x0001);

      define('PARAM_INT', 0x0002);

      define('PARAM_INTEGER', 0x0002); // Alias for PARAM_INT

      define('PARAM_ALPHA', 0x0004);

      define('PARAM_ACTION', 0x0004); // Alias for PARAM_ALPHA

      define('PARAM_FORMAT', 0x0004); // Alias for PARAM_ALPHA

      define('PARAM_NOTAGS', 0x0008);

      define('PARAM_FILE', 0x0010);

      define('PARAM_PATH', 0x0020);

      define('PARAM_HOST', 0x0040); // FQDN or IPv4 dotted quad

      define('PARAM_URL', 0x0080);

      define('PARAM_LOCALURL', 0x0180); // NOT orthogonal to the others! Implies PARAM_URL!

      define('PARAM_CLEANFILE',0x0200);

      define('PARAM_ALPHANUM', 0x0400); //numbers or letters only

      define('PARAM_BOOL', 0x0800); //convert to value 1 or 0 using empty()

      define('PARAM_CLEANHTML',0x1000); //actual HTML code that you want cleaned and slashes removed

      define('PARAM_ALPHAEXT', 0x2000); // PARAM_ALPHA plus the chars in quotes: /-_ allowed

      define('PARAM_SAFEDIR', 0x4000); // safe directory name, suitable for include() and require()

      /**

      • Definition of page types

      */

      define('PAGE_COURSE_VIEW', 'course-view');

      /// PARAMETER HANDLING ////////////////////////////////////////////////////

      /**

      • Returns a particular value for the named variable, taken from
      • POST or GET. If the parameter doesn't exist then an error is
      • thrown because we require this variable.

      *

      • This function should be used to initialise all required values
      • in a script that are based on parameters. Usually it will be
      • used like this:
      • $id = required_param('id');

      *

      • @param string $varname the name of the parameter variable we want
      • @param integer $options a bit field that specifies any cleaning needed
      • @return mixed

      */

      function required_param($varname, $options=PARAM_CLEAN) {

      if (isset($_POST[$varname]))

      { // POST has precedence $param = $_POST[$varname]; } else if (isset($_GET[$varname])) { $param = $_GET[$varname]; } else { error('A required parameter ('.$varname.') was missing'); }



      return clean_param($param, $options);

      }



      /**

      * Returns a particular value for the named variable, taken from

      * POST or GET, otherwise returning a given default.

      *

      * This function should be used to initialise all optional values

      * in a script that are based on parameters. Usually it will be

      * used like this:

      * $name = optional_param('name', 'Fred');

      *

      * @param string $varname the name of the parameter variable we want

      * @param mixed $default the default value to return if nothing is found

      * @param integer $options a bit field that specifies any cleaning needed

      * @return mixed

      */

      function optional_param($varname, $default=NULL, $options=PARAM_CLEAN) {



      if (isset($_POST[$varname])) { // POST has precedence $param = $_POST[$varname]; }

      else if (isset($_GET[$varname]))

      { $param = $_GET[$varname]; }

      else

      { return $default; }

      return clean_param($param, $options);

      }

      /**

      • Used by {@link optional_param()}

        and

        {@link required_param()} to

        * clean the variables and/or cast to specific types, based on

        * an options field.

        *

        * @param mixed $param the variable we are cleaning

        * @param integer $options a bit field that specifies the cleaning needed

        * @return mixed

        */

        function clean_param($param, $options) {



        global $CFG;



        if (!$options) { return $param; // Return raw value }



        if ((string)$param == (string)(int)$param) { // It's just an integer return (int)$param; }



        if ($options & PARAM_CLEAN) { $param = stripslashes($param); // Needed by kses to work fine $param = clean_text($param); // Sweep for scripts, etc $param = addslashes($param); // Restore original request parameter slashes }



        if ($options & PARAM_INT) { $param = (int)$param; // Convert to integer }



        if ($options & PARAM_ALPHA) { // Remove everything not a-z $param = eregi_replace('[^a-zA-Z]', '', $param); }



        if ($options & PARAM_ALPHANUM) { // Remove everything not a-zA-Z0-9 $param = eregi_replace('[^A-Za-z0-9]', '', $param); }



        if ($options & PARAM_ALPHAEXT) { // Remove everything not a-zA-Z/_- $param = eregi_replace('[^a-zA-Z/_-]', '', $param); }



        if ($options & PARAM_BOOL) { // Convert to 1 or 0 $param = empty($param) ? 0 : 1; }



        if ($options & PARAM_NOTAGS) { // Strip all tags completely $param = strip_tags($param); }



        if ($options & PARAM_SAFEDIR) { // Remove everything not a-zA-Z0-9_- $param = eregi_replace('[^a-zA-Z0-9_-]', '', $param); }



        if ($options & PARAM_CLEANFILE) { // allow only safe characters $param = clean_filename($param); }



        if ($options & PARAM_FILE) { // Strip all suspicious characters from filename

        $param = ereg_replace('[[:cntrl:]]/[<>`\/\':\\/]', '', $param);

        $param = ereg_replace('\.\.+', '', $param);

        if($param == '.') { $param = ''; }

        }



        if ($options & PARAM_PATH) { // Strip all suspicious characters from file path $param = str_replace('\\\'', '\'', $param); $param = str_replace('\\', '', $param); $param = str_replace('\\', '/', $param); $param = ereg_replace('[[:cntrl:]]/[<>`\/\':]', '', $param); $param = ereg_replace('\.\.+', '', $param); $param = ereg_replace('//+', '/', $param); $param = ereg_replace('/(\./)+', '/', $param); }



        if ($options & PARAM_HOST) { // allow FQDN or IPv4 dotted quad

        preg_replace('/[^\.\d\w-]/','', $param ); // only allowed chars

        // match ipv4 dotted quad

        if (preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/',$param, $match)){

        // confirm values are ok

        if ( $match[0] > 255

        // $match[1] > 255

        // $match[3] > 255

        // $match[4] > 255 ) { // hmmm, what kind of dotted quad is this? $param = ''; }

        } elseif ( preg_match('/^[\w\d\.-]+$/', $param) // dots, hyphens, numbers

        && !preg_match('/^[\.-]/', $param) // no leading dots/hyphens

        && !preg_match('/[\.-]$/', $param) // no trailing dots/hyphens

        ) { // all is ok - $param is respected } else { // all is not ok... $param=''; }

        }



        if ($options & PARAM_URL) { // allow safe ftp, http, mailto urls



        include_once($CFG->dirroot . '/lib/validateurlsyntax.php');



        //

        // Parameters to validateurlsyntax()

        //

        // s? scheme is optional

        // H? http optional

        // S? https optional

        // F? ftp optional

        // E? mailto optional

        // u- user section not allowed

        // P- password not allowed

        // a? address optional

        // I? Numeric IP address optional (can use IP or domain)

        // p- port not allowed – restrict to default port

        // f? file path section optional

        // q? query section optional

        // r? fragment (anchor) optional

        //

        if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E?u-P-a?I?p-f?q?r?')) { // all is ok, param is respected } else { $param =''; // not really ok }

        $options ^= PARAM_URL; // Turn off the URL bit so that simple PARAM_URLs don't test true for PARAM_LOCALURL

        }



        if ($options & PARAM_LOCALURL) {

        // assume we passed the PARAM_URL test...

        // allow http absolute, root relative and relative URLs within wwwroot

        if (!empty($param)) {

        if (preg_match(':/:', $param)) { // root-relative, ok! } elseif (preg_match('/'.preg_quote($CFG->wwwroot, '/').'/i',$param)) { // absolute, and matches our wwwroot } else {

        // relative - let's make sure there are no tricks

        if (validateUrlSyntax($param, 's-u-P-a-p-f+q?r?')) { // looks ok. } else { $param = ''; }

        }

        }

        }



        if ($options & PARAM_CLEANHTML) { $param = stripslashes($param); // Remove any slashes $param = clean_text($param); // Sweep for scripts, etc $param = trim($param); // Sweep for scripts, etc }



        return $param;

        }



        /**

        * For security purposes, this function will check that the currently

        * given sesskey (passed as a parameter to the script or this function)

        * matches that of the current user.

        *

        * @param string $sesskey optionally provided sesskey

        * @return boolean

        */

        function confirm_sesskey($sesskey=NULL) {

        global $USER;



        if (!empty($USER->ignoresesskey) // !empty($CFG->ignoresesskey)) { return true; }



        if (empty($sesskey)) { $sesskey = required_param('sesskey'); // Check script parameters }



        if (!isset($USER->sesskey)) { return false; }



        return ($USER->sesskey === $sesskey);

        }





        /**

        * Improved ensure a variable is set

        * Issue a (custom) error message if not

        * @param mixed $var the variable

        * @param string $error optional additional error message

        */

        function assert_var_set( $var, $error='' ) {

        if (! isset($var)) { error( a required variable is not set - $error ); }

        }





        /**

        * Ensure that a variable is set

        *

        * If $var is undefined throw an error, otherwise return $var.

        * This function will soon be made obsolete by {@link required_param()}

      *

      • @param mixed $var the variable which may be unset
      • @param mixed $default the value to return if $var is unset

      */

      function require_variable($var) {

      if (! isset($var))

      { error('A required parameter was missing'); }

      }

      /**

      • Ensure that a variable is set

      *

      • If $var is undefined set it (by reference), otherwise return $var.

      *

      • @param mixed $var the variable which may be unset
      • @param mixed $default the value to return if $var is unset

      */

      function optional_variable(&$var, $default=0) {

      if (! isset($var))

      { $var = $default; }

      }

      /**

      • Set a key in global configuration

      *

      • Set a key/value pair in both this session's {@link $CFG} global variable

        * and in the 'config' database table for future sessions.

        *

        * Can also be used to update keys for plugin-scoped configs in config_plugin table.

        * In that case it doesn't affect $CFG.

        *

        * @param string $name the key to set

        * @param string $value the value to set

        * @param string $plugin (optional) the plugin scope

        * @uses $CFG

        * @return bool

        */

        function set_config($name, $value, $plugin=NULL) {

        /// No need for get_config because they are usually always available in $CFG



        global $CFG;



        if (empty($plugin)) {

        $CFG->$name = $value; // So it's defined for this invocation at least



        if (get_field('config', 'name', 'name', $name)) { return set_field('config', 'value', $value, 'name', $name); } else { $config->name = $name; $config->value = $value; return insert_record('config', $config); }

        } else { // plugin scope

        if ($id = get_field('config_plugins', 'id', 'name', $name, 'plugin', $plugin)) { return set_field('config_plugins', 'value', $value, 'id', $id); } else { $config->plugin = $plugin; $config->name = $name; $config->value = $value; return insert_record('config_plugins', $config); }

        }

        }



        /**

        * Get configuration values from the global config table

        * or the config_plugins table.

        *

        * If called with no parameters it will do the right thing

        * generating $CFG safely from the database without overwriting

        * existing values.

        *

        * @param string $plugin

        * @param string $name

        * @uses $CFG

        * @return hash-like object or single value

        *

        */

        function get_config($plugin=NULL, $name=NULL) {



        global $CFG;



        if (!empty($name)) { // the user is asking for a specific value

        if (!empty($plugin)) { return get_record('config_plugins', 'plugin' , $plugin, 'name', $name); } else { return get_record('config', 'name', $name); }

        }



        // the user is after a recordset

        if (!empty($plugin)) {

        if ($configs=get_records('config_plugins', 'plugin', $plugin, '', 'name,value')) {

        $configs = (array)$configs;

        $localcfg = array();

        foreach ($configs as $config) { $localcfg[$config->name] = $config->value; }

        return (object)$localcfg;

        } else { return false; }

        } else {

        // this was originally in setup.php

        if ($configs = get_records('config')) {

        $localcfg = (array)$CFG;

        foreach ($configs as $config) {

        if (!isset($localcfg[$config->name])) { $localcfg[$config->name] = $config->value; } else {

        if ($localcfg[$config->name] != $config->value ) {

        // complain if the DB has a different

        // value than config.php does

        error_log(\$CFG->{$config->name} in config.php ({$localcfg[$config->name]}) overrides database setting ({$config->value}));

        }

        }

        }



        $localcfg = (object)$localcfg;

        return $localcfg;

        } else { // preserve $CFG if DB returns nothing or error return $CFG; }



        }

        }





        /**

        * Refresh current $USER session global variable with all their current preferences.

        * @uses $USER

        */

        function reload_user_preferences() {



        global $USER;



        if(empty($USER) // empty($USER->id)) { return false; }



        unset($USER->preference);



        if ($preferences = get_records('user_preferences', 'userid', $USER->id)) {

        foreach ($preferences as $preference) { $USER->preference[$preference->name] = $preference->value; }

        } else { //return empty preference array to hold new values $USER->preference = array(); }

        }



        /**

        * Sets a preference for the current user

        * Optionally, can set a preference for a different user object

        * @uses $USER

        * @todo Add a better description and include usage examples.

        * @param string $name The key to set as preference for the specified user

        * @param string $value The value to set forthe $name key in the specified user's record

        * @param int $userid A moodle user ID

        * @todo Add inline links to $USER and user functions in above line.

        * @return boolean

        */

        function set_user_preference($name, $value, $otheruser=NULL) {



        global $USER;



        if (empty($otheruser)){

        if (!empty($USER) && !empty($USER->id)) { $userid = $USER->id; } else { return false; }

        } else { $userid = $otheruser; }



        if (empty($name)) { return false; }



        if ($preference = get_record('user_preferences', 'userid', $userid, 'name', $name)) {

        if (set_field('user_preferences', 'value', $value, 'id', $preference->id)) {

        if (empty($otheruser) and !empty($USER)) { $USER->preference[$name] = $value; }

        return true;

        } else { return false; }



        } else {

        $preference->userid = $userid;

        $preference->name = $name;

        $preference->value = (string)$value;

        if (insert_record('user_preferences', $preference)) {

        if (empty($otheruser) and !empty($USER)) { $USER->preference[$name] = $value; }

        return true;

        } else { return false; }

        }

        }



        /**

        * Unsets a preference completely by deleting it from the database

        * Optionally, can set a preference for a different user id

        * @uses $USER

        * @param string $name The key to unset as preference for the specified user

        * @param int $userid A moodle user ID

        * @return boolean

        */

        function unset_user_preference($name, $userid=NULL) {



        global $USER;



        if (empty($userid)){

        if(!empty($USER) && !empty($USER->id)) { $userid = $USER->id; }

        else { return false; }

        }



        return delete_records('user_preferences', 'userid', $userid, 'name', $name);

        }





        /**

        * Sets a whole array of preferences for the current user

        * @param array $prefarray An array of key/value pairs to be set

        * @param int $userid A moodle user ID

        * @return boolean

        */

        function set_user_preferences($prefarray, $userid=NULL) {



        global $USER;



        if (!is_array($prefarray) or empty($prefarray)) { return false; }



        if (empty($userid)){

        if (!empty($USER) && !empty($USER->id)) { $userid = NULL; // Continue with the current user below } else { return false; // No-one to set! }

        }



        $return = true;

        foreach ($prefarray as $name => $value) { // The order is important; if the test for return is done first, then // if one function call fails all the remaining ones will be optimized away $return = set_user_preference($name, $value, $userid) and $return; }

        return $return;

        }



        /**

        * If no arguments are supplied this function will return

        * all of the current user preferences as an array.

        * If a name is specified then this function

        * attempts to return that particular preference value. If

        * none is found, then the optional value $default is returned,

        * otherwise NULL.

        * @param string $name Name of the key to use in finding a preference value

        * @param string $default Value to be returned if the $name key is not set in the user preferences

        * @param int $userid A moodle user ID

        * @uses $USER

        * @return string

        */

        function get_user_preferences($name=NULL, $default=NULL, $userid=NULL) {



        global $USER;



        if (empty($userid)) { // assume current user

        if (empty($USER->preference)) { return $default; // Default value (or NULL) }

        if (empty($name)) { return $USER->preference; // Whole array }

        if (!isset($USER->preference[$name])) { return $default; // Default value (or NULL) }

        return $USER->preference[$name]; // The single value



        } else {

        $preference = get_records_menu('user_preferences', 'userid', $userid, 'name', 'name,value');



        if (empty($name)) { return $preference; }

        if (!isset($preference[$name])) { return $default; // Default value (or NULL) }

        return $preference[$name]; // The single value

        }

        }





        /// FUNCTIONS FOR HANDLING TIME ////////////////////////////////////////////



        /**

        * Given date parts in user time produce a GMT timestamp.

        *

        * @param int $year The year part to create timestamp of.

        * @param int $month The month part to create timestamp of.

        * @param int $day The day part to create timestamp of.

        * @param int $hour The hour part to create timestamp of.

        * @param int $minute The minute part to create timestamp of.

        * @param int $second The second part to create timestamp of.

        * @param float $timezone

        * @return int timestamp

        * @todo Finish documenting this function

        */

        function make_timestamp($year, $month=1, $day=1, $hour=0, $minute=0, $second=0, $timezone=99, $applydst=true) {



        $timezone = get_user_timezone_offset($timezone);



        if (abs($timezone) > 13) { $time = mktime((int)$hour,(int)$minute,(int)$second,(int)$month,(int)$day,(int)$year); } else {

        $time = gmmktime((int)$hour,(int)$minute,(int)$second,(int)$month,(int)$day,(int)$year);

        $time = usertime($time, $timezone);

        if($applydst) { $time -= dst_offset_on($time); }

        }



        return $time;



        }



        /**

        * Given an amount of time in seconds, returns string

        * formatted nicely as months, days, hours etc as needed

        *

        * @uses MINSECS

        * @uses HOURSECS

        * @uses DAYSECS

        * @param int $totalsecs ?

        * @param array $str ?

        * @return string

        * @todo Finish documenting this function

        */

        function format_time($totalsecs, $str=NULL) {



        $totalsecs = abs($totalsecs);



        if (!$str) { // Create the str structure the slow way $str->day = get_string('day'); $str->days = get_string('days'); $str->hour = get_string('hour'); $str->hours = get_string('hours'); $str->min = get_string('min'); $str->mins = get_string('mins'); $str->sec = get_string('sec'); $str->secs = get_string('secs'); }



        $days = floor($totalsecs/DAYSECS);

        $remainder = $totalsecs - ($days*DAYSECS);

        $hours = floor($remainder/HOURSECS);

        $remainder = $remainder - ($hours*HOURSECS);

        $mins = floor($remainder/MINSECS);

        $secs = $remainder - ($mins*MINSECS);



        $ss = ($secs == 1) ? $str->sec : $str->secs;

        $sm = ($mins == 1) ? $str->min : $str->mins;

        $sh = ($hours == 1) ? $str->hour : $str->hours;

        $sd = ($days == 1) ? $str->day : $str->days;



        $odays = '';

        $ohours = '';

        $omins = '';

        $osecs = '';



        if ($days) $odays = $days .' '. $sd;

        if ($hours) $ohours = $hours .' '. $sh;

        if ($mins) $omins = $mins .' '. $sm;

        if ($secs) $osecs = $secs .' '. $ss;



        if ($days) return $odays .' '. $ohours;

        if ($hours) return $ohours .' '. $omins;

        if ($mins) return $omins .' '. $osecs;

        if ($secs) return $osecs;

        return get_string('now');

        }



        /**

        * Returns a formatted string that represents a date in user time

        * <b>WARNING: note that the format is for strftime(), not date().</b>

        * Because of a bug in most Windows time libraries, we can't use

        * the nicer %e, so we have to use %d which has leading zeroes.

        * A lot of the fuss in the function is just getting rid of these leading

        * zeroes as efficiently as possible.

        *

        * If parameter fixday = true (default), then take off leading

        * zero from %d, else mantain it.

        *

        * @uses HOURSECS

        * @param int $date timestamp in GMT

        * @param string $format strftime format

        * @param float $timezone

        * @param boolean $fixday If true (default) then the leading

        * zero from %d is removed. If false then the leading zero is mantained.

        * @return string

        */

        function userdate($date, $format='', $timezone=99, $fixday = true) {



        global $CFG;



        static $strftimedaydatetime;



        if ($format == '') {

        if (empty($strftimedaydatetime)) { $strftimedaydatetime = get_string('strftimedaydatetime'); }

        $format = $strftimedaydatetime;

        }



        if (!empty($CFG->nofixday)) { // Config.php can force %d not to be fixed. $fixday = false; } else if ($fixday) { $formatnoday = str_replace('%d', 'DD', $format); $fixday = ($formatnoday != $format); }



        $date += dst_offset_on($date);



        $timezone = get_user_timezone_offset($timezone);



        if (abs($timezone) > 13) { /// Server time

        if ($fixday) { $datestring = strftime($formatnoday, $date); $daystring = str_replace(' 0', '', strftime(' %d', $date)); $datestring = str_replace('DD', $daystring, $datestring); } else { $datestring = strftime($format, $date); }

        } else {

        $date += (int)($timezone * 3600);

        if ($fixday) { $datestring = gmstrftime($formatnoday, $date); $daystring = str_replace(' 0', '', gmstrftime(' %d', $date)); $datestring = str_replace('DD', $daystring, $datestring); } else { $datestring = gmstrftime($format, $date); }

        }

        //Bojan Milosavljevic: Language specific unicode from (primarily Windows) server system date to current language charset

        sys_to_currlang_charset ($datestring);

        return $datestring;

        }



        /**

        * Converts (Windows) Unicode characters to current language character set

        *

        * @param string $datestr A date string to convert

        * @return string the converted date

        * @todo SHOULD BE MORE GENERAL!... find server-system- and current-language charset and then map corresponding chars,

        * but this is just a patch

        */

        function sys_to_currlang_charset(&$datestr) {



        //COPIED FROM get_string FUNCTION!!

        global $CFG;



        $lang = current_language();





        /// Define the two or three major locations of language strings for this module



        $location = $CFG->dirroot.'/lang/';



        /// Bojan Milosavljevic: functions for language specific grammar

        $langfunfile = $location.$lang.'/langfun.php';

        if (file_exists($langfunfile)) {

        include_once($langfunfile);

        $maps = charset_maps();





        // END OF COPIED CODE FROM get_string FUNCTION





        foreach ($maps as $i => $map) { $datestr = str_replace ($i, $map,$datestr); }

        }

        return 0;

        }



        /**

        * Given a $time timestamp in GMT (seconds since epoch),

        * returns an array that represents the date in user time

        *

        * @uses HOURSECS

        * @param int $time Timestamp in GMT

        * @param float $timezone

        * @return array An array that represents the date in user time

        * @todo Finish documenting this function

        */

        function usergetdate($time, $timezone=99) {



        $timezone = get_user_timezone_offset($timezone);



        if (abs($timezone) > 13) { // Server time return getdate($time); }



        // There is no gmgetdate so we use gmdate instead

        $time += dst_offset_on($time);

        $time += intval((float)$timezone * HOURSECS);



        $datestring = gmstrftime('%S_%M_%H_%d_%m_%Y_%w_%j_%A_%B', $time);



        list(

        $getdate['seconds'],

        $getdate['minutes'],

        $getdate['hours'],

        $getdate['mday'],

        $getdate['mon'],

        $getdate['year'],

        $getdate['wday'],

        $getdate['yday'],

        $getdate['weekday'],

        $getdate['month']

        ) = explode('_', $datestring);



        return $getdate;

        }



        /**

        * Given a GMT timestamp (seconds since epoch), offsets it by

        * the timezone. eg 3pm in India is 3pm GMT - 7 * 3600 seconds

        *

        * @uses HOURSECS

        * @param int $date Timestamp in GMT

        * @param float $timezone

        * @return int

        */

        function usertime($date, $timezone=99) {



        $timezone = get_user_timezone_offset($timezone);



        if (abs($timezone) > 13) { return $date; }

        return $date - (int)($timezone * HOURSECS);

        }



        /**

        * Given a time, return the GMT timestamp of the most recent midnight

        * for the current user.

        *

        * @param int $date Timestamp in GMT

        * @param float $timezone ?

        * @return ?

        */

        function usergetmidnight($date, $timezone=99) { $timezone = get_user_timezone_offset($timezone); $userdate = usergetdate($date, $timezone); // Time of midnight of this user's day, in GMT return make_timestamp($userdate['year'], $userdate['mon'], $userdate['mday'], 0, 0, 0, $timezone); }



        /**

        * Returns a string that prints the user's timezone

        *

        * @param float $timezone The user's timezone

        * @return string

        */

        function usertimezone($timezone=99) {



        $tz = get_user_timezone($timezone);



        if (!is_float($tz)) { return $tz; }



        if(abs($tz) > 13) { // Server time return get_string('serverlocaltime'); }



        if($tz == intval($tz)) { // Don't show .0 for whole hours $tz = intval($tz); }



        if($tz == 0) { return 'GMT'; }

        else if($tz > 0) { return 'GMT+'.$tz; }

        else { return 'GMT'.$tz; }



        }



        /**

        * Returns a float which represents the user's timezone difference from GMT in hours

        * Checks various settings and picks the most dominant of those which have a value

        *

        * @uses $CFG

        * @uses $USER

        * @param float $tz The user's timezone

        * @return int

        */

        function get_user_timezone_offset($tz = 99) {



        global $USER, $CFG;



        $tz = get_user_timezone($tz);



        if (is_float($tz)) { return $tz; } else {

        $tzrecord = get_timezone_record($tz);

        if (empty($tzrecord)) { return 99.0; }

        return (float)$tzrecord->gmtoff / HOURMINS;

        }

        }



        function get_user_timezone($tz = 99) {

        global $USER, $CFG;



        $timezones = array(

        $tz,

        isset($CFG->forcetimezone) ? $CFG->forcetimezone : 99,

        isset($USER->timezone) ? $USER->timezone : 99,

        isset($CFG->timezone) ? $CFG->timezone : 99,

        );



        $tz = 99;



        while(($tz == '' // $tz == 99) && $next = each($timezones)) { $tz = $next['value']; }



        return is_numeric($tz) ? (float) $tz : $tz;

        }



        function get_timezone_record($timezonename) {

        global $CFG, $db;

        static $cache = NULL;



        if ($cache === NULL) { $cache = array(); }



        if (isset($cache[$timezonename])) { return $cache[$timezonename]; }



        return get_record_sql('SELECT * FROM '.$CFG->prefix.'timezone

        WHERE name = '.$db->qstr($timezonename).' ORDER BY year DESC', true);

        }



        function calculate_user_dst_table($from_year = NULL, $to_year = NULL) {

        global $CFG, $USER;



        if (empty($USER)) { return false; }



        $usertz = get_user_timezone();



        if (is_float($usertz)) { // Trivial timezone, no DST return false; }



        if (!empty($USER->dstoffsettz) && $USER->dstoffsettz != $usertz) { // We have precalculated values, but the user's effective TZ has changed in the meantime, so reset unset($USER->dstoffsets); unset($USER->dstrange); }



        if (!empty($USER->dstoffsets) && empty($from_year) && empty($to_year)) { // Repeat calls which do not request specific year ranges stop here, we have already calculated the table // This will be the return path most of the time, pretty light computationally return true; }



        // Reaching here means we either need to extend our table or create it from scratch



        // Remember which TZ we calculated these changes for

        $USER->dstoffsettz = $usertz;



        if(empty($USER->dstoffsets)) { // If we 're creating from scratch, put the two guard elements in there $USER->dstoffsets = array(1 => NULL, 0 => NULL); }

        if(empty($USER->dstrange)) {

        // If creating from scratch

        $from = max((empty($from_year) ? intval(date('Y')) - 3 : $from_year), 1971);

        $to = min((empty($to_year) ? intval(date('Y')) + 3 : $to_year), 2035);



        // Fill in the array with the extra years we need to process

        $yearstoprocess = array();

        for($i = $from; $i <= $to; ++$i) { $yearstoprocess[] = $i; }



        // Take note of which years we have processed for future calls

        $USER->dstrange = array($from, $to);

        }

        else {

        // If needing to extend the table, do the same

        $yearstoprocess = array();



        $from = max((empty($from_year) ? $USER->dstrange[0] : $from_year), 1971);

        $to = min((empty($to_year) ? $USER->dstrange[1] : $to_year), 2035);



        if($from < $USER->dstrange[0]) {

        // Take note of which years we need to process and then note that we have processed them for future calls

        for($i = $from; $i < $USER->dstrange[0]; ++$i) { $yearstoprocess[] = $i; }

        $USER->dstrange[0] = $from;

        }

        if($to > $USER->dstrange[1]) {

        // Take note of which years we need to process and then note that we have processed them for future calls

        for($i = $USER->dstrange[1] + 1; $i <= $to; ++$i) { $yearstoprocess[] = $i; }

        $USER->dstrange[1] = $to;

        }

        }



        if(empty($yearstoprocess)) { // This means that there was a call requesting a SMALLER range than we have already calculated return true; }



        // From now on, we know that the array has at least the two guard elements, and $yearstoprocess has the years we need

        // Also, the array is sorted in descending timestamp order!



        // Get DB data

        $presetrecords = get_records('timezone', 'name', $usertz, 'year DESC', 'year, gmtoff, dstoff, dst_month, dst_startday, dst_weekday, dst_skipweeks, dst_time, std_month, std_startday, std_weekday, std_skipweeks, std_time');

        if(empty($presetrecords)) { return false; }



        // Remove ending guard (first element of the array)

        reset($USER->dstoffsets);

        unset($USER->dstoffsets[key($USER->dstoffsets)]);



        // Add all required change timestamps

        foreach($yearstoprocess as $y) {

        // Find the record which is in effect for the year $y

        foreach($presetrecords as $year => $preset) {

        if($year <= $y) { break; }

        }



        $changes = dst_changes_for_year($y, $preset);



        if($changes === NULL) { continue; }

        if($changes['dst'] != 0) { $USER->dstoffsets[$changes['dst']] = $preset->dstoff * MINSECS; }

        if($changes['std'] != 0) { $USER->dstoffsets[$changes['std']] = 0; }

        }



        // Put in a guard element at the top

        $maxtimestamp = max(array_keys($USER->dstoffsets));

        $USER->dstoffsets[($maxtimestamp + DAYSECS)] = NULL; // DAYSECS is arbitrary, any small number will do



        // Sort again

        krsort($USER->dstoffsets);



        return true;

        }



        function dst_changes_for_year($year, $timezone) {



        if($timezone->dst_startday == 0 && $timezone->dst_weekday == 0 && $timezone->std_startday == 0 && $timezone->std_weekday == 0) { return NULL; }



        $monthdaydst = find_day_in_month($timezone->dst_startday, $timezone->dst_weekday, $timezone->dst_month, $year);

        $monthdaystd = find_day_in_month($timezone->std_startday, $timezone->std_weekday, $timezone->std_month, $year);



        list($dst_hour, $dst_min) = explode(':', $timezone->dst_time);

        list($std_hour, $std_min) = explode(':', $timezone->std_time);



        $timedst = make_timestamp($year, $timezone->dst_month, $monthdaydst, 0, 0, 0, 99, false);

        $timestd = make_timestamp($year, $timezone->std_month, $monthdaystd, 0, 0, 0, 99, false);



        // Instead of putting hour and minute in make_timestamp(), we add them afterwards.

        // This has the advantage of being able to have negative values for hour, i.e. for timezones

        // where GMT time would be in the PREVIOUS day than the local one on which DST changes.



        $timedst += $dst_hour * HOURSECS + $dst_min * MINSECS;

        $timestd += $std_hour * HOURSECS + $std_min * MINSECS;



        return array('dst' => $timedst, 0 => $timedst, 'std' => $timestd, 1 => $timestd);

        }



        // $time must NOT be compensated at all, it has to be a pure timestamp

        function dst_offset_on($time) {

        global $USER;



        if(!calculate_user_dst_table()) { return 0; }



        if(empty($USER) // empty($USER->dstoffsets)) { return 0; }



        reset($USER->dstoffsets);

        while(list($from, $offset) = each($USER->dstoffsets)) {

        if($from <= $time) { break; }

        }



        // This is the normal return path

        if($offset !== NULL) { return $offset; }



        // Reaching this point means we haven't calculated far enough, do it now:

        // Calculate extra DST changes if needed and recurse. The recursion always

        // moves toward the stopping condition, so will always end.



        if($from == 0) {

        // We need a year smaller than $USER->dstrange[0]

        if($USER->dstrange[0] == 1971) { return 0; }

        calculate_user_dst_table($USER->dstrange[0] - 5, NULL);

        return dst_offset_on($time);

        }

        else {

        // We need a year larger than $USER->dstrange[1]

        if($USER->dstrange[1] == 2035) { return 0; }

        calculate_user_dst_table(NULL, $USER->dstrange[1] + 5);

        return dst_offset_on($time);

        }

        }



        function find_day_in_month($startday, $weekday, $month, $year) {



        $daysinmonth = days_in_month($month, $year);



        if($weekday == -1) { // Don't care about weekday, so return: // abs($startday) if $startday != -1 // $daysinmonth otherwise return ($startday == -1) ? $daysinmonth : abs($startday); }



        // From now on we 're looking for a specific weekday



        // Give end of month its actual value, since we know it

        if($startday == -1) { $startday = -1 * $daysinmonth; }



        // Starting from day $startday, the sign is the direction



        if($startday < 1) {



        $startday = abs($startday);

        $lastmonthweekday = strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0));



        // This is the last such weekday of the month

        $lastinmonth = $daysinmonth + $weekday - $lastmonthweekday;

        if($lastinmonth > $daysinmonth) { $lastinmonth -= 7; }



        // Find the first such weekday <= $startday

        while($lastinmonth > $startday) { $lastinmonth -= 7; }



        return $lastinmonth;



        }

        else {



        $indexweekday = strftime('%w', mktime(12, 0, 0, $month, $startday, $year, 0));



        $diff = $weekday - $indexweekday;

        if($diff < 0) { $diff += 7; }



        // This is the first such weekday of the month equal to or after $startday

        $firstfromindex = $startday + $diff;



        return $firstfromindex;



        }

        }



        function days_in_month($month, $year) { return intval(date('t', mktime(12, 0, 0, $month, 1, $year, 0))); }



        function dayofweek($day, $month, $year) { // I wonder if this is any different from // strftime('%w', mktime(12, 0, 0, $month, $daysinmonth, $year, 0)); return intval(date('w', mktime(12, 0, 0, $month, $day, $year, 0))); }



        /// USER AUTHENTICATION AND LOGIN ////////////////////////////////////////



        // Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey

        // if one does not already exist, but does not overwrite existing sesskeys. Returns the

        // sesskey string if $USER exists, or boolean false if not.

        function sesskey() {

        global $USER;



        if(!isset($USER)) { return false; }



        if (empty($USER->sesskey)) { $USER->sesskey = random_string(10); }



        return $USER->sesskey;

        }



        /**

        * This function checks that the current user is logged in and has the

        * required privileges

        *

        * This function checks that the current user is logged in, and optionally

        * whether they are allowed to be in a particular course and view a particular

        * course module.

        * If they are not logged in, then it redirects them to the site login unless

        * $autologinguest is set and {@link $CFG}

        ->autologinguests is set to 1 in which

      • case they are automatically logged in as guests.
      • If $courseid is given and the user is not enrolled in that course then the
      • user is redirected to the course enrolment page.
      • If $cm is given and the coursemodule is hidden and the user is not a teacher
      • in the course then the user is redirected to the course home page.

      *

      • @uses $CFG
      • @uses $SESSION
      • @uses $USER
      • @uses $FULLME
      • @uses SITEID
      • @uses $MoodleSession
      • @param int $courseid id of the course
      • @param boolean $autologinguest
      • @param $cm course module object

      */

      function require_login($courseid=0, $autologinguest=true, $cm=null) {

      global $CFG, $SESSION, $USER, $FULLME, $MoodleSession;

      // First check that the user is logged in to the site.

      if (! (isset($USER->loggedin) and $USER->confirmed and ($USER->site == $CFG->wwwroot)) ) { // They're not

      $SESSION->wantsurl = $FULLME;

      if (!empty($_SERVER['HTTP_REFERER']))

      { $SESSION->fromurl = $_SERVER['HTTP_REFERER']; }

      $USER = NULL;

      if ($autologinguest and $CFG->autologinguests and $courseid and get_field('course','guest','id',$courseid))

      { $loginguest = '?loginguest=true'; }

      else

      { $loginguest = ''; }

      if (empty($CFG->loginhttps))

      { redirect($CFG->wwwroot .'/login/index.php'. $loginguest); }

      else

      { $wwwroot = str_replace('http','https', $CFG->wwwroot); redirect($wwwroot .'/login/index.php'. $loginguest); }

      exit;

      }

      // check whether the user should be changing password

      // reload_user_preferences(); // Why is this necessary? Seems wasteful. - MD

      if (!empty($USER->preference['auth_forcepasswordchange'])){

      if (is_internal_auth() // $CFG->

      {'auth_'.$USER->auth.'_stdchangepassword'}

      )

      { $SESSION->wantsurl = $FULLME; redirect($CFG->wwwroot .'/login/change_password.php'); }

      elseif($CFG->changepassword)

      { redirect($CFG->changepassword); }

      else

      { error('You cannot proceed without changing your password. However there is no available page for changing it. Please contact your Moodle Administrator.'); }

      }

      // Check that the user account is properly set up

      if (user_not_fully_set_up($USER))

      { $SESSION->wantsurl = $FULLME; redirect($CFG->wwwroot .'/user/edit.php?id='. $USER->id .'&course='. SITEID); }

      // Make sure current IP matches the one for this session (if required)

      if (!empty($CFG->tracksessionip)) {

      if ($USER->sessionIP != md5(getremoteaddr()))

      { error(get_string('sessionipnomatch', 'error')); }

      }

      // Make sure the USER has a sesskey set up. Used for checking script parameters.

      sesskey();

      // Check that the user has agreed to a site policy if there is one

      if (!empty($CFG->sitepolicy)) {

      if (!$USER->policyagreed)

      { $SESSION->wantsurl = $FULLME; redirect($CFG->wwwroot .'/user/policy.php'); }

      }

      // If the site is currently under maintenance, then print a message

      if (!isadmin()) {

      if (file_exists($CFG->dataroot.'/'.SITEID.'/maintenance.html'))

      { print_maintenance_message(); exit; }

      }

      // Next, check if the user can be in a particular course

      if ($courseid) {

      if ($courseid == SITEID) { // Anyone can be in the site course

      if (isset($cm) and !$cm->visible and !isteacher(SITEID))

      { // Not allowed to see module, send to course page redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden')); }

      return;

      }

      if (!empty($USER->student[$courseid]) or !empty($USER->teacher[$courseid]) or !empty($USER->admin)) {

      if (isset($USER->realuser)) { // Make sure the REAL person can also access this course

      if (!isteacher($courseid, $USER->realuser)) { print_header(); notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/'); }

      }

      if (isset($cm) and !$cm->visible and !isteacher($courseid)) { // Not allowed to see module, send to course page redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden')); }

      return; // user is a member of this course.

      }

      if (! $course = get_record('course', 'id', $courseid))

      { error('That course doesn\'t exist'); }

      if (!$course->visible)

      { print_header(); notice(get_string('coursehidden'), $CFG->wwwroot .'/'); }

      if ($USER->username == 'guest') {

      switch ($course->guest) {

      case 0: // Guests not allowed

      print_header();

      notice(get_string('guestsnotallowed', '', $course->fullname), $CFG->wwwroot/login/index.php);

      break;

      case 1: // Guests allowed

      if (isset($cm) and !$cm->visible)

      { // Not allowed to see module, send to course page redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden')); }

      return;

      case 2: // Guests allowed with key (drop through)

      break;

      }

      }

      //User is not enrolled in the course, wants to access course content

      //as a guest, and course setting allow unlimited guest access

      //Code cribbed from course/loginas.php

      if (strstr($FULLME,username=guest) && ($course->guest==1)) {

      $realuser = $USER->id;

      $realname = fullname($USER, true);

      $USER = guest_user();

      $USER->loggedin = true;

      $USER->site = $CFG->wwwroot;

      $USER->realuser = $realuser;

      $USER->sessionIP = md5(getremoteaddr()); // Store the current IP in the session

      if (isset($SESSION->currentgroup))

      { // Remember current cache setting for later $SESSION->oldcurrentgroup = $SESSION->currentgroup; unset($SESSION->currentgroup); }

      $guest_name = fullname($USER, true);

      add_to_log($course->id, course, loginas, ../user/view.php?id=$course->id&$USER->id$, $realname -> $guest_name);

      if (isset($cm) and !$cm->visible)

      { // Not allowed to see module, send to course page redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden')); }

      return;

      }

      // Currently not enrolled in the course, so see if they want to enrol

      $SESSION->wantsurl = $FULLME;

      redirect($CFG->wwwroot .'/course/enrol.php?id='. $courseid);

      die;

      }

      }

      /**

      • This is a weaker version of {@link require_login()}

        which only requires login

      • when called from within a course rather than the site page, unless
      • the forcelogin option is turned on.

      *

      • @uses $CFG
      • @param object $course The course object in question
      • @param boolean $autologinguest Allow autologin guests if that is wanted
      • @param object $cm Course activity module if known

      */

      function require_course_login($course, $autologinguest=true, $cm=null) {

      global $CFG;

      if (!empty($CFG->forcelogin))

      { require_login(); }

      if ($course->id != SITEID)

      { require_login($course->id, $autologinguest, $cm); }

      }

      /**

      • Modify the user table by setting the currently logged in user's
      • last login to now.

      *

      • @uses $USER
      • @return boolean

      */

      function update_user_login_times()

      { global $USER; $USER->lastlogin = $user->lastlogin = $USER->currentlogin; $USER->currentlogin = $user->lastaccess = $user->currentlogin = time(); $user->id = $USER->id; return update_record('user', $user); }

      /**

      • Determines if a user has completed setting up their account.

      *

      • @param user $user A {@link $USER}

        object to test for the existance of a valid name and email

      • @return boolean

      */

      function user_not_fully_set_up($user)

      { return ($user->username != 'guest' and (empty($user->firstname) or empty($user->lastname) or empty($user->email) or over_bounce_threshold($user))); }

      function over_bounce_threshold($user) {

      global $CFG;

      if (empty($CFG->handlebounces))

      { return false; }

      // set sensible defaults

      if (empty($CFG->minbounces)) { $CFG->minbounces = 10; }

      if (empty($CFG->bounceratio)) { $CFG->bounceratio = .20; }

      $bouncecount = 0;

      $sendcount = 0;

      if ($bounce = get_record('user_preferences','userid',$user->id,'name','email_bounce_count')) { $bouncecount = $bounce->value; }

      if ($send = get_record('user_preferences','userid',$user->id,'name','email_send_count')) { $sendcount = $send->value; }

      return ($bouncecount >= $CFG->minbounces && $bouncecount/$sendcount >= $CFG->bounceratio);

      }



      /**

      * @param $user - object containing an id

      * @param $reset - will reset the count to 0

      */

      function set_send_count($user,$reset=false) {

      if ($pref = get_record('user_preferences','userid',$user->id,'name','email_send_count')) { $pref->value = (!empty($reset)) ? 0 : $pref->value+1; update_record('user_preferences',$pref); }

      else if (!empty($reset)) { // if it's not there and we're resetting, don't bother. // make a new one $pref->name = 'email_send_count'; $pref->value = 1; $pref->userid = $user->id; insert_record('user_preferences',$pref); }

      }



      /**

      * @param $user - object containing an id

      * @param $reset - will reset the count to 0

      */

      function set_bounce_count($user,$reset=false) {

      if ($pref = get_record('user_preferences','userid',$user->id,'name','email_bounce_count')) { $pref->value = (!empty($reset)) ? 0 : $pref->value+1; update_record('user_preferences',$pref); }

      else if (!empty($reset)) { // if it's not there and we're resetting, don't bother. // make a new one $pref->name = 'email_bounce_count'; $pref->value = 1; $pref->userid = $user->id; insert_record('user_preferences',$pref); }

      }



      /**

      * Keeps track of login attempts

      *

      * @uses $SESSION

      */

      function update_login_count() {



      global $SESSION;



      $max_logins = 10;



      if (empty($SESSION->logincount)) { $SESSION->logincount = 1; } else { $SESSION->logincount++; }



      if ($SESSION->logincount > $max_logins) { unset($SESSION->wantsurl); error(get_string('errortoomanylogins')); }

      }



      /**

      * Resets login attempts

      *

      * @uses $SESSION

      */

      function reset_login_count() { global $SESSION; $SESSION->logincount = 0; }



      /**

      * check_for_restricted_user

      *

      * @uses $CFG

      * @uses $USER

      * @param string $username ?

      * @param string $redirect ?

      * @todo Finish documenting this function

      */

      function check_for_restricted_user($username=NULL, $redirect='') {

      global $CFG, $USER;



      if (!$username) {

      if (!empty($USER->username)) { $username = $USER->username; } else { return false; }

      }



      if (!empty($CFG->restrictusers)) {

      $names = explode(',', $CFG->restrictusers);

      if (in_array($username, $names)) { error(get_string('restricteduser', 'error', fullname($USER)), $redirect); }

      }

      }



      function sync_metacourses() {



      global $CFG;



      if (!$courses = get_records_sql(SELECT DISTINCT parent_course,1 FROM {$CFG->prefix}course_meta)) { return; }



      foreach ($courses as $course) { sync_metacourse($course->parent_course); }

      }





      /**

      * Goes through all enrolment records for the courses inside the metacourse and sync with them.

      */



      function sync_metacourse($metacourseid) {



      global $CFG,$db;



      if (!$metacourse = get_record(course,id,$metacourseid)) { return false; }

      if (count_records('course_meta','parent_course',$metacourseid) == 0) { // if there are no child courses for this meta course, nuke the enrolments

      if ($enrolments = get_records('user_students','course',$metacourseid,'','userid,1')) {

      foreach ($enrolments as $enrolment)

      { unenrol_student($enrolment->userid,$metacourseid); }

      }

      return true;

      }

      // this will return a list of userids from user_student for enrolments in the metacourse that shouldn't be there.

      $sql = SELECT parent.userid,max(child.course) as course

      FROM {$CFG->prefix}course_meta meta

      JOIN {$CFG->prefix}user_students parent

      ON meta.parent_course = parent.course

      LEFT OUTER JOIN {$CFG->prefix}user_students child

      ON child.course = meta.child_course

      AND child.userid = parent.userid

      WHERE meta.parent_course = $metacourseid

      GROUP BY child.course,parent.userid

      ORDER BY parent.userid,child.course;

      $res = $db->Execute($sql);

      //iterate results

      $enrolmentstodelete = array();

      while( !$res->EOF && isset($res->fields) )

      { $enrolmentstodelete[] = $res->fields; $res->MoveNext(); }

      if (!empty($enrolmentstodelete)) {

      $last->id = 0;

      $last->course = 0;

      foreach ($enrolmentstodelete as $enrolment) {

      $enrolment = (object)$enrolment;

      if (count($enrolmentstodelete) == 1 && empty($enrolment->course))

      { unenrol_student($enrolment->userid,$metacourseid); break; }

      if ($last->id != $enrolment->userid) { // we've changed

      if (empty($last->course) && !empty($last->id))

      { unenrol_student($last->id,$metacourseid); // doing it this way for forum subscriptions etc. }

      $last->course = 0;

      $last->id = $enrolment->userid;

      }

      if (!empty($enrolment->course))

      { $last->course = $enrolment->course; }

      }

      if (!empty($last->id) && empty($last->course))

      { unenrol_student($last->id,$metacourseid); // doing it this way for forum subscriptions etc. }

      }

      // this will return a list of userids that need to be enrolled in the metacourse

      $sql = SELECT DISTINCT child.userid,1

      FROM {$CFG->prefix}course_meta meta

      JOIN {$CFG->prefix}user_students child

      ON meta.child_course = child.course

      LEFT OUTER JOIN {$CFG->prefix}user_students parent

      ON meta.parent_course = parent.course

      AND parent.userid = child.userid

      WHERE parent.course IS NULL

      AND meta.parent_course = $metacourseid;

      if ($userstoadd = get_records_sql($sql)) {

      foreach ($userstoadd as $user)

      { enrol_student($user->userid,$metacourseid); }

      }

      // and next make sure that we have the right start time and end time (ie max and min) for them all.

      if ($enrolments = get_records('user_students','course',$metacourseid,'','id,userid')) {

      foreach ($enrolments as $enrol) {

      if ($maxmin = get_record_sql(SELECT min(timestart) AS timestart, max(timeend) AS timeend

      FROM {$CFG->prefix}user_students u JOIN {$CFG->prefix}course_meta mc ON u.course = mc.child_course WHERE userid = $enrol->userid

      AND mc.parent_course = $metacourseid))

      { $enrol->timestart = $maxmin->timestart; $enrol->timeend = $maxmin->timeend; update_record('user_students',$enrol); }

      }

      }

      return true;

      }

      /**

      • Adds a record to the metacourse table and calls sync_metacoures

      */

      function add_to_metacourse ($metacourseid, $courseid) {

      if (!$metacourse = get_record(course,id,$metacourseid))

      { return false; }



      if (!$course = get_record(course,id,$courseid)) { return false; }

      if (!$record = get_record(course_meta,parent_course,$metacourseid,child_course,$courseid)) {

      $rec->parent_course = $metacourseid;

      $rec->child_course = $courseid;

      if (!insert_record('course_meta',$rec))

      { return false; }

      return sync_metacourse($metacourseid);

      }

      return true;

      }

      /**

      • Removes the record from the metacourse table and calls sync_metacourse

      */

      function remove_from_metacourse($metacourseid, $courseid) {

      if (delete_records('course_meta','parent_course',$metacourseid,'child_course',

            moodle.com Moodle HQ
            imported Imported (Inactive)
            Votes:
            3 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved:

                Error rendering 'clockify-timesheets-time-tracking-reports:timer-sidebar'. Please contact your Jira administrators.