/**
 * This file keeps track of upgrades to
 * the forum module
 *
 * Sometimes, changes between versions involve
 * alterations to database structures and other
 * major things that may break installations.
 *
 * The upgrade function in this file will attempt
 * to perform all the necessary actions to upgrade
 * your older installation to the current version.
 *
 * If there's something it cannot do itself, it
 * will tell you what you need to do.
 *
 * The commands in here will all be database-neutral,
 * using the methods of database_manager class
 *
 * Please do not forget to use upgrade_set_timeout()
 * before any action that may take longer time to finish.
 *
 * @package mod-forum
 * @copyright 2003 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
function xmldb_forum_upgrade($oldversion)
{
    global $CFG, $DB, $OUTPUT;
    $dbman = $DB->get_manager();
    // Loads ddl manager and xmldb classes.
    // Moodle v2.2.0 release upgrade line.
    // Put any upgrade step following this.
    // Moodle v2.3.0 release upgrade line.
    // Put any upgrade step following this.
    // Moodle v2.4.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2013020500) {
        // Define field displaywordcount to be added to forum.
        $table = new xmldb_table('forum');
        $field = new xmldb_field('displaywordcount', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'completionposts');
        // Conditionally launch add field displaywordcount.
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2013020500, 'forum');
    }
    // Forcefully assign mod/forum:allowforcesubscribe to frontpage role, as we missed that when
    // capability was introduced.
    if ($oldversion < 2013021200) {
        // If capability mod/forum:allowforcesubscribe is defined then set it for frontpage role.
        if (get_capability_info('mod/forum:allowforcesubscribe')) {
            assign_legacy_capabilities('mod/forum:allowforcesubscribe', array('frontpage' => CAP_ALLOW));
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2013021200, 'forum');
    }
    // Moodle v2.5.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2013071000) {
        // Define table forum_digests to be created.
        $table = new xmldb_table('forum_digests');
        // Adding fields to table forum_digests.
        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
        $table->add_field('forum', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
        $table->add_field('maildigest', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '-1');
        // Adding keys to table forum_digests.
        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
        $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
        $table->add_key('forum', XMLDB_KEY_FOREIGN, array('forum'), 'forum', array('id'));
        $table->add_key('forumdigest', XMLDB_KEY_UNIQUE, array('forum', 'userid', 'maildigest'));
        // Conditionally launch create table for forum_digests.
        if (!$dbman->table_exists($table)) {
            $dbman->create_table($table);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2013071000, 'forum');
    }
    // Moodle v2.6.0 release upgrade line.
    // Put any upgrade step following this.
    return true;
}
 /**
  * Checks if the plugin is instantiable by the current user in the specified context.
  * Checks capability addinstance. If the plugin doesn't have the capability, it cannot be instantiated.
  *
  * @param context $context Either course or dataform (module) context.
  * @return boolean
  */
 public function is_instantiable($context)
 {
     $capability = 'dataformview/' . $this->name . ':addinstance';
     if (!get_capability_info($capability)) {
         // If the capability does not exist, the plugin cannot be instantiated.
         return false;
     }
     return has_capability($capability, $context);
 }
Exemple #3
0
 /**
  * Check modifying capability record is not exposed to other code.
  */
 public function test_capabilities_mutation()
 {
     $oldcap = get_capability_info('moodle/site:config');
     $cap = get_capability_info('moodle/site:config');
     unset($cap->name);
     $newcap = get_capability_info('moodle/site:config');
     $this->assertFalse(isset($cap->name));
     $this->assertTrue(isset($newcap->name));
     $this->assertTrue(isset($oldcap->name));
 }
function xmldb_block_demostudent_upgrade($oldversion = 0)
{
    global $CFG;
    $result = true;
    // Assign moodle/course:viewhiddencourses capability to demostudent role.
    if ($oldversion < 2013100201) {
        if (get_capability_info('moodle/course:viewhiddencourses')) {
            $demostudentroleid = get_roleid_by_roleshortname('demostudent');
            assign_capability('moodle/course:viewhiddencourses', CAP_ALLOW, $demostudentroleid, 1);
        }
        upgrade_plugin_savepoint(true, 2013100201, 'block', 'demostudent');
    }
    return $result;
}
Exemple #5
0
/**
 * This file keeps track of upgrades to
 * the forum module
 *
 * Sometimes, changes between versions involve
 * alterations to database structures and other
 * major things that may break installations.
 *
 * The upgrade function in this file will attempt
 * to perform all the necessary actions to upgrade
 * your older installation to the current version.
 *
 * If there's something it cannot do itself, it
 * will tell you what you need to do.
 *
 * The commands in here will all be database-neutral,
 * using the methods of database_manager class
 *
 * Please do not forget to use upgrade_set_timeout()
 * before any action that may take longer time to finish.
 *
 * @package mod-forum
 * @copyright 2003 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

function xmldb_forum_upgrade($oldversion) {
    global $CFG, $DB, $OUTPUT;

    $dbman = $DB->get_manager(); // Loads ddl manager and xmldb classes.

    // Moodle v2.2.0 release upgrade line.
    // Put any upgrade step following this.

    // Moodle v2.3.0 release upgrade line.
    // Put any upgrade step following this.

    // Moodle v2.4.0 release upgrade line.
    // Put any upgrade step following this.

    // Moodle v2.5.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2013020500) {

        // Define field displaywordcount to be added to forum.
        $table = new xmldb_table('forum');
        $field = new xmldb_field('displaywordcount', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'completionposts');

        // Conditionally launch add field displaywordcount.
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }

        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2013020500, 'forum');
    }

    // Forcefully assign mod/forum:allowforcesubscribe to frontpage role, as we missed that when
    // capability was introduced.
    if ($oldversion < 2013021200) {
        // If capability mod/forum:allowforcesubscribe is defined then set it for frontpage role.
        if (get_capability_info('mod/forum:allowforcesubscribe')) {
            assign_legacy_capabilities('mod/forum:allowforcesubscribe', array('frontpage' => CAP_ALLOW));
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2013021200, 'forum');
    }

    return true;
}
Exemple #6
0
/**
 * This file keeps track of upgrades to
 * the forum module
 *
 * Sometimes, changes between versions involve
 * alterations to database structures and other
 * major things that may break installations.
 *
 * The upgrade function in this file will attempt
 * to perform all the necessary actions to upgrade
 * your older installation to the current version.
 *
 * If there's something it cannot do itself, it
 * will tell you what you need to do.
 *
 * The commands in here will all be database-neutral,
 * using the methods of database_manager class
 *
 * Please do not forget to use upgrade_set_timeout()
 * before any action that may take longer time to finish.
 *
 * @package mod-forum
 * @copyright 2003 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

function xmldb_forum_upgrade($oldversion) {
    global $CFG, $DB, $OUTPUT;

    $dbman = $DB->get_manager(); // loads ddl manager and xmldb classes


    // Moodle v2.2.0 release upgrade line
    // Put any upgrade step following this

    // Moodle v2.3.0 release upgrade line
    // Put any upgrade step following this

    // Forcefully assign mod/forum:allowforcesubscribe to frontpage role, as we missed that when
    // capability was introduced.
    if ($oldversion < 2012061702) {
        // If capability mod/forum:allowforcesubscribe is defined then set it for frontpage role.
        if (get_capability_info('mod/forum:allowforcesubscribe')) {
            assign_legacy_capabilities('mod/forum:allowforcesubscribe', array('frontpage' => CAP_ALLOW));
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2012061702, 'forum');
    }
    return true;
}
Exemple #7
0
/**
 * Used by {@link optional_param()} and {@link required_param()} to
 * clean the variables and/or cast to specific types, based on
 * an options field.
 * <code>
 * $course->format = clean_param($course->format, PARAM_ALPHA);
 * $selectedgradeitem = clean_param($selectedgradeitem, PARAM_INT);
 * </code>
 *
 * @param mixed $param the variable we are cleaning
 * @param string $type expected format of param after cleaning.
 * @return mixed
 * @throws coding_exception
 */
