function definition() { global $DB; $mform = $this->_form; // Clear the observer cache to ensure observers for any newly-installed plugins are added $cache = \cache::make('core', 'observers'); $cache->delete('all'); list($instance, $plugin, $context) = $this->_customdata; $mform->addElement('header', 'header', get_string('pluginname', 'enrol_auto')); $mform->addElement('text', 'name', get_string('custominstancename', 'enrol')); $mform->setType('name', PARAM_TEXT); $options = array(ENROL_INSTANCE_ENABLED => get_string('yes'), ENROL_INSTANCE_DISABLED => get_string('no')); $mform->addElement('select', 'status', get_string('status', 'enrol_auto'), $options); $mform->addHelpButton('status', 'status', 'enrol_auto'); $options = array(ENROL_AUTO_COURSE_VIEWED => get_string('courseview', 'enrol_auto'), ENROL_AUTO_LOGIN => get_string('userlogin', 'enrol_auto'), ENROL_AUTO_MOD_VIEWED => get_string('modview', 'enrol_auto')); $mform->addElement('select', 'customint3', get_string('enrolon', 'enrol_auto'), $options); $mform->addHelpButton('customint3', 'enrolon', 'enrol_auto'); $mods = \enrol_auto\helper::get_mods_with_viewed_event(); $modgroup = array(); foreach ($mods as $modname) { $modgroup[] = $mform->createElement('checkbox', $modname, '', get_string('pluginname', "mod_{$modname}")); } $mform->addGroup($modgroup, 'customtext2', get_string('modviewmods', 'enrol_auto'), '<br>', true); $mform->disabledIf('customtext2', 'customint3', 'neq', ENROL_AUTO_MOD_VIEWED); $roles = $this->extend_assignable_roles($context, $instance->roleid); $mform->addElement('select', 'roleid', get_string('role', 'enrol_auto'), $roles); $mform->addElement('advcheckbox', 'customint2', get_string('sendcoursewelcomemessage', 'enrol_auto')); $mform->addHelpButton('customint2', 'sendcoursewelcomemessage', 'enrol_auto'); $mform->addElement('textarea', 'customtext1', get_string('customwelcomemessage', 'enrol_auto'), array('cols' => '60', 'rows' => '8')); $mform->addHelpButton('customtext1', 'customwelcomemessage', 'enrol_auto'); $mform->disabledIf('customtext1', 'customint2', 'notchecked'); $mform->addElement('hidden', 'id'); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'courseid'); $mform->setType('courseid', PARAM_INT); $this->add_action_buttons(true, $instance->id ? null : get_string('addinstance', 'enrol')); $instance->customtext2 = array_flip(explode(',', $instance->customtext2)); $instance->customtext2 = array_map(function ($a) { return 1; }, $instance->customtext2); $this->set_data($instance); }
/** * Create new instance of string manager * * @param string $otherroot location of downloaded lang packs - usually $CFG->dataroot/lang * @param string $localroot usually the same as $otherroot * @param array $translist limit list of visible translations */ public function __construct($otherroot, $localroot, $translist) { $this->otherroot = $otherroot; $this->localroot = $localroot; if ($translist) { $this->translist = array_combine($translist, $translist); } else { $this->translist = array(); } if ($this->get_revision() > 0) { // We can use a proper cache, establish the cache using the 'String cache' definition. $this->cache = cache::make('core', 'string'); $this->menucache = cache::make('core', 'langmenu'); } else { // We only want a cache for the length of the request, create a static cache. $options = array('simplekeys' => true, 'simpledata' => true); $this->cache = cache::make_from_params(cache_store::MODE_REQUEST, 'core', 'string', array(), $options); $this->menucache = cache::make_from_params(cache_store::MODE_REQUEST, 'core', 'langmenu', array(), $options); } }
public function validation($data, $files) { $errors = array(); // Submit is redirected if error occurs, so we store errordata in session. $sessionerrordata = array(); $cache = cache::make('format_socialwall', 'commentformerrors'); $cache->delete($data['postid']); // ... check if comment is all empty. if (isset($data['submitcomment'])) { if (empty($data['text'])) { $errors['text'] = get_string('textrequired', 'format_socialwall'); $sessionerrordata['text'] = array('message' => $errors['text'], 'value' => $data['text']); } } // ... store or clean. if (!empty($sessionerrordata)) { $cache->set($data['postid'], $sessionerrordata); } return $errors; }
function block_twitter_get_tweets($handle) { global $CFG, $COURSE, $DB; $cache = cache::make('block_twitter', 'tweets'); $tweets = $cache->get('tweets_' . $handle); if ($tweets !== false) { return $tweets; } $tconf = get_config('block_twitter'); $connection = new TwitterOAuth($tconf->consumerkey, $tconf->consumersecret, $tconf->accesstoken, $tconf->accesssecret); $connection->resetLastResponse(); $tweets = $connection->get("statuses/user_timeline", ["screen_name" => $handle, 'exclude_replies' => true, 'trim_user' => false]); if (isset($tweets->errors)) { $error = reset($tweets->errors); debugging(get_string('error') . ' ' . $error->code . ': ' . $error->message, DEBUG_DEVELOPER); return ''; } $cache->set('tweets_' . $handle, $tweets); return $tweets; }
/** * Get settings for repository instance. * * @param string $config a specific option to get. * @return mixed returns an array of options. If $config is not empty, then it returns that option, * or null if the option does not exist. */ public function get_option($config = '') { global $DB; $cache = cache::make('core', 'repositories'); if (($entries = $cache->get('ops:' . $this->id)) === false) { $entries = $DB->get_records('repository_instance_config', array('instanceid' => $this->id)); $cache->set('ops:' . $this->id, $entries); } $ret = array(); foreach ($entries as $entry) { $ret[$entry->name] = $entry->value; } if (!empty($config)) { if (isset($ret[$config])) { return $ret[$config]; } else { return null; } } else { return $ret; } }
/** * Override the cache::construct method. * * This function gets overriden so that we can process any invalidation events if need be. * If the definition doesn't have any invalidation events then this occurs exactly as it would for the cache class. * Otherwise we look at the last invalidation time and then check the invalidation data for events that have occured * between then now. * * You should not call this method from your code, instead you should use the cache::make methods. * * @param cache_definition $definition * @param cache_store $store * @param cache_loader|cache_data_source $loader */ public function __construct(cache_definition $definition, cache_store $store, $loader = null) { // First up copy the loadeduserid to the current user id. $this->currentuserid = self::$loadeduserid; parent::__construct($definition, $store, $loader); // This will trigger check tracked user. If this gets removed a call to that will need to be added here in its place. $this->set(self::LASTACCESS, cache::now()); if ($definition->has_invalidation_events()) { $lastinvalidation = $this->get('lastsessioninvalidation'); if ($lastinvalidation === false) { // This is a new session, there won't be anything to invalidate. Set the time of the last invalidation and // move on. $this->set('lastsessioninvalidation', cache::now()); return; } else { if ($lastinvalidation == cache::now()) { // We've already invalidated during this request. return; } } // Get the event invalidation cache. $cache = cache::make('core', 'eventinvalidation'); $events = $cache->get_many($definition->get_invalidation_events()); $todelete = array(); $purgeall = false; // Iterate the returned data for the events. foreach ($events as $event => $keys) { if ($keys === false) { // No data to be invalidated yet. continue; } // Look at each key and check the timestamp. foreach ($keys as $key => $timestamp) { // If the timestamp of the event is more than or equal to the last invalidation (happened between the last // invalidation and now)then we need to invaliate the key. if ($timestamp >= $lastinvalidation) { if ($key === 'purged') { $purgeall = true; break; } else { $todelete[] = $key; } } } } if ($purgeall) { $this->purge(); } else { if (!empty($todelete)) { $todelete = array_unique($todelete); $this->delete_many($todelete); } } // Set the time of the last invalidation. $this->set('lastsessioninvalidation', cache::now()); } }
/** * Performs synchronisation of an external file if the previous one has expired. * * This function must be implemented for external repositories supporting * FILE_REFERENCE, it is called for existing aliases when their filesize, * contenthash or timemodified are requested. It is not called for internal * repositories (see {@link repository::has_moodle_files()}), references to * internal files are updated immediately when source is modified. * * Referenced files may optionally keep their content in Moodle filepool (for * thumbnail generation or to be able to serve cached copy). In this * case both contenthash and filesize need to be synchronized. Otherwise repositories * should use contenthash of empty file and correct filesize in bytes. * * Note that this function may be run for EACH file that needs to be synchronised at the * moment. If anything is being downloaded or requested from external sources there * should be a small timeout. The synchronisation is performed to update the size of * the file and/or to update image and re-generated image preview. There is nothing * fatal if syncronisation fails but it is fatal if syncronisation takes too long * and hangs the script generating a page. * * Note: If you wish to call $file->get_filesize(), $file->get_contenthash() or * $file->get_timemodified() make sure that recursion does not happen. * * Called from {@link stored_file::sync_external_file()} * * @uses stored_file::set_missingsource() * @uses stored_file::set_synchronized() * @param stored_file $file * @return bool false when file does not need synchronisation, true if it was synchronised */ public function sync_reference(stored_file $file) { if ($file->get_repository_id() != $this->id) { // This should not really happen because the function can be called from stored_file only. return false; } if ($this->has_moodle_files()) { // References to local files need to be synchronised only once. // Later they will be synchronised automatically when the source is changed. if ($file->get_referencelastsync()) { return false; } $fs = get_file_storage(); $params = file_storage::unpack_reference($file->get_reference(), true); if (!is_array($params) || !($storedfile = $fs->get_file($params['contextid'], $params['component'], $params['filearea'], $params['itemid'], $params['filepath'], $params['filename']))) { $file->set_missingsource(); } else { $file->set_synchronized($storedfile->get_contenthash(), $storedfile->get_filesize()); } return true; } // Backward compatibility (Moodle 2.3-2.5) implementation that calls // methods repository::get_reference_file_lifetime(), repository::sync_individual_file() // and repository::get_file_by_reference(). These methods are removed from the // base repository class but may still be implemented by the child classes. // THIS IS NOT A GOOD EXAMPLE of implementation. For good examples see the overwriting methods. if (!method_exists($this, 'get_file_by_reference')) { // Function get_file_by_reference() is not implemented. No synchronisation. return false; } // Check if the previous sync result is still valid. if (method_exists($this, 'get_reference_file_lifetime')) { $lifetime = $this->get_reference_file_lifetime($file->get_reference()); } else { // Default value that was hardcoded in Moodle 2.3 - 2.5. $lifetime = 60 * 60 * 24; } if (($lastsynced = $file->get_referencelastsync()) && $lastsynced + $lifetime >= time()) { return false; } $cache = cache::make('core', 'repositories'); if (($lastsyncresult = $cache->get('sync:'.$file->get_referencefileid())) !== false) { if ($lastsyncresult === true) { // We are in the process of synchronizing this reference. // Avoid recursion when calling $file->get_filesize() and $file->get_contenthash(). return false; } else { // We have synchronised the same reference inside this request already. // It looks like the object $file was created before the synchronisation and contains old data. if (!empty($lastsyncresult['missing'])) { $file->set_missingsource(); } else { $cache->set('sync:'.$file->get_referencefileid(), true); if ($file->get_contenthash() != $lastsyncresult['contenthash'] || $file->get_filesize() != $lastsyncresult['filesize']) { $file->set_synchronized($lastsyncresult['contenthash'], $lastsyncresult['filesize']); } $cache->set('sync:'.$file->get_referencefileid(), $lastsyncresult); } return true; } } // Weird function sync_individual_file() that was present in API in 2.3 - 2.5, default value was true. if (method_exists($this, 'sync_individual_file') && !$this->sync_individual_file($file)) { return false; } // Set 'true' into the cache to indicate that file is in the process of synchronisation. $cache->set('sync:'.$file->get_referencefileid(), true); // Create object with the structure that repository::get_file_by_reference() expects. $reference = new stdClass(); $reference->id = $file->get_referencefileid(); $reference->reference = $file->get_reference(); $reference->referencehash = sha1($file->get_reference()); $reference->lastsync = $file->get_referencelastsync(); $reference->lifetime = $lifetime; $fileinfo = $this->get_file_by_reference($reference); $contenthash = null; $filesize = null; $fs = get_file_storage(); if (!empty($fileinfo->filesize)) { // filesize returned if (!empty($fileinfo->contenthash) && $fs->content_exists($fileinfo->contenthash)) { // contenthash is specified and valid $contenthash = $fileinfo->contenthash; } else if ($fileinfo->filesize == $file->get_filesize()) { // we don't know the new contenthash but the filesize did not change, // assume the contenthash did not change either $contenthash = $file->get_contenthash(); } else { // we can't save empty contenthash so generate contenthash from empty string list($contenthash, $unused1, $unused2) = $fs->add_string_to_pool(''); } $filesize = $fileinfo->filesize; } else if (!empty($fileinfo->filepath)) { // File path returned list($contenthash, $filesize, $newfile) = $fs->add_file_to_pool($fileinfo->filepath); } else if (!empty($fileinfo->handle) && is_resource($fileinfo->handle)) { // File handle returned $contents = ''; while (!feof($fileinfo->handle)) { $contents .= fread($fileinfo->handle, 8192); } fclose($fileinfo->handle); list($contenthash, $filesize, $newfile) = $fs->add_string_to_pool($contents); } else if (isset($fileinfo->content)) { // File content returned list($contenthash, $filesize, $newfile) = $fs->add_string_to_pool($fileinfo->content); } if (!isset($contenthash) or !isset($filesize)) { $file->set_missingsource(null); $cache->set('sync:'.$file->get_referencefileid(), array('missing' => true)); } else { // update files table $file->set_synchronized($contenthash, $filesize); $cache->set('sync:'.$file->get_referencefileid(), array('contenthash' => $contenthash, 'filesize' => $filesize)); } return true; }
/** * Handle the creation and caching of the temporary tables. * * @return cache_application The temp_tables cachestore to complete operations on. */ protected function get_temp_tables_cache() { // Using connection data to prevent collisions when using the same temp table name with different db connections. $properties = array('dbfamily' => $this->get_dbfamily(), 'settings' => $this->get_settings_hash()); return cache::make('core', 'temp_tables', $properties); }
/** * Add the moodle YUI module metadata for the moodle group to the YUI_config instance. * * If js caching is disabled, metadata will not be served causing YUI to calculate * module dependencies as each module is loaded. * * If metadata does not exist it will be created and stored in a MUC entry. * * @return void */ public function add_moodle_metadata() { global $CFG; if (!isset($this->groups['moodle'])) { throw new coding_exception('The Moodle YUI module does not exist. You must define the moodle module config using YUI_config->add_module_config first.'); } if (!isset($this->groups['moodle']['modules'])) { $this->groups['moodle']['modules'] = array(); } $cache = cache::make('core', 'yuimodules'); if (!isset($CFG->jsrev) || $CFG->jsrev == -1) { $metadata = array(); $cache->delete('metadata'); } else { // Attempt to get the metadata from the cache. if (!($metadata = $cache->get('metadata'))) { $metadata = $this->get_moodle_metadata(); $cache->set('metadata', $metadata); } } // Merge with any metadata added specific to this page which was added manually. $this->groups['moodle']['modules'] = array_merge($this->groups['moodle']['modules'], $metadata); }
/** * Returns list of enabled portfolio plugins * * Portfolio plugin is enabled if there is at least one record in the {portfolio_instance} * table for it. * * @param bool $disablecache do not attempt to obtain data from the cache * @return array array of stdClasses with properties plugin and visible indexed by plugin */ protected static function get_enabled_portfolios($disablecache = false) { global $DB; $cache = cache::make('core', 'plugininfo_portfolio'); $enabled = $cache->get('enabled'); if ($enabled === false or $disablecache) { $enabled = array(); $instances = $DB->get_recordset('portfolio_instance', null, '', 'plugin,visible'); foreach ($instances as $instance) { if (isset($enabled[$instance->plugin])) { if ($instance->visible) { $enabled[$instance->plugin]->visible = $instance->visible; } } else { $enabled[$instance->plugin] = $instance; } } $instances->close(); $cache->set('enabled', $enabled); } return $enabled; }
/** * KSES replacement cleaning function - uses HTML Purifier. * * @param string $text The (X)HTML string to purify * @param array $options Array of options; currently only option supported is 'allowid' (if set, * does not remove id attributes when cleaning) * @return string */ function purify_html($text, $options = array()) { global $CFG; $text = (string) $text; static $purifiers = array(); static $caches = array(); // Purifier code can change only during major version upgrade. $version = empty($CFG->version) ? 0 : $CFG->version; $cachedir = "{$CFG->localcachedir}/htmlpurifier/{$version}"; if (!file_exists($cachedir)) { // Purging of caches may remove the cache dir at any time, // luckily file_exists() results should be cached for all existing directories. $purifiers = array(); $caches = array(); gc_collect_cycles(); make_localcache_directory('htmlpurifier', false); check_dir_exists($cachedir); } $allowid = empty($options['allowid']) ? 0 : 1; $allowobjectembed = empty($CFG->allowobjectembed) ? 0 : 1; $type = 'type_' . $allowid . '_' . $allowobjectembed; if (!array_key_exists($type, $caches)) { $caches[$type] = cache::make('core', 'htmlpurifier', array('type' => $type)); } $cache = $caches[$type]; // Add revision number and all options to the text key so that it is compatible with local cluster node caches. $key = "|{$version}|{$allowobjectembed}|{$allowid}|{$text}"; $filteredtext = $cache->get($key); if ($filteredtext === true) { // The filtering did not change the text last time, no need to filter anything again. return $text; } else { if ($filteredtext !== false) { return $filteredtext; } } if (empty($purifiers[$type])) { require_once $CFG->libdir . '/htmlpurifier/HTMLPurifier.safe-includes.php'; require_once $CFG->libdir . '/htmlpurifier/locallib.php'; $config = HTMLPurifier_Config::createDefault(); $config->set('HTML.DefinitionID', 'moodlehtml'); $config->set('HTML.DefinitionRev', 2); $config->set('Cache.SerializerPath', $cachedir); $config->set('Cache.SerializerPermissions', $CFG->directorypermissions); $config->set('Core.NormalizeNewlines', false); $config->set('Core.ConvertDocumentToFragment', true); $config->set('Core.Encoding', 'UTF-8'); $config->set('HTML.Doctype', 'XHTML 1.0 Transitional'); $config->set('URI.AllowedSchemes', array('http' => true, 'https' => true, 'ftp' => true, 'irc' => true, 'nntp' => true, 'news' => true, 'rtsp' => true, 'rtmp' => true, 'teamspeak' => true, 'gopher' => true, 'mms' => true, 'mailto' => true)); $config->set('Attr.AllowedFrameTargets', array('_blank')); if ($allowobjectembed) { $config->set('HTML.SafeObject', true); $config->set('Output.FlashCompat', true); $config->set('HTML.SafeEmbed', true); } if ($allowid) { $config->set('Attr.EnableID', true); } if ($def = $config->maybeGetRawHTMLDefinition()) { $def->addElement('nolink', 'Block', 'Flow', array()); // Skip our filters inside. $def->addElement('tex', 'Inline', 'Inline', array()); // Tex syntax, equivalent to $$xx$$. $def->addElement('algebra', 'Inline', 'Inline', array()); // Algebra syntax, equivalent to @@xx@@. $def->addElement('lang', 'Block', 'Flow', array(), array('lang' => 'CDATA')); // Original multilang style - only our hacked lang attribute. $def->addAttribute('span', 'xxxlang', 'CDATA'); // Current very problematic multilang. } $purifier = new HTMLPurifier($config); $purifiers[$type] = $purifier; } else { $purifier = $purifiers[$type]; } $multilang = strpos($text, 'class="multilang"') !== false; $filteredtext = $text; if ($multilang) { $filteredtextregex = '/<span(\\s+lang="([a-zA-Z0-9_-]+)"|\\s+class="multilang"){2}\\s*>/'; $filteredtext = preg_replace($filteredtextregex, '<span xxxlang="${2}">', $filteredtext); } $filteredtext = (string) $purifier->purify($filteredtext); if ($multilang) { $filteredtext = preg_replace('/<span xxxlang="([a-zA-Z0-9_-]+)">/', '<span lang="${1}" class="multilang">', $filteredtext); } if ($text === $filteredtext) { // No need to store the filtered text, next time we will just return unfiltered text // because it was not changed by purifying. $cache->set($key, true); } else { $cache->set($key, $filteredtext); } return $filteredtext; }
/** * Invalidate the filters cache. * * @return void */ public function invalidate_filters_cache() { $cache = cache::make('block_xp', 'filters'); $cache->delete('filters_' . $this->manager->get_courseid()); }
$moodle_url = "http://" . $_SERVER['SERVER_NAME'] . ":" . $_SERVER['SERVER_PORT'] . "/moodle"; header('Location: ' . $moodle_url); } // get the OMERO server URL $omero_server = get_config('omero', 'omero_restendpoint'); // get the Image ID $image_id = required_param("id", PARAM_INT); // get the Image LastUpdate $image_last_update = required_param("lastUpdate", PARAM_INT); // get the size of the image thumbnail $image_width = optional_param("width", 128, PARAM_INT); $image_height = optional_param("height", 128, PARAM_INT); // optional param for forcing image reload $force_reload = optional_param("force", false, PARAM_BOOL); // get a reference to the cache $cache = cache::make('repository_omero', 'thumbnail_cache'); // computer the key of the cached element $cache_key = urlencode("{$omero_server}-{$image_id}-{$image_last_update}"); // try to the get thumbnail from the cache $file = $force_reload ? null : $cache->get($cache_key); // download the file is needed and update the cache if ($force_reload || !$file) { $cache->acquire_lock($cache_key); try { $file = $force_reload ? null : $cache->get($cache_key); if (!$file) { $url = "{$omero_server}/ome_seadragon/deepzoom/get/thumbnail/{$image_id}.dzi"; //$url = "${omero_server}/webgateway/render_thumbnail/${image_id}/${image_width}/${image_height}"; $c = new curl(); $file = $c->download_one($url, array("size" => $image_height, "width" => $image_width, "height" => $image_height)); if ($file) {
/** * Checked if a user has attempted/viewed/etc. an activity/resource * * @param array $modules The modules used in the course * @param stdClass $config The blocks configuration settings * @param array $events The possible events that can occur for modules * @param int $userid The user's id * @param int $instance The instance of the block * @return array an describing the user's attempts based on module+instance identifiers */ function block_progress_attempts($modules, $config, $events, $userid, $course) { global $DB; $attempts = array(); $modernlogging = false; $cachingused = false; // Get readers for 2.7 onwards. if (function_exists('get_log_manager')) { $modernlogging = true; $logmanager = get_log_manager(); $readers = $logmanager->get_readers(); $numreaders = count($readers); } // Get cache store if caching is working 2.4 onwards. if (class_exists('cache')) { $cachingused = true; $cachedlogs = cache::make('block_progress', 'cachedlogs'); $cachedlogviews = $cachedlogs->get($userid); if (empty($cachedlogviews)) { $cachedlogviews = array(); } $cachedlogsupdated = false; } foreach ($events as $event) { $module = $modules[$event['type']]; $uniqueid = $event['type'] . $event['id']; $parameters = array('courseid' => $course, 'courseid1' => $course, 'userid' => $userid, 'userid1' => $userid, 'eventid' => $event['id'], 'eventid1' => $event['id'], 'cmid' => $event['cm']->id, 'cmid1' => $event['cm']->id); // Check for passing grades as unattempted, passed or failed. if (isset($config->{'action_' . $uniqueid}) && $config->{'action_' . $uniqueid} == 'passed') { $query = $module['actions'][$config->{'action_' . $uniqueid}]; $graderesult = $DB->get_record_sql($query, $parameters); if ($graderesult === false || $graderesult->finalgrade === null) { $attempts[$uniqueid] = false; } else { $attempts[$uniqueid] = $graderesult->finalgrade >= $graderesult->gradepass ? true : 'failed'; } } else { if (isset($config->{'action_' . $uniqueid}) && $config->{'action_' . $uniqueid} == 'viewed') { $attempts[$uniqueid] = false; // Check if the value is cached. if ($cachingused && array_key_exists($uniqueid, $cachedlogviews) && $cachedlogviews[$uniqueid]) { $attempts[$uniqueid] = true; } else { if ($modernlogging) { foreach ($readers as $logstore => $reader) { if ($reader instanceof \core\log\sql_internal_table_reader || $reader instanceof \core\log\sql_internal_reader) { $logtable = '{' . $reader->get_internal_log_table_name() . '}'; $query = preg_replace('/\\{log\\}/', $logtable, $module['actions']['viewed']['sql_internal_reader']); } else { if ($reader instanceof logstore_legacy\log\store) { $query = $module['actions']['viewed']['logstore_legacy']; } else { // No logs available. continue; } } $attempts[$uniqueid] = $DB->record_exists_sql($query, $parameters) ? true : false; if ($attempts[$uniqueid]) { $cachedlogviews[$uniqueid] = true; $cachedlogsupdated = true; break; } } } else { $query = $module['actions']['viewed']['logstore_legacy']; $attempts[$uniqueid] = $DB->record_exists_sql($query, $parameters) ? true : false; if ($cachingused && $attempts[$uniqueid]) { $cachedlogviews[$uniqueid] = true; $cachedlogsupdated = true; } } } } else { // If activity completion is used, check completions table. if (isset($config->{'action_' . $uniqueid}) && $config->{'action_' . $uniqueid} == 'activity_completion') { $query = 'SELECT id FROM {course_modules_completion} WHERE userid = :userid AND coursemoduleid = :cmid AND completionstate >= 1'; } else { $action = isset($config->{'action_' . $uniqueid}) ? $config->{'action_' . $uniqueid} : $module['defaultAction']; $query = $module['actions'][$action]; } // Check if the user has attempted the module. $attempts[$uniqueid] = $DB->record_exists_sql($query, $parameters) ? true : false; } } } // Update log cache if new values were added. if ($cachingused && $cachedlogsupdated) { $cachedlogs->set($userid, $cachedlogviews); } return $attempts; }
/** * Update a calendar subscription. Also updates the associated cache. * * @param stdClass|array $subscription Subscription record. * @throws coding_exception If something goes wrong * @since Moodle 2.5 */ function calendar_update_subscription($subscription) { global $DB; if (is_array($subscription)) { $subscription = (object) $subscription; } if (empty($subscription->id) || !$DB->record_exists('event_subscriptions', array('id' => $subscription->id))) { throw new coding_exception('Cannot update a subscription without a valid id'); } $DB->update_record('event_subscriptions', $subscription); // Update cache. $cache = cache::make('core', 'calendar_subscriptions'); $cache->set($subscription->id, $subscription); // Trigger event, calendar subscription updated. $eventparams = array('userid' => $subscription->userid, 'objectid' => $subscription->id, 'context' => calendar_get_calendar_context($subscription), 'other' => array('eventtype' => $subscription->eventtype, 'courseid' => $subscription->courseid)); $event = \core\event\calendar_subscription_updated::create($eventparams); $event->trigger(); }
/** * Load list of all present plugins - call before using $this->presentplugins. */ protected function load_present_plugins() { if ($this->presentplugins) { return; } $cache = cache::make('core', 'plugin_manager'); $present = $cache->get('present'); if (is_array($present)) { $this->presentplugins = $present; return; } $this->presentplugins = array(); $plugintypes = core_component::get_plugin_types(); foreach ($plugintypes as $type => $typedir) { $plugs = core_component::get_plugin_list($type); foreach ($plugs as $plug => $fullplug) { $module = new stdClass(); $plugin = new stdClass(); $plugin->version = null; include $fullplug . '/version.php'; // Check if the legacy $module syntax is still used. if (!is_object($module) or count((array) $module) > 0) { debugging('Unsupported $module syntax detected in version.php of the ' . $type . '_' . $plug . ' plugin.'); $skipcache = true; } // Check if the component is properly declared. if (empty($plugin->component) or $plugin->component !== $type . '_' . $plug) { debugging('Plugin ' . $type . '_' . $plug . ' does not declare valid $plugin->component in its version.php.'); $skipcache = true; } $this->presentplugins[$type][$plug] = $plugin; } } if (empty($skipcache)) { $cache->set('present', $this->presentplugins); } }
/** * This function returns a nice list representing category tree * for display or to use in a form <select> element * * List is cached for 10 minutes * * For example, if you have a tree of categories like: * Miscellaneous (id = 1) * Subcategory (id = 2) * Sub-subcategory (id = 4) * Other category (id = 3) * Then after calling this function you will have * array(1 => 'Miscellaneous', * 2 => 'Miscellaneous / Subcategory', * 4 => 'Miscellaneous / Subcategory / Sub-subcategory', * 3 => 'Other category'); * * If you specify $requiredcapability, then only categories where the current * user has that capability will be added to $list. * If you only have $requiredcapability in a child category, not the parent, * then the child catgegory will still be included. * * If you specify the option $excludeid, then that category, and all its children, * are omitted from the tree. This is useful when you are doing something like * moving categories, where you do not want to allow people to move a category * to be the child of itself. * * See also {@link make_categories_options()} * * @param string/array $requiredcapability if given, only categories where the current * user has this capability will be returned. Can also be an array of capabilities, * in which case they are all required. * @param integer $excludeid Exclude this category and its children from the lists built. * @param string $separator string to use as a separator between parent and child category. Default ' / ' * @return array of strings */ public static function make_categories_list($requiredcapability = '', $excludeid = 0, $separator = ' / ') { global $DB; $coursecatcache = cache::make('core', 'coursecat'); // Check if we cached the complete list of user-accessible category names ($baselist) or list of ids // with requried cap ($thislist). $currentlang = current_language(); $basecachekey = $currentlang . '_catlist'; $baselist = $coursecatcache->get($basecachekey); $thislist = false; $thiscachekey = null; if (!empty($requiredcapability)) { $requiredcapability = (array) $requiredcapability; $thiscachekey = 'catlist:' . serialize($requiredcapability); if ($baselist !== false && ($thislist = $coursecatcache->get($thiscachekey)) !== false) { $thislist = preg_split('|,|', $thislist, -1, PREG_SPLIT_NO_EMPTY); } } else { if ($baselist !== false) { $thislist = array_keys($baselist); } } if ($baselist === false) { // We don't have $baselist cached, retrieve it. Retrieve $thislist again in any case. $ctxselect = context_helper::get_preload_record_columns_sql('ctx'); $sql = "SELECT cc.id, cc.sortorder, cc.name, cc.visible, cc.parent, cc.path, {$ctxselect}\n FROM {course_categories} cc\n JOIN {context} ctx ON cc.id = ctx.instanceid AND ctx.contextlevel = :contextcoursecat\n ORDER BY cc.sortorder"; $rs = $DB->get_recordset_sql($sql, array('contextcoursecat' => CONTEXT_COURSECAT)); $baselist = array(); $thislist = array(); foreach ($rs as $record) { // If the category's parent is not visible to the user, it is not visible as well. if (!$record->parent || isset($baselist[$record->parent])) { context_helper::preload_from_record($record); $context = context_coursecat::instance($record->id); if (!$record->visible && !has_capability('moodle/category:viewhiddencategories', $context)) { // No cap to view category, added to neither $baselist nor $thislist. continue; } $baselist[$record->id] = array('name' => format_string($record->name, true, array('context' => $context)), 'path' => $record->path); if (!empty($requiredcapability) && !has_all_capabilities($requiredcapability, $context)) { // No required capability, added to $baselist but not to $thislist. continue; } $thislist[] = $record->id; } } $rs->close(); $coursecatcache->set($basecachekey, $baselist); if (!empty($requiredcapability)) { $coursecatcache->set($thiscachekey, join(',', $thislist)); } } else { if ($thislist === false) { // We have $baselist cached but not $thislist. Simplier query is used to retrieve. $ctxselect = context_helper::get_preload_record_columns_sql('ctx'); $sql = "SELECT ctx.instanceid AS id, {$ctxselect}\n FROM {context} ctx WHERE ctx.contextlevel = :contextcoursecat"; $contexts = $DB->get_records_sql($sql, array('contextcoursecat' => CONTEXT_COURSECAT)); $thislist = array(); foreach (array_keys($baselist) as $id) { context_helper::preload_from_record($contexts[$id]); if (has_all_capabilities($requiredcapability, context_coursecat::instance($id))) { $thislist[] = $id; } } $coursecatcache->set($thiscachekey, join(',', $thislist)); } } // Now build the array of strings to return, mind $separator and $excludeid. $names = array(); foreach ($thislist as $id) { $path = preg_split('|/|', $baselist[$id]['path'], -1, PREG_SPLIT_NO_EMPTY); if (!$excludeid || !in_array($excludeid, $path)) { $namechunks = array(); foreach ($path as $parentid) { $namechunks[] = $baselist[$parentid]['name']; } $names[$id] = join($separator, $namechunks); } } return $names; }
public function test_course_add_cm_to_section() { global $DB; $this->resetAfterTest(true); // Create course with 1 section. $course = $this->getDataGenerator()->create_course(array('shortname' => 'GrowingCourse', 'fullname' => 'Growing Course', 'numsections' => 1), array('createsections' => true)); // Trash modinfo. rebuild_course_cache($course->id, true); // Create some cms for testing. $cmids = array(); for ($i = 0; $i < 4; $i++) { $cmids[$i] = $DB->insert_record('course_modules', array('course' => $course->id)); } // Add it to section that exists. course_add_cm_to_section($course, $cmids[0], 1); // Check it got added to sequence. $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 1)); $this->assertEquals($cmids[0], $sequence); // Add a second, this time using courseid variant of parameters. $coursecacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); course_add_cm_to_section($course->id, $cmids[1], 1); $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 1)); $this->assertEquals($cmids[0] . ',' . $cmids[1], $sequence); // Check that modinfo cache was reset but not rebuilt (important for performance if calling repeatedly). $this->assertGreaterThan($coursecacherev, $DB->get_field('course', 'cacherev', array('id' => $course->id))); $this->assertEmpty(cache::make('core', 'coursemodinfo')->get($course->id)); // Add one to section that doesn't exist (this might rebuild modinfo). course_add_cm_to_section($course, $cmids[2], 2); $this->assertEquals(3, $DB->count_records('course_sections', array('course' => $course->id))); $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 2)); $this->assertEquals($cmids[2], $sequence); // Add using the 'before' option. course_add_cm_to_section($course, $cmids[3], 2, $cmids[2]); $this->assertEquals(3, $DB->count_records('course_sections', array('course' => $course->id))); $sequence = $DB->get_field('course_sections', 'sequence', array('course' => $course->id, 'section' => 2)); $this->assertEquals($cmids[3] . ',' . $cmids[2], $sequence); }
public function test_matching_cacherev() { global $DB, $CFG; $this->resetAfterTest(); $this->setAdminUser(); $cache = cache::make('core', 'coursemodinfo'); // Generate the course and pre-requisite module. $course = $this->getDataGenerator()->create_course(array('format' => 'topics', 'numsections' => 3), array('createsections' => true)); // Make sure the cacherev is set. $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan(0, $cacherev); $prevcacherev = $cacherev; // Reset course cache and make sure cacherev is bumped up but cache is empty. rebuild_course_cache($course->id, true); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $this->assertEmpty($cache->get($course->id)); $prevcacherev = $cacherev; // Build course cache. Cacherev should not change but cache is now not empty. Make sure cacherev is the same everywhere. $modinfo = get_fast_modinfo($course->id); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertEquals($prevcacherev, $cacherev); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Little trick to check that cache is not rebuilt druing the next step - substitute the value in MUC and later check that it is still there. $cache->set($course->id, (object) array_merge((array) $cachedvalue, array('secretfield' => 1))); // Clear static cache and call get_fast_modinfo() again (pretend we are in another request). Cache should not be rebuilt. course_modinfo::clear_instance_cache(); $modinfo = get_fast_modinfo($course->id); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertEquals($prevcacherev, $cacherev); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertNotEmpty($cachedvalue->secretfield); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Rebuild course cache. Cacherev must be incremented everywhere. rebuild_course_cache($course->id); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $modinfo = get_fast_modinfo($course->id); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Update cacherev in DB and make sure the cache will be rebuilt on the next call to get_fast_modinfo(). increment_revision_number('course', 'cacherev', 'id = ?', array($course->id)); // We need to clear static cache for course_modinfo instances too. course_modinfo::clear_instance_cache(); $modinfo = get_fast_modinfo($course->id); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Reset cache for all courses and make sure this course cache is reset. rebuild_course_cache(0, true); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $this->assertEmpty($cache->get($course->id)); // Rebuild again. $modinfo = get_fast_modinfo($course->id); $cachedvalue = $cache->get($course->id); $this->assertNotEmpty($cachedvalue); $this->assertEquals($cacherev, $cachedvalue->cacherev); $this->assertEquals($cacherev, $modinfo->get_course()->cacherev); $prevcacherev = $cacherev; // Purge all caches and make sure cacherev is increased and data from MUC erased. purge_all_caches(); $cacherev = $DB->get_field('course', 'cacherev', array('id' => $course->id)); $this->assertGreaterThan($prevcacherev, $cacherev); $this->assertEmpty($cache->get($course->id)); }
/** * Returns datailed information about columns in table. This information is cached internally. * @param string $table name * @param bool $usecache * @return array array of database_column_info objects indexed with column names */ public function get_columns($table, $usecache = true) { if ($usecache) { $properties = array('dbfamily' => $this->get_dbfamily(), 'settings' => $this->get_settings_hash()); $cache = cache::make('core', 'databasemeta', $properties); if ($data = $cache->get($table)) { return $data; } } $structure = array(); if (!$this->temptables->is_temptable($table)) { // normal table, get metadata from own schema $sql = "SELECT column_name AS name,\n data_type AS type,\n numeric_precision AS max_length,\n character_maximum_length AS char_max_length,\n numeric_scale AS scale,\n is_nullable AS is_nullable,\n columnproperty(object_id(quotename(table_schema) + '.' +\n quotename(table_name)), column_name, 'IsIdentity') AS auto_increment,\n column_default AS default_value\n FROM INFORMATION_SCHEMA.COLUMNS\n WHERE table_name = '{" . $table . "}'\n ORDER BY ordinal_position"; } else { // temp table, get metadata from tempdb schema $sql = "SELECT column_name AS name,\n data_type AS type,\n numeric_precision AS max_length,\n character_maximum_length AS char_max_length,\n numeric_scale AS scale,\n is_nullable AS is_nullable,\n columnproperty(object_id(quotename(table_schema) + '.' +\n quotename(table_name)), column_name, 'IsIdentity') AS auto_increment,\n column_default AS default_value\n FROM tempdb.INFORMATION_SCHEMA.COLUMNS\n JOIN tempdb..sysobjects ON name = table_name\n WHERE id = object_id('tempdb..{" . $table . "}')\n ORDER BY ordinal_position"; } list($sql, $params, $type) = $this->fix_sql_params($sql, null); $this->query_start($sql, null, SQL_QUERY_AUX); $result = mssql_query($sql, $this->mssql); $this->query_end($result); if (!$result) { return array(); } while ($rawcolumn = mssql_fetch_assoc($result)) { $rawcolumn = (object) $rawcolumn; $info = new stdClass(); $info->name = $rawcolumn->name; $info->type = $rawcolumn->type; $info->meta_type = $this->mssqltype2moodletype($info->type); // Prepare auto_increment info $info->auto_increment = $rawcolumn->auto_increment ? true : false; // Define type for auto_increment columns $info->meta_type = $info->auto_increment && $info->meta_type == 'I' ? 'R' : $info->meta_type; // id columns being auto_incremnt are PK by definition $info->primary_key = $info->name == 'id' && $info->meta_type == 'R' && $info->auto_increment; if ($info->meta_type === 'C' and $rawcolumn->char_max_length == -1) { // This is NVARCHAR(MAX), not a normal NVARCHAR. $info->max_length = -1; $info->meta_type = 'X'; } else { // Put correct length for character and LOB types $info->max_length = $info->meta_type == 'C' ? $rawcolumn->char_max_length : $rawcolumn->max_length; $info->max_length = $info->meta_type == 'X' || $info->meta_type == 'B' ? -1 : $info->max_length; } // Scale $info->scale = $rawcolumn->scale; // Prepare not_null info $info->not_null = $rawcolumn->is_nullable == 'NO' ? true : false; // Process defaults $info->has_default = !empty($rawcolumn->default_value); if ($rawcolumn->default_value === NULL) { $info->default_value = NULL; } else { $info->default_value = preg_replace("/^[\\(N]+[']?(.*?)[']?[\\)]+\$/", '\\1', $rawcolumn->default_value); } // Process binary $info->binary = $info->meta_type == 'B' ? true : false; $structure[$info->name] = new database_column_info($info); } $this->free_result($result); if ($usecache) { $cache->set($table, $structure); } return $structure; }
/** * Returns detailed information about columns in table. This information is cached internally. * @param string $table name * @param bool $usecache * @return database_column_info[] array of database_column_info objects indexed with column names */ public function get_columns($table, $usecache = true) { if ($usecache) { $properties = array('dbfamily' => $this->get_dbfamily(), 'settings' => $this->get_settings_hash()); $cache = cache::make('core', 'databasemeta', $properties); if ($data = $cache->get($table)) { return $data; } } $structure = array(); $sql = "SELECT column_name, data_type, character_maximum_length, numeric_precision,\n numeric_scale, is_nullable, column_type, column_default, column_key, extra\n FROM information_schema.columns\n WHERE table_name = '" . $this->prefix . $table . "'\n AND table_schema = '" . $this->dbname . "'\n ORDER BY ordinal_position"; $this->query_start($sql, null, SQL_QUERY_AUX); $result = $this->mysqli->query($sql); $this->query_end(true); // Don't want to throw anything here ever. MDL-30147 if ($result === false) { return array(); } if ($result->num_rows > 0) { // standard table exists while ($rawcolumn = $result->fetch_assoc()) { $info = (object) $this->get_column_info((object) $rawcolumn); $structure[$info->name] = new database_column_info($info); } $result->close(); } else { // temporary tables are not in information schema, let's try it the old way $result->close(); $sql = "SHOW COLUMNS FROM {$this->prefix}{$table}"; $this->query_start($sql, null, SQL_QUERY_AUX); $result = $this->mysqli->query($sql); $this->query_end(true); if ($result === false) { return array(); } while ($rawcolumn = $result->fetch_assoc()) { $rawcolumn = (object) array_change_key_case($rawcolumn, CASE_LOWER); $rawcolumn->column_name = $rawcolumn->field; unset($rawcolumn->field); $rawcolumn->column_type = $rawcolumn->type; unset($rawcolumn->type); $rawcolumn->character_maximum_length = null; $rawcolumn->numeric_precision = null; $rawcolumn->numeric_scale = null; $rawcolumn->is_nullable = $rawcolumn->null; unset($rawcolumn->null); $rawcolumn->column_default = $rawcolumn->default; unset($rawcolumn->default); $rawcolumn->column_key = $rawcolumn->key; unset($rawcolumn->default); if (preg_match('/(enum|varchar)\\((\\d+)\\)/i', $rawcolumn->column_type, $matches)) { $rawcolumn->data_type = $matches[1]; $rawcolumn->character_maximum_length = $matches[2]; } else { if (preg_match('/([a-z]*int[a-z]*)\\((\\d+)\\)/i', $rawcolumn->column_type, $matches)) { $rawcolumn->data_type = $matches[1]; $rawcolumn->numeric_precision = $matches[2]; $rawcolumn->max_length = $rawcolumn->numeric_precision; $type = strtoupper($matches[1]); if ($type === 'BIGINT') { $maxlength = 18; } else { if ($type === 'INT' or $type === 'INTEGER') { $maxlength = 9; } else { if ($type === 'MEDIUMINT') { $maxlength = 6; } else { if ($type === 'SMALLINT') { $maxlength = 4; } else { if ($type === 'TINYINT') { $maxlength = 2; } else { // This should not happen. $maxlength = 0; } } } } } if ($maxlength < $rawcolumn->max_length) { $rawcolumn->max_length = $maxlength; } } else { if (preg_match('/(decimal)\\((\\d+),(\\d+)\\)/i', $rawcolumn->column_type, $matches)) { $rawcolumn->data_type = $matches[1]; $rawcolumn->numeric_precision = $matches[2]; $rawcolumn->numeric_scale = $matches[3]; } else { if (preg_match('/(double|float)(\\((\\d+),(\\d+)\\))?/i', $rawcolumn->column_type, $matches)) { $rawcolumn->data_type = $matches[1]; $rawcolumn->numeric_precision = isset($matches[3]) ? $matches[3] : null; $rawcolumn->numeric_scale = isset($matches[4]) ? $matches[4] : null; } else { if (preg_match('/([a-z]*text)/i', $rawcolumn->column_type, $matches)) { $rawcolumn->data_type = $matches[1]; $rawcolumn->character_maximum_length = -1; // unknown } else { if (preg_match('/([a-z]*blob)/i', $rawcolumn->column_type, $matches)) { $rawcolumn->data_type = $matches[1]; } else { $rawcolumn->data_type = $rawcolumn->column_type; } } } } } } $info = $this->get_column_info($rawcolumn); $structure[$info->name] = new database_column_info($info); } $result->close(); } if ($usecache) { $cache->set($table, $structure); } return $structure; }
if (!empty($data->all)) { $search->delete_index(); } else { $anydelete = false; // We check that the component exist and is enabled. foreach ($searchareas as $areaid => $searcharea) { if (!empty($data->{$areaid})) { $anydelete = true; $search->delete_index($areaid); } } } if (!empty($data->all) || $anydelete) { echo $OUTPUT->notification(get_string('deleted', 'report_search'), 'notifysuccess'); // Purge the cache. $cache = \cache::make('core', 'search_results'); $cache->purge(); } } if (!empty($data->reindex)) { // Force full reindex. Quite heavy operation. $search->index(true); $search->optimize_index(); echo $OUTPUT->notification(get_string('indexed', 'report_search'), 'notifysuccess'); } } // After processing the form as config might change depending on the action. $areasconfig = $search->get_areas_config($searchareas); // Ensure that all search areas that we are going to display have config. $missingareas = array_diff_key($searchareas, $areasconfig); foreach ($missingareas as $searcharea) {
/** * Purges a cache of all information on a given event. * * @param string $event */ public static function purge_by_event($event) { $instance = cache_config::instance(); $invalidationeventset = false; $factory = cache_factory::instance(); foreach ($instance->get_definitions() as $name => $definitionarr) { $definition = cache_definition::load($name, $definitionarr); if ($definition->invalidates_on_event($event)) { // Create the cache. $cache = $factory->create_cache($definition); // Initialise, in case of a store. if ($cache instanceof cache_store) { $cache->initialise($definition); } // Purge the cache. $cache->purge(); // We need to flag the event in the "Event invalidation" cache if it hasn't already happened. if ($invalidationeventset === false) { // Get the event invalidation cache. $cache = cache::make('core', 'eventinvalidation'); // Create a key to invalidate all. $data = array('purged' => cache::now()); // Set that data back to the cache. $cache->set($event, $data); // This only needs to occur once. $invalidationeventset = true; } } } }
/** * Clear a course out completely, deleting all content but don't delete the course itself. * * This function does not verify any permissions. * * Please note this function also deletes all user enrolments, * enrolment instances and role assignments by default. * * $options: * - 'keep_roles_and_enrolments' - false by default * - 'keep_groups_and_groupings' - false by default * * @param int $courseid The id of the course that is being deleted * @param bool $showfeedback Whether to display notifications of each action the function performs. * @param array $options extra options * @return bool true if all the removals succeeded. false if there were any failures. If this * method returns false, some of the removals will probably have succeeded, and others * failed, but you have no way of knowing which. */ function remove_course_contents($courseid, $showfeedback = true, array $options = null) { global $CFG, $DB, $OUTPUT; require_once $CFG->libdir . '/badgeslib.php'; require_once $CFG->libdir . '/completionlib.php'; require_once $CFG->libdir . '/questionlib.php'; require_once $CFG->libdir . '/gradelib.php'; require_once $CFG->dirroot . '/group/lib.php'; require_once $CFG->dirroot . '/tag/coursetagslib.php'; require_once $CFG->dirroot . '/comment/lib.php'; require_once $CFG->dirroot . '/rating/lib.php'; require_once $CFG->dirroot . '/notes/lib.php'; // Handle course badges. badges_handle_course_deletion($courseid); // NOTE: these concatenated strings are suboptimal, but it is just extra info... $strdeleted = get_string('deleted') . ' - '; // Some crazy wishlist of stuff we should skip during purging of course content. $options = (array) $options; $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); $coursecontext = context_course::instance($courseid); $fs = get_file_storage(); // Delete course completion information, this has to be done before grades and enrols. $cc = new completion_info($course); $cc->clear_criteria(); if ($showfeedback) { echo $OUTPUT->notification($strdeleted . get_string('completion', 'completion'), 'notifysuccess'); } // Remove all data from gradebook - this needs to be done before course modules // because while deleting this information, the system may need to reference // the course modules that own the grades. remove_course_grades($courseid, $showfeedback); remove_grade_letters($coursecontext, $showfeedback); // Delete course blocks in any all child contexts, // they may depend on modules so delete them first. $childcontexts = $coursecontext->get_child_contexts(); // Returns all subcontexts since 2.2. foreach ($childcontexts as $childcontext) { blocks_delete_all_for_context($childcontext->id); } unset($childcontexts); blocks_delete_all_for_context($coursecontext->id); if ($showfeedback) { echo $OUTPUT->notification($strdeleted . get_string('type_block_plural', 'plugin'), 'notifysuccess'); } // Delete every instance of every module, // this has to be done before deleting of course level stuff. $locations = core_component::get_plugin_list('mod'); foreach ($locations as $modname => $moddir) { if ($modname === 'NEWMODULE') { continue; } if ($module = $DB->get_record('modules', array('name' => $modname))) { include_once "{$moddir}/lib.php"; // Shows php warning only if plugin defective. $moddelete = $modname . '_delete_instance'; // Delete everything connected to an instance. $moddeletecourse = $modname . '_delete_course'; // Delete other stray stuff (uncommon). if ($instances = $DB->get_records($modname, array('course' => $course->id))) { foreach ($instances as $instance) { if ($cm = get_coursemodule_from_instance($modname, $instance->id, $course->id)) { // Delete activity context questions and question categories. question_delete_activity($cm, $showfeedback); } if (function_exists($moddelete)) { // This purges all module data in related tables, extra user prefs, settings, etc. $moddelete($instance->id); } else { // NOTE: we should not allow installation of modules with missing delete support! debugging("Defective module '{$modname}' detected when deleting course contents: missing function {$moddelete}()!"); $DB->delete_records($modname, array('id' => $instance->id)); } if ($cm) { // Delete cm and its context - orphaned contexts are purged in cron in case of any race condition. context_helper::delete_instance(CONTEXT_MODULE, $cm->id); $DB->delete_records('course_modules', array('id' => $cm->id)); } } } if (function_exists($moddeletecourse)) { // Execute ptional course cleanup callback. $moddeletecourse($course, $showfeedback); } if ($instances and $showfeedback) { echo $OUTPUT->notification($strdeleted . get_string('pluginname', $modname), 'notifysuccess'); } } else { // Ooops, this module is not properly installed, force-delete it in the next block. } } // We have tried to delete everything the nice way - now let's force-delete any remaining module data. // Remove all data from availability and completion tables that is associated // with course-modules belonging to this course. Note this is done even if the // features are not enabled now, in case they were enabled previously. $DB->delete_records_select('course_modules_completion', 'coursemoduleid IN (SELECT id from {course_modules} WHERE course=?)', array($courseid)); // Remove course-module data. $cms = $DB->get_records('course_modules', array('course' => $course->id)); foreach ($cms as $cm) { if ($module = $DB->get_record('modules', array('id' => $cm->module))) { try { $DB->delete_records($module->name, array('id' => $cm->instance)); } catch (Exception $e) { // Ignore weird or missing table problems. } } context_helper::delete_instance(CONTEXT_MODULE, $cm->id); $DB->delete_records('course_modules', array('id' => $cm->id)); } if ($showfeedback) { echo $OUTPUT->notification($strdeleted . get_string('type_mod_plural', 'plugin'), 'notifysuccess'); } // Cleanup the rest of plugins. $cleanuplugintypes = array('report', 'coursereport', 'format'); foreach ($cleanuplugintypes as $type) { $plugins = get_plugin_list_with_function($type, 'delete_course', 'lib.php'); foreach ($plugins as $plugin => $pluginfunction) { $pluginfunction($course->id, $showfeedback); } if ($showfeedback) { echo $OUTPUT->notification($strdeleted . get_string('type_' . $type . '_plural', 'plugin'), 'notifysuccess'); } } // Delete questions and question categories. question_delete_course($course, $showfeedback); if ($showfeedback) { echo $OUTPUT->notification($strdeleted . get_string('questions', 'question'), 'notifysuccess'); } // Make sure there are no subcontexts left - all valid blocks and modules should be already gone. $childcontexts = $coursecontext->get_child_contexts(); // Returns all subcontexts since 2.2. foreach ($childcontexts as $childcontext) { $childcontext->delete(); } unset($childcontexts); // Remove all roles and enrolments by default. if (empty($options['keep_roles_and_enrolments'])) { // This hack is used in restore when deleting contents of existing course. role_unassign_all(array('contextid' => $coursecontext->id, 'component' => ''), true); enrol_course_delete($course); if ($showfeedback) { echo $OUTPUT->notification($strdeleted . get_string('type_enrol_plural', 'plugin'), 'notifysuccess'); } } // Delete any groups, removing members and grouping/course links first. if (empty($options['keep_groups_and_groupings'])) { groups_delete_groupings($course->id, $showfeedback); groups_delete_groups($course->id, $showfeedback); } // Filters be gone! filter_delete_all_for_context($coursecontext->id); // Notes, you shall not pass! note_delete_all($course->id); // Die comments! comment::delete_comments($coursecontext->id); // Ratings are history too. $delopt = new stdclass(); $delopt->contextid = $coursecontext->id; $rm = new rating_manager(); $rm->delete_ratings($delopt); // Delete course tags. coursetag_delete_course_tags($course->id, $showfeedback); // Delete calendar events. $DB->delete_records('event', array('courseid' => $course->id)); $fs->delete_area_files($coursecontext->id, 'calendar'); // Delete all related records in other core tables that may have a courseid // This array stores the tables that need to be cleared, as // table_name => column_name that contains the course id. $tablestoclear = array('backup_courses' => 'courseid', 'user_lastaccess' => 'courseid'); foreach ($tablestoclear as $table => $col) { $DB->delete_records($table, array($col => $course->id)); } // Delete all course backup files. $fs->delete_area_files($coursecontext->id, 'backup'); // Cleanup course record - remove links to deleted stuff. $oldcourse = new stdClass(); $oldcourse->id = $course->id; $oldcourse->summary = ''; $oldcourse->cacherev = 0; $oldcourse->legacyfiles = 0; $oldcourse->enablecompletion = 0; if (!empty($options['keep_groups_and_groupings'])) { $oldcourse->defaultgroupingid = 0; } $DB->update_record('course', $oldcourse); // Delete course sections. $DB->delete_records('course_sections', array('course' => $course->id)); // Delete legacy, section and any other course files. $fs->delete_area_files($coursecontext->id, 'course'); // Files from summary and section. // Delete all remaining stuff linked to context such as files, comments, ratings, etc. if (empty($options['keep_roles_and_enrolments']) and empty($options['keep_groups_and_groupings'])) { // Easy, do not delete the context itself... $coursecontext->delete_content(); } else { // Hack alert!!!! // We can not drop all context stuff because it would bork enrolments and roles, // there might be also files used by enrol plugins... } // Delete legacy files - just in case some files are still left there after conversion to new file api, // also some non-standard unsupported plugins may try to store something there. fulldelete($CFG->dataroot . '/' . $course->id); // Delete from cache to reduce the cache size especially makes sense in case of bulk course deletion. $cachemodinfo = cache::make('core', 'coursemodinfo'); $cachemodinfo->delete($courseid); // Trigger a course content deleted event. $event = \core\event\course_content_deleted::create(array('objectid' => $course->id, 'context' => $coursecontext, 'other' => array('shortname' => $course->shortname, 'fullname' => $course->fullname, 'options' => $options))); $event->add_record_snapshot('course', $course); $event->trigger(); return true; }
public function test_get_config() { global $CFG; $this->resetAfterTest(); // Preparation. set_config('phpunit_test_get_config_1', 'test 1'); set_config('phpunit_test_get_config_2', 'test 2', 'mod_forum'); if (!is_array($CFG->config_php_settings)) { $CFG->config_php_settings = array(); } $CFG->config_php_settings['phpunit_test_get_config_3'] = 'test 3'; if (!is_array($CFG->forced_plugin_settings)) { $CFG->forced_plugin_settings = array(); } if (!array_key_exists('mod_forum', $CFG->forced_plugin_settings)) { $CFG->forced_plugin_settings['mod_forum'] = array(); } $CFG->forced_plugin_settings['mod_forum']['phpunit_test_get_config_4'] = 'test 4'; $CFG->phpunit_test_get_config_5 = 'test 5'; // Testing. $this->assertSame('test 1', get_config('core', 'phpunit_test_get_config_1')); $this->assertSame('test 2', get_config('mod_forum', 'phpunit_test_get_config_2')); $this->assertSame('test 3', get_config('core', 'phpunit_test_get_config_3')); $this->assertSame('test 4', get_config('mod_forum', 'phpunit_test_get_config_4')); $this->assertFalse(get_config('core', 'phpunit_test_get_config_5')); $this->assertFalse(get_config('core', 'phpunit_test_get_config_x')); $this->assertFalse(get_config('mod_forum', 'phpunit_test_get_config_x')); // Test config we know to exist. $this->assertSame($CFG->dataroot, get_config('core', 'dataroot')); $this->assertSame($CFG->phpunit_dataroot, get_config('core', 'phpunit_dataroot')); $this->assertSame($CFG->dataroot, get_config('core', 'phpunit_dataroot')); $this->assertSame(get_config('core', 'dataroot'), get_config('core', 'phpunit_dataroot')); // Test setting a config var that already exists. set_config('phpunit_test_get_config_1', 'test a'); $this->assertSame('test a', $CFG->phpunit_test_get_config_1); $this->assertSame('test a', get_config('core', 'phpunit_test_get_config_1')); // Test cache invalidation. $cache = cache::make('core', 'config'); $this->assertInternalType('array', $cache->get('core')); $this->assertInternalType('array', $cache->get('mod_forum')); set_config('phpunit_test_get_config_1', 'test b'); $this->assertFalse($cache->get('core')); set_config('phpunit_test_get_config_4', 'test c', 'mod_forum'); $this->assertFalse($cache->get('mod_forum')); }
/** * Get a list of all the plugins that define a certain API function in a certain file. * * @param string $function the part of the name of the function after the * frankenstyle prefix. e.g 'hook' if you are looking for functions with * names like report_courselist_hook. * @param string $file the name of file within the plugin that defines the * function. Defaults to lib.php. * @param bool $include Whether to include the files that contain the functions or not. * @return array with [plugintype][plugin] = functionname */ function get_plugins_with_function($function, $file = 'lib.php', $include = true) { global $CFG; $cache = \cache::make('core', 'plugin_functions'); // Including both although I doubt that we will find two functions definitions with the same name. // Clearning the filename as cache_helper::hash_key only allows a-zA-Z0-9_. $key = $function . '_' . clean_param($file, PARAM_ALPHA); if ($pluginfunctions = $cache->get($key)) { // Checking that the files are still available. foreach ($pluginfunctions as $plugintype => $plugins) { $allplugins = \core_component::get_plugin_list($plugintype); foreach ($plugins as $plugin => $fullpath) { // Cache might be out of sync with the codebase, skip the plugin if it is not available. if (empty($allplugins[$plugin])) { unset($pluginfunctions[$plugintype][$plugin]); continue; } $fileexists = file_exists($allplugins[$plugin] . DIRECTORY_SEPARATOR . $file); if ($include && $fileexists) { // Include the files if it was requested. include_once $allplugins[$plugin] . DIRECTORY_SEPARATOR . $file; } else { if (!$fileexists) { // If the file is not available any more it should not be returned. unset($pluginfunctions[$plugintype][$plugin]); } } } } return $pluginfunctions; } $pluginfunctions = array(); // To fill the cached. Also, everything should continue working with cache disabled. $plugintypes = \core_component::get_plugin_types(); foreach ($plugintypes as $plugintype => $unused) { // We need to include files here. $pluginswithfile = \core_component::get_plugin_list_with_file($plugintype, $file, true); foreach ($pluginswithfile as $plugin => $notused) { $fullfunction = $plugintype . '_' . $plugin . '_' . $function; $pluginfunction = false; if (function_exists($fullfunction)) { // Function exists with standard name. Store, indexed by frankenstyle name of plugin. $pluginfunction = $fullfunction; } else { if ($plugintype === 'mod') { // For modules, we also allow plugin without full frankenstyle but just starting with the module name. $shortfunction = $plugin . '_' . $function; if (function_exists($shortfunction)) { $pluginfunction = $shortfunction; } } } if ($pluginfunction) { if (empty($pluginfunctions[$plugintype])) { $pluginfunctions[$plugintype] = array(); } $pluginfunctions[$plugintype][$plugin] = $pluginfunction; } } } $cache->set($key, $pluginfunctions); return $pluginfunctions; }
/** * Gets group data for a course. * * This returns an object with the following properties: * - groups : An array of all the groups in the course. * - groupings : An array of all the groupings within the course. * - mappings : An array of group to grouping mappings. * * @param int $courseid The course id to get data for. * @param cache $cache The cache if it has already been initialised. If not a new one will be created. * @return stdClass */ function groups_get_course_data($courseid, cache $cache = null) { if ($cache === null) { // Initialise a cache if we wern't given one. $cache = cache::make('core', 'groupdata'); } // Try to retrieve it from the cache. $data = $cache->get($courseid); if ($data === false) { $data = groups_cache_groupdata($courseid, $cache); } return $data; }
/** * Called when a message provider wants to send a message. * This functions checks the message recipient's message processor configuration then * sends the message to the configured processors * * Required parameters of the $eventdata object: * component string component name. must exist in message_providers * name string message type name. must exist in message_providers * userfrom object|int the user sending the message * userto object|int the message recipient * subject string the message subject * fullmessage string the full message in a given format * fullmessageformat int the format if the full message (FORMAT_MOODLE, FORMAT_HTML, ..) * fullmessagehtml string the full version (the message processor will choose with one to use) * smallmessage string the small version of the message * * Optional parameters of the $eventdata object: * notification bool should the message be considered as a notification rather than a personal message * contexturl string if this is a notification then you can specify a url to view the event. For example the forum post the user is being notified of. * contexturlname string the display text for contexturl * * Note: processor failure is is not reported as false return value, * earlier versions did not do it consistently either. * * @todo MDL-55449 Drop support for stdClass in Moodle 3.6 * @category message * @param \core\message\message $eventdata information about the message (component, userfrom, userto, ...) * @return mixed the integer ID of the new message or false if there was a problem with submitted data */ function message_send($eventdata) { global $CFG, $DB; // TODO MDL-55449 Drop support for stdClass in Moodle 3.6. if ($eventdata instanceof \stdClass) { if (!isset($eventdata->courseid)) { $eventdata->courseid = null; } debugging('eventdata as \stdClass is deprecated. Please use core\message\message instead.', DEBUG_DEVELOPER); } //new message ID to return $messageid = false; // Fetch default (site) preferences $defaultpreferences = get_message_output_default_preferences(); $preferencebase = $eventdata->component.'_'.$eventdata->name; // If message provider is disabled then don't do any processing. if (!empty($defaultpreferences->{$preferencebase.'_disable'})) { return $messageid; } // By default a message is a notification. Only personal/private messages aren't notifications. if (!isset($eventdata->notification)) { $eventdata->notification = 1; } if (!is_object($eventdata->userto)) { $eventdata->userto = core_user::get_user($eventdata->userto); } if (!is_object($eventdata->userfrom)) { $eventdata->userfrom = core_user::get_user($eventdata->userfrom); } if (!$eventdata->userto) { debugging('Attempt to send msg to unknown user', DEBUG_NORMAL); return false; } if (!$eventdata->userfrom) { debugging('Attempt to send msg from unknown user', DEBUG_NORMAL); return false; } // Verify all necessary data fields are present. if (!isset($eventdata->userto->auth) or !isset($eventdata->userto->suspended) or !isset($eventdata->userto->deleted) or !isset($eventdata->userto->emailstop)) { debugging('Necessary properties missing in userto object, fetching full record', DEBUG_DEVELOPER); $eventdata->userto = core_user::get_user($eventdata->userto->id); } $usertoisrealuser = (core_user::is_real_user($eventdata->userto->id) != false); // If recipient is internal user (noreply user), and emailstop is set then don't send any msg. if (!$usertoisrealuser && !empty($eventdata->userto->emailstop)) { debugging('Attempt to send msg to internal (noreply) user', DEBUG_NORMAL); return false; } //after how long inactive should the user be considered logged off? if (isset($CFG->block_online_users_timetosee)) { $timetoshowusers = $CFG->block_online_users_timetosee * 60; } else { $timetoshowusers = 300;//5 minutes } // Work out if the user is logged in or not if (!empty($eventdata->userto->lastaccess) && (time()-$timetoshowusers) < $eventdata->userto->lastaccess) { $userstate = 'loggedin'; } else { $userstate = 'loggedoff'; } // Create the message object $savemessage = new stdClass(); $savemessage->courseid = $eventdata->courseid; $savemessage->useridfrom = $eventdata->userfrom->id; $savemessage->useridto = $eventdata->userto->id; $savemessage->subject = $eventdata->subject; $savemessage->fullmessage = $eventdata->fullmessage; $savemessage->fullmessageformat = $eventdata->fullmessageformat; $savemessage->fullmessagehtml = $eventdata->fullmessagehtml; $savemessage->smallmessage = $eventdata->smallmessage; $savemessage->notification = $eventdata->notification; $savemessage->eventtype = $eventdata->name; $savemessage->component = $eventdata->component; if (!empty($eventdata->contexturl)) { $savemessage->contexturl = (string)$eventdata->contexturl; } else { $savemessage->contexturl = null; } if (!empty($eventdata->contexturlname)) { $savemessage->contexturlname = (string)$eventdata->contexturlname; } else { $savemessage->contexturlname = null; } $savemessage->timecreated = time(); if (PHPUNIT_TEST and class_exists('phpunit_util')) { // Add some more tests to make sure the normal code can actually work. $componentdir = core_component::get_component_directory($eventdata->component); if (!$componentdir or !is_dir($componentdir)) { throw new coding_exception('Invalid component specified in message-send(): '.$eventdata->component); } if (!file_exists("$componentdir/db/messages.php")) { throw new coding_exception("$eventdata->component does not contain db/messages.php necessary for message_send()"); } $messageproviders = null; include("$componentdir/db/messages.php"); if (!isset($messageproviders[$eventdata->name])) { throw new coding_exception("Missing messaging defaults for event '$eventdata->name' in '$eventdata->component' messages.php file"); } unset($componentdir); unset($messageproviders); // Now ask phpunit if it wants to catch this message. if (phpunit_util::is_redirecting_messages()) { $savemessage->timeread = time(); $messageid = $DB->insert_record('message_read', $savemessage); $message = $DB->get_record('message_read', array('id'=>$messageid)); phpunit_util::message_sent($message); return $messageid; } } // Fetch enabled processors $processors = get_message_processors(true); // Preset variables $processorlist = array(); // Fill in the array of processors to be used based on default and user preferences foreach ($processors as $processor) { // Skip adding processors for internal user, if processor doesn't support sending message to internal user. if (!$usertoisrealuser && !$processor->object->can_send_to_any_users()) { continue; } // First find out permissions $defaultpreference = $processor->name.'_provider_'.$preferencebase.'_permitted'; if (isset($defaultpreferences->{$defaultpreference})) { $permitted = $defaultpreferences->{$defaultpreference}; } else { // MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't // exist in the message_provider table (thus there is no default settings for them). $preferrormsg = "Could not load preference $defaultpreference. Make sure the component and name you supplied to message_send() are valid."; throw new coding_exception($preferrormsg); } // Find out if user has configured this output // Some processors cannot function without settings from the user $userisconfigured = $processor->object->is_user_configured($eventdata->userto); // DEBUG: notify if we are forcing unconfigured output if ($permitted == 'forced' && !$userisconfigured) { debugging('Attempt to force message delivery to user who has "'.$processor->name.'" output unconfigured', DEBUG_NORMAL); } // Populate the list of processors we will be using if ($permitted == 'forced' && $userisconfigured) { // An admin is forcing users to use this message processor. Use this processor unconditionally. $processorlist[] = $processor->name; } else if ($permitted == 'permitted' && $userisconfigured && !$eventdata->userto->emailstop) { // User has not disabled notifications // See if user set any notification preferences, otherwise use site default ones $userpreferencename = 'message_provider_'.$preferencebase.'_'.$userstate; if ($userpreference = get_user_preferences($userpreferencename, null, $eventdata->userto)) { if (in_array($processor->name, explode(',', $userpreference))) { $processorlist[] = $processor->name; } } else if (isset($defaultpreferences->{$userpreferencename})) { if (in_array($processor->name, explode(',', $defaultpreferences->{$userpreferencename}))) { $processorlist[] = $processor->name; } } } } // Only cache messages, not notifications. if (empty($savemessage->notification)) { // Cache the timecreated value of the last message between these two users. $cache = cache::make('core', 'message_time_last_message_between_users'); $key = \core_message\helper::get_last_message_time_created_cache_key($savemessage->useridfrom, $savemessage->useridto); $cache->set($key, $savemessage->timecreated); } // Store unread message just in case we get a fatal error any time later. $savemessage->id = $DB->insert_record('message', $savemessage); $eventdata->savedmessageid = $savemessage->id; // Let the manager do the sending or buffering when db transaction in progress. return \core\message\manager::send_message($eventdata, $savemessage, $processorlist); }
/** * Returns a cache instance to use for the expand course cache. * @return cache_session */ protected function get_expand_course_cache() { if ($this->cacheexpandcourse === null) { $this->cacheexpandcourse = cache::make('core', 'navigation_expandcourse'); } return $this->cacheexpandcourse; }
/** * Update a calendar subscription. Also updates the associated cache. * * @param stdClass|array $subscription Subscription record. * @throws coding_exception If something goes wrong * @since Moodle 2.5 */ function calendar_update_subscription($subscription) { global $DB; if (is_array($subscription)) { $subscription = (object) $subscription; } if (empty($subscription->id) || !$DB->record_exists('event_subscriptions', array('id' => $subscription->id))) { throw new coding_exception('Cannot update a subscription without a valid id'); } $DB->update_record('event_subscriptions', $subscription); // Update cache. $cache = cache::make('core', 'calendar_subscriptions'); $cache->set($subscription->id, $subscription); }