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;
}
Example #5
0
 /**
  * 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;
     }
 }
Example #6
0
 /**
  * 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());
     }
 }
Example #7
0
    /**
     * 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;
    }
Example #8
0
 /**
  * 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);
 }
Example #10
0
 /**
  * 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;
 }
Example #11
0
/**
 * 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;
}
Example #15
0
/**
 * 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();
}
Example #16
0
 /**
  * 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;
 }
Example #18
0
 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;
 }
Example #22
0
        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) {
Example #23
0
 /**
  * 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;
             }
         }
     }
 }
Example #24
0
/**
 * 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'));
 }
Example #26
0
/**
 * 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;
}
Example #27
0
/**
 * 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;
}
Example #28
0
/**
 * 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);
}
Example #29
0
 /**
  * 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;
 }
Example #30
0
/**
 * 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);
}