function clean_param($param, $type)
{
    global $CFG;
    if (is_array($param)) {
        throw new coding_exception('clean_param() can not process arrays, please use clean_param_array() instead.');
    } else {
        if (is_object($param)) {
            if (method_exists($param, '__toString')) {
                $param = $param->__toString();
            } else {
                throw new coding_exception('clean_param() can not process objects, please use clean_param_array() instead.');
            }
        }
    }
    switch ($type) {
        case PARAM_RAW:
            // No cleaning at all.
            $param = fix_utf8($param);
            return $param;
        case PARAM_RAW_TRIMMED:
            // No cleaning, but strip leading and trailing whitespace.
            $param = fix_utf8($param);
            return trim($param);
        case PARAM_CLEAN:
            // General HTML cleaning, try to use more specific type if possible this is deprecated!
            // Please use more specific type instead.
            if (is_numeric($param)) {
                return $param;
            }
            $param = fix_utf8($param);
            // Sweep for scripts, etc.
            return clean_text($param);
        case PARAM_CLEANHTML:
            // Clean html fragment.
            $param = fix_utf8($param);
            // Sweep for scripts, etc.
            $param = clean_text($param, FORMAT_HTML);
            return trim($param);
        case PARAM_INT:
            // Convert to integer.
            return (int) $param;
        case PARAM_FLOAT:
            // Convert to float.
            return (double) $param;
        case PARAM_ALPHA:
            // Remove everything not `a-z`.
            return preg_replace('/[^a-zA-Z]/i', '', $param);
        case PARAM_ALPHAEXT:
            // Remove everything not `a-zA-Z_-` (originally allowed "/" too).
            return preg_replace('/[^a-zA-Z_-]/i', '', $param);
        case PARAM_ALPHANUM:
            // Remove everything not `a-zA-Z0-9`.
            return preg_replace('/[^A-Za-z0-9]/i', '', $param);
        case PARAM_ALPHANUMEXT:
            // Remove everything not `a-zA-Z0-9_-`.
            return preg_replace('/[^A-Za-z0-9_-]/i', '', $param);
        case PARAM_SEQUENCE:
            // Remove everything not `0-9,`.
            return preg_replace('/[^0-9,]/i', '', $param);
        case PARAM_BOOL:
            // Convert to 1 or 0.
            $tempstr = strtolower($param);
            if ($tempstr === 'on' or $tempstr === 'yes' or $tempstr === 'true') {
                $param = 1;
            } else {
                if ($tempstr === 'off' or $tempstr === 'no' or $tempstr === 'false') {
                    $param = 0;
                } else {
                    $param = empty($param) ? 0 : 1;
                }
            }
            return $param;
        case PARAM_NOTAGS:
            // Strip all tags.
            $param = fix_utf8($param);
            return strip_tags($param);
        case PARAM_TEXT:
            // Leave only tags needed for multilang.
            $param = fix_utf8($param);
            // If the multilang syntax is not correct we strip all tags because it would break xhtml strict which is required
            // for accessibility standards please note this cleaning does not strip unbalanced '>' for BC compatibility reasons.
            do {
                if (strpos($param, '</lang>') !== false) {
                    // Old and future mutilang syntax.
                    $param = strip_tags($param, '<lang>');
                    if (!preg_match_all('/<.*>/suU', $param, $matches)) {
                        break;
                    }
                    $open = false;
                    foreach ($matches[0] as $match) {
                        if ($match === '</lang>') {
                            if ($open) {
                                $open = false;
                                continue;
                            } else {
                                break 2;
                            }
                        }
                        if (!preg_match('/^<lang lang="[a-zA-Z0-9_-]+"\\s*>$/u', $match)) {
                            break 2;
                        } else {
                            $open = true;
                        }
                    }
                    if ($open) {
                        break;
                    }
                    return $param;
                } else {
                    if (strpos($param, '</span>') !== false) {
                        // Current problematic multilang syntax.
                        $param = strip_tags($param, '<span>');
                        if (!preg_match_all('/<.*>/suU', $param, $matches)) {
                            break;
                        }
                        $open = false;
                        foreach ($matches[0] as $match) {
                            if ($match === '</span>') {
                                if ($open) {
                                    $open = false;
                                    continue;
                                } else {
                                    break 2;
                                }
                            }
                            if (!preg_match('/^<span(\\s+lang="[a-zA-Z0-9_-]+"|\\s+class="multilang"){2}\\s*>$/u', $match)) {
                                break 2;
                            } else {
                                $open = true;
                            }
                        }
                        if ($open) {
                            break;
                        }
                        return $param;
                    }
                }
            } while (false);
            // Easy, just strip all tags, if we ever want to fix orphaned '&' we have to do that in format_string().
            return strip_tags($param);
        case PARAM_COMPONENT:
            // We do not want any guessing here, either the name is correct or not
            // please note only normalised component names are accepted.
            if (!preg_match('/^[a-z]+(_[a-z][a-z0-9_]*)?[a-z0-9]+$/', $param)) {
                return '';
            }
            if (strpos($param, '__') !== false) {
                return '';
            }
            if (strpos($param, 'mod_') === 0) {
                // Module names must not contain underscores because we need to differentiate them from invalid plugin types.
                if (substr_count($param, '_') != 1) {
                    return '';
                }
            }
            return $param;
        case PARAM_PLUGIN:
        case PARAM_AREA:
            // We do not want any guessing here, either the name is correct or not.
            if (!is_valid_plugin_name($param)) {
                return '';
            }
            return $param;
        case PARAM_SAFEDIR:
            // Remove everything not a-zA-Z0-9_- .
            return preg_replace('/[^a-zA-Z0-9_-]/i', '', $param);
        case PARAM_SAFEPATH:
            // Remove everything not a-zA-Z0-9/_- .
            return preg_replace('/[^a-zA-Z0-9\\/_-]/i', '', $param);
        case PARAM_FILE:
            // Strip all suspicious characters from filename.
            $param = fix_utf8($param);
            $param = preg_replace('~[[:cntrl:]]|[&<>"`\\|\':\\\\/]~u', '', $param);
            if ($param === '.' || $param === '..') {
                $param = '';
            }
            return $param;
        case PARAM_PATH:
            // Strip all suspicious characters from file path.
            $param = fix_utf8($param);
            $param = str_replace('\\', '/', $param);
            // Explode the path and clean each element using the PARAM_FILE rules.
            $breadcrumb = explode('/', $param);
            foreach ($breadcrumb as $key => $crumb) {
                if ($crumb === '.' && $key === 0) {
                    // Special condition to allow for relative current path such as ./currentdirfile.txt.
                } else {
                    $crumb = clean_param($crumb, PARAM_FILE);
                }
                $breadcrumb[$key] = $crumb;
            }
            $param = implode('/', $breadcrumb);
            // Remove multiple current path (./././) and multiple slashes (///).
            $param = preg_replace('~//+~', '/', $param);
            $param = preg_replace('~/(\\./)+~', '/', $param);
            return $param;
        case PARAM_HOST:
            // Allow FQDN or IPv4 dotted quad.
            $param = preg_replace('/[^\\.\\d\\w-]/', '', $param);
            // 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 = '';
                }
            } else {
                if (preg_match('/^[\\w\\d\\.-]+$/', $param) && !preg_match('/^[\\.-]/', $param) && !preg_match('/[\\.-]$/', $param)) {
                    // All is ok - $param is respected.
                } else {
                    // All is not ok...
                    $param = '';
                }
            }
            return $param;
        case PARAM_URL:
            // Allow safe ftp, http, mailto urls.
            $param = fix_utf8($param);
            include_once $CFG->dirroot . '/lib/validateurlsyntax.php';
            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 {
                // Not really ok.
                $param = '';
            }
            return $param;
        case PARAM_LOCALURL:
            // Allow http absolute, root relative and relative URLs within wwwroot.
            $param = clean_param($param, PARAM_URL);
            if (!empty($param)) {
                // Simulate the HTTPS version of the site.
                $httpswwwroot = str_replace('http://', 'https://', $CFG->wwwroot);
                if ($param === $CFG->wwwroot) {
                    // Exact match;
                } else {
                    if (!empty($CFG->loginhttps) && $param === $httpswwwroot) {
                        // Exact match;
                    } else {
                        if (preg_match(':^/:', $param)) {
                            // Root-relative, ok!
                        } else {
                            if (preg_match('/^' . preg_quote($CFG->wwwroot . '/', '/') . '/i', $param)) {
                                // Absolute, and matches our wwwroot.
                            } else {
                                if (!empty($CFG->loginhttps) && preg_match('/^' . preg_quote($httpswwwroot . '/', '/') . '/i', $param)) {
                                    // Absolute, and matches our httpswwwroot.
                                } 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 = '';
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return $param;
        case PARAM_PEM:
            $param = trim($param);
            // PEM formatted strings may contain letters/numbers and the symbols:
            //   forward slash: /
            //   plus sign:     +
            //   equal sign:    =
            //   , surrounded by BEGIN and END CERTIFICATE prefix and suffixes.
            if (preg_match('/^-----BEGIN CERTIFICATE-----([\\s\\w\\/\\+=]+)-----END CERTIFICATE-----$/', trim($param), $matches)) {
                list($wholething, $body) = $matches;
                unset($wholething, $matches);
                $b64 = clean_param($body, PARAM_BASE64);
                if (!empty($b64)) {
                    return "-----BEGIN CERTIFICATE-----\n{$b64}\n-----END CERTIFICATE-----\n";
                } else {
                    return '';
                }
            }
            return '';
        case PARAM_BASE64:
            if (!empty($param)) {
                // PEM formatted strings may contain letters/numbers and the symbols
                //   forward slash: /
                //   plus sign:     +
                //   equal sign:    =.
                if (0 >= preg_match('/^([\\s\\w\\/\\+=]+)$/', trim($param))) {
                    return '';
                }
                $lines = preg_split('/[\\s]+/', $param, -1, PREG_SPLIT_NO_EMPTY);
                // Each line of base64 encoded data must be 64 characters in length, except for the last line which may be less
                // than (or equal to) 64 characters long.
                for ($i = 0, $j = count($lines); $i < $j; $i++) {
                    if ($i + 1 == $j) {
                        if (64 < strlen($lines[$i])) {
                            return '';
                        }
                        continue;
                    }
                    if (64 != strlen($lines[$i])) {
                        return '';
                    }
                }
                return implode("\n", $lines);
            } else {
                return '';
            }
        case PARAM_TAG:
            $param = fix_utf8($param);
            // Please note it is not safe to use the tag name directly anywhere,
            // it must be processed with s(), urlencode() before embedding anywhere.
            // Remove some nasties.
            $param = preg_replace('~[[:cntrl:]]|[<>`]~u', '', $param);
            // Convert many whitespace chars into one.
            $param = preg_replace('/\\s+/u', ' ', $param);
            $param = core_text::substr(trim($param), 0, TAG_MAX_LENGTH);
            return $param;
        case PARAM_TAGLIST:
            $param = fix_utf8($param);
            $tags = explode(',', $param);
            $result = array();
            foreach ($tags as $tag) {
                $res = clean_param($tag, PARAM_TAG);
                if ($res !== '') {
                    $result[] = $res;
                }
            }
            if ($result) {
                return implode(',', $result);
            } else {
                return '';
            }
        case PARAM_CAPABILITY:
            if (get_capability_info($param)) {
                return $param;
            } else {
                return '';
            }
        case PARAM_PERMISSION:
            $param = (int) $param;
            if (in_array($param, array(CAP_INHERIT, CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT))) {
                return $param;
            } else {
                return CAP_INHERIT;
            }
        case PARAM_AUTH:
            $param = clean_param($param, PARAM_PLUGIN);
            if (empty($param)) {
                return '';
            } else {
                if (exists_auth_plugin($param)) {
                    return $param;
                } else {
                    return '';
                }
            }
        case PARAM_LANG:
            $param = clean_param($param, PARAM_SAFEDIR);
            if (get_string_manager()->translation_exists($param)) {
                return $param;
            } else {
                // Specified language is not installed or param malformed.
                return '';
            }
        case PARAM_THEME:
            $param = clean_param($param, PARAM_PLUGIN);
            if (empty($param)) {
                return '';
            } else {
                if (file_exists("{$CFG->dirroot}/theme/{$param}/config.php")) {
                    return $param;
                } else {
                    if (!empty($CFG->themedir) and file_exists("{$CFG->themedir}/{$param}/config.php")) {
                        return $param;
                    } else {
                        // Specified theme is not installed.
                        return '';
                    }
                }
            }
        case PARAM_USERNAME:
            $param = fix_utf8($param);
            $param = trim($param);
            // Convert uppercase to lowercase MDL-16919.
            $param = core_text::strtolower($param);
            if (empty($CFG->extendedusernamechars)) {
                $param = str_replace(" ", "", $param);
                // Regular expression, eliminate all chars EXCEPT:
                // alphanum, dash (-), underscore (_), at sign (@) and period (.) characters.
                $param = preg_replace('/[^-\\.@_a-z0-9]/', '', $param);
            }
            return $param;
        case PARAM_EMAIL:
            $param = fix_utf8($param);
            if (validate_email($param)) {
                return $param;
            } else {
                return '';
            }
        case PARAM_STRINGID:
            if (preg_match('|^[a-zA-Z][a-zA-Z0-9\\.:/_-]*$|', $param)) {
                return $param;
            } else {
                return '';
            }
        case PARAM_TIMEZONE:
            // Can be int, float(with .5 or .0) or string seperated by '/' and can have '-_'.
            $param = fix_utf8($param);
            $timezonepattern = '/^(([+-]?(0?[0-9](\\.[5|0])?|1[0-3](\\.0)?|1[0-2]\\.5))|(99)|[[:alnum:]]+(\\/?[[:alpha:]_-])+)$/';
            if (preg_match($timezonepattern, $param)) {
                return $param;
            } else {
                return '';
            }
        default:
            // Doh! throw error, switched parameters in optional_param or another serious problem.
            print_error("unknownparamtype", '', '', $type);
    }
}
Exemple #8
0
    /**
     * Checks whether mod/...:view capability restricts the current user's access.
     *
     * @return bool True if the user access is restricted.
     */
    public function is_user_access_restricted_by_capability() {
        $userid = $this->modinfo->get_user_id();
        if ($userid == -1) {
            return null;
        }
        $capability = 'mod/' . $this->modname . ':view';
        $capabilityinfo = get_capability_info($capability);
        if (!$capabilityinfo) {
            // Capability does not exist, no one is prevented from seeing the activity.
            return false;
        }

        // You are blocked if you don't have the capability.
        return !has_capability($capability, $this->get_context(), $userid);
    }
Exemple #9
0
/**
 * Used by {@link optional_param()} and {@link required_param()} to
 * clean the variables and/or cast to specific types, based on
 * an options field.
 * <code>
 * $course->format = clean_param($course->format, PARAM_ALPHA);
 * $selectedgrade_item = clean_param($selectedgrade_item, PARAM_INT);
 * </code>
 *
 * @param mixed $param the variable we are cleaning
 * @param int $type expected format of param after cleaning.
 * @return mixed
 */
function clean_param($param, $type)
{
    global $CFG;
    if (is_array($param)) {
        // Let's loop
        $newparam = array();
        foreach ($param as $key => $value) {
            $newparam[$key] = clean_param($value, $type);
        }
        return $newparam;
    }
    switch ($type) {
        case PARAM_RAW:
            // no cleaning at all
            return $param;
        case PARAM_RAW_TRIMMED:
            // no cleaning, but strip leading and trailing whitespace.
            return trim($param);
        case PARAM_CLEAN:
            // General HTML cleaning, try to use more specific type if possible
            // this is deprecated!, please use more specific type instead
            if (is_numeric($param)) {
                return $param;
            }
            return clean_text($param);
            // Sweep for scripts, etc
        // Sweep for scripts, etc
        case PARAM_CLEANHTML:
            // clean html fragment
            $param = clean_text($param, FORMAT_HTML);
            // Sweep for scripts, etc
            return trim($param);
        case PARAM_INT:
            return (int) $param;
            // Convert to integer
        // Convert to integer
        case PARAM_FLOAT:
        case PARAM_NUMBER:
            return (double) $param;
            // Convert to float
        // Convert to float
        case PARAM_ALPHA:
            // Remove everything not a-z
            return preg_replace('/[^a-zA-Z]/i', '', $param);
        case PARAM_ALPHAEXT:
            // Remove everything not a-zA-Z_- (originally allowed "/" too)
            return preg_replace('/[^a-zA-Z_-]/i', '', $param);
        case PARAM_ALPHANUM:
            // Remove everything not a-zA-Z0-9
            return preg_replace('/[^A-Za-z0-9]/i', '', $param);
        case PARAM_ALPHANUMEXT:
            // Remove everything not a-zA-Z0-9_-
            return preg_replace('/[^A-Za-z0-9_-]/i', '', $param);
        case PARAM_SEQUENCE:
            // Remove everything not 0-9,
            return preg_replace('/[^0-9,]/i', '', $param);
        case PARAM_BOOL:
            // Convert to 1 or 0
            $tempstr = strtolower($param);
            if ($tempstr === 'on' or $tempstr === 'yes' or $tempstr === 'true') {
                $param = 1;
            } else {
                if ($tempstr === 'off' or $tempstr === 'no' or $tempstr === 'false') {
                    $param = 0;
                } else {
                    $param = empty($param) ? 0 : 1;
                }
            }
            return $param;
        case PARAM_NOTAGS:
            // Strip all tags
            return strip_tags($param);
        case PARAM_TEXT:
            // leave only tags needed for multilang
            // if the multilang syntax is not correct we strip all tags
            // because it would break xhtml strict which is required for accessibility standards
            // please note this cleaning does not strip unbalanced '>' for BC compatibility reasons
            do {
                if (strpos($param, '</lang>') !== false) {
                    // old and future mutilang syntax
                    $param = strip_tags($param, '<lang>');
                    if (!preg_match_all('/<.*>/suU', $param, $matches)) {
                        break;
                    }
                    $open = false;
                    foreach ($matches[0] as $match) {
                        if ($match === '</lang>') {
                            if ($open) {
                                $open = false;
                                continue;
                            } else {
                                break 2;
                            }
                        }
                        if (!preg_match('/^<lang lang="[a-zA-Z0-9_-]+"\\s*>$/u', $match)) {
                            break 2;
                        } else {
                            $open = true;
                        }
                    }
                    if ($open) {
                        break;
                    }
                    return $param;
                } else {
                    if (strpos($param, '</span>') !== false) {
                        // current problematic multilang syntax
                        $param = strip_tags($param, '<span>');
                        if (!preg_match_all('/<.*>/suU', $param, $matches)) {
                            break;
                        }
                        $open = false;
                        foreach ($matches[0] as $match) {
                            if ($match === '</span>') {
                                if ($open) {
                                    $open = false;
                                    continue;
                                } else {
                                    break 2;
                                }
                            }
                            if (!preg_match('/^<span(\\s+lang="[a-zA-Z0-9_-]+"|\\s+class="multilang"){2}\\s*>$/u', $match)) {
                                break 2;
                            } else {
                                $open = true;
                            }
                        }
                        if ($open) {
                            break;
                        }
                        return $param;
                    }
                }
            } while (false);
            // easy, just strip all tags, if we ever want to fix orphaned '&' we have to do that in format_string()
            return strip_tags($param);
        case PARAM_SAFEDIR:
            // Remove everything not a-zA-Z0-9_-
            return preg_replace('/[^a-zA-Z0-9_-]/i', '', $param);
        case PARAM_SAFEPATH:
            // Remove everything not a-zA-Z0-9/_-
            return preg_replace('/[^a-zA-Z0-9\\/_-]/i', '', $param);
        case PARAM_FILE:
            // Strip all suspicious characters from filename
            $param = preg_replace('~[[:cntrl:]]|[&<>"`\\|\':\\\\/]~u', '', $param);
            $param = preg_replace('~\\.\\.+~', '', $param);
            if ($param === '.') {
                $param = '';
            }
            return $param;
        case PARAM_PATH:
            // Strip all suspicious characters from file path
            $param = str_replace('\\', '/', $param);
            $param = preg_replace('~[[:cntrl:]]|[&<>"`\\|\':]~u', '', $param);
            $param = preg_replace('~\\.\\.+~', '', $param);
            $param = preg_replace('~//+~', '/', $param);
            return preg_replace('~/(\\./)+~', '/', $param);
        case PARAM_HOST:
            // allow FQDN or IPv4 dotted quad
            $param = 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) && !preg_match('/^[\\.-]/', $param) && !preg_match('/[\\.-]$/', $param)) {
                // all is ok - $param is respected
            } else {
                // all is not ok...
                $param = '';
            }
            return $param;
        case PARAM_URL:
            // allow safe ftp, http, mailto urls
            include_once $CFG->dirroot . '/lib/validateurlsyntax.php';
            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
            }
            return $param;
        case PARAM_LOCALURL:
            // allow http absolute, root relative and relative URLs within wwwroot
            $param = clean_param($param, PARAM_URL);
            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 = '';
                    }
                }
            }
            return $param;
        case PARAM_PEM:
            $param = trim($param);
            // PEM formatted strings may contain letters/numbers and the symbols
            // forward slash: /
            // plus sign:     +
            // equal sign:    =
            // , surrounded by BEGIN and END CERTIFICATE prefix and suffixes
            if (preg_match('/^-----BEGIN CERTIFICATE-----([\\s\\w\\/\\+=]+)-----END CERTIFICATE-----$/', trim($param), $matches)) {
                list($wholething, $body) = $matches;
                unset($wholething, $matches);
                $b64 = clean_param($body, PARAM_BASE64);
                if (!empty($b64)) {
                    return "-----BEGIN CERTIFICATE-----\n{$b64}\n-----END CERTIFICATE-----\n";
                } else {
                    return '';
                }
            }
            return '';
        case PARAM_BASE64:
            if (!empty($param)) {
                // PEM formatted strings may contain letters/numbers and the symbols
                // forward slash: /
                // plus sign:     +
                // equal sign:    =
                if (0 >= preg_match('/^([\\s\\w\\/\\+=]+)$/', trim($param))) {
                    return '';
                }
                $lines = preg_split('/[\\s]+/', $param, -1, PREG_SPLIT_NO_EMPTY);
                // Each line of base64 encoded data must be 64 characters in
                // length, except for the last line which may be less than (or
                // equal to) 64 characters long.
                for ($i = 0, $j = count($lines); $i < $j; $i++) {
                    if ($i + 1 == $j) {
                        if (64 < strlen($lines[$i])) {
                            return '';
                        }
                        continue;
                    }
                    if (64 != strlen($lines[$i])) {
                        return '';
                    }
                }
                return implode("\n", $lines);
            } else {
                return '';
            }
        case PARAM_TAG:
            // Please note it is not safe to use the tag name directly anywhere,
            // it must be processed with s(), urlencode() before embedding anywhere.
            // remove some nasties
            $param = preg_replace('~[[:cntrl:]]|[<>`]~u', '', $param);
            //convert many whitespace chars into one
            $param = preg_replace('/\\s+/', ' ', $param);
            $textlib = textlib_get_instance();
            $param = $textlib->substr(trim($param), 0, TAG_MAX_LENGTH);
            return $param;
        case PARAM_TAGLIST:
            $tags = explode(',', $param);
            $result = array();
            foreach ($tags as $tag) {
                $res = clean_param($tag, PARAM_TAG);
                if ($res !== '') {
                    $result[] = $res;
                }
            }
            if ($result) {
                return implode(',', $result);
            } else {
                return '';
            }
        case PARAM_CAPABILITY:
            if (get_capability_info($param)) {
                return $param;
            } else {
                return '';
            }
        case PARAM_PERMISSION:
            $param = (int) $param;
            if (in_array($param, array(CAP_INHERIT, CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT))) {
                return $param;
            } else {
                return CAP_INHERIT;
            }
        case PARAM_AUTH:
            $param = clean_param($param, PARAM_SAFEDIR);
            if (exists_auth_plugin($param)) {
                return $param;
            } else {
                return '';
            }
        case PARAM_LANG:
            $param = clean_param($param, PARAM_SAFEDIR);
            if (get_string_manager()->translation_exists($param)) {
                return $param;
            } else {
                return '';
                // Specified language is not installed or param malformed
            }
        case PARAM_THEME:
            $param = clean_param($param, PARAM_SAFEDIR);
            if (file_exists("{$CFG->dirroot}/theme/{$param}/config.php")) {
                return $param;
            } else {
                if (!empty($CFG->themedir) and file_exists("{$CFG->themedir}/{$param}/config.php")) {
                    return $param;
                } else {
                    return '';
                    // Specified theme is not installed
                }
            }
        case PARAM_USERNAME:
            $param = str_replace(" ", "", $param);
            $param = moodle_strtolower($param);
            // Convert uppercase to lowercase MDL-16919
            if (empty($CFG->extendedusernamechars)) {
                // regular expression, eliminate all chars EXCEPT:
                // alphanum, dash (-), underscore (_), at sign (@) and period (.) characters.
                $param = preg_replace('/[^-\\.@_a-z0-9]/', '', $param);
            }
            return $param;
        case PARAM_EMAIL:
            if (validate_email($param)) {
                return $param;
            } else {
                return '';
            }
        case PARAM_STRINGID:
            if (preg_match('|^[a-zA-Z][a-zA-Z0-9\\.:/_-]*$|', $param)) {
                return $param;
            } else {
                return '';
            }
        default:
            // throw error, switched parameters in optional_param or another serious problem
            print_error("unknownparamtype", '', '', $type);
    }
}
 /**
  * Returns true if the user can add a block to a page.
  *
  * @param moodle_page $page
  * @param string $capability the capability to check
  * @return boolean true if user can add a block, false otherwise.
  */
 private function has_add_block_capability($page, $capability)
 {
     // Check if the capability exists.
     if (!get_capability_info($capability)) {
         // Debug warning that the capability does not exist, but no more than once per page.
         static $warned = array();
         if (!isset($warned[$this->name()])) {
             debugging('The block ' . $this->name() . ' does not define the standard capability ' . $capability, DEBUG_DEVELOPER);
             $warned[$this->name()] = 1;
         }
         // If the capability does not exist, the block can always be added.
         return true;
     } else {
         return has_capability($capability, $page->context);
     }
 }
/**
 * Check whether a user has a particular capability in a given context.
 *
 * For example::
 *      $context = get_context_instance(CONTEXT_MODULE, $cm->id);
 *      has_capability('mod/forum:replypost',$context)
 *
 * By default checks the capabilities of the current user, but you can pass a
 * different userid. By default will return true for admin users, but you can override that with the fourth argument.
 *
 * Guest and not-logged-in users can never get any dangerous capability - that is any write capability
 * or capabilities with XSS, config or data loss risks.
 *
 * @param string $capability the name of the capability to check. For example mod/forum:view
 * @param object $context the context to check the capability in. You normally get this with {@link get_context_instance}.
 * @param integer|object $user A user id or object. By default (null) checks the permissions of the current user.
 * @param boolean $doanything If false, ignores effect of admin role assignment
 * @return boolean true if the user has this capability. Otherwise false.
 */
function has_capability($capability, $context, $user = null, $doanything = true)
{
    global $USER, $CFG, $DB, $SCRIPT, $ACCESSLIB_PRIVATE;
    if (during_initial_install()) {
        if ($SCRIPT === "/{$CFG->admin}/index.php" or $SCRIPT === "/{$CFG->admin}/cliupgrade.php") {
            // we are in an installer - roles can not work yet
            return true;
        } else {
            return false;
        }
    }
    if (strpos($capability, 'moodle/legacy:') === 0) {
        throw new coding_exception('Legacy capabilities can not be used any more!');
    }
    // the original $CONTEXT here was hiding serious errors
    // for security reasons do not reuse previous context
    if (empty($context)) {
        debugging('Incorrect context specified');
        return false;
    }
    if (!is_bool($doanything)) {
        throw new coding_exception('Capability parameter "doanything" is wierd ("' . $doanything . '"). This has to be fixed in code.');
    }
    // make sure there is a real user specified
    if ($user === null) {
        $userid = isset($USER->id) ? $USER->id : 0;
    } else {
        $userid = is_object($user) ? $user->id : $user;
    }
    // capability must exist
    if (!($capinfo = get_capability_info($capability))) {
        debugging('Capability "' . $capability . '" was not found! This should be fixed in code.');
        return false;
    }
    // make sure the guest account and not-logged-in users never get any risky caps no matter what the actual settings are.
    if ($capinfo->captype === 'write' or (int) $capinfo->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS)) {
        if (isguestuser($userid) or $userid == 0) {
            return false;
        }
    }
    if (is_null($context->path) or $context->depth == 0) {
        //this should not happen
        $contexts = array(SYSCONTEXTID, $context->id);
        $context->path = '/' . SYSCONTEXTID . '/' . $context->id;
        debugging('Context id ' . $context->id . ' does not have valid path, please use build_context_path()', DEBUG_DEVELOPER);
    } else {
        $contexts = explode('/', $context->path);
        array_shift($contexts);
    }
    if (CLI_SCRIPT && !isset($USER->access)) {
        // In cron, some modules setup a 'fake' $USER,
        // ensure we load the appropriate accessdata.
        if (isset($ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) {
            $ACCESSLIB_PRIVATE->dirtycontexts = null;
            //load fresh dirty contexts
        } else {
            load_user_accessdata($userid);
            $ACCESSLIB_PRIVATE->dirtycontexts = array();
        }
        $USER->access = $ACCESSLIB_PRIVATE->accessdatabyuser[$userid];
    } else {
        if (isset($USER->id) && $USER->id == $userid && !isset($USER->access)) {
            // caps not loaded yet - better to load them to keep BC with 1.8
            // not-logged-in user or $USER object set up manually first time here
            load_all_capabilities();
            $ACCESSLIB_PRIVATE->accessdatabyuser = array();
            // reset the cache for other users too, the dirty contexts are empty now
            $ACCESSLIB_PRIVATE->roledefinitions = array();
        }
    }
    // Load dirty contexts list if needed
    if (!isset($ACCESSLIB_PRIVATE->dirtycontexts)) {
        if (isset($USER->access['time'])) {
            $ACCESSLIB_PRIVATE->dirtycontexts = get_dirty_contexts($USER->access['time']);
        } else {
            $ACCESSLIB_PRIVATE->dirtycontexts = array();
        }
    }
    // Careful check for staleness...
    if (count($ACCESSLIB_PRIVATE->dirtycontexts) !== 0 and is_contextpath_dirty($contexts, $ACCESSLIB_PRIVATE->dirtycontexts)) {
        // reload all capabilities - preserving loginas, roleswitches, etc
        // and then cleanup any marks of dirtyness... at least from our short
        // term memory! :-)
        $ACCESSLIB_PRIVATE->accessdatabyuser = array();
        $ACCESSLIB_PRIVATE->roledefinitions = array();
        if (CLI_SCRIPT) {
            load_user_accessdata($userid);
            $USER->access = $ACCESSLIB_PRIVATE->accessdatabyuser[$userid];
            $ACCESSLIB_PRIVATE->dirtycontexts = array();
        } else {
            reload_all_capabilities();
        }
    }
    // Find out if user is admin - it is not possible to override the doanything in any way
    // and it is not possible to switch to admin role either.
    if ($doanything) {
        if (is_siteadmin($userid)) {
            if ($userid != $USER->id) {
                return true;
            }
            // make sure switchrole is not used in this context
            if (empty($USER->access['rsw'])) {
                return true;
            }
            $parts = explode('/', trim($context->path, '/'));
            $path = '';
            $switched = false;
            foreach ($parts as $part) {
                $path .= '/' . $part;
                if (!empty($USER->access['rsw'][$path])) {
                    $switched = true;
                    break;
                }
            }
            if (!$switched) {
                return true;
            }
            //ok, admin switched role in this context, let's use normal access control rules
        }
    }
    // divulge how many times we are called
    //// error_log("has_capability: id:{$context->id} path:{$context->path} userid:$userid cap:$capability");
    if (isset($USER->id) && $USER->id == $userid) {
        // we must accept strings and integers in $userid
        //
        // For the logged in user, we have $USER->access
        // which will have all RAs and caps preloaded for
        // course and above contexts.
        //
        // Contexts below courses && contexts that do not
        // hang from courses are loaded into $USER->access
        // on demand, and listed in $USER->access[loaded]
        //
        if ($context->contextlevel <= CONTEXT_COURSE) {
            // Course and above are always preloaded
            return has_capability_in_accessdata($capability, $context, $USER->access);
        }
        // Load accessdata for below-the-course contexts
        if (!path_inaccessdata($context->path, $USER->access)) {
            // error_log("loading access for context {$context->path} for $capability at {$context->contextlevel} {$context->id}");
            // $bt = debug_backtrace();
            // error_log("bt {$bt[0]['file']} {$bt[0]['line']}");
            load_subcontext($USER->id, $context, $USER->access);
        }
        return has_capability_in_accessdata($capability, $context, $USER->access);
    }
    if (!isset($ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) {
        load_user_accessdata($userid);
    }
    if ($context->contextlevel <= CONTEXT_COURSE) {
        // Course and above are always preloaded
        return has_capability_in_accessdata($capability, $context, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]);
    }
    // Load accessdata for below-the-course contexts as needed
    if (!path_inaccessdata($context->path, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid])) {
        // error_log("loading access for context {$context->path} for $capability at {$context->contextlevel} {$context->id}");
        // $bt = debug_backtrace();
        // error_log("bt {$bt[0]['file']} {$bt[0]['line']}");
        load_subcontext($userid, $context, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]);
    }
    return has_capability_in_accessdata($capability, $context, $ACCESSLIB_PRIVATE->accessdatabyuser[$userid]);
}
Exemple #12
0
 /**
  * Builds and returns the rows that will make up the right part of the grader report
  * @param boolean $displayaverages whether to display average rows in the table
  * @return array Array of html_table_row objects
  */
 public function get_right_rows($displayaverages)
 {
     global $CFG, $USER, $OUTPUT, $DB, $PAGE;
     $rows = array();
     $this->rowcount = 0;
     $numrows = count($this->gtree->get_levels());
     $numusers = count($this->users);
     $gradetabindex = 1;
     $columnstounset = array();
     $strgrade = $this->get_lang_string('grade');
     $strfeedback = $this->get_lang_string("feedback");
     $arrows = $this->get_sort_arrows();
     $jsarguments = array('cfg' => array('ajaxenabled' => false), 'items' => array(), 'users' => array(), 'feedback' => array(), 'grades' => array());
     $jsscales = array();
     // Get preferences once.
     $showactivityicons = $this->get_pref('showactivityicons');
     $quickgrading = $this->get_pref('quickgrading');
     $showquickfeedback = $this->get_pref('showquickfeedback');
     $enableajax = $this->get_pref('enableajax');
     $showanalysisicon = $this->get_pref('showanalysisicon');
     // Get strings which are re-used inside the loop.
     $strftimedatetimeshort = get_string('strftimedatetimeshort');
     $strexcludedgrades = get_string('excluded', 'grades');
     $strerror = get_string('error');
     foreach ($this->gtree->get_levels() as $key => $row) {
         $headingrow = new html_table_row();
         $headingrow->attributes['class'] = 'heading_name_row';
         foreach ($row as $columnkey => $element) {
             $sortlink = clone $this->baseurl;
             if (isset($element['object']->id)) {
                 $sortlink->param('sortitemid', $element['object']->id);
             }
             $eid = $element['eid'];
             $object = $element['object'];
             $type = $element['type'];
             $categorystate = @$element['categorystate'];
             if (!empty($element['colspan'])) {
                 $colspan = $element['colspan'];
             } else {
                 $colspan = 1;
             }
             if (!empty($element['depth'])) {
                 $catlevel = 'catlevel' . $element['depth'];
             } else {
                 $catlevel = '';
             }
             // Element is a filler
             if ($type == 'filler' or $type == 'fillerfirst' or $type == 'fillerlast') {
                 $fillercell = new html_table_cell();
                 $fillercell->attributes['class'] = $type . ' ' . $catlevel;
                 $fillercell->colspan = $colspan;
                 $fillercell->text = '&nbsp;';
                 // This is a filler cell; don't use a <th>, it'll confuse screen readers.
                 $fillercell->header = false;
                 $headingrow->cells[] = $fillercell;
             } else {
                 if ($type == 'category') {
                     // Make sure the grade category has a grade total or at least has child grade items.
                     if (grade_tree::can_output_item($element)) {
                         // Element is a category.
                         $categorycell = new html_table_cell();
                         $categorycell->attributes['class'] = 'category ' . $catlevel;
                         $categorycell->colspan = $colspan;
                         $categorycell->text = $this->get_course_header($element);
                         $categorycell->header = true;
                         $categorycell->scope = 'col';
                         // Print icons.
                         if ($USER->gradeediting[$this->courseid]) {
                             $categorycell->text .= $this->get_icons($element);
                         }
                         $headingrow->cells[] = $categorycell;
                     }
                 } else {
                     // Element is a grade_item
                     if ($element['object']->id == $this->sortitemid) {
                         if ($this->sortorder == 'ASC') {
                             $arrow = $this->get_sort_arrow('up', $sortlink);
                         } else {
                             $arrow = $this->get_sort_arrow('down', $sortlink);
                         }
                     } else {
                         $arrow = $this->get_sort_arrow('move', $sortlink);
                     }
                     $headerlink = $this->gtree->get_element_header($element, true, $showactivityicons, false, false, true);
                     $itemcell = new html_table_cell();
                     $itemcell->attributes['class'] = $type . ' ' . $catlevel . ' highlightable' . ' i' . $element['object']->id;
                     $itemcell->attributes['data-itemid'] = $element['object']->id;
                     if ($element['object']->is_hidden()) {
                         $itemcell->attributes['class'] .= ' dimmed_text';
                     }
                     $singleview = '';
                     // FIXME: MDL-52678 This is extremely hacky we should have an API for inserting grade column links.
                     if (get_capability_info('gradereport/singleview:view')) {
                         if (has_all_capabilities(array('gradereport/singleview:view', 'moodle/grade:viewall', 'moodle/grade:edit'), $this->context)) {
                             $url = new moodle_url('/grade/report/singleview/index.php', array('id' => $this->course->id, 'item' => 'grade', 'itemid' => $element['object']->id));
                             $singleview = $OUTPUT->action_icon($url, new pix_icon('t/editstring', get_string('singleview', 'grades', $element['object']->get_name())));
                         }
                     }
                     $itemcell->colspan = $colspan;
                     $itemcell->text = shorten_text($headerlink) . $arrow . $singleview;
                     $itemcell->header = true;
                     $itemcell->scope = 'col';
                     $headingrow->cells[] = $itemcell;
                 }
             }
         }
         $rows[] = $headingrow;
     }
     $rows = $this->get_right_icons_row($rows);
     // Preload scale objects for items with a scaleid and initialize tab indices
     $scaleslist = array();
     $tabindices = array();
     foreach ($this->gtree->get_items() as $itemid => $item) {
         $scale = null;
         if (!empty($item->scaleid)) {
             $scaleslist[] = $item->scaleid;
             $jsarguments['items'][$itemid] = array('id' => $itemid, 'name' => $item->get_name(true), 'type' => 'scale', 'scale' => $item->scaleid, 'decimals' => $item->get_decimals());
         } else {
             $jsarguments['items'][$itemid] = array('id' => $itemid, 'name' => $item->get_name(true), 'type' => 'value', 'scale' => false, 'decimals' => $item->get_decimals());
         }
         $tabindices[$item->id]['grade'] = $gradetabindex;
         $tabindices[$item->id]['feedback'] = $gradetabindex + $numusers;
         $gradetabindex += $numusers * 2;
     }
     $scalesarray = array();
     if (!empty($scaleslist)) {
         $scalesarray = $DB->get_records_list('scale', 'id', $scaleslist);
     }
     $jsscales = $scalesarray;
     // Get all the grade items if the user can not view hidden grade items.
     // It is possible that the user is simply viewing the 'Course total' by switching to the 'Aggregates only' view
     // and that this user does not have the ability to view hidden items. In this case we still need to pass all the
     // grade items (in case one has been hidden) as the course total shown needs to be adjusted for this particular
     // user.
     if (!$this->canviewhidden) {
         $allgradeitems = grade_item::fetch_all(array('courseid' => $this->courseid));
     }
     foreach ($this->users as $userid => $user) {
         if ($this->canviewhidden) {
             $altered = array();
             $unknown = array();
         } else {
             $usergrades = $this->allgrades[$userid];
             $hidingaffected = grade_grade::get_hiding_affected($usergrades, $allgradeitems);
             $altered = $hidingaffected['altered'];
             $unknown = $hidingaffected['unknown'];
             unset($hidingaffected);
         }
         $itemrow = new html_table_row();
         $itemrow->id = 'user_' . $userid;
         $fullname = fullname($user);
         $jsarguments['users'][$userid] = $fullname;
         foreach ($this->gtree->items as $itemid => $unused) {
             $item =& $this->gtree->items[$itemid];
             $grade = $this->grades[$userid][$item->id];
             $itemcell = new html_table_cell();
             $itemcell->id = 'u' . $userid . 'i' . $itemid;
             $itemcell->attributes['data-itemid'] = $itemid;
             // Get the decimal points preference for this item
             $decimalpoints = $item->get_decimals();
             if (in_array($itemid, $unknown)) {
                 $gradeval = null;
             } else {
                 if (array_key_exists($itemid, $altered)) {
                     $gradeval = $altered[$itemid];
                 } else {
                     $gradeval = $grade->finalgrade;
                 }
             }
             if (!empty($grade->finalgrade)) {
                 $gradevalforjs = null;
                 if ($item->scaleid && !empty($scalesarray[$item->scaleid])) {
                     $gradevalforjs = (int) $gradeval;
                 } else {
                     $gradevalforjs = format_float($gradeval, $decimalpoints);
                 }
                 $jsarguments['grades'][] = array('user' => $userid, 'item' => $itemid, 'grade' => $gradevalforjs);
             }
             // MDL-11274
             // Hide grades in the grader report if the current grader doesn't have 'moodle/grade:viewhidden'
             if (!$this->canviewhidden and $grade->is_hidden()) {
                 if (!empty($CFG->grade_hiddenasdate) and $grade->get_datesubmitted() and !$item->is_category_item() and !$item->is_course_item()) {
                     // the problem here is that we do not have the time when grade value was modified, 'timemodified' is general modification date for grade_grades records
                     $itemcell->text = "<span class='datesubmitted'>" . userdate($grade->get_datesubmitted(), $strftimedatetimeshort) . "</span>";
                 } else {
                     $itemcell->text = '-';
                 }
                 $itemrow->cells[] = $itemcell;
                 continue;
             }
             // emulate grade element
             $eid = $this->gtree->get_grade_eid($grade);
             $element = array('eid' => $eid, 'object' => $grade, 'type' => 'grade');
             $itemcell->attributes['class'] .= ' grade i' . $itemid;
             if ($item->is_category_item()) {
                 $itemcell->attributes['class'] .= ' cat';
             }
             if ($item->is_course_item()) {
                 $itemcell->attributes['class'] .= ' course';
             }
             if ($grade->is_overridden()) {
                 $itemcell->attributes['class'] .= ' overridden';
                 $itemcell->attributes['aria-label'] = get_string('overriddengrade', 'gradereport_grader');
             }
             if (!empty($grade->feedback)) {
                 $feedback = wordwrap(trim(format_string($grade->feedback, $grade->feedbackformat)), 34, '<br>');
                 $itemcell->attributes['data-feedback'] = $feedback;
                 $jsarguments['feedback'][] = array('user' => $userid, 'item' => $itemid, 'content' => $feedback);
             }
             if ($grade->is_excluded()) {
                 // Adding white spaces before and after to prevent a screenreader from
                 // thinking that the words are attached to the next/previous <span> or text.
                 $itemcell->text .= " <span class='excludedfloater'>" . $strexcludedgrades . "</span> ";
             }
             // Do not show any icons if no grade (no record in DB to match)
             if (!$item->needsupdate and $USER->gradeediting[$this->courseid]) {
                 $itemcell->text .= $this->get_icons($element);
             }
             $hidden = '';
             if ($grade->is_hidden()) {
                 $hidden = ' dimmed_text ';
             }
             $gradepass = '******';
             if ($grade->is_passed($item)) {
                 $gradepass = '******';
             } else {
                 if (is_null($grade->is_passed($item))) {
                     $gradepass = '';
                 }
             }
             // if in editing mode, we need to print either a text box
             // or a drop down (for scales)
             // grades in item of type grade category or course are not directly editable
             if ($item->needsupdate) {
                 $itemcell->text .= "<span class='gradingerror{$hidden}'>" . $strerror . "</span>";
             } else {
                 if ($USER->gradeediting[$this->courseid]) {
                     if ($item->scaleid && !empty($scalesarray[$item->scaleid])) {
                         $itemcell->attributes['class'] .= ' grade_type_scale';
                     } else {
                         if ($item->gradetype == GRADE_TYPE_VALUE) {
                             $itemcell->attributes['class'] .= ' grade_type_value';
                         } else {
                             if ($item->gradetype == GRADE_TYPE_TEXT) {
                                 $itemcell->attributes['class'] .= ' grade_type_text';
                             }
                         }
                     }
                     if ($item->scaleid && !empty($scalesarray[$item->scaleid])) {
                         $scale = $scalesarray[$item->scaleid];
                         $gradeval = (int) $gradeval;
                         // scales use only integers
                         $scales = explode(",", $scale->scale);
                         // reindex because scale is off 1
                         // MDL-12104 some previous scales might have taken up part of the array
                         // so this needs to be reset
                         $scaleopt = array();
                         $i = 0;
                         foreach ($scales as $scaleoption) {
                             $i++;
                             $scaleopt[$i] = $scaleoption;
                         }
                         if ($quickgrading and $grade->is_editable()) {
                             $oldval = empty($gradeval) ? -1 : $gradeval;
                             if (empty($item->outcomeid)) {
                                 $nogradestr = $this->get_lang_string('nograde');
                             } else {
                                 $nogradestr = $this->get_lang_string('nooutcome', 'grades');
                             }
                             $attributes = array('tabindex' => $tabindices[$item->id]['grade'], 'id' => 'grade_' . $userid . '_' . $item->id);
                             $gradelabel = $fullname . ' ' . $item->itemname;
                             $itemcell->text .= html_writer::label(get_string('useractivitygrade', 'gradereport_grader', $gradelabel), $attributes['id'], false, array('class' => 'accesshide'));
                             $itemcell->text .= html_writer::select($scaleopt, 'grade[' . $userid . '][' . $item->id . ']', $gradeval, array(-1 => $nogradestr), $attributes);
                         } else {
                             if (!empty($scale)) {
                                 $scales = explode(",", $scale->scale);
                                 // invalid grade if gradeval < 1
                                 if ($gradeval < 1) {
                                     $itemcell->text .= "<span class='gradevalue{$hidden}{$gradepass}'>-</span>";
                                 } else {
                                     $gradeval = $grade->grade_item->bounded_grade($gradeval);
                                     //just in case somebody changes scale
                                     $itemcell->text .= "<span class='gradevalue{$hidden}{$gradepass}'>{$scales[$gradeval - 1]}</span>";
                                 }
                             }
                         }
                     } else {
                         if ($item->gradetype != GRADE_TYPE_TEXT) {
                             // Value type
                             if ($quickgrading and $grade->is_editable()) {
                                 $value = format_float($gradeval, $decimalpoints);
                                 $gradelabel = $fullname . ' ' . $item->itemname;
                                 $itemcell->text .= '<label class="accesshide" for="grade_' . $userid . '_' . $item->id . '">' . get_string('useractivitygrade', 'gradereport_grader', $gradelabel) . '</label>';
                                 $itemcell->text .= '<input size="6" tabindex="' . $tabindices[$item->id]['grade'] . '" type="text" class="text" title="' . $strgrade . '" name="grade[' . $userid . '][' . $item->id . ']" id="grade_' . $userid . '_' . $item->id . '" value="' . $value . '" />';
                             } else {
                                 $itemcell->text .= "<span class='gradevalue{$hidden}{$gradepass}'>" . format_float($gradeval, $decimalpoints) . "</span>";
                             }
                         }
                     }
                     // If quickfeedback is on, print an input element
                     if ($showquickfeedback and $grade->is_editable()) {
                         $feedbacklabel = $fullname . ' ' . $item->itemname;
                         $itemcell->text .= '<label class="accesshide" for="feedback_' . $userid . '_' . $item->id . '">' . get_string('useractivityfeedback', 'gradereport_grader', $feedbacklabel) . '</label>';
                         $itemcell->text .= '<input class="quickfeedback" tabindex="' . $tabindices[$item->id]['feedback'] . '" id="feedback_' . $userid . '_' . $item->id . '" size="6" title="' . $strfeedback . '" type="text" name="feedback[' . $userid . '][' . $item->id . ']" value="' . s($grade->feedback) . '" />';
                     }
                 } else {
                     // Not editing
                     $gradedisplaytype = $item->get_displaytype();
                     if ($item->scaleid && !empty($scalesarray[$item->scaleid])) {
                         $itemcell->attributes['class'] .= ' grade_type_scale';
                     } else {
                         if ($item->gradetype == GRADE_TYPE_VALUE) {
                             $itemcell->attributes['class'] .= ' grade_type_value';
                         } else {
                             if ($item->gradetype == GRADE_TYPE_TEXT) {
                                 $itemcell->attributes['class'] .= ' grade_type_text';
                             }
                         }
                     }
                     // Only allow edting if the grade is editable (not locked, not in a unoverridable category, etc).
                     if ($enableajax && $grade->is_editable()) {
                         // If a grade item is type text, and we don't have show quick feedback on, it can't be edited.
                         if ($item->gradetype != GRADE_TYPE_TEXT || $showquickfeedback) {
                             $itemcell->attributes['class'] .= ' clickable';
                         }
                     }
                     if ($item->needsupdate) {
                         $itemcell->text .= "<span class='gradingerror{$hidden}{$gradepass}'>" . $error . "</span>";
                     } else {
                         // The max and min for an aggregation may be different to the grade_item.
                         if (!is_null($gradeval)) {
                             $item->grademax = $grade->get_grade_max();
                             $item->grademin = $grade->get_grade_min();
                         }
                         $itemcell->text .= "<span class='gradevalue{$hidden}{$gradepass}'>" . grade_format_gradevalue($gradeval, $item, true, $gradedisplaytype, null) . "</span>";
                         if ($showanalysisicon) {
                             $itemcell->text .= $this->gtree->get_grade_analysis_icon($grade);
                         }
                     }
                 }
             }
             // Enable keyboard navigation if the grade is editable (not locked, not in a unoverridable category, etc).
             if ($enableajax && $grade->is_editable()) {
                 // If a grade item is type text, and we don't have show quick feedback on, it can't be edited.
                 if ($item->gradetype != GRADE_TYPE_TEXT || $showquickfeedback) {
                     $itemcell->attributes['class'] .= ' gbnavigable';
                 }
             }
             if (!empty($this->gradeserror[$item->id][$userid])) {
                 $itemcell->text .= $this->gradeserror[$item->id][$userid];
             }
             $itemrow->cells[] = $itemcell;
         }
         $rows[] = $itemrow;
     }
     if ($enableajax) {
         $jsarguments['cfg']['ajaxenabled'] = true;
         $jsarguments['cfg']['scales'] = array();
         foreach ($jsscales as $scale) {
             // Trim the scale values, as they may have a space that is ommitted from values later.
             $jsarguments['cfg']['scales'][$scale->id] = array_map('trim', explode(',', $scale->scale));
         }
         $jsarguments['cfg']['feedbacktrunclength'] = $this->feedback_trunc_length;
         // Student grades and feedback are already at $jsarguments['feedback'] and $jsarguments['grades']
     }
     $jsarguments['cfg']['isediting'] = (bool) $USER->gradeediting[$this->courseid];
     $jsarguments['cfg']['courseid'] = $this->courseid;
     $jsarguments['cfg']['studentsperpage'] = $this->get_students_per_page();
     $jsarguments['cfg']['showquickfeedback'] = (bool) $showquickfeedback;
     $module = array('name' => 'gradereport_grader', 'fullpath' => '/grade/report/grader/module.js', 'requires' => array('base', 'dom', 'event', 'event-mouseenter', 'event-key', 'io-queue', 'json-parse', 'overlay'));
     $PAGE->requires->js_init_call('M.gradereport_grader.init_report', $jsarguments, false, $module);
     $PAGE->requires->strings_for_js(array('addfeedback', 'feedback', 'grade'), 'grades');
     $PAGE->requires->strings_for_js(array('ajaxchoosescale', 'ajaxclicktoclose', 'ajaxerror', 'ajaxfailedupdate', 'ajaxfieldchanged'), 'gradereport_grader');
     if (!$enableajax && $USER->gradeediting[$this->courseid]) {
         $PAGE->requires->yui_module('moodle-core-formchangechecker', 'M.core_formchangechecker.init', array(array('formid' => 'gradereport_grader')));
         $PAGE->requires->string_for_js('changesmadereallygoaway', 'moodle');
     }
     $rows = $this->get_right_range_row($rows);
     if ($displayaverages) {
         $rows = $this->get_right_avg_row($rows, true);
         $rows = $this->get_right_avg_row($rows);
     }
     return $rows;
 }
Exemple #13
0
/**
 * This file keeps track of upgrades to
 * the forum module
 *
 * Sometimes, changes between versions involve
 * alterations to database structures and other
 * major things that may break installations.
 *
 * The upgrade function in this file will attempt
 * to perform all the necessary actions to upgrade
 * your older installation to the current version.
 *
 * If there's something it cannot do itself, it
 * will tell you what you need to do.
 *
 * The commands in here will all be database-neutral,
 * using the methods of database_manager class
 *
 * Please do not forget to use upgrade_set_timeout()
 * before any action that may take longer time to finish.
 *
 * @package   mod_forum
 * @copyright 2003 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
function xmldb_forum_upgrade($oldversion)
{
    global $CFG, $DB, $OUTPUT;
    $dbman = $DB->get_manager();
    // Loads ddl manager and xmldb classes.
    // Moodle v2.2.0 release upgrade line.
    // Put any upgrade step following this.
    // Moodle v2.3.0 release upgrade line.
    // Put any upgrade step following this.
    // Moodle v2.4.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2013020500) {
        // Define field displaywordcount to be added to forum.
        $table = new xmldb_table('forum');
        $field = new xmldb_field('displaywordcount', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'completionposts');
        // Conditionally launch add field displaywordcount.
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2013020500, 'forum');
    }
    // Forcefully assign mod/forum:allowforcesubscribe to frontpage role, as we missed that when
    // capability was introduced.
    if ($oldversion < 2013021200) {
        // If capability mod/forum:allowforcesubscribe is defined then set it for frontpage role.
        if (get_capability_info('mod/forum:allowforcesubscribe')) {
            assign_legacy_capabilities('mod/forum:allowforcesubscribe', array('frontpage' => CAP_ALLOW));
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2013021200, 'forum');
    }
    // Moodle v2.5.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2013071000) {
        // Define table forum_digests to be created.
        $table = new xmldb_table('forum_digests');
        // Adding fields to table forum_digests.
        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
        $table->add_field('forum', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
        $table->add_field('maildigest', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '-1');
        // Adding keys to table forum_digests.
        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
        $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
        $table->add_key('forum', XMLDB_KEY_FOREIGN, array('forum'), 'forum', array('id'));
        $table->add_key('forumdigest', XMLDB_KEY_UNIQUE, array('forum', 'userid', 'maildigest'));
        // Conditionally launch create table for forum_digests.
        if (!$dbman->table_exists($table)) {
            $dbman->create_table($table);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2013071000, 'forum');
    }
    // Moodle v2.6.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2014040400) {
        // Define index userid-postid (not unique) to be dropped form forum_read.
        $table = new xmldb_table('forum_read');
        $index = new xmldb_index('userid-postid', XMLDB_INDEX_NOTUNIQUE, array('userid', 'postid'));
        // Conditionally launch drop index userid-postid.
        if ($dbman->index_exists($table, $index)) {
            $dbman->drop_index($table, $index);
        }
        // Define index postid-userid (not unique) to be added to forum_read.
        $index = new xmldb_index('postid-userid', XMLDB_INDEX_NOTUNIQUE, array('postid', 'userid'));
        // Conditionally launch add index postid-userid.
        if (!$dbman->index_exists($table, $index)) {
            $dbman->add_index($table, $index);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2014040400, 'forum');
    }
    // Moodle v2.7.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2014051201) {
        // Incorrect values that need to be replaced.
        $replacements = array(11 => 20, 12 => 50, 13 => 100);
        // Run the replacements.
        foreach ($replacements as $old => $new) {
            $DB->set_field('forum', 'maxattachments', $new, array('maxattachments' => $old));
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2014051201, 'forum');
    }
    if ($oldversion < 2014081500) {
        // Define index course (not unique) to be added to forum_discussions.
        $table = new xmldb_table('forum_discussions');
        $index = new xmldb_index('course', XMLDB_INDEX_NOTUNIQUE, array('course'));
        // Conditionally launch add index course.
        if (!$dbman->index_exists($table, $index)) {
            $dbman->add_index($table, $index);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2014081500, 'forum');
    }
    if ($oldversion < 2014081900) {
        // Define table forum_discussion_subs to be created.
        $table = new xmldb_table('forum_discussion_subs');
        // Adding fields to table forum_discussion_subs.
        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
        $table->add_field('forum', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
        $table->add_field('discussion', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
        $table->add_field('preference', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1');
        // Adding keys to table forum_discussion_subs.
        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
        $table->add_key('forum', XMLDB_KEY_FOREIGN, array('forum'), 'forum', array('id'));
        $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
        $table->add_key('discussion', XMLDB_KEY_FOREIGN, array('discussion'), 'forum_discussions', array('id'));
        $table->add_key('user_discussions', XMLDB_KEY_UNIQUE, array('userid', 'discussion'));
        // Conditionally launch create table for forum_discussion_subs.
        if (!$dbman->table_exists($table)) {
            $dbman->create_table($table);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2014081900, 'forum');
    }
    if ($oldversion < 2014103000) {
        // Find records with multiple userid/postid combinations and find the lowest ID.
        // Later we will remove all those which don't match this ID.
        $sql = "\n            SELECT MIN(id) as lowid, userid, postid\n            FROM {forum_read}\n            GROUP BY userid, postid\n            HAVING COUNT(id) > 1";
        if ($duplicatedrows = $DB->get_recordset_sql($sql)) {
            foreach ($duplicatedrows as $row) {
                $DB->delete_records_select('forum_read', 'userid = ? AND postid = ? AND id <> ?', array($row->userid, $row->postid, $row->lowid));
            }
        }
        $duplicatedrows->close();
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2014103000, 'forum');
    }
    if ($oldversion < 2014110300) {
        // Changing precision of field preference on table forum_discussion_subs to (10).
        $table = new xmldb_table('forum_discussion_subs');
        $field = new xmldb_field('preference', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '1', 'discussion');
        // Launch change of precision for field preference.
        $dbman->change_field_precision($table, $field);
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2014110300, 'forum');
    }
    // Moodle v2.8.0 release upgrade line.
    // Put any upgrade step following this.
    // Moodle v2.9.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2015102900) {
        // Groupid = 0 is never valid.
        $DB->set_field('forum_discussions', 'groupid', -1, array('groupid' => 0));
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2015102900, 'forum');
    }
    // Moodle v3.0.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2015120800) {
        // Add support for pinned discussions.
        $table = new xmldb_table('forum_discussions');
        $field = new xmldb_field('pinned', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'timeend');
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2015120800, 'forum');
    }
    return true;
}
/**
 * This file keeps track of upgrades to
 * the forum module
 *
 * Sometimes, changes between versions involve
 * alterations to database structures and other
 * major things that may break installations.
 *
 * The upgrade function in this file will attempt
 * to perform all the necessary actions to upgrade
 * your older installation to the current version.
 *
 * If there's something it cannot do itself, it
 * will tell you what you need to do.
 *
 * The commands in here will all be database-neutral,
 * using the methods of database_manager class
 *
 * Please do not forget to use upgrade_set_timeout()
 * before any action that may take longer time to finish.
 *
 * @package   mod_hsuforum
 * @copyright 2003 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 * @copyright Copyright (c) 2012 Moodlerooms Inc. (http://www.moodlerooms.com)
 * @author Mark Nielsen
 */
function xmldb_hsuforum_upgrade($oldversion)
{
    global $CFG, $DB, $OUTPUT;
    $dbman = $DB->get_manager();
    // loads ddl manager and xmldb classes
    //===== 1.9.0 upgrade line ======//
    if ($oldversion < 2011112801) {
        /// HSUFORUM UPGRADES
        // Rename field hsuforum on table hsuforum_discussions to forum
        $table = new xmldb_table('hsuforum_discussions');
        $field = new xmldb_field('hsuforum', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'course');
        // Launch rename field hsuforum
        $dbman->rename_field($table, $field, 'forum');
        // Rename field hsuforum on table hsuforum_subscriptions to forum
        $table = new xmldb_table('hsuforum_subscriptions');
        $field = new xmldb_field('hsuforum', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'userid');
        // Launch rename field hsuforum
        $dbman->rename_field($table, $field, 'forum');
        // Rename field hsuforumid on table hsuforum_read to forumid
        $table = new xmldb_table('hsuforum_read');
        $field = new xmldb_field('hsuforumid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'userid');
        // Launch rename field hsuforumid
        $dbman->rename_field($table, $field, 'forumid');
        // hsuforum_discussion_subscripts was too long of a name
        // Define table hsuforum_discussion_subscripts to be renamed to hsuforum_subscriptions_disc
        $table = new xmldb_table('hsuforum_discussion_subscripts');
        // Launch rename table for hsuforum_discussion_subscripts
        $dbman->rename_table($table, 'hsuforum_subscriptions_disc');
        /// HSUFORUM UPGRADES END
        //MDL-13866 - send forum ratins to gradebook again
        require_once $CFG->dirroot . '/mod/hsuforum/lib.php';
        hsuforum_upgrade_grades();
        upgrade_mod_savepoint(true, 2011112801, 'hsuforum');
    }
    if ($oldversion < 2011112802) {
        /// Define field completiondiscussions to be added to forum
        $table = new xmldb_table('hsuforum');
        $field = new xmldb_field('completiondiscussions');
        $field->set_attributes(XMLDB_TYPE_INTEGER, '9', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'blockperiod');
        /// Launch add field completiondiscussions
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }
        $field = new xmldb_field('completionreplies');
        $field->set_attributes(XMLDB_TYPE_INTEGER, '9', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'completiondiscussions');
        /// Launch add field completionreplies
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }
        /// Define field completionposts to be added to forum
        $field = new xmldb_field('completionposts');
        $field->set_attributes(XMLDB_TYPE_INTEGER, '9', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'completionreplies');
        /// Launch add field completionposts
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }
        upgrade_mod_savepoint(true, 2011112802, 'hsuforum');
    }
    if ($oldversion < 2011112803) {
        /////////////////////////////////////
        /// new file storage upgrade code ///
        /////////////////////////////////////
        $fs = get_file_storage();
        $empty = $DB->sql_empty();
        // silly oracle empty string handling workaround
        $sqlfrom = "FROM {hsuforum_posts} p\n                    JOIN {hsuforum_discussions} d ON d.id = p.discussion\n                    JOIN {hsuforum} f ON f.id = d.forum\n                    JOIN {modules} m ON m.name = 'hsuforum'\n                    JOIN {course_modules} cm ON (cm.module = m.id AND cm.instance = f.id)\n                   WHERE p.attachment <> '{$empty}' AND p.attachment <> '1'";
        $count = $DB->count_records_sql("SELECT COUNT('x') {$sqlfrom}");
        $rs = $DB->get_recordset_sql("SELECT p.id, p.attachment, p.userid, d.forum, f.course, cm.id AS cmid {$sqlfrom} ORDER BY f.course, f.id, d.id");
        if ($rs->valid()) {
            $pbar = new progress_bar('migrateforumfiles', 500, true);
            $i = 0;
            foreach ($rs as $post) {
                $i++;
                upgrade_set_timeout(60);
                // set up timeout, may also abort execution
                $pbar->update($i, $count, "Migrating forum posts - {$i}/{$count}.");
                $attachmentmigrated = false;
                $basepath = "{$CFG->dataroot}/{$post->course}/{$CFG->moddata}/hsuforum/{$post->forum}/{$post->id}";
                $files = get_directory_list($basepath);
                foreach ($files as $file) {
                    $filepath = "{$basepath}/{$file}";
                    if (!is_readable($filepath)) {
                        //file missing??
                        echo $OUTPUT->notification("File not readable, skipping: " . $filepath);
                        $post->attachment = '';
                        $DB->update_record('hsuforum_posts', $post);
                        continue;
                    }
                    $context = context_module::instance($post->cmid);
                    $filearea = 'attachment';
                    $filename = clean_param(pathinfo($filepath, PATHINFO_BASENAME), PARAM_FILE);
                    if ($filename === '') {
                        echo $OUTPUT->notification("Unsupported post filename, skipping: " . $filepath);
                        $post->attachment = '';
                        $DB->update_record('hsuforum_posts', $post);
                        continue;
                    }
                    if (!$fs->file_exists($context->id, 'mod_hsuforum', $filearea, $post->id, '/', $filename)) {
                        $file_record = array('contextid' => $context->id, 'component' => 'mod_hsuforum', 'filearea' => $filearea, 'itemid' => $post->id, 'filepath' => '/', 'filename' => $filename, 'userid' => $post->userid);
                        if ($fs->create_file_from_pathname($file_record, $filepath)) {
                            $attachmentmigrated = true;
                            unlink($filepath);
                        }
                    }
                }
                if ($attachmentmigrated) {
                    $post->attachment = '1';
                    $DB->update_record('hsuforum_posts', $post);
                }
                // remove dirs if empty
                @rmdir("{$CFG->dataroot}/{$post->course}/{$CFG->moddata}/hsuforum/{$post->forum}/{$post->id}");
                @rmdir("{$CFG->dataroot}/{$post->course}/{$CFG->moddata}/hsuforum/{$post->forum}");
                @rmdir("{$CFG->dataroot}/{$post->course}/{$CFG->moddata}/hsuforum");
            }
        }
        $rs->close();
        upgrade_mod_savepoint(true, 2011112803, 'hsuforum');
    }
    if ($oldversion < 2011112804) {
        /// Define field maxattachments to be added to forum
        $table = new xmldb_table('hsuforum');
        $field = new xmldb_field('maxattachments', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '1', 'maxbytes');
        /// Conditionally launch add field maxattachments
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }
        /// HSUFORUM specific upgrades to maxattach and multiattach
        $field = new xmldb_field('maxattach', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '5');
        if ($dbman->field_exists($table, $field)) {
            $DB->execute("\n                UPDATE {hsuforum}\n                   SET maxattachments = maxattach\n            ");
            $dbman->drop_field($table, $field);
        }
        $field = new xmldb_field('multiattach', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '1');
        if ($dbman->field_exists($table, $field)) {
            // This disabled attachments, so clear out maxattachments
            $DB->execute("\n                UPDATE {hsuforum}\n                   SET maxattachments = 0\n                 WHERE multiattach = 0\n            ");
            $dbman->drop_field($table, $field);
        }
        /// forum savepoint reached
        upgrade_mod_savepoint(true, 2011112804, 'hsuforum');
    }
    if ($oldversion < 2011112805) {
        /// Rename field format on table hsuforum_posts to messageformat
        $table = new xmldb_table('hsuforum_posts');
        $field = new xmldb_field('format', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'message');
        /// Launch rename field format
        $dbman->rename_field($table, $field, 'messageformat');
        /// forum savepoint reached
        upgrade_mod_savepoint(true, 2011112805, 'hsuforum');
    }
    if ($oldversion < 2011112806) {
        /// Define field messagetrust to be added to hsuforum_posts
        $table = new xmldb_table('hsuforum_posts');
        $field = new xmldb_field('messagetrust', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'messageformat');
        /// Launch add field messagetrust
        $dbman->add_field($table, $field);
        /// forum savepoint reached
        upgrade_mod_savepoint(true, 2011112806, 'hsuforum');
    }
    if ($oldversion < 2011112807) {
        $trustmark = '#####TRUSTTEXT#####';
        $rs = $DB->get_recordset_sql("SELECT * FROM {hsuforum_posts} WHERE message LIKE ?", array($trustmark . '%'));
        foreach ($rs as $post) {
            if (strpos($post->message, $trustmark) !== 0) {
                // probably lowercase in some DBs?
                continue;
            }
            $post->message = str_replace($trustmark, '', $post->message);
            $post->messagetrust = 1;
            $DB->update_record('hsuforum_posts', $post);
        }
        $rs->close();
        /// forum savepoint reached
        upgrade_mod_savepoint(true, 2011112807, 'hsuforum');
    }
    if ($oldversion < 2011112808) {
        /// Define field introformat to be added to forum
        $table = new xmldb_table('hsuforum');
        $field = new xmldb_field('introformat', XMLDB_TYPE_INTEGER, '4', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'intro');
        /// Launch add field introformat
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }
        // conditionally migrate to html format in intro
        if ($CFG->texteditors !== 'textarea') {
            $rs = $DB->get_recordset('hsuforum', array('introformat' => FORMAT_MOODLE), '', 'id,intro,introformat');
            foreach ($rs as $f) {
                $f->intro = text_to_html($f->intro, false, false, true);
                $f->introformat = FORMAT_HTML;
                $DB->update_record('hsuforum', $f);
                upgrade_set_timeout();
            }
            $rs->close();
        }
        /// forum savepoint reached
        upgrade_mod_savepoint(true, 2011112808, 'hsuforum');
    }
    /// Dropping all enums/check contraints from core. MDL-18577
    if ($oldversion < 2011112809) {
        /// Changing list of values (enum) of field type on table forum to none
        $table = new xmldb_table('hsuforum');
        $field = new xmldb_field('type', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, 'general', 'course');
        /// Launch change of list of values for field type
        $dbman->drop_enum_from_field($table, $field);
        /// forum savepoint reached
        upgrade_mod_savepoint(true, 2011112809, 'hsuforum');
    }
    if ($oldversion < 2011112810) {
        /// Clean existing wrong rates. MDL-18227
        $DB->delete_records('hsuforum_ratings', array('post' => 0));
        /// forum savepoint reached
        upgrade_mod_savepoint(true, 2011112810, 'hsuforum');
    }
    if ($oldversion < 2011112811) {
        //migrate forumratings to the central rating table
        $table = new xmldb_table('hsuforum_ratings');
        if ($dbman->table_exists($table)) {
            //forum ratings only have a single time column so use it for both time created and modified
            $sql = "INSERT INTO {rating} (contextid, component, ratingarea, scaleid, itemid, rating, userid, timecreated, timemodified)\n\n                    SELECT cxt.id, 'mod_hsuforum', 'post', f.scale, r.post AS itemid, r.rating, r.userid, r.time AS timecreated, r.time AS timemodified\n                      FROM {hsuforum_ratings} r\n                      JOIN {hsuforum_posts} p ON p.id=r.post\n                      JOIN {hsuforum_discussions} d ON d.id=p.discussion\n                      JOIN {hsuforum} f ON f.id=d.forum\n                      JOIN {course_modules} cm ON cm.instance=f.id\n                      JOIN {context} cxt ON cxt.instanceid=cm.id\n                      JOIN {modules} m ON m.id=cm.module\n                     WHERE m.name = :modname AND cxt.contextlevel = :contextlevel";
            $params['modname'] = 'hsuforum';
            $params['contextlevel'] = CONTEXT_MODULE;
            $DB->execute($sql, $params);
            //now drop hsuforum_ratings
            $dbman->drop_table($table);
        }
        upgrade_mod_savepoint(true, 2011112811, 'hsuforum');
    }
    if ($oldversion < 2011112812) {
        // Remove the forum digests message provider MDL-23145
        $DB->delete_records('message_providers', array('name' => 'digests', 'component' => 'mod_hsuforum'));
        // forum savepoint reached
        upgrade_mod_savepoint(true, 2011112812, 'hsuforum');
    }
    if ($oldversion < 2011112813) {
        // rename files from borked upgrade in 2.0dev
        $fs = get_file_storage();
        $rs = $DB->get_recordset('files', array('component' => 'mod_form'));
        foreach ($rs as $oldrecord) {
            $file = $fs->get_file_instance($oldrecord);
            $newrecord = array('component' => 'mod_hsuforum');
            if (!$fs->file_exists($oldrecord->contextid, 'mod_hsuforum', $oldrecord->filearea, $oldrecord->itemid, $oldrecord->filepath, $oldrecord->filename)) {
                $fs->create_file_from_storedfile($newrecord, $file);
            }
            $file->delete();
        }
        $rs->close();
        upgrade_mod_savepoint(true, 2011112813, 'hsuforum');
    }
    if ($oldversion < 2011112814) {
        // rating.component and rating.ratingarea have now been added as mandatory fields.
        // Presently you can only rate forum posts so component = 'mod_hsuforum' and ratingarea = 'post'
        // for all ratings with a forum context.
        // We want to update all ratings that belong to a forum context and don't already have a
        // component set.
        // This could take a while reset upgrade timeout to 5 min
        upgrade_set_timeout(60 * 20);
        $sql = "UPDATE {rating}\n                SET component = 'mod_hsuforum', ratingarea = 'post'\n                WHERE contextid IN (\n                    SELECT ctx.id\n                      FROM {context} ctx\n                      JOIN {course_modules} cm ON cm.id = ctx.instanceid\n                      JOIN {modules} m ON m.id = cm.module\n                     WHERE ctx.contextlevel = 70 AND\n                           m.name = 'hsuforum'\n                ) AND component = 'unknown'";
        $DB->execute($sql);
        upgrade_mod_savepoint(true, 2011112814, 'hsuforum');
    }
    // Moodle v2.1.0 release upgrade line
    // Put any upgrade step following this
    // Moodle v2.2.0 release upgrade line
    // Put any upgrade step following this
    if ($oldversion < 2011112907) {
        /// Conditionally add field privatereply to be added to hsuforum
        $table = new xmldb_table('hsuforum_posts');
        $field = new xmldb_field('privatereply');
        $field->set_attributes(XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0', 'flags');
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }
        // Define index privatereply (not unique) to be added to hsuforum_posts
        $table = new xmldb_table('hsuforum_posts');
        $index = new xmldb_index('privatereply', XMLDB_INDEX_NOTUNIQUE, array('privatereply'));
        // Conditionally launch add index privatereply
        if (!$dbman->index_exists($table, $index)) {
            $dbman->add_index($table, $index);
        }
        // Rename field hsuforumid on table hsuforum_track_prefs to forumid
        $table = new xmldb_table('hsuforum_track_prefs');
        $field = new xmldb_field('hsuforumid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'userid');
        // Conditionally launch rename field hsuforumid (if it exists)
        if ($dbman->field_exists($table, $field)) {
            $dbman->rename_field($table, $field, 'forumid');
        }
        // hsuforum savepoint reached
        upgrade_mod_savepoint(true, 2011112907, 'hsuforum');
    }
    // Moodle v2.2.0 release upgrade line
    // Put any upgrade step following this
    // Moodle v2.3.0 release upgrade line
    // Put any upgrade step following this
    // Moodle v2.4.0 release upgrade line
    // Put any upgrade step following this
    // Forcefully assign mod/hsuforum:allowforcesubscribe to frontpage role, as we missed that when
    // capability was introduced.
    if ($oldversion < 2012112901) {
        // If capability mod/hsuforum:allowforcesubscribe is defined then set it for frontpage role.
        if (get_capability_info('mod/hsuforum:allowforcesubscribe')) {
            assign_legacy_capabilities('mod/hsuforum:allowforcesubscribe', array('frontpage' => CAP_ALLOW));
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2012112901, 'hsuforum');
    }
    if ($oldversion < 2012112902) {
        // Forum and hsuforum were reading from the same $CFG values, create new ones for hsuforum.
        $digestmailtime = 17;
        // Default in settings.php
        if (!empty($CFG->digestmailtime)) {
            $digestmailtime = $CFG->digestmailtime;
        }
        set_config('hsuforum_digestmailtime', $digestmailtime);
        $digestmailtimelast = 0;
        if (!empty($CFG->digestmailtimelast)) {
            $digestmailtimelast = $CFG->digestmailtimelast;
        }
        set_config('hsuforum_digestmailtimelast', $digestmailtimelast);
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2012112902, 'hsuforum');
    }
    if ($oldversion < 2013020500) {
        // Define field displaywordcount to be added to forum.
        $table = new xmldb_table('hsuforum');
        $field = new xmldb_field('displaywordcount', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'completionposts');
        // Conditionally launch add field displaywordcount.
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2013020500, 'hsuforum');
    }
    // Moodle v2.5.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2014021900) {
        // Define table hsuforum_digests to be created.
        $table = new xmldb_table('hsuforum_digests');
        // Adding fields to table hsuforum_digests.
        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
        $table->add_field('forum', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
        $table->add_field('maildigest', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '-1');
        // Adding keys to table hsuforum_digests.
        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
        $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
        $table->add_key('forum', XMLDB_KEY_FOREIGN, array('forum'), 'forum', array('id'));
        $table->add_key('forumdigest', XMLDB_KEY_UNIQUE, array('forum', 'userid', 'maildigest'));
        // Conditionally launch create table for hsuforum_digests.
        if (!$dbman->table_exists($table)) {
            $dbman->create_table($table);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2014021900, 'hsuforum');
    }
    // Moodle v2.6.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2014040400) {
        // Define index userid-postid (not unique) to be dropped form hsuforum_read.
        $table = new xmldb_table('hsuforum_read');
        $index = new xmldb_index('userid-postid', XMLDB_INDEX_NOTUNIQUE, array('userid', 'postid'));
        // Conditionally launch drop index userid-postid.
        if ($dbman->index_exists($table, $index)) {
            $dbman->drop_index($table, $index);
        }
        // Define index postid-userid (not unique) to be added to hsuforum_read.
        $index = new xmldb_index('postid-userid', XMLDB_INDEX_NOTUNIQUE, array('postid', 'userid'));
        // Conditionally launch add index postid-userid.
        if (!$dbman->index_exists($table, $index)) {
            $dbman->add_index($table, $index);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2014040400, 'hsuforum');
    }
    // Moodle v2.7.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2014051201) {
        // Incorrect values that need to be replaced.
        $replacements = array(11 => 20, 12 => 50, 13 => 100);
        // Run the replacements.
        foreach ($replacements as $old => $new) {
            $DB->set_field('hsuforum', 'maxattachments', $new, array('maxattachments' => $old));
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2014051201, 'hsuforum');
    }
    if ($oldversion < 2014051203) {
        // Find records with multiple userid/postid combinations and find the lowest ID.
        // Later we will remove all those which don't match this ID.
        $sql = "\n            SELECT MIN(id) as lowid, userid, postid\n            FROM {hsuforum_read}\n            GROUP BY userid, postid\n            HAVING COUNT(id) > 1";
        if ($duplicatedrows = $DB->get_recordset_sql($sql)) {
            foreach ($duplicatedrows as $row) {
                $DB->delete_records_select('hsuforum_read', 'userid = ? AND postid = ? AND id <> ?', array($row->userid, $row->postid, $row->lowid));
            }
        }
        $duplicatedrows->close();
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2014051203, 'hsuforum');
    }
    if ($oldversion < 2014092400) {
        // Define fields to be added to hsuforum table.
        $table = new xmldb_table('hsuforum');
        $fields = array();
        $fields[] = new xmldb_field('showsubstantive', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'displaywordcount');
        $fields[] = new xmldb_field('showbookmark', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'showsubstantive');
        // Go through each field and add if it doesn't already exist.
        foreach ($fields as $field) {
            // Conditionally launch add field.
            if (!$dbman->field_exists($table, $field)) {
                $dbman->add_field($table, $field);
            }
        }
        // Hsuforum savepoint reached.
        upgrade_mod_savepoint(true, 2014092400, 'hsuforum');
    }
    if ($oldversion < 2014093000) {
        // Define fields to be added to hsuforum table.
        $table = new xmldb_table('hsuforum');
        $field = new xmldb_field('allowprivatereplies', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'showbookmark');
        // Conditionally launch add field.
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }
        // Hsuforum savepoint reached.
        upgrade_mod_savepoint(true, 2014093000, 'hsuforum');
    }
    if ($oldversion < 2014093001) {
        // Set default settings for existing forums.
        $DB->execute("\n                UPDATE {hsuforum}\n                   SET allowprivatereplies = 1,\n                       showsubstantive = 1,\n                       showbookmark = 1\n\n        ");
        // Hsuforum savepoint reached.
        upgrade_mod_savepoint(true, 2014093001, 'hsuforum');
    }
    // Convert global configs to plugin configs
    if ($oldversion < 2014100600) {
        $configs = array('allowforcedreadtracking', 'cleanreadtime', 'digestmailtime', 'digestmailtimelast', 'disablebookmark', 'disablesubstantive', 'displaymode', 'enablerssfeeds', 'enabletimedposts', 'lastreadclean', 'longpost', 'manydiscussions', 'maxattachments', 'maxbytes', 'oldpostdays', 'replytouser', 'shortpost', 'showbookmark', 'showsubstantive', 'trackingtype', 'trackreadposts', 'usermarksread');
        // Migrate legacy configs to plugin configs.
        foreach ($configs as $config) {
            $oldvar = 'hsuforum_' . $config;
            if (isset($CFG->{$oldvar})) {
                // Set new config variable up based on legacy config.
                set_config($config, $CFG->{$oldvar}, 'hsuforum');
                // Delete legacy config.
                unset_config($oldvar);
            }
        }
        // Hsuforum savepoint reached.
        upgrade_mod_savepoint(true, 2014100600, 'hsuforum');
    }
    if ($oldversion < 2014121700) {
        // Define fields to be added to hsuforum table.
        $table = new xmldb_table('hsuforum');
        $field = new xmldb_field('showrecent', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'displaywordcount');
        // Conditionally launch add field.
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }
        // Hsuforum savepoint reached.
        upgrade_mod_savepoint(true, 2014121700, 'hsuforum');
    }
    return true;
}
Exemple #15
0
/**
 * This file keeps track of upgrades to
 * the forum module
 *
 * Sometimes, changes between versions involve
 * alterations to database structures and other
 * major things that may break installations.
 *
 * The upgrade function in this file will attempt
 * to perform all the necessary actions to upgrade
 * your older installation to the current version.
 *
 * If there's something it cannot do itself, it
 * will tell you what you need to do.
 *
 * The commands in here will all be database-neutral,
 * using the methods of database_manager class
 *
 * Please do not forget to use upgrade_set_timeout()
 * before any action that may take longer time to finish.
 *
 * @package   mod_forum
 * @copyright 2003 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
function xmldb_forum_upgrade($oldversion)
{
    global $CFG, $DB, $OUTPUT;
    $dbman = $DB->get_manager();
    // Loads ddl manager and xmldb classes.
    // Moodle v2.2.0 release upgrade line.
    // Put any upgrade step following this.
    // Moodle v2.3.0 release upgrade line.
    // Put any upgrade step following this.
    // Moodle v2.4.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2013020500) {
        // Define field displaywordcount to be added to forum.
        $table = new xmldb_table('forum');
        $field = new xmldb_field('displaywordcount', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'completionposts');
        // Conditionally launch add field displaywordcount.
        if (!$dbman->field_exists($table, $field)) {
            $dbman->add_field($table, $field);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2013020500, 'forum');
    }
    // Forcefully assign mod/forum:allowforcesubscribe to frontpage role, as we missed that when
    // capability was introduced.
    if ($oldversion < 2013021200) {
        // If capability mod/forum:allowforcesubscribe is defined then set it for frontpage role.
        if (get_capability_info('mod/forum:allowforcesubscribe')) {
            assign_legacy_capabilities('mod/forum:allowforcesubscribe', array('frontpage' => CAP_ALLOW));
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2013021200, 'forum');
    }
    // Moodle v2.5.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2013071000) {
        // Define table forum_digests to be created.
        $table = new xmldb_table('forum_digests');
        // Adding fields to table forum_digests.
        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
        $table->add_field('forum', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
        $table->add_field('maildigest', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '-1');
        // Adding keys to table forum_digests.
        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
        $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
        $table->add_key('forum', XMLDB_KEY_FOREIGN, array('forum'), 'forum', array('id'));
        $table->add_key('forumdigest', XMLDB_KEY_UNIQUE, array('forum', 'userid', 'maildigest'));
        // Conditionally launch create table for forum_digests.
        if (!$dbman->table_exists($table)) {
            $dbman->create_table($table);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2013071000, 'forum');
    }
    // Moodle v2.6.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2014040400) {
        // Define index userid-postid (not unique) to be dropped form forum_read.
        $table = new xmldb_table('forum_read');
        $index = new xmldb_index('userid-postid', XMLDB_INDEX_NOTUNIQUE, array('userid', 'postid'));
        // Conditionally launch drop index userid-postid.
        if ($dbman->index_exists($table, $index)) {
            $dbman->drop_index($table, $index);
        }
        // Define index postid-userid (not unique) to be added to forum_read.
        $index = new xmldb_index('postid-userid', XMLDB_INDEX_NOTUNIQUE, array('postid', 'userid'));
        // Conditionally launch add index postid-userid.
        if (!$dbman->index_exists($table, $index)) {
            $dbman->add_index($table, $index);
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2014040400, 'forum');
    }
    // Moodle v2.7.0 release upgrade line.
    // Put any upgrade step following this.
    if ($oldversion < 2014051201) {
        // Incorrect values that need to be replaced.
        $replacements = array(11 => 20, 12 => 50, 13 => 100);
        // Run the replacements.
        foreach ($replacements as $old => $new) {
            $DB->set_field('forum', 'maxattachments', $new, array('maxattachments' => $old));
        }
        // Forum savepoint reached.
        upgrade_mod_savepoint(true, 2014051201, 'forum');
    }
    return true;
}
Exemple #16
0
/**
 * Is the user allowed to add this type of module to this course?
 * @param object $course the course settings. Only $course->id is used.
 * @param string $modname the module name. E.g. 'forum' or 'quiz'.
 * @return bool whether the current user is allowed to add this type of module to this course.
 */
function course_allowed_module($course, $modname)
{
    global $DB;
    if (is_numeric($modname)) {
        throw new coding_exception('Function course_allowed_module no longer
                supports numeric module ids. Please update your code to pass the module name.');
    }
    $capability = 'mod/' . $modname . ':addinstance';
    if (!get_capability_info($capability)) {
        // Debug warning that the capability does not exist, but no more than once per page.
        static $warned = array();
        $archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
        if (!isset($warned[$modname]) && $archetype !== MOD_ARCHETYPE_SYSTEM) {
            debugging('The module ' . $modname . ' does not define the standard capability ' . $capability, DEBUG_DEVELOPER);
            $warned[$modname] = 1;
        }
        // If the capability does not exist, the module can always be added.
        return true;
    }
    $coursecontext = context_course::instance($course->id);
    return has_capability($capability, $coursecontext);
}
    /**
     * Allows/denies a capability at the specified context
     *
     * @throws Exception
     * @param array $data
     * @return void
     */
    protected function process_permission_override($data) {

        // Will throw an exception if it does not exist.
        $context = $this->get_context($data['contextlevel'], $data['reference']);

        switch ($data['permission']) {
            case get_string('allow', 'role'):
                $permission = CAP_ALLOW;
                break;
            case get_string('prevent', 'role'):
                $permission = CAP_PREVENT;
                break;
            case get_string('prohibit', 'role'):
                $permission = CAP_PROHIBIT;
                break;
            default:
                throw new Exception('The \'' . $data['permission'] . '\' permission does not exist');
                break;
        }

        if (is_null(get_capability_info($data['capability']))) {
            throw new Exception('The \'' . $data['capability'] . '\' capability does not exist');
        }

        role_change_permission($data['roleid'], $context, $data['capability'], $permission);
    }
Exemple #18
0
 /**
  * Function used to return a list of users where the given user has a particular capability.
  *
  * This is used e.g. to find all the users where someone is able to manage their learning plans,
  * it also would be useful for mentees etc.
  *
  * @param string $capability - The capability string we are filtering for. If '' is passed,
  *                             an always matching filter is returned.
  * @param int $userid - The user id we are using for the access checks. Defaults to current user.
  * @param int $type - The type of named params to return (passed to $DB->get_in_or_equal).
  * @param string $prefix - The type prefix for the db table (passed to $DB->get_in_or_equal).
  * @return list($sql, $params) Same as $DB->get_in_or_equal().
  * @todo MDL-52243 Move this function to lib/accesslib.php
  */
 public static function filter_users_with_capability_on_user_context_sql($capability, $userid = 0, $type = SQL_PARAMS_QM, $prefix = 'param')
 {
     global $USER, $DB;
     $allresultsfilter = array('> 0', array());
     $noresultsfilter = array('= -1', array());
     if (empty($capability)) {
         return $allresultsfilter;
     }
     if (!($capinfo = get_capability_info($capability))) {
         throw new coding_exception('Capability does not exist: ' . $capability);
     }
     if (empty($userid)) {
         $userid = $USER->id;
     }
     // Make sure the guest account and not-logged-in users never get any risky caps no matter what the actual settings are.
     if ($capinfo->captype === 'write' or $capinfo->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS)) {
         if (isguestuser($userid) or $userid == 0) {
             return $noresultsfilter;
         }
     }
     if (is_siteadmin($userid)) {
         // No filtering for site admins.
         return $allresultsfilter;
     }
     // Check capability on system level.
     $syscontext = context_system::instance();
     $hassystem = has_capability($capability, $syscontext, $userid);
     $access = get_user_access_sitewide($userid);
     // Build up a list of level 2 contexts (candidates to be user context).
     $filtercontexts = array();
     foreach ($access['ra'] as $path => $role) {
         $parts = explode('/', $path);
         if (count($parts) == 3) {
             $filtercontexts[$parts[2]] = $parts[2];
         } else {
             if (count($parts) > 3) {
                 // We know this is not a user context because there is another path with more than 2 levels.
                 unset($filtercontexts[$parts[2]]);
             }
         }
     }
     // Add all contexts in which a role may be overidden.
     foreach ($access['rdef'] as $pathandroleid => $def) {
         $matches = array();
         if (!isset($def[$capability])) {
             // The capability is not mentioned, we can ignore.
             continue;
         }
         list($contextpath, $roleid) = explode(':', $pathandroleid, 2);
         $parts = explode('/', $contextpath);
         if (count($parts) != 3) {
             // Only get potential user contexts, they only ever have 2 slashes /parentId/Id.
             continue;
         }
         $filtercontexts[$parts[2]] = $parts[2];
     }
     // No interesting contexts - return all or no results.
     if (empty($filtercontexts)) {
         if ($hassystem) {
             return $allresultsfilter;
         } else {
             return $noresultsfilter;
         }
     }
     // Fetch all interesting contexts for further examination.
     list($insql, $params) = $DB->get_in_or_equal($filtercontexts, SQL_PARAMS_NAMED);
     $params['level'] = CONTEXT_USER;
     $fields = context_helper::get_preload_record_columns_sql('ctx');
     $interestingcontexts = $DB->get_recordset_sql('SELECT ' . $fields . '
                                                    FROM {context} ctx
                                                    WHERE ctx.contextlevel = :level
                                                      AND ctx.id ' . $insql . '
                                                    ORDER BY ctx.id', $params);
     if ($hassystem) {
         // If allowed at system, search for exceptions prohibiting the capability at user context.
         $excludeusers = array();
         foreach ($interestingcontexts as $contextrecord) {
             $candidateuserid = $contextrecord->ctxinstance;
             context_helper::preload_from_record($contextrecord);
             $usercontext = context_user::instance($candidateuserid);
             // Has capability should use the data already preloaded.
             if (!has_capability($capability, $usercontext, $userid)) {
                 $excludeusers[$candidateuserid] = $candidateuserid;
             }
         }
         // Construct SQL excluding users with this role assigned for this user.
         if (empty($excludeusers)) {
             $interestingcontexts->close();
             return $allresultsfilter;
         }
         list($sql, $params) = $DB->get_in_or_equal($excludeusers, $type, $prefix, false);
     } else {
         // If not allowed at system, search for exceptions allowing the capability at user context.
         $allowusers = array();
         foreach ($interestingcontexts as $contextrecord) {
             $candidateuserid = $contextrecord->ctxinstance;
             context_helper::preload_from_record($contextrecord);
             $usercontext = context_user::instance($candidateuserid);
             // Has capability should use the data already preloaded.
             if (has_capability($capability, $usercontext, $userid)) {
                 $allowusers[$candidateuserid] = $candidateuserid;
             }
         }
         // Construct SQL excluding users with this role assigned for this user.
         if (empty($allowusers)) {
             $interestingcontexts->close();
             return $noresultsfilter;
         }
         list($sql, $params) = $DB->get_in_or_equal($allowusers, $type, $prefix);
     }
     $interestingcontexts->close();
     // Return the goods!.
     return array($sql, $params);
 }
Exemple #19
0
/**
 * Check whether a user has a particular capability in a given context.
 *
 * For example:
 *      $context = get_context_instance(CONTEXT_MODULE, $cm->id);
 *      has_capability('mod/forum:replypost',$context)
 *
 * By default checks the capabilities of the current user, but you can pass a
 * different userid. By default will return true for admin users, but you can override that with the fourth argument.
 *
 * Guest and not-logged-in users can never get any dangerous capability - that is any write capability
 * or capabilities with XSS, config or data loss risks.
 *
 * @param string $capability the name of the capability to check. For example mod/forum:view
 * @param context $context the context to check the capability in. You normally get this with {@link get_context_instance}.
 * @param integer|object $user A user id or object. By default (null) checks the permissions of the current user.
 * @param boolean $doanything If false, ignores effect of admin role assignment
 * @return boolean true if the user has this capability. Otherwise false.
 */
function has_capability($capability, context $context, $user = null, $doanything = true)
{
    global $USER, $CFG, $SCRIPT, $ACCESSLIB_PRIVATE;
    if (during_initial_install()) {
        if ($SCRIPT === "/{$CFG->admin}/index.php" or $SCRIPT === "/{$CFG->admin}/cli/install.php" or $SCRIPT === "/{$CFG->admin}/cli/install_database.php") {
            // we are in an installer - roles can not work yet
            return true;
        } else {
            return false;
        }
    }
    if (strpos($capability, 'moodle/legacy:') === 0) {
        throw new coding_exception('Legacy capabilities can not be used any more!');
    }
    if (!is_bool($doanything)) {
        throw new coding_exception('Capability parameter "doanything" is wierd, only true or false is allowed. This has to be fixed in code.');
    }
    // capability must exist
    if (!($capinfo = get_capability_info($capability))) {
        debugging('Capability "' . $capability . '" was not found! This has to be fixed in code.');
        return false;
    }
    if (!isset($USER->id)) {
        // should never happen
        $USER->id = 0;
    }
    // make sure there is a real user specified
    if ($user === null) {
        $userid = $USER->id;
    } else {
        $userid = is_object($user) ? $user->id : $user;
    }
    // make sure forcelogin cuts off not-logged-in users if enabled
    if (!empty($CFG->forcelogin) and $userid == 0) {
        return false;
    }
    // make sure the guest account and not-logged-in users never get any risky caps no matter what the actual settings are.
    if ($capinfo->captype === 'write' or $capinfo->riskbitmask & (RISK_XSS | RISK_CONFIG | RISK_DATALOSS)) {
        if (isguestuser($userid) or $userid == 0) {
            return false;
        }
    }
    // somehow make sure the user is not deleted and actually exists
    if ($userid != 0) {
        if ($userid == $USER->id and isset($USER->deleted)) {
            // this prevents one query per page, it is a bit of cheating,
            // but hopefully session is terminated properly once user is deleted
            if ($USER->deleted) {
                return false;
            }
        } else {
            if (!context_user::instance($userid, IGNORE_MISSING)) {
                // no user context == invalid userid
                return false;
            }
        }
    }
    // context path/depth must be valid
    if (empty($context->path) or $context->depth == 0) {
        // this should not happen often, each upgrade tries to rebuild the context paths
        debugging('Context id ' . $context->id . ' does not have valid path, please use build_context_path()');
        if (is_siteadmin($userid)) {
            return true;
        } else {
            return false;
        }
    }
    // Find out if user is admin - it is not possible to override the doanything in any way
    // and it is not possible to switch to admin role either.
    if ($doanything) {
        if (is_siteadmin($userid)) {
            if ($userid != $USER->id) {
                return true;
            }
            // make sure switchrole is not used in this context
            if (empty($USER->access['rsw'])) {
                return true;
            }
            $parts = explode('/', trim($context->path, '/'));
            $path = '';
            $switched = false;
            foreach ($parts as $part) {
                $path .= '/' . $part;
                if (!empty($USER->access['rsw'][$path])) {
                    $switched = true;
                    break;
                }
            }
            if (!$switched) {
                return true;
            }
            //ok, admin switched role in this context, let's use normal access control rules
        }
    }
    // Careful check for staleness...
    $context->reload_if_dirty();
    if ($USER->id == $userid) {
        if (!isset($USER->access)) {
            load_all_capabilities();
        }
        $access =& $USER->access;
    } else {
        // make sure user accessdata is really loaded
        get_user_accessdata($userid, true);
        $access =& $ACCESSLIB_PRIVATE->accessdatabyuser[$userid];
    }
    // Load accessdata for below-the-course context if necessary,
    // all contexts at and above all courses are already loaded
    if ($context->contextlevel != CONTEXT_COURSE and $coursecontext = $context->get_course_context(false)) {
        load_course_context($userid, $coursecontext, $access);
    }
    return has_capability_in_accessdata($capability, $context, $access);
}
Exemple #20
0
 *
 * This tool lets the adminstrator edit the definition for all roles for one
 * particular capability. So, for example, suppose you want to make sure that
 * only certain roles have the mod/quiz:preview capability, then this is the
 * tool for you. You can do that on one page, without having to click through
 * the pages that show each different role definition.
 *
 * @package    tool_editrolesbycap
 * @copyright  2012 The Open University
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
require_once dirname(__FILE__) . '/../../../config.php';
require_once dirname(__FILE__) . '/locallib.php';
require_once $CFG->libdir . '/adminlib.php';
$cap = optional_param('cap', '', PARAM_CAPABILITY);
$capability = get_capability_info($cap);
require_login();
$context = context_system::instance();
require_capability('moodle/role:manage', $context);
$showadvanced = get_user_preferences('definerole_showadvanced', false);
if (optional_param('toggleadvanced', false, PARAM_BOOL)) {
    $showadvanced = !$showadvanced;
    set_user_preference('definerole_showadvanced', $showadvanced);
}
$params = array();
if ($capability) {
    $capability->name = $cap;
    $params['cap'] = $capability->name;
}
admin_externalpage_setup('tooleditrolesbycap', '', $params);
$form = new tool_editrolesbycap_capability_form(new moodle_url('/admin/tool/editrolesbycap/index.php'), $params);