Merge branch 'master' into install_master

This commit is contained in:
AMOS bot
2013-08-31 00:43:52 +00:00
351 changed files with 15664 additions and 3795 deletions
+35 -33
View File
@@ -59,6 +59,10 @@ if (empty($_GET['cache']) and empty($_POST['cache']) and empty($_GET['sesskey'])
if (function_exists('opcache_reset')) {
opcache_reset();
}
$cache = 0;
} else {
$cache = 1;
}
require('../config.php');
@@ -74,22 +78,28 @@ $showallplugins = optional_param('showallplugins', 0, PARAM_BOOL);
$agreelicense = optional_param('agreelicense', 0, PARAM_BOOL);
$fetchupdates = optional_param('fetchupdates', 0, PARAM_BOOL);
$newaddonreq = optional_param('installaddonrequest', null, PARAM_RAW);
$cache = optional_param('cache', 0, PARAM_BOOL);
// Set up PAGE.
$url = new moodle_url('/admin/index.php');
if (!is_null($newaddonreq)) {
// We need to set the eventual add-on installation request in the $PAGE's URL
// so that it is stored in $SESSION->wantsurl and the admin is redirected
// correctly once they are logged-in.
$url->param('installaddonrequest', $newaddonreq);
}
if ($cache) {
$url->param('cache', $cache);
$url->param('cache', 1);
}
$PAGE->set_url($url);
unset($url);
// Are we returning from an add-on installation request at moodle.org/plugins?
if ($newaddonreq and !$cache and empty($CFG->disableonclickaddoninstall)) {
$target = new moodle_url('/admin/tool/installaddon/index.php', array(
'installaddonrequest' => $newaddonreq,
'confirm' => 0));
if (!isloggedin() or isguestuser()) {
// Login and go the the add-on tool page.
$SESSION->wantsurl = $target->out();
redirect(get_login_url());
}
redirect($target);
}
$PAGE->set_pagelayout('admin'); // Set a default pagelayout
$documentationlink = '<a href="http://docs.moodle.org/en/Installation">Installation docs</a>';
@@ -153,6 +163,7 @@ if (!core_tables_exist()) {
$PAGE->set_heading($strinstallation);
$PAGE->set_cacheable(false);
/** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
echo $output->install_licence_page();
die();
@@ -167,6 +178,7 @@ if (!core_tables_exist()) {
$PAGE->set_heading($strinstallation . ' - Moodle ' . $CFG->target_release);
$PAGE->set_cacheable(false);
/** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
echo $output->install_environment_page($maturity, $envstatus, $environment_results, $release);
die();
@@ -221,14 +233,12 @@ if (empty($CFG->version)) {
}
// Detect config cache inconsistency, this happens when you switch branches on dev servers.
if ($cache) {
if ($CFG->version != $DB->get_field('config', 'value', array('name'=>'version'))) {
purge_all_caches();
redirect(new moodle_url('/admin/index.php'), 'Config cache inconsistency detected, resetting caches...');
}
if ($CFG->version != $DB->get_field('config', 'value', array('name'=>'version'))) {
purge_all_caches();
redirect(new moodle_url('/admin/index.php'), 'Config cache inconsistency detected, resetting caches...');
}
if ($version > $CFG->version) { // upgrade
if (!$cache and $version > $CFG->version) { // upgrade
// We purge all of MUC's caches here.
// Caches are disabled for upgrade by CACHE_DISABLE_ALL so we must set the first arg to true.
// This ensures a real config object is loaded and the stores will be purged.
@@ -245,6 +255,7 @@ if ($version > $CFG->version) { // upgrade
$PAGE->set_title($stradministration);
$PAGE->set_cacheable(false);
/** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
echo $output->upgrade_stale_php_files_page();
die();
@@ -260,6 +271,7 @@ if ($version > $CFG->version) { // upgrade
$PAGE->set_heading($strdatabasechecking);
$PAGE->set_cacheable(false);
/** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
echo $output->upgrade_confirm_page($a->newversion, $maturity);
die();
@@ -274,6 +286,7 @@ if ($version > $CFG->version) { // upgrade
$PAGE->set_heading($strcurrentrelease);
$PAGE->set_cacheable(false);
/** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
echo $output->upgrade_environment_page($release, $envstatus, $environment_results);
die();
@@ -288,10 +301,12 @@ if ($version > $CFG->version) { // upgrade
$reloadurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1, 'confirmrelease' => 1));
/** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
// check plugin dependencies first
$failed = array();
if (!plugin_manager::instance()->all_plugins_ok($version, $failed)) {
$output = $PAGE->get_renderer('core', 'admin');
echo $output->unsatisfied_dependencies_page($version, $failed, $reloadurl);
die();
}
@@ -305,8 +320,6 @@ if ($version > $CFG->version) { // upgrade
redirect($reloadurl);
}
$output = $PAGE->get_renderer('core', 'admin');
$deployer = available_update_deployer::instance();
if ($deployer->enabled()) {
$deployer->initialize($reloadurl, $reloadurl);
@@ -333,15 +346,15 @@ if ($version > $CFG->version) { // upgrade
}
// Updated human-readable release version if necessary
if ($release <> $CFG->release) { // Update the release version
if (!$cache and $release <> $CFG->release) { // Update the release version
set_config('release', $release);
}
if ($branch <> $CFG->branch) { // Update the branch
if (!$cache and $branch <> $CFG->branch) { // Update the branch
set_config('branch', $branch);
}
if (moodle_needs_upgrading()) {
if (!$cache and moodle_needs_upgrading()) {
if (!$PAGE->headerprinted) {
// means core upgrade or installation was not already done
if (!$confirmplugins) {
@@ -450,8 +463,8 @@ if (during_initial_install()) {
// Now we can be sure everything was upgraded and caches work fine,
// redirect if necessary to make sure caching is enabled.
if (!$cache and !optional_param('sesskey', '', PARAM_RAW)) {
redirect(new moodle_url($PAGE->url, array('cache' => 1)));
if (!$cache) {
redirect(new moodle_url('/admin/index.php', array('cache' => 1)));
}
// Check for valid admin user - no guest autologin
@@ -473,17 +486,6 @@ if (!empty($id) and $id == $CFG->siteidentifier) {
set_config('registered', time());
}
// Check if we are returning from an add-on installation request at moodle.org/plugins
if (!is_null($newaddonreq)) {
if (!empty($CFG->disableonclickaddoninstall)) {
// The feature is disabled in config.php, ignore the request.
} else {
redirect(new moodle_url('/admin/tool/installaddon/index.php', array(
'installaddonrequest' => $newaddonreq,
'confirm' => 0)));
}
}
// setup critical warnings before printing admin tree block
$insecuredataroot = is_dataroot_insecure(true);
$SESSION->admin_critical_warning = ($insecuredataroot==INSECURE_DATAROOT_ERROR);
+35 -5
View File
@@ -38,9 +38,6 @@ require_once($CFG->libdir . '/adminlib.php');
require_once($CFG->libdir . '/pluginlib.php');
require_once($CFG->libdir . '/filelib.php');
admin_externalpage_setup('pluginsoverview');
require_capability('moodle/site:config', context_system::instance());
$fetchremote = optional_param('fetchremote', false, PARAM_BOOL);
$updatesonly = optional_param('updatesonly', false, PARAM_BOOL);
$contribonly = optional_param('contribonly', false, PARAM_BOOL);
@@ -48,12 +45,30 @@ $uninstall = optional_param('uninstall', '', PARAM_COMPONENT);
$delete = optional_param('delete', '', PARAM_COMPONENT);
$confirmed = optional_param('confirm', false, PARAM_BOOL);
$output = $PAGE->get_renderer('core', 'admin');
// NOTE: do not use admin_externalpage_setup() here because it loads
// full admin tree which is not possible during uninstallation.
require_login();
$syscontext = context_system::instance();
require_capability('moodle/site:config', $syscontext);
$pluginman = plugin_manager::instance();
if ($uninstall) {
require_sesskey();
if (!$confirmed) {
admin_externalpage_setup('pluginsoverview');
} else {
$PAGE->set_url('/admin/plugins.php');
$PAGE->set_context($syscontext);
$PAGE->set_pagelayout('maintenance');
$PAGE->set_popup_notification_allowed(false);
}
/** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
$pluginfo = $pluginman->get_plugin_info($uninstall);
// Make sure we know the plugin.
@@ -104,6 +119,15 @@ if ($uninstall) {
if ($delete and $confirmed) {
require_sesskey();
$PAGE->set_url('/admin/plugins.php');
$PAGE->set_context($syscontext);
$PAGE->set_pagelayout('maintenance');
$PAGE->set_popup_notification_allowed(false);
/** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
$pluginfo = $pluginman->get_plugin_info($delete);
// Make sure we know the plugin.
@@ -143,9 +167,15 @@ if ($delete and $confirmed) {
if (function_exists('opcache_reset')) {
opcache_reset();
}
redirect($PAGE->url);
// We need to execute upgrade to make sure everything including caches is up to date.
redirect(new moodle_url('/admin/index.php'));
}
admin_externalpage_setup('pluginsoverview');
/** @var core_admin_renderer $output */
$output = $PAGE->get_renderer('core', 'admin');
$checker = available_update_checker::instance();
// Filtering options.
+13 -7
View File
@@ -140,12 +140,13 @@ class core_admin_renderer extends plugin_renderer_base {
public function upgrade_confirm_page($strnewversion, $maturity) {
$output = '';
$continueurl = new moodle_url('index.php', array('confirmupgrade' => 1));
$cancelurl = new moodle_url('index.php');
$continueurl = new moodle_url('/admin/index.php', array('confirmupgrade' => 1));
$continue = new single_button($continueurl, get_string('continue'), 'get');
$cancelurl = new moodle_url('/admin/index.php');
$output .= $this->header();
$output .= $this->maturity_warning($maturity);
$output .= $this->confirm(get_string('upgradesure', 'admin', $strnewversion), $continueurl, $cancelurl);
$output .= $this->confirm(get_string('upgradesure', 'admin', $strnewversion), $continue, $cancelurl);
$output .= $this->footer();
return $output;
@@ -412,6 +413,10 @@ class core_admin_renderer extends plugin_renderer_base {
$pluginname = $pluginman->plugin_name($pluginfo->component);
// Do not show navigation here, they must click one of the buttons.
$this->page->set_pagelayout('maintenance');
$this->page->set_cacheable(false);
$output .= $this->output->header();
$output .= $this->output->heading(get_string('uninstalling', 'core_plugin', array('name' => $pluginname)));
@@ -425,7 +430,8 @@ class core_admin_renderer extends plugin_renderer_base {
'uninstalldeleteconfirmexternal');
}
$output .= $this->output->confirm($confirm, $continueurl, $this->page->url);
// After any uninstall we must execute full upgrade to finish the cleanup!
$output .= $this->output->confirm($confirm, $continueurl, new moodle_url('/admin/index.php'));
$output .= $this->output->footer();
return $output;
@@ -442,7 +448,7 @@ class core_admin_renderer extends plugin_renderer_base {
public function plugin_uninstall_results_page(plugin_manager $pluginman, plugininfo_base $pluginfo, progress_trace_buffer $progress) {
$output = '';
$pluginname = $pluginman->plugin_name($pluginfo->component);
$pluginname = $pluginfo->component;
$output .= $this->output->header();
$output .= $this->output->heading(get_string('uninstalling', 'core_plugin', array('name' => $pluginname)));
@@ -451,7 +457,7 @@ class core_admin_renderer extends plugin_renderer_base {
$output .= $this->output->box(get_string('uninstalldelete', 'core_plugin',
array('name' => $pluginname, 'rootdir' => $pluginfo->rootdir)), 'generalbox uninstalldelete');
$output .= $this->output->continue_button($this->page->url);
$output .= $this->output->continue_button(new moodle_url('/admin/index.php'));
$output .= $this->output->footer();
return $output;
@@ -708,7 +714,7 @@ class core_admin_renderer extends plugin_renderer_base {
if (!$registered) {
$registerbutton = $this->single_button(new moodle_url('registration/register.php',
$registerbutton = $this->single_button(new moodle_url('/admin/registration/register.php',
array('huburl' => HUB_MOODLEORGHUBURL, 'hubname' => 'Moodle.org')),
get_string('register', 'admin'));
+1 -2
View File
@@ -1646,8 +1646,7 @@ class auth_plugin_ldap extends auth_plugin_base {
// Now start the whole NTLM machinery.
if($this->config->ntlmsso_ie_fastpath == AUTH_NTLM_FASTPATH_YESATTEMPT ||
$this->config->ntlmsso_ie_fastpath == AUTH_NTLM_FASTPATH_YESFORM) {
if(check_browser_version('MSIE')) {
if (core_useragent::check_ie_version()) {
$sesskey = sesskey();
redirect($CFG->wwwroot.'/auth/ldap/ntlmsso_magic.php?sesskey='.$sesskey);
} else if ($this->config->ntlmsso_ie_fastpath == AUTH_NTLM_FASTPATH_YESFORM) {
+1 -1
View File
@@ -28,7 +28,7 @@ $file = $CFG->dirroot.'/pix/spacer.gif';
if ($authplugin->ntlmsso_magic($sesskey) && file_exists($file)) {
if (!empty($authplugin->config->ntlmsso_ie_fastpath)) {
if (check_browser_version('MSIE')) {
if (core_useragent::check_ie_version()) {
// $PAGE->https_required() up above takes care of what $CFG->httpswwwroot should be.
redirect($CFG->httpswwwroot.'/auth/ldap/ntlmsso_finish.php');
}
+7 -1
View File
@@ -1421,6 +1421,12 @@ class restore_course_structure_step extends restore_structure_step {
$data->theme = '';
}
// Check if this is an old SCORM course format.
if ($data->format == 'scorm') {
$data->format = 'singleactivity';
$data->activitytype = 'scorm';
}
// Course record ready, update it
$DB->update_record('course', $data);
@@ -3624,7 +3630,7 @@ class restore_process_file_aliases_queue extends restore_execution_step {
// Iterate over aliases in the queue.
foreach ($rs as $record) {
$info = restore_dbops::decode_backup_temp_info($record->info);
$info = backup_controller_dbops::decode_backup_temp_info($record->info);
// Try to pick a repository instance that should serve the alias.
$repository = $this->choose_repository($info);
@@ -59,7 +59,7 @@ class database_logger extends base_logger {
if ($this->levelcol) {
$columns[$this->levelcol] = $level;
}
$columns[$this->messagecol] = $message; //TODO: should this be cleaned?
$columns[$this->messagecol] = clean_param($message, PARAM_NOTAGS);
return $this->insert_log_record($this->logtable, $columns);
}
@@ -69,6 +69,6 @@ class database_logger extends base_logger {
// to preserve DB logs if the whole backup/restore transaction is
// rollback
global $DB;
return $DB->insert_record($this->logtable, $columns, false); // Don't return inserted id
return $DB->insert_record($table, $columns, false); // Don't return inserted id
}
}
@@ -69,7 +69,7 @@ class backup_base_optigroup_testcase extends basic_testcase {
/**
* Incorrect creation tests (attributes and final elements)
*/
function itest_wrong_creation() {
function test_wrong_creation() {
// Create instance with invalid name
try {
+1 -1
View File
@@ -140,7 +140,7 @@ class core_backup_renderer extends plugin_renderer_base {
}
if (empty($table)) {
$table = new html_table();
$table->head = array('Module', 'Title', 'Userinfo');
$table->head = array(get_string('module','backup'), get_string('title','backup'), get_string('userinfo','backup'));
$table->colclasses = array('modulename', 'moduletitle', 'userinfoincluded');
$table->align = array('left','left', 'center');
$table->attributes = array('class'=>'activitytable generaltable');
@@ -100,13 +100,6 @@ Feature: Restore Moodle 2 course backups
And I follow "Edit settings"
And I expand all fieldsets
And the "id_format" field should match "Social format" value
And I fill the moodle form with:
| id_format | SCORM format |
And I press "Save changes"
And I should see "Adding a new SCORM package"
And I follow "Edit settings"
And I expand all fieldsets
And the "id_format" field should match "SCORM format" value
And I press "Cancel"
@javascript
+2 -1
View File
@@ -595,8 +595,9 @@ class blog_listing {
$userid = $USER->id;
}
$allnamefields = get_all_user_name_fields(true, 'u');
// The query used to locate blog entries is complicated. It will be built from the following components:
$requiredfields = "p.*, u.firstname, u.lastname, u.email"; // the SELECT clause
$requiredfields = "p.*, $allnamefields, u.email"; // the SELECT clause
$tables = array('p' => 'post', 'u' => 'user'); // components of the FROM clause (table_id => table_name)
$conditions = array('u.deleted = 0', 'p.userid = u.id', '(p.module = \'blog\' OR p.module = \'blog_external\')'); // components of the WHERE clause (conjunction)
+189 -144
View File
@@ -271,7 +271,7 @@ class cache implements cache_loader {
* In advanced cases an array may be useful such as in situations requiring the multi-key functionality.
* @param int $strictness One of IGNORE_MISSING | MUST_EXIST
* @return mixed|false The data from the cache or false if the key did not exist within the cache.
* @throws moodle_exception
* @throws coding_exception
*/
public function get($key, $strictness = IGNORE_MISSING) {
// 1. Parse the key.
@@ -329,7 +329,7 @@ class cache implements cache_loader {
}
// 5. Validate strictness.
if ($strictness === MUST_EXIST && $result === false) {
throw new moodle_exception('Requested key did not exist in any cache stores and could not be loaded.');
throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
}
// 6. Set it to the store if we got it from the loader/datasource.
if ($setaftervalidation) {
@@ -363,7 +363,7 @@ class cache implements cache_loader {
* @return array An array of key value pairs for the items that could be retrieved from the cache.
* If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
* Otherwise any key that did not exist will have a data value of false within the results.
* @throws moodle_exception
* @throws coding_exception
*/
public function get_many(array $keys, $strictness = IGNORE_MISSING) {
@@ -420,7 +420,7 @@ class cache implements cache_loader {
$missingkeys = array();
foreach ($result as $key => $value) {
if ($value === false) {
$missingkeys[] = ($usingloader) ? $key : $parsedkeys[$key];
$missingkeys[] = $parsedkeys[$key];
}
}
if (!empty($missingkeys)) {
@@ -430,11 +430,9 @@ class cache implements cache_loader {
$resultmissing = $this->datasource->load_many_for_cache($missingkeys);
}
foreach ($resultmissing as $key => $value) {
$pkey = ($usingloader) ? $key : $keysparsed[$key];
$realkey = ($usingloader) ? $parsedkeys[$key] : $key;
$result[$pkey] = $value;
$result[$keysparsed[$key]] = $value;
if ($value !== false) {
$this->set($realkey, $value);
$this->set($key, $value);
}
}
unset($resultmissing);
@@ -453,7 +451,7 @@ class cache implements cache_loader {
if ($strictness === MUST_EXIST) {
foreach ($keys as $key) {
if (!array_key_exists($key, $fullresult)) {
throw new moodle_exception('Not all the requested keys existed within the cache stores.');
throw new coding_exception('Not all the requested keys existed within the cache stores.');
}
}
}
@@ -483,6 +481,11 @@ class cache implements cache_loader {
if ($this->perfdebug) {
cache_helper::record_cache_set($this->storetype, $this->definition->get_id());
}
if ($this->loader !== false) {
// We have a loader available set it there as well.
// We have to let the loader do its own parsing of data as it may be unique.
$this->loader->set($key, $data);
}
if (is_object($data) && $data instanceof cacheable_object) {
$data = new cache_cached_object($data);
} else if (!is_scalar($data)) {
@@ -506,6 +509,7 @@ class cache implements cache_loader {
* Removes references where required.
*
* @param stdClass|array $data
* @return mixed What ever was put in but without any references.
*/
protected function unref($data) {
if ($this->definition->uses_simple_data()) {
@@ -593,6 +597,11 @@ class cache implements cache_loader {
* ... if they care that is.
*/
public function set_many(array $keyvaluearray) {
if ($this->loader !== false) {
// We have a loader available set it there as well.
// We have to let the loader do its own parsing of data as it may be unique.
$this->loader->set_many($keyvaluearray);
}
$data = array();
$simulatettl = $this->has_a_ttl() && !$this->store_supports_native_ttl();
$usepersistcache = $this->is_using_persist_cache();
@@ -858,7 +867,7 @@ class cache implements cache_loader {
* Returns the loader associated with this instance.
*
* @since 2.4.4
* @return cache_loader|false
* @return cache|false
*/
protected function get_loader() {
return $this->loader;
@@ -1340,7 +1349,6 @@ class cache_application extends cache implements cache_loader_with_locking {
* @param string|int $key The key for the data being requested.
* @param int $strictness One of IGNORE_MISSING | MUST_EXIST
* @return mixed|false The data from the cache or false if the key did not exist within the cache.
* @throws moodle_exception
*/
public function get($key, $strictness = IGNORE_MISSING) {
if ($this->requirelockingread && $this->check_lock_state($key) === false) {
@@ -1364,7 +1372,7 @@ class cache_application extends cache implements cache_loader_with_locking {
* @return array An array of key value pairs for the items that could be retrieved from the cache.
* If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
* Otherwise any key that did not exist will have a data value of false within the results.
* @throws moodle_exception
* @throws coding_exception
*/
public function get_many(array $keys, $strictness = IGNORE_MISSING) {
if ($this->requirelockingread) {
@@ -1458,6 +1466,7 @@ class cache_application extends cache implements cache_loader_with_locking {
* @todo we should support locking in the session as well. Should be pretty simple to set up.
*
* @internal don't use me directly.
* @method cache_store|cache_is_searchable get_store() Returns the cache store which must implement both cache_is_searchable.
*
* @package core
* @category cache
@@ -1494,6 +1503,11 @@ class cache_session extends cache {
*/
const KEY_PREFIX = 'sess_';
/**
* This is the key used to track last access.
*/
const LASTACCESS = '__lastaccess__';
/**
* Override the cache::construct method.
*
@@ -1507,12 +1521,15 @@ class cache_session extends cache {
* @param cache_definition $definition
* @param cache_store $store
* @param cache_loader|cache_data_source $loader
* @return void
*/
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) {
@@ -1561,6 +1578,21 @@ class cache_session extends cache {
}
}
/**
* Sets the session id for the loader.
*/
protected function set_session_id() {
$this->sessionid = preg_replace('#[^a-zA-Z0-9_]#', '_', session_id());
}
/**
* Returns the prefix used for all keys.
* @return string
*/
protected function get_key_prefix() {
return 'u'.$this->currentuserid.'_'.$this->sessionid;
}
/**
* Parses the key turning it into a string (or array is required) suitable to be passed to the cache store.
*
@@ -1573,17 +1605,18 @@ class cache_session extends cache {
* @return string|array String unless the store supports multi-identifiers in which case an array if returned.
*/
protected function parse_key($key) {
if ($key === 'lastaccess') {
$key = '__lastaccess__';
$prefix = $this->get_key_prefix();
if ($key === self::LASTACCESS) {
return $key.$prefix;
}
return 'sess_'.parent::parse_key($key);
return $prefix.'_'.parent::parse_key($key);
}
/**
* Check that this cache instance is tracking the current user.
*/
protected function check_tracked_user() {
if (isset($_SESSION['USER']->id)) {
if (isset($_SESSION['USER']->id) && $_SESSION['USER']->id !== null) {
// Get the id of the current user.
$new = $_SESSION['USER']->id;
} else {
@@ -1597,55 +1630,25 @@ class cache_session extends cache {
// This way we don't bloat the session.
$this->purge();
// Update the session id just in case!
$this->sessionid = session_id();
$this->set_session_id();
}
self::$loadeduserid = $new;
$this->currentuserid = $new;
} else if ($new !== $this->currentuserid) {
// The current user matches the loaded user but not the user last used by this cache.
$this->purge();
$this->purge_current_user();
$this->currentuserid = $new;
// Update the session id just in case!
$this->sessionid = session_id();
$this->set_session_id();
}
}
/**
* Gets the session data.
*
* @param bool $force If true the session data will be loaded from the store again.
* @return array An array of session data.
* Purges the session cache of all data belonging to the current user.
*/
protected function get_session_data($force = false) {
if ($this->sessionid === null) {
$this->sessionid = session_id();
}
if (is_array($this->session) && !$force) {
return $this->session;
}
$session = parent::get($this->sessionid);
if ($session === false) {
$session = array();
}
// We have to write here to ensure that the lastaccess time is recorded.
// And also in order to ensure the session entry exists as when we save it on __destruct
// $CFG is likely to have already been destroyed.
$this->save_session($session);
return $this->session;
}
/**
* Saves the session data.
*
* This function also updates the last access time.
*
* @param array $session
* @return bool
*/
protected function save_session(array $session) {
$session['lastaccess'] = time();
$this->session = $session;
return parent::set($this->sessionid, $this->session);
public function purge_current_user() {
$keys = $this->get_store()->find_all($this->get_key_prefix());
$this->get_store()->delete_many($keys);
}
/**
@@ -1656,7 +1659,7 @@ class cache_session extends cache {
* In advanced cases an array may be useful such as in situations requiring the multi-key functionality.
* @param int $strictness One of IGNORE_MISSING | MUST_EXIST
* @return mixed|false The data from the cache or false if the key did not exist within the cache.
* @throws moodle_exception
* @throws coding_exception
*/
public function get($key, $strictness = IGNORE_MISSING) {
// Check the tracked user.
@@ -1664,10 +1667,8 @@ class cache_session extends cache {
// 2. Parse the key.
$parsedkey = $this->parse_key($key);
// 3. Get it from the store.
$result = false;
$session = $this->get_session_data();
if (array_key_exists($parsedkey, $session)) {
$result = $session[$parsedkey];
$result = $this->get_store()->get($parsedkey);
if ($result !== false) {
if ($result instanceof cache_ttl_wrapper) {
if ($result->has_expired()) {
$this->get_store()->delete($parsedkey);
@@ -1681,10 +1682,9 @@ class cache_session extends cache {
}
}
// 4. Load if from the loader/datasource if we don't already have it.
$setaftervalidation = false;
if ($result === false) {
if ($this->perfdebug) {
cache_helper::record_cache_miss('**static session**', $this->get_definition()->get_id());
cache_helper::record_cache_miss($this->storetype, $this->get_definition()->get_id());
}
if ($this->get_loader() !== false) {
// We must pass the original (unparsed) key to the next loader in the chain.
@@ -1694,19 +1694,18 @@ class cache_session extends cache {
} else if ($this->get_datasource() !== false) {
$result = $this->get_datasource()->load_for_cache($key);
}
$setaftervalidation = ($result !== false);
// 5. Set it to the store if we got it from the loader/datasource.
if ($result !== false) {
$this->set($key, $result);
}
} else if ($this->perfdebug) {
cache_helper::record_cache_hit('**static session**', $this->get_definition()->get_id());
cache_helper::record_cache_hit($this->storetype, $this->get_definition()->get_id());
}
// 5. Validate strictness.
if ($strictness === MUST_EXIST && $result === false) {
throw new moodle_exception('Requested key did not exist in any cache stores and could not be loaded.');
throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
}
// 6. Set it to the store if we got it from the loader/datasource.
if ($setaftervalidation) {
$this->set($key, $result);
}
// 7. Make sure we don't pass back anything that could be a reference.
// 6. Make sure we don't pass back anything that could be a reference.
// We don't want people modifying the data in the cache.
if (!is_scalar($result)) {
// If data is an object it will be a reference.
@@ -1737,8 +1736,14 @@ class cache_session extends cache {
*/
public function set($key, $data) {
$this->check_tracked_user();
$loader = $this->get_loader();
if ($loader !== false) {
// We have a loader available set it there as well.
// We have to let the loader do its own parsing of data as it may be unique.
$loader->set($key, $data);
}
if ($this->perfdebug) {
cache_helper::record_cache_set('**static session**', $this->get_definition()->get_id());
cache_helper::record_cache_set($this->storetype, $this->get_definition()->get_id());
}
if (is_object($data) && $data instanceof cacheable_object) {
$data = new cache_cached_object($data);
@@ -1750,12 +1755,10 @@ class cache_session extends cache {
$data = $this->unref($data);
}
// We dont' support native TTL here as we consolidate data for sessions.
if ($this->has_a_ttl()) {
if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
$data = new cache_ttl_wrapper($data, $this->get_definition()->get_ttl());
}
$session = $this->get_session_data();
$session[$this->parse_key($key)] = $data;
return $this->save_session($session);
return $this->get_store()->set($this->parse_key($key), $data);
}
/**
@@ -1767,15 +1770,12 @@ class cache_session extends cache {
* @return bool True of success, false otherwise.
*/
public function delete($key, $recurse = true) {
$this->check_tracked_user();
$parsedkey = $this->parse_key($key);
if ($recurse && $this->get_loader() !== false) {
// Delete from the bottom of the stack first.
$this->get_loader()->delete($key, $recurse);
}
$session = $this->get_session_data();
unset($session[$parsedkey]);
return $this->save_session($session);
return $this->get_store()->delete($parsedkey);
}
/**
@@ -1794,15 +1794,72 @@ class cache_session extends cache {
* @return array An array of key value pairs for the items that could be retrieved from the cache.
* If MUST_EXIST was used and not all keys existed within the cache then an exception will be thrown.
* Otherwise any key that did not exist will have a data value of false within the results.
* @throws moodle_exception
* @throws coding_exception
*/
public function get_many(array $keys, $strictness = IGNORE_MISSING) {
$this->check_tracked_user();
$return = array();
$parsedkeys = array();
$keymap = array();
foreach ($keys as $key) {
$return[$key] = $this->get($key, $strictness);
$parsedkey = $this->parse_key($key);
$parsedkeys[$key] = $parsedkey;
$keymap[$parsedkey] = $key;
}
$result = $this->get_store()->get_many($parsedkeys);
$return = array();
$missingkeys = array();
$hasmissingkeys = false;
foreach ($result as $parsedkey => $value) {
$key = $keymap[$parsedkey];
if ($value instanceof cache_ttl_wrapper) {
/* @var cache_ttl_wrapper $value */
if ($value->has_expired()) {
$this->delete($keymap[$parsedkey]);
$value = false;
} else {
$value = $value->data;
}
}
if ($value instanceof cache_cached_object) {
/* @var cache_cached_object $value */
$value = $value->restore_object();
}
$return[$key] = $value;
if ($value === false) {
$hasmissingkeys = true;
$missingkeys[$parsedkey] = $key;
}
}
if ($hasmissingkeys) {
// We've got missing keys - we've got to check any loaders or data sources.
$loader = $this->get_loader();
$datasource = $this->get_datasource();
if ($loader !== false) {
foreach ($loader->get_many($missingkeys) as $key => $value) {
if ($value !== false) {
$return[$key] = $value;
unset($missingkeys[$parsedkeys[$key]]);
}
}
}
$hasmissingkeys = count($missingkeys) > 0;
if ($datasource !== false && $hasmissingkeys) {
// We're still missing keys but we've got a datasource.
foreach ($datasource->load_many_for_cache($missingkeys) as $key => $value) {
if ($value !== false) {
$return[$key] = $value;
unset($missingkeys[$parsedkeys[$key]]);
}
}
$hasmissingkeys = count($missingkeys) > 0;
}
}
if ($hasmissingkeys && $strictness === MUST_EXIST) {
throw new coding_exception('Requested key did not exist in any cache stores and could not be loaded.');
}
return $return;
}
/**
@@ -1814,18 +1871,12 @@ class cache_session extends cache {
* @return int The number of items successfully deleted.
*/
public function delete_many(array $keys, $recurse = true) {
$this->check_tracked_user();
$parsedkeys = array_map(array($this, 'parse_key'), $keys);
if ($recurse && $this->get_loader() !== false) {
// Delete from the bottom of the stack first.
$this->get_loader()->delete_many($keys, $recurse);
}
$session = $this->get_session_data();
foreach ($parsedkeys as $parsedkey) {
unset($session[$parsedkey]);
}
$this->save_session($session);
return count($keys);
return $this->get_store()->delete_many($parsedkeys);
}
/**
@@ -1853,8 +1904,15 @@ class cache_session extends cache {
*/
public function set_many(array $keyvaluearray) {
$this->check_tracked_user();
$session = $this->get_session_data();
$simulatettl = $this->has_a_ttl();
$loader = $this->get_loader();
if ($loader !== false) {
// We have a loader available set it there as well.
// We have to let the loader do its own parsing of data as it may be unique.
$loader->set_many($keyvaluearray);
}
$data = array();
$definitionid = $this->get_definition()->get_ttl();
$simulatettl = $this->has_a_ttl() && !$this->store_supports_native_ttl();
foreach ($keyvaluearray as $key => $value) {
if (is_object($value) && $value instanceof cacheable_object) {
$value = new cache_cached_object($value);
@@ -1866,16 +1924,17 @@ class cache_session extends cache {
$value = $this->unref($value);
}
if ($simulatettl) {
$value = new cache_ttl_wrapper($value, $this->get_definition()->get_ttl());
$value = new cache_ttl_wrapper($value, $definitionid);
}
$parsedkey = $this->parse_key($key);
$session[$parsedkey] = $value;
$data[$key] = array(
'key' => $this->parse_key($key),
'value' => $value
);
}
if ($this->perfdebug) {
cache_helper::record_cache_set($this->storetype, $this->get_definition()->get_id());
cache_helper::record_cache_set($this->storetype, $definitionid);
}
$this->save_session($session);
return count($keyvaluearray);
return $this->get_store()->set_many($data);
}
/**
@@ -1884,13 +1943,9 @@ class cache_session extends cache {
* @return bool True on success, false otherwise
*/
public function purge() {
// 1. Purge the session object.
$this->session = array();
// 2. Delete the record for this users session from the store.
$this->get_store()->delete($this->sessionid);
// 3. Optionally purge any stacked loaders in the same way.
$this->get_store()->purge();
if ($this->get_loader()) {
$this->get_loader()->delete($this->sessionid);
$this->get_loader()->purge();
}
return true;
}
@@ -1919,21 +1974,27 @@ class cache_session extends cache {
public function has($key, $tryloadifpossible = false) {
$this->check_tracked_user();
$parsedkey = $this->parse_key($key);
$session = $this->get_session_data();
$has = false;
if ($this->has_a_ttl()) {
$store = $this->get_store();
if ($this->has_a_ttl() && !$this->store_supports_native_ttl()) {
// The data has a TTL and the store doesn't support it natively.
// We must fetch the data and expect a ttl wrapper.
if (array_key_exists($parsedkey, $session)) {
$data = $session[$parsedkey];
$has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
}
$data = $store->get($parsedkey);
$has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
} else if (!$this->store_supports_key_awareness()) {
// The store doesn't support key awareness, get the data and check it manually... puke.
// Either no TTL is set of the store supports its handling natively.
$data = $store->get($parsedkey);
$has = ($data !== false);
} else {
$has = array_key_exists($parsedkey, $session);
// The store supports key awareness, this is easy!
// Either no TTL is set of the store supports its handling natively.
/* @var cache_store|cache_is_key_aware $store */
$has = $store->has($parsedkey);
}
if (!$has && $tryloadifpossible) {
$result = null;
if ($this->get_loader() !== false) {
$result = $this->get_loader()->get($key);
$result = $this->get_loader()->get($parsedkey);
} else if ($this->get_datasource() !== null) {
$result = $this->get_datasource()->load_for_cache($key);
}
@@ -1960,25 +2021,18 @@ class cache_session extends cache {
*/
public function has_all(array $keys) {
$this->check_tracked_user();
$session = $this->get_session_data();
foreach ($keys as $key) {
$has = false;
$parsedkey = $this->parse_key($key);
if ($this->has_a_ttl()) {
// The data has a TTL and the store doesn't support it natively.
// We must fetch the data and expect a ttl wrapper.
if (array_key_exists($parsedkey, $session)) {
$data = $session[$parsedkey];
$has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
if (($this->has_a_ttl() && !$this->store_supports_native_ttl()) || !$this->store_supports_key_awareness()) {
foreach ($keys as $key) {
if (!$this->has($key)) {
return false;
}
} else {
$has = array_key_exists($parsedkey, $session);
}
if (!$has) {
return false;
}
return true;
}
return true;
// The cache must be key aware and if support native ttl if it a ttl is set.
/* @var cache_store|cache_is_key_aware $store */
$store = $this->get_store();
return $store->has_all(array_map(array($this, 'parse_key'), $keys));
}
/**
@@ -1995,26 +2049,17 @@ class cache_session extends cache {
* @return bool True if the cache has at least one of the given keys
*/
public function has_any(array $keys) {
$this->check_tracked_user();
$session = $this->get_session_data();
foreach ($keys as $key) {
$has = false;
$parsedkey = $this->parse_key($key);
if ($this->has_a_ttl()) {
// The data has a TTL and the store doesn't support it natively.
// We must fetch the data and expect a ttl wrapper.
if (array_key_exists($parsedkey, $session)) {
$data = $session[$parsedkey];
$has = ($data instanceof cache_ttl_wrapper && !$data->has_expired());
if (($this->has_a_ttl() && !$this->store_supports_native_ttl()) || !$this->store_supports_key_awareness()) {
foreach ($keys as $key) {
if ($this->has($key)) {
return true;
}
} else {
$has = array_key_exists($parsedkey, $session);
}
if ($has) {
return true;
}
return false;
}
return false;
/* @var cache_store|cache_is_key_aware $store */
$store = $this->get_store();
return $store->has_any(array_map(array($this, 'parse_key'), $keys));
}
/**
+7 -11
View File
@@ -353,23 +353,19 @@ class cachestore_file extends cache_store implements cache_is_key_aware, cache_i
if (!$readfile) {
return false;
}
// Check the filesize first, likely not needed but important none the less.
$filesize = filesize($file);
if (!$filesize) {
return false;
}
// Open ensuring the file for writing, truncating it and setting the pointer to the start.
// Open ensuring the file for reading in binary format.
if (!$handle = fopen($file, 'rb')) {
return false;
}
// Lock it up!
// We don't care if this succeeds or not, on some systems it will, on some it won't, meah either way.
flock($handle, LOCK_SH);
// HACK ALERT
// There is a problem when reading from the file during PHPUNIT tests. For one reason or another the filesize is not correct
// Doesn't happen during normal operation, just during unit tests.
// Read it.
$data = fread($handle, $filesize+128);
$data = '';
// Read the data in 1Mb chunks. Small caches will not loop more than once. We don't use filesize as it may
// be cached with a different value than what we need to read from the file.
do {
$data .= fread($handle, 1048576);
} while (!feof($handle));
// Unlock it.
flock($handle, LOCK_UN);
// Return it unserialised.
+1 -1
View File
@@ -132,8 +132,8 @@ class cachestore_memcache extends cache_store implements cache_is_configurable {
$this->connection = new Memcache;
foreach ($this->servers as $server) {
$this->connection->addServer($server[0], $server[1], true, $server[2]);
// Test the connection to this server.
}
// Test the connection to the pool of servers.
$this->isready = @$this->connection->set($this->parse_key('ping'), 'ping', MEMCACHE_COMPRESSED, 1);
}
+1
View File
@@ -141,6 +141,7 @@ class cachestore_memcached extends cache_store implements cache_is_configurable
}
$this->connection->addServers($this->servers);
}
// Test the connection to the pool of servers.
$this->isready = @$this->connection->set("ping", 'ping', 1);
}
+100 -20
View File
@@ -118,7 +118,7 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
/**
* The maximum size for the store, or false if there isn't one.
* @var bool
* @var bool|int
*/
protected $maxsize = false;
@@ -202,7 +202,7 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
*/
public function initialise(cache_definition $definition) {
$this->storeid = $definition->generate_definition_hash();
$this->store = &self::register_store_id($definition->get_id());
$this->store = &self::register_store_id($this->name.'-'.$definition->get_id());
$this->ttl = $definition->get_ttl();
$maxsize = $definition->get_maxsize();
if ($maxsize !== null) {
@@ -210,6 +210,7 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
$this->maxsize = abs((int)$maxsize);
$this->storecount = count($this->store);
}
$this->check_ttl();
}
/**
@@ -238,9 +239,17 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
public function get($key) {
if (isset($this->store[$key])) {
if ($this->ttl == 0) {
return $this->store[$key][0];
$value = $this->store[$key][0];
if ($this->maxsize !== false) {
// Make sure the element is now in the end of array.
$this->set($key, $value);
}
return $value;
} else if ($this->store[$key][1] >= (cache::now() - $this->ttl)) {
return $this->store[$key][0];
} else {
// Element is present but has expired.
$this->check_ttl();
}
}
return false;
@@ -257,20 +266,32 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
*/
public function get_many($keys) {
$return = array();
$maxtime = 0;
if ($this->ttl != 0) {
$maxtime = cache::now() - $this->ttl;
}
$hasexpiredelements = false;
foreach ($keys as $key) {
$return[$key] = false;
if (isset($this->store[$key])) {
if ($this->ttl == 0) {
$return[$key] = $this->store[$key][0];
if ($this->maxsize !== false) {
// Make sure the element is now in the end of array.
$this->set($key, $return[$key], false);
}
} else if ($this->store[$key][1] >= $maxtime) {
$return[$key] = $this->store[$key][0];
} else {
$hasexpiredelements = true;
}
}
}
if ($hasexpiredelements) {
// There are some elements that are present but have expired.
$this->check_ttl();
}
return $return;
}
@@ -279,24 +300,27 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
*
* @param string $key The key to use.
* @param mixed $data The data to set.
* @param bool $testmaxsize If set to true then we test the maxsize arg and reduce if required.
* @param bool $testmaxsize If set to true then we test the maxsize arg and reduce if required. If this is set to false you will
* need to perform these checks yourself. This allows for bulk set's to be performed and maxsize tests performed once.
* @return bool True if the operation was a success false otherwise.
*/
public function set($key, $data, $testmaxsize = true) {
$testmaxsize = ($testmaxsize && $this->maxsize !== false);
if ($testmaxsize) {
$increment = (!isset($this->store[$key]));
$increment = $this->maxsize !== false && !isset($this->store[$key]);
if (($this->maxsize !== false && !$increment) || $this->ttl != 0) {
// Make sure the element is added to the end of $this->store array.
unset($this->store[$key]);
}
if ($this->ttl == 0) {
$this->store[$key][0] = $data;
if ($this->ttl === 0) {
$this->store[$key] = array($data, 0);
} else {
$this->store[$key] = array($data, cache::now());
}
if ($testmaxsize && $increment) {
if ($increment) {
$this->storecount++;
if ($this->storecount > $this->maxsize) {
$this->reduce_for_maxsize();
}
}
if ($testmaxsize && $this->storecount > $this->maxsize) {
$this->reduce_for_maxsize();
}
return true;
}
@@ -311,12 +335,26 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
*/
public function set_many(array $keyvaluearray) {
$count = 0;
$increment = 0;
foreach ($keyvaluearray as $pair) {
$this->set($pair['key'], $pair['value'], false);
$key = $pair['key'];
$data = $pair['value'];
$count++;
if ($this->maxsize !== false || $this->ttl !== 0) {
// Make sure the element is added to the end of $this->store array.
$this->delete($key);
$increment++;
} else if (!isset($this->store[$key])) {
$increment++;
}
if ($this->ttl === 0) {
$this->store[$key] = array($data, 0);
} else {
$this->store[$key] = array($data, cache::now());
}
}
if ($this->maxsize !== false) {
$this->storecount += $count;
$this->storecount += $increment;
if ($this->storecount > $this->maxsize) {
$this->reduce_for_maxsize();
}
@@ -348,6 +386,7 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
* @return bool
*/
public function has_all(array $keys) {
$maxtime = 0;
if ($this->ttl != 0) {
$maxtime = cache::now() - $this->ttl;
}
@@ -370,6 +409,7 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
* @return bool
*/
public function has_any(array $keys) {
$maxtime = 0;
if ($this->ttl != 0) {
$maxtime = cache::now() - $this->ttl;
}
@@ -389,12 +429,14 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
* @return bool Returns true if the operation was a success, false otherwise.
*/
public function delete($key) {
$result = isset($this->store[$key]);
if (!isset($this->store[$key])) {
return false;
}
unset($this->store[$key]);
if ($this->maxsize !== false) {
$this->storecount--;
}
return $result;
return true;
}
/**
@@ -404,17 +446,18 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
* @return int The number of items successfully deleted.
*/
public function delete_many(array $keys) {
$count = 0;
// The number of items that have actually being removed.
$reduction = 0;
foreach ($keys as $key) {
if (isset($this->store[$key])) {
$count++;
$reduction++;
}
unset($this->store[$key]);
}
if ($this->maxsize !== false) {
$this->storecount -= $count;
$this->storecount -= $reduction;
}
return $count;
return $reduction;
}
/**
@@ -489,12 +532,40 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
return $this->name;
}
/**
* Removes expired elements.
* @return int number of removed elements
*/
protected function check_ttl() {
if ($this->ttl === 0) {
return 0;
}
$maxtime = cache::now() - $this->ttl;
$count = 0;
for ($value = reset($this->store); $value !== false; $value = next($this->store)) {
if ($value[1] >= $maxtime) {
// We know that elements are sorted by ttl so no need to continue.
break;
}
$count++;
}
if ($count) {
// Remove first $count elements as they are expired.
$this->store = array_slice($this->store, $count, null, true);
if ($this->maxsize !== false) {
$this->storecount -= $count;
}
}
return $count;
}
/**
* Finds all of the keys being stored in the cache store instance.
*
* @return array
*/
public function find_all() {
$this->check_ttl();
return array_keys($this->store);
}
@@ -502,6 +573,7 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
* Finds all of the keys whose keys start with the given prefix.
*
* @param string $prefix
* @return array An array of keys.
*/
public function find_by_prefix($prefix) {
$return = array();
@@ -512,4 +584,12 @@ class cachestore_session extends session_data_store implements cache_is_key_awar
}
return $return;
}
/**
* This store supports native TTL handling.
* @return bool
*/
public function store_supports_native_ttl() {
return true;
}
}
+115 -35
View File
@@ -49,60 +49,66 @@ class cachestore_session_test extends cachestore_tests {
* Test the maxsize option.
*/
public function test_maxsize() {
$defid = 'phpunit/testmaxsize';
$config = cache_config_phpunittest::instance();
$config->phpunit_add_definition($defid, array(
$config->phpunit_add_definition('phpunit/one', array(
'mode' => cache_store::MODE_SESSION,
'component' => 'phpunit',
'area' => 'testmaxsize',
'area' => 'one',
'maxsize' => 3
));
$definition = cache_definition::load($defid, $config->get_definition_by_id($defid));
$instance = cachestore_session::initialise_test_instance($definition);
$this->assertTrue($instance->set('key1', 'value1'));
$this->assertTrue($instance->set('key2', 'value2'));
$this->assertTrue($instance->set('key3', 'value3'));
$config->phpunit_add_definition('phpunit/two', array(
'mode' => cache_store::MODE_SESSION,
'component' => 'phpunit',
'area' => 'two',
'maxsize' => 3
));
$this->assertTrue($instance->has('key1'));
$this->assertTrue($instance->has('key2'));
$this->assertTrue($instance->has('key3'));
$cacheone = cache::make('phpunit', 'one');
$this->assertTrue($instance->set('key4', 'value4'));
$this->assertTrue($instance->set('key5', 'value5'));
$this->assertTrue($cacheone->set('key1', 'value1'));
$this->assertTrue($cacheone->set('key2', 'value2'));
$this->assertTrue($cacheone->set('key3', 'value3'));
$this->assertFalse($instance->has('key1'));
$this->assertFalse($instance->has('key2'));
$this->assertTrue($instance->has('key3'));
$this->assertTrue($instance->has('key4'));
$this->assertTrue($instance->has('key5'));
$this->assertTrue($cacheone->has('key1'));
$this->assertTrue($cacheone->has('key2'));
$this->assertTrue($cacheone->has('key3'));
$this->assertFalse($instance->get('key1'));
$this->assertFalse($instance->get('key2'));
$this->assertEquals('value3', $instance->get('key3'));
$this->assertEquals('value4', $instance->get('key4'));
$this->assertEquals('value5', $instance->get('key5'));
$this->assertTrue($cacheone->set('key4', 'value4'));
$this->assertTrue($cacheone->set('key5', 'value5'));
$this->assertFalse($cacheone->has('key1'));
$this->assertFalse($cacheone->has('key2'));
$this->assertTrue($cacheone->has('key3'));
$this->assertTrue($cacheone->has('key4'));
$this->assertTrue($cacheone->has('key5'));
$this->assertFalse($cacheone->get('key1'));
$this->assertFalse($cacheone->get('key2'));
$this->assertEquals('value3', $cacheone->get('key3'));
$this->assertEquals('value4', $cacheone->get('key4'));
$this->assertEquals('value5', $cacheone->get('key5'));
// Test adding one more.
$this->assertTrue($instance->set('key6', 'value6'));
$this->assertFalse($instance->get('key3'));
$this->assertTrue($cacheone->set('key6', 'value6'));
$this->assertFalse($cacheone->get('key3'));
// Test reducing and then adding to make sure we don't lost one.
$this->assertTrue($instance->delete('key6'));
$this->assertTrue($instance->set('key7', 'value7'));
$this->assertEquals('value4', $instance->get('key4'));
$this->assertTrue($cacheone->delete('key6'));
$this->assertTrue($cacheone->set('key7', 'value7'));
$this->assertEquals('value4', $cacheone->get('key4'));
// Set the same key three times to make sure it doesn't count overrides.
for ($i = 0; $i < 3; $i++) {
$this->assertTrue($instance->set('key8', 'value8'));
$this->assertTrue($cacheone->set('key8', 'value8'));
}
$this->assertEquals('value7', $instance->get('key7'), 'Overrides are incorrectly incrementing size');
$this->assertEquals('value7', $cacheone->get('key7'), 'Overrides are incorrectly incrementing size');
// Test adding many.
$this->assertEquals(3, $instance->set_many(array(
array('key' => 'keyA', 'value' => 'valueA'),
array('key' => 'keyB', 'value' => 'valueB'),
array('key' => 'keyC', 'value' => 'valueC')
$this->assertEquals(3, $cacheone->set_many(array(
'keyA' => 'valueA',
'keyB' => 'valueB',
'keyC' => 'valueC'
)));
$this->assertEquals(array(
'key4' => false,
@@ -112,8 +118,82 @@ class cachestore_session_test extends cachestore_tests {
'keyA' => 'valueA',
'keyB' => 'valueB',
'keyC' => 'valueC'
), $instance->get_many(array(
), $cacheone->get_many(array(
'key4', 'key5', 'key6', 'key7', 'keyA', 'keyB', 'keyC'
)));
$cachetwo = cache::make('phpunit', 'two');
// Test adding many.
$this->assertEquals(3, $cacheone->set_many(array(
'keyA' => 'valueA',
'keyB' => 'valueB',
'keyC' => 'valueC'
)));
$this->assertEquals(3, $cachetwo->set_many(array(
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3'
)));
$this->assertEquals(array(
'keyA' => 'valueA',
'keyB' => 'valueB',
'keyC' => 'valueC'
), $cacheone->get_many(array(
'keyA', 'keyB', 'keyC'
)));
$this->assertEquals(array(
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3'
), $cachetwo->get_many(array(
'key1', 'key2', 'key3'
)));
// Test that that cache deletes element that was least recently accessed.
$this->assertEquals('valueA', $cacheone->get('keyA'));
$cacheone->set('keyD', 'valueD');
$this->assertEquals('valueA', $cacheone->get('keyA'));
$this->assertFalse($cacheone->get('keyB'));
$this->assertEquals(array('keyD' => 'valueD', 'keyC' => 'valueC'), $cacheone->get_many(array('keyD', 'keyC')));
$cacheone->set('keyE', 'valueE');
$this->assertFalse($cacheone->get('keyB'));
$this->assertFalse($cacheone->get('keyA'));
$this->assertEquals(array('keyA' => false, 'keyE' => 'valueE', 'keyD' => 'valueD', 'keyC' => 'valueC'),
$cacheone->get_many(array('keyA', 'keyE', 'keyD', 'keyC')));
// Overwrite keyE (moves it to the end of array), and set keyF.
$cacheone->set_many(array('keyE' => 'valueE', 'keyF' => 'valueF'));
$this->assertEquals(array('keyC' => 'valueC', 'keyE' => 'valueE', 'keyD' => false, 'keyF' => 'valueF'),
$cacheone->get_many(array('keyC', 'keyE', 'keyD', 'keyF')));
}
public function test_ttl() {
$config = cache_config_phpunittest::instance();
$config->phpunit_add_definition('phpunit/three', array(
'mode' => cache_store::MODE_SESSION,
'component' => 'phpunit',
'area' => 'three',
'maxsize' => 3,
'ttl' => 3
));
$cachethree = cache::make('phpunit', 'three');
// Make sure that when cache with ttl is full the elements that were added first are deleted first regardless of access time.
$cachethree->set('key1', 'value1');
$cachethree->set('key2', 'value2');
$cachethree->set('key3', 'value3');
$cachethree->set('key4', 'value4');
$this->assertFalse($cachethree->get('key1'));
$this->assertEquals('value4', $cachethree->get('key4'));
$cachethree->set('key5', 'value5');
$this->assertFalse($cachethree->get('key2'));
$this->assertEquals('value4', $cachethree->get('key4'));
$cachethree->set_many(array('key6' => 'value6', 'key7' => 'value7'));
$this->assertEquals(array('key3' => false, 'key4' => false, 'key5' => 'value5', 'key6' => 'value6', 'key7' => 'value7'),
$cachethree->get_many(array('key3', 'key4', 'key5', 'key6', 'key7')));
}
}
+90 -3
View File
@@ -1126,9 +1126,9 @@ class core_cache_testcase extends advanced_testcase {
}
/**
* Test that multiple loaders work ok.
* Test that multiple application loaders work ok.
*/
public function test_multiple_loaders() {
public function test_multiple_application_loaders() {
$instance = cache_config_phpunittest::instance(true);
$instance->phpunit_add_file_store('phpunittest1');
$instance->phpunit_add_file_store('phpunittest2');
@@ -1173,6 +1173,93 @@ class core_cache_testcase extends advanced_testcase {
$this->assertFalse($result['a']);
$this->assertEquals('B', $result['b']);
$this->assertFalse($result['c']);
// Test non-recursive deletes.
$this->assertTrue($cache->set('test', 'test'));
$this->assertSame('test', $cache->get('test'));
$this->assertTrue($cache->delete('test', false));
// We should still have it on a deeper loader.
$this->assertSame('test', $cache->get('test'));
// Test non-recusive with many functions.
$this->assertSame(3, $cache->set_many(array(
'one' => 'one',
'two' => 'two',
'three' => 'three'
)));
$this->assertSame('one', $cache->get('one'));
$this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three')));
$this->assertSame(3, $cache->delete_many(array('one', 'two', 'three'), false));
$this->assertSame('one', $cache->get('one'));
$this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three')));
}
/**
* Test that multiple application loaders work ok.
*/
public function test_multiple_session_loaders() {
/* @var cache_config_phpunittest $instance */
$instance = cache_config_phpunittest::instance(true);
$instance->phpunit_add_session_store('phpunittest1');
$instance->phpunit_add_session_store('phpunittest2');
$instance->phpunit_add_definition('phpunit/multi_loader', array(
'mode' => cache_store::MODE_SESSION,
'component' => 'phpunit',
'area' => 'multi_loader'
));
$instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest1', 3);
$instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest2', 2);
$cache = cache::make('phpunit', 'multi_loader');
$this->assertInstanceOf('cache_session', $cache);
$this->assertFalse($cache->get('test'));
$this->assertTrue($cache->set('test', 'test'));
$this->assertEquals('test', $cache->get('test'));
$this->assertTrue($cache->delete('test'));
$this->assertFalse($cache->get('test'));
$this->assertTrue($cache->set('test', 'test'));
$this->assertTrue($cache->purge());
$this->assertFalse($cache->get('test'));
// Test the many commands.
$this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C')));
$result = $cache->get_many(array('a', 'b', 'c'));
$this->assertInternalType('array', $result);
$this->assertCount(3, $result);
$this->assertArrayHasKey('a', $result);
$this->assertArrayHasKey('b', $result);
$this->assertArrayHasKey('c', $result);
$this->assertEquals('A', $result['a']);
$this->assertEquals('B', $result['b']);
$this->assertEquals('C', $result['c']);
$this->assertEquals($result, $cache->get_many(array('a', 'b', 'c')));
$this->assertEquals(2, $cache->delete_many(array('a', 'c')));
$result = $cache->get_many(array('a', 'b', 'c'));
$this->assertInternalType('array', $result);
$this->assertCount(3, $result);
$this->assertArrayHasKey('a', $result);
$this->assertArrayHasKey('b', $result);
$this->assertArrayHasKey('c', $result);
$this->assertFalse($result['a']);
$this->assertEquals('B', $result['b']);
$this->assertFalse($result['c']);
// Test non-recursive deletes.
$this->assertTrue($cache->set('test', 'test'));
$this->assertSame('test', $cache->get('test'));
$this->assertTrue($cache->delete('test', false));
// We should still have it on a deeper loader.
$this->assertSame('test', $cache->get('test'));
// Test non-recusive with many functions.
$this->assertSame(3, $cache->set_many(array(
'one' => 'one',
'two' => 'two',
'three' => 'three'
)));
$this->assertSame('one', $cache->get('one'));
$this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three')));
$this->assertSame(3, $cache->delete_many(array('one', 'two', 'three'), false));
$this->assertSame('one', $cache->get('one'));
$this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three')));
}
/**
@@ -1448,4 +1535,4 @@ class core_cache_testcase extends advanced_testcase {
$this->assertInstanceOf('cache_request', $cache);
$this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements());
}
}
}
+18
View File
@@ -94,6 +94,24 @@ class cache_config_phpunittest extends cache_config_writer {
);
}
/**
* Forcefully adds a session store.
*
* @param string $name
*/
public function phpunit_add_session_store($name) {
$this->configstores[$name] = array(
'name' => $name,
'plugin' => 'session',
'configuration' => array(),
'features' => 14,
'modes' => 2,
'default' => true,
'class' => 'cachestore_session',
'lock' => 'cachelock_file_default',
);
}
/**
* Forcefully injects a definition => store mapping.
*
-17
View File
@@ -1,17 +0,0 @@
<?php
// format.php - course format featuring single activity
// included from view.php
$module = $course->format;
require_once($CFG->dirroot.'/mod/'.$module.'/locallib.php');
$strgroups = get_string('groups');
$strgroupmy = get_string('groupmy');
$editing = $PAGE->user_is_editing();
$moduleformat = $module.'_course_format_display';
if (function_exists($moduleformat)) {
$moduleformat($USER,$course);
} else {
echo $OUTPUT->notification('The module '. $module. ' does not support single activity course format');
}
-101
View File
@@ -1,101 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file contains main class for the course format SCORM
*
* @since 2.0
* @package format_scorm
* @copyright 2009 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot. '/course/format/lib.php');
/**
* Main class for the Scorm course format
*
* @package format_scorm
* @copyright 2012 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class format_scorm extends format_base {
/**
* The URL to use for the specified course
*
* @param int|stdClass $section Section object from database or just field course_sections.section
* if null the course view page is returned
* @param array $options options for view URL. At the moment core uses:
* 'navigation' (bool) if true and section has no separate page, the function returns null
* 'sr' (int) used by multipage formats to specify to which section to return
* @return null|moodle_url
*/
public function get_view_url($section, $options = array()) {
if (!empty($options['navigation']) && $section !== null) {
return null;
}
return new moodle_url('/course/view.php', array('id' => $this->courseid));
}
/**
* Loads all of the course sections into the navigation
*
* @param global_navigation $navigation
* @param navigation_node $node The course node within the navigation
*/
public function extend_course_navigation($navigation, navigation_node $node) {
// Scorm course format does not extend course navigation
}
/**
* Returns the list of blocks to be automatically added for the newly created course
*
* @return array of default blocks, must contain two keys BLOCK_POS_LEFT and BLOCK_POS_RIGHT
* each of values is an array of block names (for left and right side columns)
*/
public function get_default_blocks() {
return array(
BLOCK_POS_LEFT => array(),
BLOCK_POS_RIGHT => array('news_items', 'recent_activity', 'calendar_upcoming')
);
}
/**
* Allows course format to execute code on moodle_page::set_course()
*
* If user is on course view page and there is no scorm module added to the course
* and the user has 'moodle/course:update' capability, redirect to create module
* form. This function is executed before the output starts
*
* @param moodle_page $page instance of page calling set_course
*/
public function page_set_course(moodle_page $page) {
global $PAGE;
if ($PAGE == $page && $page->has_set_url() &&
$page->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
$modinfo = get_fast_modinfo($this->courseid);
if (empty($modinfo->instances['scorm'])
&& has_capability('moodle/course:update', context_course::instance($this->courseid))) {
// Redirect to create a new activity
$url = new moodle_url('/course/modedit.php',
array('course' => $this->courseid, 'section' => 0, 'add' => 'scorm'));
redirect($url);
}
}
}
}
+18 -9
View File
@@ -1736,35 +1736,44 @@ function reorder_sections($sections, $origin_position, $target_position) {
* Move the module object $mod to the specified $section
* If $beforemod exists then that is the module
* before which $modid should be inserted
* All parameters are objects
*
* @param stdClass|cm_info $mod
* @param stdClass|section_info $section
* @param int|stdClass $beforemod id or object with field id corresponding to the module
* before which the module needs to be included. Null for inserting in the
* end of the section
* @return int new value for module visibility (0 or 1)
*/
function moveto_module($mod, $section, $beforemod=NULL) {
global $OUTPUT, $DB;
/// Remove original module from original section
// Current module visibility state - return value of this function.
$modvisible = $mod->visible;
// Remove original module from original section.
if (! delete_mod_from_section($mod->id, $mod->section)) {
echo $OUTPUT->notification("Could not delete module from existing section");
}
// if moving to a hidden section then hide module
// If moving to a hidden section then hide module.
if ($mod->section != $section->id) {
if (!$section->visible && $mod->visible) {
// Set this in the object because it is sent as a response to ajax calls.
$mod->visible = 0;
// Module was visible but must become hidden after moving to hidden section.
$modvisible = 0;
set_coursemodule_visible($mod->id, 0);
// Set visibleold to 1 so module will be visible when section is made visible.
$DB->set_field('course_modules', 'visibleold', 1, array('id' => $mod->id));
}
if ($section->visible && !$mod->visible) {
// Hidden module was moved to the visible section, restore the module visibility from visibleold.
set_coursemodule_visible($mod->id, $mod->visibleold);
// Set this in the object because it is sent as a response to ajax calls.
$mod->visible = $mod->visibleold;
$modvisible = $mod->visibleold;
}
}
/// Add the module into the new section
// Add the module into the new section.
course_add_cm_to_section($section->course, $mod->id, $section->section, $beforemod);
return true;
return $modvisible;
}
/**
+29 -9
View File
@@ -26,6 +26,16 @@ require_once("../config.php");
require_once($CFG->dirroot.'/course/lib.php');
require_once($CFG->libdir.'/coursecatlib.php');
/**
* Limit the total number of categories where user has 'moodle/course:manage'
* permission in order for "Move categories" dropdown to be displayed on this page.
* If number of categories exceeds this limit, user can always use edit category
* form to change the parent. Otherwise the page size becomes too big.
*/
if (!defined('COURSECAT_QUICKMOVE_LIMIT')) {
define('COURSECAT_QUICKMOVE_LIMIT', 200);
}
// Category id.
$id = optional_param('categoryid', 0, PARAM_INT);
// Which page to show.
@@ -350,6 +360,8 @@ if (!empty($searchcriteria)) {
echo $OUTPUT->heading(new lang_string('searchresults'));
} else if (!$coursecat->id) {
// Print out the categories with all the knobs.
$manageablecategories = coursecat::make_categories_list('moodle/category:manage');
$displaymovecategoryto = !empty($manageablecategories) && (count($manageablecategories) <= COURSECAT_QUICKMOVE_LIMIT);
$table = new html_table;
$table->id = 'coursecategories';
$table->attributes['class'] = 'admintable generaltable editcourse';
@@ -357,17 +369,21 @@ if (!empty($searchcriteria)) {
get_string('categories'),
get_string('courses'),
get_string('edit'),
get_string('movecategoryto'),
);
if ($displaymovecategoryto) {
$table->head[] = get_string('movecategoryto');
}
$table->colclasses = array(
'leftalign name',
'centeralign count',
'centeralign icons',
'leftalign actions'
);
if ($displaymovecategoryto) {
$table->colclasses[] = 'leftalign actions';
}
$table->data = array();
print_category_edit($table, $coursecat);
print_category_edit($table, $coursecat, -1, false, false, $displaymovecategoryto);
echo html_writer::table($table);
} else {
@@ -655,8 +671,9 @@ echo $OUTPUT->footer();
* @param int $depth The depth of the category.
* @param bool $up True if this category can be moved up.
* @param bool $down True if this category can be moved down.
* @param bool $displaymovecategoryto whether to display a dropdown for quick category move.
*/
function print_category_edit(html_table $table, coursecat $category, $depth = -1, $up = false, $down = false) {
function print_category_edit(html_table $table, coursecat $category, $depth = -1, $up = false, $down = false, $displaymovecategoryto = true) {
global $OUTPUT;
static $str = null;
@@ -744,7 +761,7 @@ function print_category_edit(html_table $table, coursecat $category, $depth = -1
}
$actions = '';
if (has_capability('moodle/category:manage', $categorycontext)) {
if ($displaymovecategoryto && has_capability('moodle/category:manage', $categorycontext)) {
$popupurl = new moodle_url('/course/manage.php', array('movecat' => $category->id, 'sesskey' => sesskey()));
$tempdisplaylist = array(0 => get_string('top')) + coursecat::make_categories_list('moodle/category:manage', $category->id);
$select = new single_select($popupurl, 'movetocat', $tempdisplaylist, $category->parent, null, "moveform$category->id");
@@ -752,16 +769,19 @@ function print_category_edit(html_table $table, coursecat $category, $depth = -1
$actions = $OUTPUT->render($select);
}
$table->data[] = new html_table_row(array(
$thisrowdata = array(
// Category name.
new html_table_cell($categoryname),
// Course count.
new html_table_cell($category->coursecount),
// Icons.
new html_table_cell(join(' ', $icons)),
);
if ($displaymovecategoryto) {
// Actions.
new html_table_cell($actions)
));
$thisrowdata[] = new html_table_cell($actions);
}
$table->data[] = new html_table_row($thisrowdata);
}
if ($categories = $category->get_children()) {
@@ -779,7 +799,7 @@ function print_category_edit(html_table $table, coursecat $category, $depth = -1
$down = $last ? false : true;
$first = false;
print_category_edit($table, $cat, $depth+1, $up, $down);
print_category_edit($table, $cat, $depth+1, $up, $down, $displaymovecategoryto);
}
}
}
+2 -2
View File
@@ -133,8 +133,8 @@ switch($requestmethod) {
$beforemod = NULL;
}
moveto_module($cm, $section, $beforemod);
echo json_encode(array('visible' => $cm->visible));
$isvisible = moveto_module($cm, $section, $beforemod);
echo json_encode(array('visible' => $isvisible));
break;
case 'gettitle':
require_capability('moodle/course:manageactivities', $modcontext);
+16 -30
View File
@@ -856,8 +856,6 @@ class core_course_courselib_testcase extends advanced_testcase {
// Perform the move
moveto_module($cm, $newsection);
// reset of get_fast_modinfo is usually called the code calling moveto_module so call it here
get_fast_modinfo(0, 0, true);
$cms = get_fast_modinfo($course)->get_cms();
$cm = reset($cms);
@@ -883,11 +881,8 @@ class core_course_courselib_testcase extends advanced_testcase {
// Perform a second move as some issues were only seen on the second move
$newsection = get_fast_modinfo($course)->get_section_info(2);
$oldsectionid = $cm->section;
$result = moveto_module($cm, $newsection);
$this->assertTrue($result);
moveto_module($cm, $newsection);
// reset of get_fast_modinfo is usually called the code calling moveto_module so call it here
get_fast_modinfo(0, 0, true);
$cms = get_fast_modinfo($course)->get_cms();
$cm = reset($cms);
@@ -1197,12 +1192,9 @@ class core_course_courselib_testcase extends advanced_testcase {
$forumcm = $modinfo->cms[$forum->cmid];
$pagecm = $modinfo->cms[$page->cmid];
// Move the forum and the page to a hidden section.
moveto_module($forumcm, $hiddensection);
moveto_module($pagecm, $hiddensection);
// Reset modinfo cache.
get_fast_modinfo(0, 0, true);
// Move the forum and the page to a hidden section, make sure moveto_module returns 0 as new visibility state.
$this->assertEquals(0, moveto_module($forumcm, $hiddensection));
$this->assertEquals(0, moveto_module($pagecm, $hiddensection));
$modinfo = get_fast_modinfo($course);
@@ -1228,29 +1220,26 @@ class core_course_courselib_testcase extends advanced_testcase {
$this->assertEquals($quizcm->visible, 1);
// Move forum and page back to visible section.
// Make sure the visibility is restored to the original value (visible for forum and hidden for page).
$visiblesection = $modinfo->get_section_info(2);
moveto_module($forumcm, $visiblesection);
moveto_module($pagecm, $visiblesection);
$this->assertEquals(1, moveto_module($forumcm, $visiblesection));
$this->assertEquals(0, moveto_module($pagecm, $visiblesection));
// Reset modinfo cache.
get_fast_modinfo(0, 0, true);
$modinfo = get_fast_modinfo($course);
// Verify that forum has been made visible.
// Double check that forum has been made visible.
$forumcm = $modinfo->cms[$forum->cmid];
$this->assertEquals($forumcm->visible, 1);
// Verify that page has stayed invisible.
// Double check that page has stayed invisible.
$pagecm = $modinfo->cms[$page->cmid];
$this->assertEquals($pagecm->visible, 0);
// Move the page in the same section (this is what mod duplicate does_
moveto_module($pagecm, $visiblesection, $forumcm);
// Move the page in the same section (this is what mod duplicate does).
// Visibility of page remains 0.
$this->assertEquals(0, moveto_module($pagecm, $visiblesection, $forumcm));
// Reset modinfo cache.
get_fast_modinfo(0, 0, true);
// Verify that the the page is still hidden
// Double check that the the page is still hidden.
$modinfo = get_fast_modinfo($course);
$pagecm = $modinfo->cms[$page->cmid];
$this->assertEquals($pagecm->visible, 0);
@@ -1288,13 +1277,10 @@ class core_course_courselib_testcase extends advanced_testcase {
$this->assertEquals($section->id, $pagecm->section);
// Move the forum and the page to a hidden section.
moveto_module($pagecm, $section, $forumcm);
// Move the page inside the hidden section. Make sure it is hidden.
$this->assertEquals(0, moveto_module($pagecm, $section, $forumcm));
// Reset modinfo cache.
get_fast_modinfo(0, 0, true);
// Verify that the the page is still hidden
// Double check that the the page is still hidden.
$modinfo = get_fast_modinfo($course);
$pagecm = $modinfo->cms[$page->cmid];
$this->assertEquals($pagecm->visible, 0);
+4 -2
View File
@@ -219,8 +219,10 @@
redirect($CFG->wwwroot .'/');
}
$ajaxenabled = ajaxenabled();
$completion = new completion_info($course);
if ($completion->is_enabled() && ajaxenabled()) {
if ($completion->is_enabled() && $ajaxenabled) {
$PAGE->requires->string_for_js('completion-title-manual-y', 'completion');
$PAGE->requires->string_for_js('completion-title-manual-n', 'completion');
$PAGE->requires->string_for_js('completion-alt-manual-y', 'completion');
@@ -241,7 +243,7 @@
$PAGE->set_heading($course->fullname);
echo $OUTPUT->header();
if ($completion->is_enabled() && ajaxenabled()) {
if ($completion->is_enabled() && $ajaxenabled) {
// This value tracks whether there has been a dynamic change to the page.
// It is used so that if a user does this - (a) set some tickmarks, (b)
// go to another page, (c) clicks Back button - the page will
+22 -9
View File
@@ -176,25 +176,31 @@ class core_files_externallib_testcase extends advanced_testcase {
$this->assertNotEmpty($file);
}
/**
* Test getting a list of files with and without a context ID.
*/
public function test_get_files() {
global $USER, $DB;
$this->resetAfterTest();
// Set the current user to be the administrator.
$this->setAdminUser();
$USER->email = '[email protected]';
// Create a course.
$course = $this->getDataGenerator()->create_course();
$record = new stdClass();
$record->course = $course->id;
$record->name = "Mod data upload test";
$record->intro = "Some intro of some sort";
// Create a database module.
$module = $this->getDataGenerator()->create_module('data', $record);
// Create a new field in the database activity.
$field = data_get_field_new('file', $module);
// Add more detail about the field.
$fielddetail = new stdClass();
$fielddetail->d = $module->id;
$fielddetail->mode = 'add';
@@ -208,15 +214,15 @@ class core_files_externallib_testcase extends advanced_testcase {
$field->insert_field();
$recordid = data_add_record($module);
$timemodified = $DB->get_field('data_records', 'timemodified', array('id' => $recordid));
// File information for the database module record.
$datacontent = array();
$datacontent['fieldid'] = $field->field->id;
$datacontent['recordid'] = $recordid;
$datacontent['content'] = 'Simple4.txt';
// Insert the information about the file.
$contentid = $DB->insert_record('data_content', $datacontent);
// Required information for uploading a file.
$context = context_module::instance($module->id);
$usercontext = context_user::instance($USER->id);
$component = 'mod_data';
@@ -233,12 +239,18 @@ class core_files_externallib_testcase extends advanced_testcase {
$filerecord['filepath'] = '/';
$filerecord['filename'] = $filename;
// Create an area to upload the file.
$fs = get_file_storage();
// Create a file from the string that we made earlier.
$file = $fs->create_file_from_string($filerecord, $filecontent);
$timemodified = $file->get_timemodified();
// Use the web service function to return the information about the file that we just uploaded.
// The first time is with a valid context ID.
$filename = '';
$testfilelisting = core_files_external::get_files($context->id, $component, $filearea, $itemid, '/', $filename);
// With the information that we have provided we should get an object exactly like the one below.
$testdata = array();
$testdata['parents'] = array();
$testdata['parents']['0'] = array('contextid' => 1,
@@ -275,18 +287,19 @@ class core_files_externallib_testcase extends advanced_testcase {
$testdata['files']['0'] = array('contextid' => 20,
'component' => 'mod_data',
'filearea' => 'content',
'itemid' => 1,
'itemid' => '1',
'filepath' => '/',
'filename' => 'Simple4.txt',
'url' => 'http://www.example.com/moodle/pluginfile.php/20/mod_data/content/1/Simple4.txt',
'isdir' => null,
'isdir' => false,
'timemodified' => $timemodified);
// Make sure that they are the same.
$this->assertEquals($testfilelisting, $testdata);
// Try again but without the context.
// Try again but without the context. Minus one signals the function to use other variables to obtain the context.
$nocontext = -1;
$modified = 0;
// Context level and instance ID are used to determine what the context is.
$contextlevel = 'module';
$instanceid = $module->id;
$testfilelisting = core_files_external::get_files($nocontext, $component, $filearea, $itemid, '/', $filename, $modified, $contextlevel, $instanceid);
+8 -8
View File
@@ -1582,15 +1582,15 @@ class grade_report_grader extends grade_report {
* @return bool
*/
public function is_fixed_students() {
global $USER, $CFG;
global $CFG;
return $CFG->grade_report_fixedstudents &&
(check_browser_version('MSIE', '7.0') ||
check_browser_version('Firefox', '2.0') ||
check_browser_version('Gecko', '2006010100') ||
check_browser_version('Camino', '1.0') ||
check_browser_version('Opera', '6.0') ||
check_browser_version('Chrome', '6') ||
check_browser_version('Safari', '300'));
(core_useragent::check_ie_version('7.0') ||
core_useragent::check_firefox_version('2.0') ||
core_useragent::check_gecko_version('2006010100') ||
core_useragent::check_camino_version('1.0') ||
core_useragent::check_opera_version('6.0') ||
core_useragent::check_chrome_version('6') ||
core_useragent::check_safari_version('300'));
}
/**
+4 -3
View File
@@ -152,7 +152,8 @@ $currenttab = 'groups';
require('tabs.php');
$disabled = 'disabled="disabled"';
if (ajaxenabled()) {
$ajaxenabled = ajaxenabled();
if ($ajaxenabled) {
// Some buttons are enabled if single group selected
$showaddmembersform_disabled = $singlegroup ? '' : $disabled;
$showeditgroupsettingsform_disabled = $singlegroup ? '' : $disabled;
@@ -178,7 +179,7 @@ echo '<tr>'."\n";
echo "<td>\n";
echo '<p><label for="groups"><span id="groupslabel">'.get_string('groups').':</span><span id="thegrouping">&nbsp;</span></label></p>'."\n";
if (ajaxenabled()) { // TODO: move this to JS init!
if ($ajaxenabled) { // TODO: move this to JS init!
$onchange = 'M.core_group.membersCombo.refreshMembers();';
} else {
$onchange = '';
@@ -275,7 +276,7 @@ echo '</table>'."\n";
echo '</div>'."\n";
echo '</form>'."\n";
if (ajaxenabled()) {
if ($ajaxenabled) {
$PAGE->requires->js_init_call('M.core_group.init_index', array($CFG->wwwroot, $courseid));
$PAGE->requires->js_init_call('M.core_group.groupslist', array($preventgroupremoval));
}
+121 -87
View File
@@ -64,8 +64,9 @@ function groups_add_member($grouporid, $userorid, $component=null, $itemid=0) {
$group = $DB->get_record('groups', array('id'=>$groupid), '*', MUST_EXIST);
}
//check if the user a participant of the group course
if (!is_enrolled(context_course::instance($group->courseid), $userid)) {
// Check if the user a participant of the group course.
$context = context_course::instance($group->courseid);
if (!is_enrolled($context, $userid)) {
return false;
}
@@ -99,16 +100,23 @@ function groups_add_member($grouporid, $userorid, $component=null, $itemid=0) {
$DB->insert_record('groups_members', $member);
//update group info
// Update group info, and group object.
$DB->set_field('groups', 'timemodified', $member->timeadded, array('id'=>$groupid));
$group->timemodified = $member->timeadded;
//trigger groups events
$eventdata = new stdClass();
$eventdata->groupid = $groupid;
$eventdata->userid = $userid;
$eventdata->component = $member->component;
$eventdata->itemid = $member->itemid;
events_trigger('groups_member_added', $eventdata);
// Trigger group event.
$params = array(
'context' => $context,
'objectid' => $groupid,
'relateduserid' => $userid,
'other' => array(
'component' => $member->component,
'itemid' => $member->itemid
)
);
$event = \core\event\group_member_added::create($params);
$event->add_record_snapshot('groups', $group);
$event->trigger();
return true;
}
@@ -174,10 +182,8 @@ function groups_remove_member($grouporid, $userorid) {
if (is_object($userorid)) {
$userid = $userorid->id;
$user = $userorid;
} else {
$userid = $userorid;
$user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
}
if (is_object($grouporid)) {
@@ -194,14 +200,20 @@ function groups_remove_member($grouporid, $userorid) {
$DB->delete_records('groups_members', array('groupid'=>$groupid, 'userid'=>$userid));
//update group info
$DB->set_field('groups', 'timemodified', time(), array('id'=>$groupid));
// Update group info.
$time = time();
$DB->set_field('groups', 'timemodified', $time, array('id' => $groupid));
$group->timemodified = $time;
//trigger groups events
$eventdata = new stdClass();
$eventdata->groupid = $groupid;
$eventdata->userid = $userid;
events_trigger('groups_member_removed', $eventdata);
// Trigger group event.
$params = array(
'context' => context_course::instance($group->courseid),
'objectid' => $groupid,
'relateduserid' => $userid
);
$event = \core\event\group_member_removed::create($params);
$event->add_record_snapshot('groups', $group);
$event->trigger();
return true;
}
@@ -257,8 +269,14 @@ function groups_create_group($data, $editform = false, $editoroptions = false) {
// Invalidate the grouping cache for the course
cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($course->id));
//trigger groups events
events_trigger('groups_group_created', $group);
// Trigger group event.
$params = array(
'context' => $context,
'objectid' => $group->id
);
$event = \core\event\group_created::create($params);
$event->add_record_snapshot('groups', $group);
$event->trigger();
return $group->id;
}
@@ -289,8 +307,6 @@ function groups_create_grouping($data, $editoroptions=null) {
}
$id = $DB->insert_record('groupings', $data);
//trigger groups events
$data->id = $id;
if ($editoroptions !== null) {
@@ -304,7 +320,14 @@ function groups_create_grouping($data, $editoroptions=null) {
// Invalidate the grouping cache for the course
cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($data->courseid));
events_trigger('groups_grouping_created', $data);
// Trigger group event.
$params = array(
'context' => context_course::instance($data->courseid),
'objectid' => $id
);
$event = \core\event\grouping_created::create($params);
$event->set_legacy_eventdata($data);
$event->trigger();
return $id;
}
@@ -376,9 +399,14 @@ function groups_update_group($data, $editform = false, $editoroptions = false) {
groups_update_group_icon($group, $data, $editform);
}
//trigger groups events
events_trigger('groups_group_updated', $group);
// Trigger group event.
$params = array(
'context' => $context,
'objectid' => $group->id
);
$event = \core\event\group_updated::create($params);
$event->add_record_snapshot('groups', $group);
$event->trigger();
return true;
}
@@ -408,8 +436,14 @@ function groups_update_grouping($data, $editoroptions=null) {
// Invalidate the group data.
cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($data->courseid));
//trigger groups events
events_trigger('groups_grouping_updated', $data);
// Trigger group event.
$params = array(
'context' => context_course::instance($data->courseid),
'objectid' => $data->id
);
$event = \core\event\grouping_updated::create($params);
$event->set_legacy_eventdata($data);
$event->trigger();
return true;
}
@@ -454,8 +488,14 @@ function groups_delete_group($grouporid) {
// Invalidate the grouping cache for the course
cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($group->courseid));
//trigger groups events
events_trigger('groups_group_deleted', $group);
// Trigger group event.
$params = array(
'context' => $context,
'objectid' => $groupid
);
$event = \core\event\group_deleted::create($params);
$event->add_record_snapshot('groups', $group);
$event->trigger();
return true;
}
@@ -499,8 +539,14 @@ function groups_delete_grouping($groupingorid) {
// Invalidate the grouping cache for the course
cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($grouping->courseid));
//trigger groups events
events_trigger('groups_grouping_deleted', $grouping);
// Trigger group event.
$params = array(
'context' => $context,
'objectid' => $groupingid
);
$event = \core\event\grouping_deleted::create($params);
$event->add_record_snapshot('groupings', $grouping);
$event->trigger();
return true;
}
@@ -521,23 +567,27 @@ function groups_delete_group_members($courseid, $userid=0, $showfeedback=false)
return false;
}
$params = array('courseid'=>$courseid);
// Select * so that the function groups_remove_member() gets the whole record.
$groups = $DB->get_recordset('groups', array('courseid' => $courseid));
foreach ($groups as $group) {
if ($userid) {
$userids = array($userid);
} else {
$userids = $DB->get_fieldset_select('groups_members', 'userid', 'groupid = :groupid', array('groupid' => $group->id));
}
if ($userid) {
$usersql = "AND userid = :userid";
$params['userid'] = $userid;
} else {
$usersql = "";
foreach ($userids as $id) {
groups_remove_member($group, $id);
}
}
$groupssql = "SELECT id FROM {groups} g WHERE g.courseid = :courseid";
$DB->delete_records_select('groups_members', "groupid IN ($groupssql) $usersql", $params);
//trigger groups events
// TODO MDL-41312 Remove events_trigger_legacy('groups_members_removed').
// This event is kept here for backwards compatibility, because it cannot be
// translated to a new event as it is wrong.
$eventdata = new stdClass();
$eventdata->courseid = $courseid;
$eventdata->userid = $userid;
events_trigger('groups_members_removed', $eventdata);
events_trigger_legacy('groups_members_removed', $eventdata);
if ($showfeedback) {
echo $OUTPUT->notification(get_string('deleted').' - '.get_string('groupmembers', 'group'), 'notifysuccess');
@@ -557,13 +607,20 @@ function groups_delete_groupings_groups($courseid, $showfeedback=false) {
global $DB, $OUTPUT;
$groupssql = "SELECT id FROM {groups} g WHERE g.courseid = ?";
$DB->delete_records_select('groupings_groups', "groupid IN ($groupssql)", array($courseid));
$results = $DB->get_recordset_select('groupings_groups', "groupid IN ($groupssql)",
array($courseid), '', 'groupid, groupingid');
foreach ($results as $result) {
groups_unassign_grouping($result->groupingid, $result->groupid, false);
}
// Invalidate the grouping cache for the course
cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($courseid));
//trigger groups events
events_trigger('groups_groupings_groups_removed', $courseid);
// TODO MDL-41312 Remove events_trigger_legacy('groups_groupings_groups_removed').
// This event is kept here for backwards compatibility, because it cannot be
// translated to a new event as it is wrong.
events_trigger_legacy('groups_groupings_groups_removed', $courseid);
// no need to show any feedback here - we delete usually first groupings and then groups
@@ -580,31 +637,18 @@ function groups_delete_groupings_groups($courseid, $showfeedback=false) {
function groups_delete_groups($courseid, $showfeedback=false) {
global $CFG, $DB, $OUTPUT;
// delete any uses of groups
// Any associated files are deleted as part of groups_delete_groupings_groups
groups_delete_groupings_groups($courseid, $showfeedback);
groups_delete_group_members($courseid, 0, $showfeedback);
// delete group pictures and descriptions
$context = context_course::instance($courseid);
$fs = get_file_storage();
$fs->delete_area_files($context->id, 'group');
// delete group calendar events
$groupssql = "SELECT id FROM {groups} g WHERE g.courseid = ?";
$DB->delete_records_select('event', "groupid IN ($groupssql)", array($courseid));
$context = context_course::instance($courseid);
$fs = get_file_storage();
$fs->delete_area_files($context->id, 'group');
$DB->delete_records('groups', array('courseid'=>$courseid));
$groups = $DB->get_recordset('groups', array('courseid' => $courseid));
foreach ($groups as $group) {
groups_delete_group($group);
}
// Invalidate the grouping cache for the course
cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($courseid));
// trigger groups events
events_trigger('groups_groups_deleted', $courseid);
// TODO MDL-41312 Remove events_trigger_legacy('groups_groups_deleted').
// This event is kept here for backwards compatibility, because it cannot be
// translated to a new event as it is wrong.
events_trigger_legacy('groups_groups_deleted', $courseid);
if ($showfeedback) {
echo $OUTPUT->notification(get_string('deleted').' - '.get_string('groups', 'group'), 'notifysuccess');
@@ -623,28 +667,18 @@ function groups_delete_groups($courseid, $showfeedback=false) {
function groups_delete_groupings($courseid, $showfeedback=false) {
global $DB, $OUTPUT;
// delete any uses of groupings
$sql = "DELETE FROM {groupings_groups}
WHERE groupingid in (SELECT id FROM {groupings} g WHERE g.courseid = ?)";
$DB->execute($sql, array($courseid));
$groupings = $DB->get_recordset_select('groupings', 'courseid = ?', array($courseid));
foreach ($groupings as $grouping) {
groups_delete_grouping($grouping);
}
// remove the default groupingid from course
$DB->set_field('course', 'defaultgroupingid', 0, array('id'=>$courseid));
// remove the groupingid from all course modules
$DB->set_field('course_modules', 'groupingid', 0, array('course'=>$courseid));
// Delete all files associated with groupings for this course
$context = context_course::instance($courseid);
$fs = get_file_storage();
$fs->delete_area_files($context->id, 'grouping');
$DB->delete_records('groupings', array('courseid'=>$courseid));
// Invalidate the grouping cache for the course
// Invalidate the grouping cache for the course.
cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($courseid));
// trigger groups events
events_trigger('groups_groupings_deleted', $courseid);
// TODO MDL-41312 Remove events_trigger_legacy('groups_groupings_deleted').
// This event is kept here for backwards compatibility, because it cannot be
// translated to a new event as it is wrong.
events_trigger_legacy('groups_groupings_deleted', $courseid);
if ($showfeedback) {
echo $OUTPUT->notification(get_string('deleted').' - '.get_string('groupings', 'group'), 'notifysuccess');
+385
View File
@@ -0,0 +1,385 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for group lib.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/group/lib.php');
/**
* Group lib testcase.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_group_lib_testcase extends advanced_testcase {
public function test_member_added_event() {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_user();
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$this->getDataGenerator()->enrol_user($user->id, $course->id);
$sink = $this->redirectEvents();
groups_add_member($group->id, $user->id, 'mod_workshop', '123');
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = reset($events);
$expected = new stdClass();
$expected->groupid = $group->id;
$expected->userid = $user->id;
$expected->component = 'mod_workshop';
$expected->itemid = '123';
$this->assertEventLegacyData($expected, $event);
$this->assertSame('groups_member_added', $event->get_legacy_eventname());
$this->assertInstanceOf('\core\event\group_member_added', $event);
$this->assertEquals($user->id, $event->relateduserid);
$this->assertEquals(context_course::instance($course->id), $event->get_context());
$this->assertEquals($group->id, $event->objectid);
}
public function test_member_removed_event() {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$user = $this->getDataGenerator()->create_user();
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$this->getDataGenerator()->enrol_user($user->id, $course->id);
$this->getDataGenerator()->create_group_member(array('userid' => $user->id, 'groupid' => $group->id));
$sink = $this->redirectEvents();
groups_remove_member($group->id, $user->id);
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = reset($events);
$expected = new stdClass();
$expected->groupid = $group->id;
$expected->userid = $user->id;
$this->assertEventLegacyData($expected, $event);
$this->assertSame('groups_member_removed', $event->get_legacy_eventname());
$this->assertInstanceOf('\core\event\group_member_removed', $event);
$this->assertEquals($user->id, $event->relateduserid);
$this->assertEquals(context_course::instance($course->id), $event->get_context());
$this->assertEquals($group->id, $event->objectid);
}
public function test_group_created_event() {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$sink = $this->redirectEvents();
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = reset($events);
$this->assertInstanceOf('\core\event\group_created', $event);
$this->assertEventLegacyData($group, $event);
$this->assertSame('groups_group_created', $event->get_legacy_eventname());
$this->assertEquals(context_course::instance($course->id), $event->get_context());
$this->assertEquals($group->id, $event->objectid);
}
public function test_grouping_created_event() {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$sink = $this->redirectEvents();
$group = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = reset($events);
$this->assertInstanceOf('\core\event\grouping_created', $event);
// 'Repairing' the object for comparison because of type of variables being wrong.
$group->id = (int) $group->id;
$group->timemodified = (int) $group->timemodified;
$group->timecreated = (int) $group->timecreated;
unset($group->idnumber);
unset($group->configdata);
$this->assertEventLegacyData($group, $event);
$this->assertSame('groups_grouping_created', $event->get_legacy_eventname());
$this->assertEquals(context_course::instance($course->id), $event->get_context());
$this->assertEquals($group->id, $event->objectid);
}
public function test_group_updated_event() {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$sink = $this->redirectEvents();
$data = new stdClass();
$data->id = $group->id;
$data->courseid = $course->id;
$data->name = 'Backend team';
groups_update_group($data);
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = reset($events);
$this->assertInstanceOf('\core\event\group_updated', $event);
$group->name = $data->name;
$this->assertEventLegacyData($group, $event);
$this->assertSame('groups_group_updated', $event->get_legacy_eventname());
$this->assertEquals(context_course::instance($course->id), $event->get_context());
$this->assertEquals($group->id, $event->objectid);
}
public function test_grouping_updated_event() {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$group = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
$sink = $this->redirectEvents();
$data = new stdClass();
$data->id = $group->id;
$data->courseid = $course->id;
$data->name = 'Backend team';
$mostaccuratetimemodified = time();
groups_update_grouping($data);
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = reset($events);
$this->assertInstanceOf('\core\event\grouping_updated', $event);
// 'Repairing' the object for comparison because of type of variables being wrong.
$data->id = (int) $group->id;
$data->timemodified = $mostaccuratetimemodified;
$this->assertEventLegacyData($data, $event);
$this->assertSame('groups_grouping_updated', $event->get_legacy_eventname());
$this->assertEquals(context_course::instance($course->id), $event->get_context());
$this->assertEquals($group->id, $event->objectid);
}
public function test_group_deleted_event() {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$sink = $this->redirectEvents();
groups_delete_group($group->id);
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = reset($events);
$this->assertInstanceOf('\core\event\group_deleted', $event);
$this->assertEventLegacyData($group, $event);
$this->assertSame('groups_group_deleted', $event->get_legacy_eventname());
$this->assertEquals(context_course::instance($course->id), $event->get_context());
$this->assertEquals($group->id, $event->objectid);
}
public function test_grouping_deleted_event() {
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$group = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
$sink = $this->redirectEvents();
groups_delete_grouping($group->id);
$events = $sink->get_events();
$this->assertCount(1, $events);
$event = reset($events);
$this->assertInstanceOf('\core\event\grouping_deleted', $event);
$this->assertEventLegacyData($group, $event);
$this->assertSame('groups_grouping_deleted', $event->get_legacy_eventname());
$this->assertEquals(context_course::instance($course->id), $event->get_context());
$this->assertEquals($group->id, $event->objectid);
}
public function test_groups_delete_group_members() {
global $DB;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user1->id, $course->id);
$this->getDataGenerator()->enrol_user($user2->id, $course->id);
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
// Test deletion of all the users.
$this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user1->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user1->id)));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group2->id, 'userid' => $user1->id)));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user2->id)));
groups_delete_group_members($course->id);
$this->assertFalse($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user1->id)));
$this->assertFalse($DB->record_exists('groups_members', array('groupid' => $group2->id, 'userid' => $user1->id)));
$this->assertFalse($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user2->id)));
// Test deletion of a specific user.
$this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user1->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user1->id)));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group2->id, 'userid' => $user1->id)));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user2->id)));
groups_delete_group_members($course->id, $user2->id);
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user1->id)));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group2->id, 'userid' => $user1->id)));
$this->assertFalse($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user2->id)));
}
public function test_groups_remove_member() {
global $DB;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$user1 = $this->getDataGenerator()->create_user();
$user2 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user1->id, $course->id);
$this->getDataGenerator()->enrol_user($user2->id, $course->id);
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group2->id, 'userid' => $user1->id));
$this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user2->id));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user1->id)));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group2->id, 'userid' => $user1->id)));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user2->id)));
groups_remove_member($group1->id, $user1->id);
$this->assertFalse($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user1->id)));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group2->id, 'userid' => $user1->id)));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user2->id)));
groups_remove_member($group1->id, $user2->id);
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group2->id, 'userid' => $user1->id)));
$this->assertFalse($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user2->id)));
groups_remove_member($group2->id, $user1->id);
$this->assertFalse($DB->record_exists('groups_members', array('groupid' => $group2->id, 'userid' => $user1->id)));
}
public function test_groups_delete_groupings_groups() {
global $DB;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$course2 = $this->getDataGenerator()->create_course();
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$group1c2 = $this->getDataGenerator()->create_group(array('courseid' => $course2->id));
$grouping1 = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
$grouping2 = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
$grouping1c2 = $this->getDataGenerator()->create_grouping(array('courseid' => $course2->id));
$this->getDataGenerator()->create_grouping_group(array('groupingid' => $grouping1->id, 'groupid' => $group1->id));
$this->getDataGenerator()->create_grouping_group(array('groupingid' => $grouping1->id, 'groupid' => $group2->id));
$this->getDataGenerator()->create_grouping_group(array('groupingid' => $grouping2->id, 'groupid' => $group1->id));
$this->getDataGenerator()->create_grouping_group(array('groupingid' => $grouping1c2->id, 'groupid' => $group1c2->id));
$this->assertTrue($DB->record_exists('groupings_groups', array('groupid' => $group1->id, 'groupingid' => $grouping1->id)));
$this->assertTrue($DB->record_exists('groupings_groups', array('groupid' => $group2->id, 'groupingid' => $grouping1->id)));
$this->assertTrue($DB->record_exists('groupings_groups', array('groupid' => $group1->id, 'groupingid' => $grouping2->id)));
$this->assertTrue($DB->record_exists('groupings_groups',
array('groupid' => $group1c2->id, 'groupingid' => $grouping1c2->id)));
groups_delete_groupings_groups($course->id);
$this->assertFalse($DB->record_exists('groupings_groups', array('groupid' => $group1->id, 'groupingid' => $grouping1->id)));
$this->assertFalse($DB->record_exists('groupings_groups', array('groupid' => $group2->id, 'groupingid' => $grouping1->id)));
$this->assertFalse($DB->record_exists('groupings_groups', array('groupid' => $group1->id, 'groupingid' => $grouping2->id)));
$this->assertTrue($DB->record_exists('groupings_groups',
array('groupid' => $group1c2->id, 'groupingid' => $grouping1c2->id)));
}
public function test_groups_delete_groups() {
global $DB;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$course2 = $this->getDataGenerator()->create_course();
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$group1c2 = $this->getDataGenerator()->create_group(array('courseid' => $course2->id));
$grouping1 = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
$grouping2 = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
$user1 = $this->getDataGenerator()->create_user();
$this->getDataGenerator()->enrol_user($user1->id, $course->id);
$this->getDataGenerator()->create_group_member(array('groupid' => $group1->id, 'userid' => $user1->id));
$this->getDataGenerator()->create_grouping_group(array('groupid' => $group1->id, 'groupingid' => $grouping1->id));
$this->assertTrue($DB->record_exists('groups', array('id' => $group1->id, 'courseid' => $course->id)));
$this->assertTrue($DB->record_exists('groups', array('id' => $group2->id, 'courseid' => $course->id)));
$this->assertTrue($DB->record_exists('groups', array('id' => $group1c2->id, 'courseid' => $course2->id)));
$this->assertTrue($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user1->id)));
$this->assertTrue($DB->record_exists('groupings', array('id' => $grouping1->id, 'courseid' => $course->id)));
$this->assertTrue($DB->record_exists('groupings_groups', array('groupid' => $group1->id, 'groupingid' => $grouping1->id)));
groups_delete_groups($course->id);
$this->assertFalse($DB->record_exists('groups', array('id' => $group1->id, 'courseid' => $course->id)));
$this->assertFalse($DB->record_exists('groups', array('id' => $group2->id, 'courseid' => $course->id)));
$this->assertTrue($DB->record_exists('groups', array('id' => $group1c2->id, 'courseid' => $course2->id)));
$this->assertFalse($DB->record_exists('groups_members', array('groupid' => $group1->id, 'userid' => $user1->id)));
$this->assertTrue($DB->record_exists('groupings', array('id' => $grouping1->id, 'courseid' => $course->id)));
$this->assertFalse($DB->record_exists('groupings_groups', array('groupid' => $group1->id, 'groupingid' => $grouping1->id)));
}
public function test_groups_delete_groupings() {
global $DB;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$course2 = $this->getDataGenerator()->create_course();
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$grouping1 = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
$grouping2 = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
$grouping1c2 = $this->getDataGenerator()->create_grouping(array('courseid' => $course2->id));
$this->getDataGenerator()->create_grouping_group(array('groupid' => $group1->id, 'groupingid' => $grouping1->id));
$this->assertTrue($DB->record_exists('groups', array('id' => $group1->id, 'courseid' => $course->id)));
$this->assertTrue($DB->record_exists('groupings', array('id' => $grouping1->id, 'courseid' => $course->id)));
$this->assertTrue($DB->record_exists('groupings', array('id' => $grouping2->id, 'courseid' => $course->id)));
$this->assertTrue($DB->record_exists('groupings', array('id' => $grouping1c2->id, 'courseid' => $course2->id)));
$this->assertTrue($DB->record_exists('groupings_groups', array('groupid' => $group1->id, 'groupingid' => $grouping1->id)));
groups_delete_groupings($course->id);
$this->assertTrue($DB->record_exists('groups', array('id' => $group1->id, 'courseid' => $course->id)));
$this->assertFalse($DB->record_exists('groupings', array('id' => $grouping1->id, 'courseid' => $course->id)));
$this->assertFalse($DB->record_exists('groupings', array('id' => $grouping2->id, 'courseid' => $course->id)));
$this->assertTrue($DB->record_exists('groupings', array('id' => $grouping1c2->id, 'courseid' => $course2->id)));
$this->assertFalse($DB->record_exists('groupings_groups', array('groupid' => $group1->id, 'groupingid' => $grouping1->id)));
}
}
+3
View File
@@ -250,6 +250,9 @@ $string['skipmodifdays'] = 'Skip courses not modified since';
$string['skipmodifdayshelp'] = 'Choose to skip courses that have not been modified since a number of days';
$string['skipmodifprev'] = 'Skip courses not modified since previous backup';
$string['skipmodifprevhelp'] = 'Choose whether or not to skip courses that have not been modified since previous backup';
$string['title'] = 'Title';
$string['totalcategorysearchresults'] = 'Total categories: {$a}';
$string['totalcoursesearchresults'] = 'Total courses: {$a}';
$string['userinfo'] = 'Userinfo';
$string['module'] = 'Module';
$string['morecoursesearchresults'] = 'More than {$a} courses found, showing first {$a} results';
+8
View File
@@ -67,6 +67,14 @@ $string['errorremovenotpermitted'] = 'You do not have permission to remove autom
$string['errorselectone'] = 'Please select a single group before choosing this option';
$string['errorselectsome'] = 'Please select one or more groups before choosing this option';
$string['evenallocation'] = 'Note: To keep group allocation even, the actual number of members per group differs from the number you specified.';
$string['event_group_created'] = 'Group created';
$string['event_group_deleted'] = 'Group deleted';
$string['event_group_member_added'] = 'Group member added';
$string['event_group_member_removed'] = 'Group member removed';
$string['event_group_updated'] = 'Group updated';
$string['event_grouping_created'] = 'Grouping created';
$string['event_grouping_deleted'] = 'Grouping deleted';
$string['event_grouping_updated'] = 'Grouping updated';
$string['existingmembers'] = 'Existing members: {$a}';
$string['filtergroups'] = 'Filter groups by:';
$string['group'] = 'Group';
+1
View File
@@ -869,6 +869,7 @@ $string['chooselogs'] = 'Choose which logs you want to see';
$string['choosereportfilter'] = 'Choose a filter for the report';
$string['choosetheme'] = 'Choose theme';
$string['chooseuser'] = 'Choose a user';
$string['eventcontentviewed'] = 'Content viewed';
$string['icqnumber'] = 'ICQ number';
$string['icon'] = 'Icon';
$string['idnumber'] = 'ID number';
+1 -1
View File
@@ -30,7 +30,7 @@ $string['alltagpages'] = 'All tag pages';
$string['count'] = 'Count';
$string['delete'] = 'Delete';
$string['deleted'] = 'Deleted';
$string['deletedcoursetags'] = 'Deleted - course tags';
$string['deletedcoursetags'] = 'Deleted - Course tags';
$string['description'] = 'Description';
$string['edittag'] = 'Edit this tag';
$string['entertags'] = 'Enter tags separated by commas';
+3
View File
@@ -329,6 +329,9 @@ function uninstall_plugin($type, $name) {
// Finally purge all caches.
purge_all_caches();
// Invalidate the hash used for upgrade detections.
set_config('allversionshash', '');
echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
}
+6 -6
View File
@@ -51,7 +51,7 @@ function ajaxenabled(array $browsers = null) {
if (!empty($browsers)) {
$valid = false;
foreach ($browsers as $brand => $version) {
if (check_browser_version($brand, $version)) {
if (core_useragent::check_browser_version($brand, $version)) {
$valid = true;
}
}
@@ -61,11 +61,11 @@ function ajaxenabled(array $browsers = null) {
}
}
$ie = check_browser_version('MSIE', 6.0);
$ff = check_browser_version('Gecko', 20051106);
$op = check_browser_version('Opera', 9.0);
$sa = check_browser_version('Safari', 412);
$ch = check_browser_version('Chrome', 6);
$ie = core_useragent::check_browser_version('MSIE', 6.0);
$ff = core_useragent::check_browser_version('Gecko', 20051106);
$op = core_useragent::check_browser_version('Opera', 9.0);
$sa = core_useragent::check_browser_version('Safari', 412);
$ch = core_useragent::check_browser_version('Chrome', 6);
if (!$ie && !$ff && !$op && !$sa && !$ch) {
/** @see http://en.wikipedia.org/wiki/User_agent */
-130
View File
@@ -1,130 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for (some of) ../ajaxlib.php.
*
* @package core
* @category phpunit
* @copyright 2009 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/ajax/ajaxlib.php');
/**
* Unit tests for ../ajaxlib.php functions.
*
* @copyright 2008 Tim Hunt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_ajaxlib_testcase extends advanced_testcase {
var $user_agents = array(
'MSIE' => array(
'5.5' => array('Windows 2000' => 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)'),
'6.0' => array('Windows XP SP2' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)'),
'7.0' => array('Windows XP SP2' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; YPC 3.0.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)')
),
'Firefox' => array(
'1.0.6' => array('Windows XP' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.10) Gecko/20050716 Firefox/1.0.6'),
'1.5' => array('Windows XP' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; nl; rv:1.8) Gecko/20051107 Firefox/1.5'),
'1.5.0.1' => array('Windows XP' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.0.1) Gecko/20060111 Firefox/1.5.0.1'),
'2.0' => array('Windows XP' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1',
'Ubuntu Linux AMD64' => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1) Gecko/20060601 Firefox/2.0 (Ubuntu-edgy)')
),
'Safari' => array(
'312' => array('Mac OS X' => 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312'),
'2.0' => array('Mac OS X' => 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/412 (KHTML, like Gecko) Safari/412')
),
'Opera' => array(
'8.51' => array('Windows XP' => 'Opera/8.51 (Windows NT 5.1; U; en)'),
'9.0' => array('Windows XP' => 'Opera/9.0 (Windows NT 5.1; U; en)',
'Debian Linux' => 'Opera/9.01 (X11; Linux i686; U; en)')
)
);
/**
* Uses the array of user agents to test ajax_lib::ajaxenabled
*/
function test_ajaxenabled() {
global $CFG;
$this->resetAfterTest(true);
$CFG->enableajax = 1;
// Should be true
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['2.0']['Windows XP'];
$this->assertTrue(ajaxenabled());
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['1.5']['Windows XP'];
$this->assertTrue(ajaxenabled());
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Safari']['2.0']['Mac OS X'];
$this->assertTrue(ajaxenabled());
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Opera']['9.0']['Windows XP'];
$this->assertTrue(ajaxenabled());
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['6.0']['Windows XP SP2'];
$this->assertTrue(ajaxenabled());
// Should be false
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['1.0.6']['Windows XP'];
$this->assertFalse(ajaxenabled());
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Safari']['312']['Mac OS X'];
$this->assertFalse(ajaxenabled());
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Opera']['8.51']['Windows XP'];
$this->assertFalse(ajaxenabled());
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['5.5']['Windows 2000'];
$this->assertFalse(ajaxenabled());
// Test array of tested browsers
$tested_browsers = array('MSIE' => 6.0, 'Gecko' => 20061111);
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['2.0']['Windows XP'];
$this->assertTrue(ajaxenabled($tested_browsers));
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['7.0']['Windows XP SP2'];
$this->assertTrue(ajaxenabled($tested_browsers));
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Safari']['2.0']['Mac OS X'];
$this->assertFalse(ajaxenabled($tested_browsers));
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Opera']['9.0']['Windows XP'];
$this->assertFalse(ajaxenabled($tested_browsers));
$tested_browsers = array('Safari' => 412, 'Opera' => 9.0);
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['2.0']['Windows XP'];
$this->assertFalse(ajaxenabled($tested_browsers));
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['7.0']['Windows XP SP2'];
$this->assertFalse(ajaxenabled($tested_browsers));
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Safari']['2.0']['Mac OS X'];
$this->assertTrue(ajaxenabled($tested_browsers));
$_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Opera']['9.0']['Windows XP'];
$this->assertTrue(ajaxenabled($tested_browsers));
}
}
+124
View File
@@ -0,0 +1,124 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Abstract event for content viewing.
*
* @package core
* @copyright 2013 Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\event;
/**
* Class content_viewed.
*
* Base class for a content view event. Each plugin must extend this to create their own content view event.
*
* An example usage:-
* $event = \report_participation\event\content_viewed::create(array('courseid' => $course->id,
* 'other' => array('content' => 'participants'));
* $event->set_page_detail();
* $event->set_legacy_logdata(array($course->id, "course", "report participation",
* "report/participation/index.php?id=$course->id", $course->id));
* $event->trigger();
* where \report_participation\event\content_viewed extends \core\event\content_viewed
*
* @package core
* @copyright 2013 Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class content_viewed extends base {
/** @var null|array $legacylogdata Legacy log data */
protected $legacylogdata = null;
/**
* Set basic properties of the event.
*/
protected function init() {
global $PAGE;
$this->data['crud'] = 'r';
$this->data['level'] = self::LEVEL_OTHER;
$this->context = $PAGE->context;
}
/**
* Set basic page properties.
*/
public function set_page_detail() {
global $PAGE;
if (!isset($this->data['other'])) {
$this->data['other'] = array();
}
$this->data['other'] = array_merge(array('url' => $PAGE->url->out_as_local_url(false),
'heading' => $PAGE->heading,
'title' => $PAGE->title), $this->data['other']);
}
/**
* Returns localised general event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventcontentviewed', 'moodle');
}
/**
* Returns non-localised description of what happened.
*
* @return string
*/
public function get_description() {
return 'User with id ' . $this->userid . ' viewed content ' . $this->get_url();
}
/**
* Set legacy logdata.
*
* @param array $legacydata legacy logdata.
*/
public function set_legacy_logdata(array $legacydata) {
$this->legacylogdata = $legacydata;
}
/**
* Get legacy logdata.
*
* @return null|array legacy log data.
*/
protected function get_legacy_logdata() {
return $this->legacylogdata;
}
/**
* Custom validation.
*
* @throws \coding_exception when validation does not pass.
* @return void
*/
protected function validate_data() {
if (debugging('', DEBUG_DEVELOPER)) {
// Make sure this class is never used without a content identifier.
if (empty($this->other['content'])) {
throw new \coding_exception('content_viewed event must define content identifier.');
}
}
}
}
+93
View File
@@ -0,0 +1,93 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* core_group created event.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\event;
defined('MOODLE_INTERNAL') || die();
/**
* core_group created event class.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_created extends \core\event\base {
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "User {$this->userid} created the group {$this->objectid}.";
}
/**
* Legacy event data if get_legacy_eventname() is not empty.
*
* @return stdClass
*/
protected function get_legacy_eventdata() {
return $this->get_record_snapshot('groups', $this->objectid);
}
/**
* Return the legacy event name.
*
* @return string
*/
public static function get_legacy_eventname() {
return 'groups_group_created';
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('event_group_created', 'group');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/group/index.php', array('id' => $this->courseid));
}
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'c';
$this->data['level'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'groups';
}
}
+93
View File
@@ -0,0 +1,93 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* core_group deleted event.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\event;
defined('MOODLE_INTERNAL') || die();
/**
* core_group deleted event class.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_deleted extends \core\event\base {
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "User {$this->userid} deleted the group {$this->objectid}.";
}
/**
* Legacy event data if get_legacy_eventname() is not empty.
*
* @return stdClass
*/
protected function get_legacy_eventdata() {
return $this->get_record_snapshot('groups', $this->objectid);
}
/**
* Return the legacy event name.
*
* @return string
*/
public static function get_legacy_eventname() {
return 'groups_group_deleted';
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('event_group_deleted', 'group');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/group/index.php', array('id' => $this->courseid));
}
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'd';
$this->data['level'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'groups';
}
}
+110
View File
@@ -0,0 +1,110 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* core_group member added event.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\event;
defined('MOODLE_INTERNAL') || die();
/**
* core_group member added event class.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_member_added extends \core\event\base {
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "User {$this->userid} added user {$this->relateduserid} to group {$this->objectid}.";
}
/**
* Legacy event data if get_legacy_eventname() is not empty.
*
* @return stdClass
*/
protected function get_legacy_eventdata() {
$eventdata = new \stdClass();
$eventdata->groupid = $this->objectid;
$eventdata->userid = $this->relateduserid;
$eventdata->component = $this->other['component'];
$eventdata->itemid = $this->other['itemid'];
return $eventdata;
}
/**
* Return the legacy event name.
*
* @return string
*/
public static function get_legacy_eventname() {
return 'groups_member_added';
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('event_group_member_added', 'group');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/group/index.php', array('id' => $this->courseid));
}
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'c';
$this->data['level'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'groups';
}
/**
* Custom validation.
*
* @throws \coding_exception
* @return void
*/
protected function validate_data() {
if (!isset($this->other['component']) || !isset($this->other['itemid'])) {
throw new \coding_exception('The component and itemid need to be set in $other, even if empty.');
}
}
}
@@ -0,0 +1,96 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* core_group member removed event.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\event;
defined('MOODLE_INTERNAL') || die();
/**
* core_group member removed event class.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_member_removed extends \core\event\base {
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "User {$this->userid} removed user {$this->relateduserid} from group {$this->objectid}.";
}
/**
* Legacy event data if get_legacy_eventname() is not empty.
*
* @return stdClass
*/
protected function get_legacy_eventdata() {
$eventdata = new \stdClass();
$eventdata->groupid = $this->objectid;
$eventdata->userid = $this->relateduserid;
return $eventdata;
}
/**
* Return the legacy event name.
*
* @return string
*/
public static function get_legacy_eventname() {
return 'groups_member_removed';
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('event_group_member_removed', 'group');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/group/index.php', array('id' => $this->courseid));
}
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'd';
$this->data['level'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'groups';
}
}
+93
View File
@@ -0,0 +1,93 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* core_group updated event.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\event;
defined('MOODLE_INTERNAL') || die();
/**
* core_group updated event class.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class group_updated extends \core\event\base {
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "User {$this->userid} updated the group {$this->objectid}.";
}
/**
* Legacy event data if get_legacy_eventname() is not empty.
*
* @return stdClass
*/
protected function get_legacy_eventdata() {
return $this->get_record_snapshot('groups', $this->objectid);
}
/**
* Return the legacy event name.
*
* @return string
*/
public static function get_legacy_eventname() {
return 'groups_group_updated';
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('event_group_updated', 'group');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/group/index.php', array('id' => $this->courseid));
}
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'u';
$this->data['level'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'groups';
}
}
+110
View File
@@ -0,0 +1,110 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* core_group grouping created event.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\event;
defined('MOODLE_INTERNAL') || die();
/**
* core_group grouping created event class.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grouping_created extends \core\event\base {
/**
* Legacy data.
*
* @var mixed
*/
protected $legacydata;
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "User {$this->userid} created the grouping {$this->objectid}.";
}
/**
* Legacy event data if get_legacy_eventname() is not empty.
*
* @return stdClass
*/
protected function get_legacy_eventdata() {
return $this->legacydata;
}
/**
* Return the legacy event name.
*
* @return string
*/
public static function get_legacy_eventname() {
return 'groups_grouping_created';
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('event_grouping_created', 'group');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/group/groupings/index.php', array('id' => $this->courseid));
}
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'c';
$this->data['level'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'groupings';
}
/**
* Set legacy data.
*
* @param mixed $legacydata.
* @return void
*/
public function set_legacy_eventdata($legacydata) {
$this->legacydata = $legacydata;
}
}
+100
View File
@@ -0,0 +1,100 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* core_group grouping deleted event.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\event;
defined('MOODLE_INTERNAL') || die();
/**
* core_group grouping deleted event class.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grouping_deleted extends \core\event\base {
/**
* Legacy data.
*
* @var mixed
*/
protected $legacydata;
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "User {$this->userid} deleted the grouping {$this->objectid}.";
}
/**
* Legacy event data if get_legacy_eventname() is not empty.
*
* @return stdClass
*/
protected function get_legacy_eventdata() {
return $this->get_record_snapshot('groupings', $this->objectid);
}
/**
* Return the legacy event name.
*
* @return string
*/
public static function get_legacy_eventname() {
return 'groups_grouping_deleted';
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('event_grouping_deleted', 'group');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/group/groupings/index.php', array('id' => $this->courseid));
}
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'd';
$this->data['level'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'groupings';
}
}
+110
View File
@@ -0,0 +1,110 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* core_group grouping updated event.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\event;
defined('MOODLE_INTERNAL') || die();
/**
* core_group grouping updated event class.
*
* @package core_group
* @copyright 2013 Frédéric Massart
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grouping_updated extends \core\event\base {
/**
* Legacy data.
*
* @var mixed
*/
protected $legacydata;
/**
* Returns description of what happened.
*
* @return string
*/
public function get_description() {
return "User {$this->userid} updated the grouping {$this->objectid}.";
}
/**
* Legacy event data if get_legacy_eventname() is not empty.
*
* @return stdClass
*/
protected function get_legacy_eventdata() {
return $this->legacydata;
}
/**
* Return the legacy event name.
*
* @return string
*/
public static function get_legacy_eventname() {
return 'groups_grouping_updated';
}
/**
* Return localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('event_grouping_updated', 'group');
}
/**
* Get URL related to the action
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/group/groupings/index.php', array('id' => $this->courseid));
}
/**
* Init method.
*
* @return void
*/
protected function init() {
$this->data['crud'] = 'u';
$this->data['level'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'groupings';
}
/**
* Set legacy data.
*
* @param mixed $legacydata.
* @return void
*/
public function set_legacy_eventdata($legacydata) {
$this->legacydata = $legacydata;
}
}
+2 -2
View File
@@ -49,7 +49,7 @@ class user_loggedin extends \core\event\base {
*
* @return array
*/
public function get_legacy_logdata() {
protected function get_legacy_logdata() {
return array(SITEID, 'user', 'login', "view.php?id=" . $this->data['objectid'] . "&course=".SITEID,
$this->data['objectid'], 0, $this->data['objectid']);
}
@@ -60,7 +60,7 @@ class user_loggedin extends \core\event\base {
* @return string
*/
public static function get_name() {
return new get_string('event_user_loggedin', 'auth');
return get_string('event_user_loggedin', 'auth');
}
/**
+31 -1
View File
@@ -238,6 +238,36 @@ class core_text {
return $result;
}
/**
* Finds the last occurrence of a character in a string within another.
* UTF-8 ONLY safe mb_strrchr().
*
* @param string $haystack The string from which to get the last occurrence of needle.
* @param string $needle The string to find in haystack.
* @param boolean $part If true, returns the portion before needle, else return the portion after (including needle).
* @return string|false False when not found.
* @since 2.4.6, 2.5.2, 2.6
*/
public static function strrchr($haystack, $needle, $part = false) {
if (function_exists('mb_strrchr')) {
return mb_strrchr($haystack, $needle, $part, 'UTF-8');
}
$pos = self::strrpos($haystack, $needle);
if ($pos === false) {
return false;
}
$length = null;
if ($part) {
$length = $pos;
$pos = 0;
}
return self::substr($haystack, $pos, $length, 'utf-8');
}
/**
* Multibyte safe strlen() function, uses mbstring or iconv for UTF-8, falls back to typo3.
*
@@ -331,7 +361,7 @@ class core_text {
* @return int the numeric position of the last occurrence of needle in haystack
*/
public static function strrpos($haystack, $needle) {
if (function_exists('mb_strpos')) {
if (function_exists('mb_strrpos')) {
return mb_strrpos($haystack, $needle, null, 'UTF-8');
} else {
return iconv_strrpos($haystack, $needle, 'UTF-8');
+751
View File
@@ -0,0 +1,751 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Environment class to aid with the detection and establishment of the working environment.
*
* @package core
* @copyright 2013 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* The user agent class.
*
* It's important to note that we do not like browser sniffing and its use in core code is highly discouraged.
* No new uses of this API will be integrated unless there is absolutely no alternative.
*
* This API supports the few browser checks we do have in core, all of which one day will hopefully be removed.
* The API will remain to support any third party use out there, however at some point like all code it will be deprecated.
*
* Use sparingly and only with good cause!
*
* @package core
* @copyright 2013 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_useragent {
/**
* The default for devices, think of as a computer.
*/
const DEVICETYPE_DEFAULT = 'default';
/**
* Legacy devices, or at least legacy browsers. These are older devices/browsers
* that don't support standards.
*/
const DEVICETYPE_LEGACY = 'legacy';
/**
* Mobile devices like your cell phone or hand held gaming device.
*/
const DEVICETYPE_MOBILE = 'mobile';
/**
* Tables, larger than hand held, but still easily portable and smaller than a laptop.
*/
const DEVICETYPE_TABLET = 'tablet';
/**
* An instance of this class.
* @var core_useragent
*/
protected static $instance = null;
/**
* The device types we track.
* @var array
*/
public static $devicetypes = array(
self::DEVICETYPE_DEFAULT,
self::DEVICETYPE_LEGACY,
self::DEVICETYPE_MOBILE,
self::DEVICETYPE_TABLET
);
/**
* The current requests user agent string if there was one.
* @var string|bool|null Null until initialised, false if none available, or string when available.
*/
protected $useragent = null;
/**
* The users device type, one of self::DEVICETYPE_*.
* @var string null until initialised
*/
protected $devicetype = null;
/**
* Custom device types entered into the admin interface.
* @var array
*/
protected $devicetypecustoms = array();
/**
* True if the user agent supports the display of svg images. False if not.
* @var bool|null Null until initialised, then true or false.
*/
protected $supportssvg = null;
/**
* Get an instance of the user agent object.
*
* @param bool $reload If set to true the user agent will be reset and all ascertations remade.
* @param string $forceuseragent The string to force as the user agent, don't use unless absolutely unavoidable.
* @return core_useragent
*/
public static function instance($reload = false, $forceuseragent = null) {
if (!self::$instance || $reload) {
self::$instance = new core_useragent($forceuseragent);
}
return self::$instance;
}
/**
* Constructs a new user agent object. Publically you must use the instance method above.
*
* @param string|null $forceuseragent Optional a user agent to force.
*/
protected function __construct($forceuseragent = null) {
global $CFG;
if (!empty($CFG->devicedetectregex)) {
$this->devicetypecustoms = json_decode($CFG->devicedetectregex);
}
if ($forceuseragent !== null) {
$this->useragent = $forceuseragent;
} else if (!empty($_SERVER['HTTP_USER_AGENT'])) {
$this->useragent = $_SERVER['HTTP_USER_AGENT'];
} else {
$this->useragent = false;
$this->devicetype = self::DEVICETYPE_DEFAULT;
}
}
/**
* Returns the user agent string.
* @return bool|string The user agent string or false if one isn't available.
*/
public static function get_user_agent_string() {
$instance = self::instance();
return $instance->useragent;
}
/**
* Returns the device type we believe is being used.
* @return string
*/
public static function get_device_type() {
$instance = self::instance();
if ($instance->devicetype === null) {
return $instance->guess_device_type();
}
return $instance->devicetype;
}
/**
* Guesses the device type the user agent is running on.
*
* @return string
*/
protected function guess_device_type() {
global $CFG;
if (empty($CFG->enabledevicedetection)) {
$this->devicetype = self::DEVICETYPE_DEFAULT;
return $this->devicetype;
}
foreach ($this->devicetypecustoms as $value => $regex) {
if (preg_match($regex, $this->useragent)) {
$this->devicetype = $value;
return $this->devicetype;
}
}
if ($this->is_useragent_mobile()) {
$this->devicetype = 'mobile';
} else if ($this->is_useragent_tablet()) {
$this->devicetype = 'tablet';
} else if (substr($this->useragent, 0, 34) === 'Mozilla/4.0 (compatible; MSIE 6.0;') {
// Safe way to check for IE6 and not get false positives for some IE 7/8 users.
$this->devicetype = 'legacy';
} else {
$this->devicetype = self::DEVICETYPE_DEFAULT;
}
return $this->devicetype;
}
/**
* Returns true if the user appears to be on a mobile device.
* @return bool
*/
protected function is_useragent_mobile() {
// Mobile detection PHP direct copy from open source detectmobilebrowser.com.
$phonesregex = '/android .+ mobile|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i';
$modelsregex = '/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|e\-|e\/|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(\-|2|g)|yas\-|your|zeto|zte\-/i';
return (preg_match($phonesregex, $this->useragent) || preg_match($modelsregex, substr($this->useragent, 0, 4)));
}
/**
* Returns true if the user appears to be on a tablet.
* @return int
*/
protected function is_useragent_tablet() {
$tabletregex = '/Tablet browser|android|iPad|iProd|GT-P1000|GT-I9000|SHW-M180S|SGH-T849|SCH-I800|Build\/ERE27|sholest/i';
return (preg_match($tabletregex, $this->useragent));
}
/**
* Gets a list of known device types.
*
* @param bool $includecustomtypes If set to true we'll include types that have been added by the admin.
* @return array
*/
public static function get_device_type_list($includecustomtypes = true) {
$types = self::$devicetypes;
if ($includecustomtypes) {
$instance = self::instance();
$types = array_merge($types, array_keys($instance->devicetypecustoms));
}
return $types;
}
/**
* Returns the theme to use for the given device type.
*
* This used to be get_selected_theme_for_device_type.
* @param null|string $devicetype The device type to find out for. Defaults to the device the user is using,
* @return bool
*/
public static function get_device_type_theme($devicetype = null) {
global $CFG;
if ($devicetype === null) {
$devicetype = self::get_device_type();
}
$themevarname = self::get_device_type_cfg_var_name($devicetype);
if (empty($CFG->$themevarname)) {
return false;
}
return $CFG->$themevarname;
}
/**
* Returns the CFG var used to find the theme to use for the given device.
*
* Used to be get_device_cfg_var_name.
*
* @param null|string $devicetype The device type to find out for. Defaults to the device the user is using,
* @return string
*/
public static function get_device_type_cfg_var_name($devicetype = null) {
if ($devicetype == self::DEVICETYPE_DEFAULT || empty($devicetype)) {
return 'theme';
}
return 'theme' . $devicetype;
}
/**
* Gets the device type the user is currently using.
* @return string
*/
public static function get_user_device_type() {
$device = self::get_device_type();
$switched = get_user_preferences('switchdevice'.$device, false);
if ($switched != false) {
return $switched;
}
return $device;
}
/**
* Switches the device type we think the user is using to what ever was given.
* @param string $newdevice
* @return bool
* @throws coding_exception
*/
public static function set_user_device_type($newdevice) {
$devicetype = self::get_device_type();
if ($newdevice == $devicetype) {
unset_user_preference('switchdevice'.$devicetype);
return true;
} else {
$devicetypes = self::get_device_type_list();
if (in_array($newdevice, $devicetypes)) {
set_user_preference('switchdevice'.$devicetype, $newdevice);
return true;
}
}
throw new coding_exception('Invalid device type provided to set_user_device_type');
}
/**
* Returns true if the user agent matches the given brand and the version is equal to or greater than that specified.
*
* @param string $brand The branch to check for.
* @param scalar $version The version if we need to find out if it is equal to or greater than that specified.
* @return bool
*/
public static function check_browser_version($brand, $version = null) {
switch ($brand) {
case 'MSIE':
// Internet Explorer.
return self::check_ie_version($version);
case 'Firefox':
// Mozilla Firefox browsers.
return self::check_firefox_version($version);
case 'Chrome':
return self::check_chrome_version($version);
case 'Opera':
// Opera.
return self::check_opera_version($version);
case 'Safari':
// Desktop version of Apple Safari browser - no mobile or touch devices.
return self::check_safari_version($version);
case 'Safari iOS':
// Safari on iPhone, iPad and iPod touch.
return self::check_safari_ios_version($version);
case 'WebKit':
// WebKit based browser - everything derived from it (Safari, Chrome, iOS, Android and other mobiles).
return self::check_webkit_version($version);
case 'Gecko':
// Gecko based browsers.
return self::check_gecko_version($version);
case 'WebKit Android':
// WebKit browser on Android.
return self::check_webkit_android_version($version);
case 'Camino':
// OSX browser using Gecke engine.
return self::check_camino_version($version);
}
// Who knows?! doesn't pass anyway.
return false;
}
/**
* Checks the user agent is camino based and that the version is equal to or greater than that specified.
*
* Camino browser is at the end of its life, its no longer being developed or supported, just don't worry about it.
*
* @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
* @return bool
*/
protected static function check_camino_version($version = null) {
// OSX browser using Gecko engine.
$useragent = self::get_user_agent_string();
if ($useragent === false) {
return false;
}
if (strpos($useragent, 'Camino') === false) {
return false;
}
if (empty($version)) {
return true; // No version specified.
}
if (preg_match("/Camino\/([0-9\.]+)/i", $useragent, $match)) {
if (version_compare($match[1], $version) >= 0) {
return true;
}
}
return false;
}
/**
* Checks the user agent is Firefox based and that the version is equal to or greater than that specified.
*
* @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
* @return bool
*/
public static function check_firefox_version($version = null) {
// Mozilla Firefox browsers.
$useragent = self::get_user_agent_string();
if ($useragent === false) {
return false;
}
if (strpos($useragent, 'Firefox') === false && strpos($useragent, 'Iceweasel') === false) {
return false;
}
if (empty($version)) {
return true; // No version specified..
}
if (preg_match("/(Iceweasel|Firefox)\/([0-9\.]+)/i", $useragent, $match)) {
if (version_compare($match[2], $version) >= 0) {
return true;
}
}
return false;
}
/**
* Checks the user agent is Gecko based and that the version is equal to or greater than that specified.
*
* @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
* @return bool
*/
public static function check_gecko_version($version = null) {
// Gecko based browsers.
// Do not look for dates any more, we expect real Firefox version here.
$useragent = self::get_user_agent_string();
if ($useragent === false) {
return false;
}
if (empty($version)) {
$version = 1;
} else if ($version > 20000000) {
// This is just a guess, it is not supposed to be 100% accurate!
if (preg_match('/^201/', $version)) {
$version = 3.6;
} else if (preg_match('/^200[7-9]/', $version)) {
$version = 3;
} else if (preg_match('/^2006/', $version)) {
$version = 2;
} else {
$version = 1.5;
}
}
if (preg_match("/(Iceweasel|Firefox)\/([0-9\.]+)/i", $useragent, $match)) {
// Use real Firefox version if specified in user agent string.
if (version_compare($match[2], $version) >= 0) {
return true;
}
} else if (preg_match("/Gecko\/([0-9\.]+)/i", $useragent, $match)) {
// Gecko might contain date or Firefox revision, let's just guess the Firefox version from the date.
$browserver = $match[1];
if ($browserver > 20000000) {
// This is just a guess, it is not supposed to be 100% accurate!
if (preg_match('/^201/', $browserver)) {
$browserver = 3.6;
} else if (preg_match('/^200[7-9]/', $browserver)) {
$browserver = 3;
} else if (preg_match('/^2006/', $version)) {
$browserver = 2;
} else {
$browserver = 1.5;
}
}
if (version_compare($browserver, $version) >= 0) {
return true;
}
}
return false;
}
/**
* Checks the user agent is IE and that the version is equal to or greater than that specified.
*
* @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
* @return bool
*/
public static function check_ie_version($version = null) {
// Internet Explorer.
$useragent = self::get_user_agent_string();
if ($useragent === false) {
return false;
}
if (strpos($useragent, 'Opera') !== false) {
// Reject Opera.
return false;
}
// In case of IE we have to deal with BC of the version parameter.
if (is_null($version)) {
$version = 5.5; // Anything older is not considered a browser at all!
}
// IE uses simple versions, let's cast it to float to simplify the logic here.
$version = round($version, 1);
// See: http://www.useragentstring.com/pages/Internet%20Explorer/.
if (preg_match("/MSIE ([0-9\.]+)/", $useragent, $match)) {
$browser = $match[1];
} else {
return false;
}
// IE8 and later versions may pretend to be IE7 for intranet sites, use Trident version instead,
// the Trident should always describe the capabilities of IE in any emulation mode.
if ($browser === '7.0' and preg_match("/Trident\/([0-9\.]+)/", $useragent, $match)) {
$browser = $match[1] + 4; // NOTE: Hopefully this will work also for future IE versions.
}
$browser = round($browser, 1);
return ($browser >= $version);
}
/**
* Checks the user agent is Opera and that the version is equal to or greater than that specified.
*
* @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
* @return bool
*/
public static function check_opera_version($version = null) {
// Opera.
$useragent = self::get_user_agent_string();
if ($useragent === false) {
return false;
}
if (strpos($useragent, 'Opera') === false) {
return false;
}
if (empty($version)) {
return true; // No version specified.
}
// Recent Opera useragents have Version/ with the actual version, e.g.:
// Opera/9.80 (Windows NT 6.1; WOW64; U; en) Presto/2.10.289 Version/12.01
// That's Opera 12.01, not 9.8.
if (preg_match("/Version\/([0-9\.]+)/i", $useragent, $match)) {
if (version_compare($match[1], $version) >= 0) {
return true;
}
} else if (preg_match("/Opera\/([0-9\.]+)/i", $useragent, $match)) {
if (version_compare($match[1], $version) >= 0) {
return true;
}
}
return false;
}
/**
* Checks the user agent is Webkit based and that the version is equal to or greater than that specified.
*
* @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
* @return bool
*/
public static function check_webkit_version($version = null) {
// WebKit based browser - everything derived from it (Safari, Chrome, iOS, Android and other mobiles).
$useragent = self::get_user_agent_string();
if ($useragent === false) {
return false;
}
if (strpos($useragent, 'AppleWebKit') === false) {
return false;
}
if (empty($version)) {
return true; // No version specified.
}
if (preg_match("/AppleWebKit\/([0-9.]+)/i", $useragent, $match)) {
if (version_compare($match[1], $version) >= 0) {
return true;
}
}
return false;
}
/**
* Checks the user agent is Safari based and that the version is equal to or greater than that specified.
*
* @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
* @return bool
*/
public static function check_safari_version($version = null) {
// Desktop version of Apple Safari browser - no mobile or touch devices.
$useragent = self::get_user_agent_string();
if ($useragent === false) {
return false;
}
if (strpos($useragent, 'AppleWebKit') === false) {
return false;
}
// Look for AppleWebKit, excluding strings with OmniWeb, Shiira and SymbianOS and any other mobile devices.
if (strpos($useragent, 'OmniWeb')) {
// Reject OmniWeb.
return false;
}
if (strpos($useragent, 'Shiira')) {
// Reject Shiira.
return false;
}
if (strpos($useragent, 'SymbianOS')) {
// Reject SymbianOS.
return false;
}
if (strpos($useragent, 'Android')) {
// Reject Androids too.
return false;
}
if (strpos($useragent, 'iPhone') or strpos($useragent, 'iPad') or strpos($useragent, 'iPod')) {
// No Apple mobile devices here - editor does not work, course ajax is not touch compatible, etc.
return false;
}
if (strpos($useragent, 'Chrome')) { // Reject chrome browsers - it needs to be tested explicitly.
return false;
}
if (empty($version)) {
return true; // No version specified.
}
if (preg_match("/AppleWebKit\/([0-9.]+)/i", $useragent, $match)) {
if (version_compare($match[1], $version) >= 0) {
return true;
}
}
return false;
}
/**
* Checks the user agent is Chrome based and that the version is equal to or greater than that specified.
*
* @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
* @return bool
*/
public static function check_chrome_version($version = null) {
// Chrome.
$useragent = self::get_user_agent_string();
if ($useragent === false) {
return false;
}
if (strpos($useragent, 'Chrome') === false) {
return false;
}
if (empty($version)) {
return true; // No version specified.
}
if (preg_match("/Chrome\/(.*)[ ]+/i", $useragent, $match)) {
if (version_compare($match[1], $version) >= 0) {
return true;
}
}
return false;
}
/**
* Checks the user agent is Webkit based and on Android and that the version is equal to or greater than that specified.
*
* @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
* @return bool
*/
public static function check_webkit_android_version($version = null) {
// WebKit browser on Android.
$useragent = self::get_user_agent_string();
if ($useragent === false) {
return false;
}
if (strpos($useragent, 'Linux; U; Android') === false) {
return false;
}
if (empty($version)) {
return true; // No version specified.
}
if (preg_match("/AppleWebKit\/([0-9]+)/i", $useragent, $match)) {
if (version_compare($match[1], $version) >= 0) {
return true;
}
}
return false;
}
/**
* Checks the user agent is Safari on iOS and that the version is equal to or greater than that specified.
*
* @param string|int $version A version to check for, returns true if its equal to or greater than that specified.
* @return bool
*/
public static function check_safari_ios_version($version = null) {
// Safari on iPhone, iPad and iPod touch.
$useragent = self::get_user_agent_string();
if ($useragent === false) {
return false;
}
if (strpos($useragent, 'AppleWebKit') === false or strpos($useragent, 'Safari') === false) {
return false;
}
if (!strpos($useragent, 'iPhone') and !strpos($useragent, 'iPad') and !strpos($useragent, 'iPod')) {
return false;
}
if (empty($version)) {
return true; // No version specified.
}
if (preg_match("/AppleWebKit\/([0-9]+)/i", $useragent, $match)) {
if (version_compare($match[1], $version) >= 0) {
return true;
}
}
return false;
}
/**
* Check if the user agent matches a given brand.
*
* Known brand: 'Windows','Linux','Macintosh','SGI','SunOS','HP-UX'
*
* @param string $brand
* @return bool
*/
public static function check_browser_operating_system($brand) {
$useragent = self::get_user_agent_string();
return ($useragent !== false && preg_match("/$brand/i", $useragent));
}
/**
* Gets an array of CSS classes to represent the user agent.
* @return array
*/
public static function get_browser_version_classes() {
$classes = array();
if (self::check_ie_version('0')) {
$classes[] = 'ie';
for ($i = 12; $i >= 6; $i--) {
if (self::check_ie_version($i)) {
$classes[] = 'ie'.$i;
break;
}
}
} else if (self::check_firefox_version() || self::check_gecko_version() || self::check_camino_version()) {
$classes[] = 'gecko';
if (preg_match('/rv\:([1-2])\.([0-9])/', self::get_user_agent_string(), $matches)) {
$classes[] = "gecko{$matches[1]}{$matches[2]}";
}
} else if (self::check_webkit_version()) {
$classes[] = 'safari';
if (self::check_safari_ios_version()) {
$classes[] = 'ios';
} else if (self::check_webkit_android_version()) {
$classes[] = 'android';
}
} else if (self::check_opera_version()) {
$classes[] = 'opera';
}
return $classes;
}
/**
* Returns true if the user agent supports the display of SVG images.
*
* @return bool
*/
public static function supports_svg() {
// IE 5 - 8 don't support SVG at all.
$instance = self::instance();
if ($instance->supportssvg === null) {
if ($instance->useragent === false) {
// Can't be sure, just say no.
$instance->supportssvg = false;
} else if (self::check_ie_version() and !self::check_ie_version('9')) {
// IE < 9 doesn't support SVG. Say no.
$instance->supportssvg = false;
} else if (preg_match('#Android +[0-2]\.#', $instance->useragent)) {
// Android < 3 doesn't support SVG. Say no.
$instance->supportssvg = false;
} else if (self::check_opera_version()) {
// Opera 12 still does not support SVG well enough. Say no.
$instance->supportssvg = false;
} else {
// Presumed fine.
$instance->supportssvg = true;
}
}
return $instance->supportssvg;
}
}
+1 -1
View File
@@ -2750,7 +2750,7 @@
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="backupid" TYPE="char" LENGTH="32" NOTNULL="true" SEQUENCE="false" COMMENT="backupid the log record belongs to"/>
<FIELD NAME="loglevel" TYPE="int" LENGTH="4" NOTNULL="true" SEQUENCE="false" COMMENT="level of the log (debug...error)"/>
<FIELD NAME="message" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="text logged"/>
<FIELD NAME="message" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="text logged"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="timestamp this log entry was created"/>
</FIELDS>
<KEYS>
+36
View File
@@ -2327,5 +2327,41 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2013081200.00);
}
if ($oldversion < 2013082300.01) {
// Define the table 'backup_logs' and the field 'message' which we will be changing from a char to a text field.
$table = new xmldb_table('backup_logs');
$field = new xmldb_field('message', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null, 'loglevel');
// Perform the change.
$dbman->change_field_type($table, $field);
// Main savepoint reached.
upgrade_main_savepoint(true, 2013082300.01);
}
// Convert SCORM course format courses to singleactivity.
if ($oldversion < 2013082700.00) {
// First set relevant singleactivity settings.
$formatoptions = new stdClass();
$formatoptions->format = 'singleactivity';
$formatoptions->sectionid = 0;
$formatoptions->name = 'activitytype';
$formatoptions->value = 'scorm';
$courses = $DB->get_recordset('course', array('format' => 'scorm'), 'id');
foreach ($courses as $course) {
$formatoptions->courseid = $course->id;
$DB->insert_record('course_format_options', $formatoptions);
}
$courses->close();
// Now update course format for these courses.
$sql = "UPDATE {course}
SET format = 'singleactivity', modinfo = '', sectioncache = ''
WHERE format = 'scorm'";
$DB->execute($sql);
upgrade_main_savepoint(true, 2013082700.00);
}
return true;
}
+544 -557
View File
File diff suppressed because it is too large Load Diff
+7 -7
View File
@@ -3,13 +3,13 @@
<TABLES>
<TABLE NAME="test_table1" COMMENT="Just a test table">
<FIELDS>
<FIELD NAME="id" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="incorrect_next_field"/> <!-- missing TYPE -->
<FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="name"/>
<FIELD NAME="name" TYPE="char" LENGTH="30" NOTNULL="false" SEQUENCE="false" DEFAULT="Moodle" PREVIOUS="course" NEXT="secondname"/>
<FIELD NAME="secondname" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false" PREVIOUS="name" NEXT="intro"/>
<FIELD NAME="intro" TYPE="text" LENGTH="medium" NOTNULL="true" SEQUENCE="false" PREVIOUS="secondname" NEXT="avatar"/>
<FIELD NAME="avatar" TYPE="binary" LENGTH="medium" NOTNULL="false" SEQUENCE="false" PREVIOUS="intro" NEXT="grade"/>
<FIELD NAME="grade" TYPE="number" LENGTH="20" DECIMALS="10" NOTNULL="false" SEQUENCE="false" PREVIOUS="avatar"/>
<FIELD NAME="id" LENGTH="10" NOTNULL="true" SEQUENCE="true"/> <!-- missing TYPE -->
<FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="name" TYPE="char" LENGTH="30" NOTNULL="false" SEQUENCE="false" DEFAULT="Moodle"/>
<FIELD NAME="secondname" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="intro" TYPE="text" LENGTH="medium" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="avatar" TYPE="binary" LENGTH="medium" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="grade" TYPE="number" LENGTH="20" DECIMALS="10" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" />
+10 -10
View File
@@ -3,21 +3,21 @@
<TABLES>
<TABLE NAME="test_table1" COMMENT="Just a test table">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="course"/>
<FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="name"/>
<FIELD NAME="name" TYPE="char" LENGTH="30" NOTNULL="false" SEQUENCE="false" DEFAULT="Moodle" PREVIOUS="course" NEXT="secondname"/>
<FIELD NAME="secondname" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false" PREVIOUS="name" NEXT="intro"/>
<FIELD NAME="intro" TYPE="text" LENGTH="medium" NOTNULL="true" SEQUENCE="false" PREVIOUS="secondname" NEXT="avatar"/>
<FIELD NAME="avatar" TYPE="binary" LENGTH="medium" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" PREVIOUS="intro" NEXT="grade"/>
<FIELD NAME="grade" TYPE="number" LENGTH="20" DECIMALS="10" NOTNULL="false" SEQUENCE="false" PREVIOUS="avatar" NEXT="path"/>
<FIELD NAME="path" TYPE="char" LENGTH="255" NOTNULL="true" PREVIOUS="grade"/>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true"/>
<FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="name" TYPE="char" LENGTH="30" NOTNULL="false" SEQUENCE="false" DEFAULT="Moodle"/>
<FIELD NAME="secondname" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="intro" TYPE="text" LENGTH="medium" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="avatar" TYPE="binary" LENGTH="medium" NOTNULL="false" UNSIGNED="false" SEQUENCE="false"/>
<FIELD NAME="grade" TYPE="number" LENGTH="20" DECIMALS="10" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="path" TYPE="char" LENGTH="255" NOTNULL="true"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" />
</KEYS>
<INDEXES>
<INDEX NAME="course" UNIQUE="false" FIELDS="course" NEXT="path"/>
<INDEX NAME="path" UNIQUE="false" FIELDS="path" HINTS="varchar_pattern_ops,unknownxyz" PREVIOUS="course"/>
<INDEX NAME="course" UNIQUE="false" FIELDS="course"/>
<INDEX NAME="path" UNIQUE="false" FIELDS="path" HINTS="varchar_pattern_ops,unknownxyz"/>
</INDEXES>
</TABLE>
</TABLES>
+114
View File
@@ -4419,3 +4419,117 @@ function get_plugin_list_with_file($plugintype, $file, $include = false) {
DEBUG_DEVELOPER);
return core_component::get_plugin_list_with_file($plugintype, $file, $include);
}
/**
* Checks to see if is the browser operating system matches the specified brand.
*
* Known brand: 'Windows','Linux','Macintosh','SGI','SunOS','HP-UX'
*
* @deprecated since 2.6
* @param string $brand The operating system identifier being tested
* @return bool true if the given brand below to the detected operating system
*/
function check_browser_operating_system($brand) {
debugging('check_browser_operating_system has been deprecated, please update your code to use core_useragent instead.', DEBUG_DEVELOPER);
return core_useragent::check_browser_operating_system($brand);
}
/**
* Checks to see if is a browser matches the specified
* brand and is equal or better version.
*
* @deprecated since 2.6
* @param string $brand The browser identifier being tested
* @param int $version The version of the browser, if not specified any version (except 5.5 for IE for BC reasons)
* @return bool true if the given version is below that of the detected browser
*/
function check_browser_version($brand, $version = null) {
debugging('check_browser_version has been deprecated, please update your code to use core_useragent instead.', DEBUG_DEVELOPER);
return core_useragent::check_browser_version($brand, $version);
}
/**
* Returns whether a device/browser combination is mobile, tablet, legacy, default or the result of
* an optional admin specified regular expression. If enabledevicedetection is set to no or not set
* it returns default
*
* @deprecated since 2.6
* @return string device type
*/
function get_device_type() {
debugging('get_device_type has been deprecated, please update your code to use core_useragent instead.', DEBUG_DEVELOPER);
return core_useragent::get_device_type();
}
/**
* Returns a list of the device types supporting by Moodle
*
* @deprecated since 2.6
* @param boolean $incusertypes includes types specified using the devicedetectregex admin setting
* @return array $types
*/
function get_device_type_list($incusertypes = true) {
debugging('get_device_type_list has been deprecated, please update your code to use core_useragent instead.', DEBUG_DEVELOPER);
return core_useragent::get_device_type_list($incusertypes);
}
/**
* Returns the theme selected for a particular device or false if none selected.
*
* @deprecated since 2.6
* @param string $devicetype
* @return string|false The name of the theme to use for the device or the false if not set
*/
function get_selected_theme_for_device_type($devicetype = null) {
debugging('get_selected_theme_for_device_type has been deprecated, please update your code to use core_useragent instead.', DEBUG_DEVELOPER);
return core_useragent::get_device_type_theme($devicetype);
}
/**
* Returns the name of the device type theme var in $CFG because there is not a convention to allow backwards compatibility.
*
* @deprecated since 2.6
* @param string $devicetype
* @return string The config variable to use to determine the theme
*/
function get_device_cfg_var_name($devicetype = null) {
debugging('get_device_cfg_var_name has been deprecated, please update your code to use core_useragent instead.', DEBUG_DEVELOPER);
return core_useragent::get_device_type_cfg_var_name($devicetype);
}
/**
* Allows the user to switch the device they are seeing the theme for.
* This allows mobile users to switch back to the default theme, or theme for any other device.
*
* @deprecated since 2.6
* @param string $newdevice The device the user is currently using.
* @return string The device the user has switched to
*/
function set_user_device_type($newdevice) {
debugging('set_user_device_type has been deprecated, please update your code to use core_useragent instead.', DEBUG_DEVELOPER);
return core_useragent::set_user_device_type($newdevice);
}
/**
* Returns the device the user is currently using, or if the user has chosen to switch devices
* for the current device type the type they have switched to.
*
* @deprecated since 2.6
* @return string The device the user is currently using or wishes to use
*/
function get_user_device_type() {
debugging('get_user_device_type has been deprecated, please update your code to use core_useragent instead.', DEBUG_DEVELOPER);
return core_useragent::get_user_device_type();
}
/**
* Returns one or several CSS class names that match the user's browser. These can be put
* in the body tag of the page to apply browser-specific rules without relying on CSS hacks
*
* @deprecated since 2.6
* @return array An array of browser version classes
*/
function get_browser_version_classes() {
debugging('get_browser_version_classes has been deprecated, please update your code to use core_useragent instead.', DEBUG_DEVELOPER);
return core_useragent::get_browser_version_classes();
}
+968 -987
View File
File diff suppressed because it is too large Load Diff
+49
View File
@@ -0,0 +1,49 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor installation steps.
*
* @package editor_atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Enable this text editor by default.
*
* @return bool
*/
function xmldb_editor_atto_install() {
global $CFG;
// Get the current list of editors.
$currentconfig = $CFG->texteditors;
if (is_null($currentconfig)) {
$currentconfig = '';
}
$editors = explode(',', $currentconfig);
// Insert atto in the second position.
array_splice($editors, 1, 0, array('atto'));
// Remove duplicates.
$editors = array_unique($editors);
// Set the new config.
unset_config('texteditors');
set_config('texteditors', implode(',', $editors));
return true;
}
@@ -1,5 +1,4 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
@@ -16,12 +15,13 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'format_scorm', language 'en', branch 'MOODLE_20_STABLE'
* Yui Editor
*
* @package format_scorm
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package editor_atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['sectionname'] = 'SCORM';
$string['pluginname'] = 'SCORM format';
defined('MOODLE_INTERNAL') || die();
$subplugins = array('atto' => 'lib/editor/atto/plugins');
+25
View File
@@ -0,0 +1,25 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'editor_atto', language 'en'.
*
* @package editor_atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Atto HTML editor';
+128
View File
@@ -0,0 +1,128 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* YUI text editor integration.
*
* @package editor_atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* This is the texteditor implementation.
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class atto_texteditor extends texteditor {
/**
* Is the current browser supported by this editor?
*
* Of course!
* @return bool
*/
public function supported_by_browser() {
return true;
}
/**
* Returns array of supported text formats.
* @return array
*/
public function get_supported_formats() {
// FORMAT_MOODLE is not supported here, sorry.
return array(FORMAT_HTML => FORMAT_HTML);
}
/**
* Returns text format preferred by this editor.
* @return int
*/
public function get_preferred_format() {
return FORMAT_HTML;
}
/**
* Does this editor support picking from repositories?
* @return bool
*/
public function supports_repositories() {
return true;
}
/**
* Use this editor for give element.
*
* @param string $elementid
* @param array $options
* @param null $fpoptions
*/
public function use_editor($elementid, array $options=null, $fpoptions=null) {
global $PAGE, $CFG;
$PAGE->requires->yui_module('moodle-editor_atto-editor',
'M.editor_atto.init',
array($this->get_init_params($elementid, $options, $fpoptions)), true);
require_once($CFG->libdir . '/pluginlib.php');
$pluginman = plugin_manager::instance();
$plugins = $pluginman->get_subplugins_of_plugin('editor_atto');
$sortedplugins = array();
foreach ($plugins as $id => $plugin) {
$sortorder = component_callback($plugin->type . '_' . $plugin->name, 'sort_order', array($elementid));
$sortedplugins[$sortorder] = $plugin;
}
ksort($sortedplugins);
foreach ($sortedplugins as $plugin) {
component_callback($plugin->type . '_' . $plugin->name, 'init_editor', array($elementid));
}
}
/**
* Create a params array to init the editor.
*
* @param string $elementid
* @param array $options
* @param array $fpoptions
*/
protected function get_init_params($elementid, array $options=null, array $fpoptions=null) {
global $PAGE;
$directionality = get_string('thisdirection', 'langconfig');
$strtime = get_string('strftimetime');
$strdate = get_string('strftimedaydate');
$lang = current_language();
$contentcss = $PAGE->theme->editor_css_url()->out(false);
$params = array(
'elementid' => $elementid,
'content_css' => $contentcss,
'language' => $lang,
'directionality' => $directionality,
'filepickeroptions' => array()
);
if ($fpoptions) {
$params['filepickeroptions'] = $fpoptions;
}
return $params;
}
}
@@ -0,0 +1,26 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'atto_bold', language 'en'.
*
* @package atto_bold
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Bold';
$string['bold'] = 'Bold';
+49
View File
@@ -0,0 +1,49 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_bold
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Initialise this plugin
* @param string $elementid
*/
function atto_bold_init_editor($elementid) {
global $PAGE, $OUTPUT;
$icon = $OUTPUT->pix_icon('bold', get_string('bold', 'atto_bold'), 'atto_bold', array('class'=>'icon'));
$PAGE->requires->yui_module('moodle-atto_bold-button',
'M.atto_bold.init',
array(array('elementid'=>$elementid, 'icon'=>$icon)),
true);
}
/**
* Return the order this plugin should be displayed in the toolbar
* @return int
*/
function atto_bold_sort_order() {
return 0;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

+68
View File
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="bold.svg" preserveAspectRatio="xMinYMid meet">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="15.839192"
inkscape:cx="-8.2482624"
inkscape:cy="15.312045"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1122"
inkscape:window-height="685"
inkscape:window-x="162"
inkscape:window-y="57"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1036.3622)">
<text
xml:space="preserve"
style="font-size:64px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Comic Sans MS;-inkscape-font-specification:Arial Bold"
x="7.2604713"
y="1049.6475"
id="text2984"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan2986"
x="7.2604713"
y="1049.6475"
style="font-size:14px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;fill:#999999;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial Bold">b</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -15,16 +15,15 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Version details
* Atto text editor integration version file.
*
* @package format
* @subpackage scorm
* @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com)
* @package atto_bold
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2013050100; // The current plugin version (Date: YYYYMMDDXX)
$plugin->requires = 2013050100; // Requires this Moodle version
$plugin->component = 'format_scorm'; // Full name of the plugin (used for diagnostics)
$plugin->version = 2013080900; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2013050100; // Requires this Moodle version.
$plugin->component = 'atto_bold'; // Full name of the plugin (used for diagnostics).
@@ -0,0 +1,40 @@
YUI.add('moodle-atto_bold-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor bold plugin.
*
* @package editor-atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.atto_bold = M.atto_bold || {
init : function(params) {
var click = function(e, elementid) {
e.preventDefault();
if (!M.editor_atto.is_active(elementid)) {
M.editor_atto.focus(elementid);
}
document.execCommand('bold', false, null);
};
M.editor_atto.add_toolbar_button(params.elementid, 'bold', params.icon, click);
}
};
}, '@VERSION@', {"requires": ["node"]});
@@ -0,0 +1 @@
YUI.add("moodle-atto_bold-button",function(e,t){M.atto_bold=M.atto_bold||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("bold",!1,null)};M.editor_atto.add_toolbar_button(e.elementid,"bold",e.icon,t)}}},"@VERSION@",{requires:["node"]});
@@ -0,0 +1,40 @@
YUI.add('moodle-atto_bold-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor bold plugin.
*
* @package editor-atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.atto_bold = M.atto_bold || {
init : function(params) {
var click = function(e, elementid) {
e.preventDefault();
if (!M.editor_atto.is_active(elementid)) {
M.editor_atto.focus(elementid);
}
document.execCommand('bold', false, null);
};
M.editor_atto.add_toolbar_button(params.elementid, 'bold', params.icon, click);
}
};
}, '@VERSION@', {"requires": ["node"]});
@@ -0,0 +1,10 @@
{
"name": "moodle-atto_bold-button",
"builds": {
"moodle-atto_bold-button": {
"jsfiles": [
"button.js"
]
}
}
}
@@ -0,0 +1,35 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor bold plugin.
*
* @package editor-atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.atto_bold = M.atto_bold || {
init : function(params) {
var click = function(e, elementid) {
e.preventDefault();
if (!M.editor_atto.is_active(elementid)) {
M.editor_atto.focus(elementid);
}
document.execCommand('bold', false, null);
};
M.editor_atto.add_toolbar_button(params.elementid, 'bold', params.icon, click);
}
};
@@ -0,0 +1,5 @@
{
"moodle-atto_bold-button": {
"requires": ["node"]
}
}
@@ -0,0 +1,26 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'atto_clear', language 'en'.
*
* @package atto_clear
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Clear formatting';
$string['clear'] = 'Clear formatting';
+52
View File
@@ -0,0 +1,52 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_clear
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Initialise this plugin
* @param string $elementid
*/
function atto_clear_init_editor($elementid) {
global $PAGE, $OUTPUT;
$icon = $OUTPUT->pix_icon('clear',
get_string('clear', 'atto_clear'),
'atto_clear',
array('class'=>'icon'));
$PAGE->requires->yui_module('moodle-atto_clear-button',
'M.atto_clear.init',
array(array('elementid'=>$elementid, 'icon'=>$icon)),
true);
}
/**
* Return the order this plugin should be displayed in the toolbar
* @return int
*/
function atto_clear_sort_order() {
return 10;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 468 B

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="clear.svg" preserveAspectRatio="xMinYMid meet">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="15.839192"
inkscape:cx="-3.8870379"
inkscape:cy="15.312045"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1122"
inkscape:window-height="685"
inkscape:window-x="162"
inkscape:window-y="57"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:clear />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1036.3622)">
<g
id="g3756"
transform="translate(0,-0.44194174)">
<rect
ry="0"
transform="translate(0,1036.3622)"
y="2.3629408"
x="7.0079331"
height="5.3664355"
width="2.0203052"
id="rect2982"
style="fill:#999999;fill-opacity:1;stroke:#999999;stroke-width:1;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
<rect
ry="0.4152233"
y="1043.2078"
x="3.9774756"
height="2.7870839"
width="8.0812302"
id="rect2984"
style="fill:#999999;fill-opacity:1;stroke:#999999;stroke-width:0.82672101;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
<rect
ry="0.053281363"
y="1045.6699"
x="4.4825521"
height="3.1562684"
width="7.0079417"
id="rect3754"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#999999;stroke-width:1;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

+29
View File
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_clear
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2013080900; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2013050100; // Requires this Moodle version.
$plugin->component = 'atto_clear'; // Full name of the plugin (used for diagnostics).
@@ -0,0 +1,40 @@
YUI.add('moodle-atto_clear-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor clear plugin.
*
* @package editor-atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.atto_clear = M.atto_clear || {
init : function(params) {
var click = function(e, elementid) {
e.preventDefault();
if (!M.editor_atto.is_active(elementid)) {
M.editor_atto.focus(elementid);
}
document.execCommand('removeFormat', false);
};
M.editor_atto.add_toolbar_button(params.elementid, 'clear', params.icon, click);
}
};
}, '@VERSION@', {"requires": ["node"]});
@@ -0,0 +1 @@
YUI.add("moodle-atto_clear-button",function(e,t){M.atto_clear=M.atto_clear||{init:function(e){var t=function(e,t){e.preventDefault(),M.editor_atto.is_active(t)||M.editor_atto.focus(t),document.execCommand("removeFormat",!1)};M.editor_atto.add_toolbar_button(e.elementid,"clear",e.icon,t)}}},"@VERSION@",{requires:["node"]});
@@ -0,0 +1,40 @@
YUI.add('moodle-atto_clear-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor clear plugin.
*
* @package editor-atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.atto_clear = M.atto_clear || {
init : function(params) {
var click = function(e, elementid) {
e.preventDefault();
if (!M.editor_atto.is_active(elementid)) {
M.editor_atto.focus(elementid);
}
document.execCommand('removeFormat', false);
};
M.editor_atto.add_toolbar_button(params.elementid, 'clear', params.icon, click);
}
};
}, '@VERSION@', {"requires": ["node"]});
@@ -0,0 +1,10 @@
{
"name": "moodle-atto_clear-button",
"builds": {
"moodle-atto_clear-button": {
"jsfiles": [
"button.js"
]
}
}
}
@@ -0,0 +1,35 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor clear plugin.
*
* @package editor-atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.atto_clear = M.atto_clear || {
init : function(params) {
var click = function(e, elementid) {
e.preventDefault();
if (!M.editor_atto.is_active(elementid)) {
M.editor_atto.focus(elementid);
}
document.execCommand('removeFormat', false);
};
M.editor_atto.add_toolbar_button(params.elementid, 'clear', params.icon, click);
}
};
@@ -0,0 +1,5 @@
{
"moodle-atto_clear-button": {
"requires": ["node"]
}
}
@@ -0,0 +1,26 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'atto_html', language 'en'.
*
* @package atto_html
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'HTML';
$string['html'] = 'HTML';
+49
View File
@@ -0,0 +1,49 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_html
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Initialise this plugin
* @param string $elementid
*/
function atto_html_init_editor($elementid) {
global $PAGE, $OUTPUT;
$icon = $OUTPUT->pix_icon('html', get_string('html', 'atto_html'), 'atto_html', array('class'=>'icon'));
$PAGE->requires->yui_module('moodle-atto_html-button',
'M.atto_html.init',
array(array('elementid'=>$elementid, 'icon'=>$icon)),
true);
}
/**
* Return the order this plugin should be displayed in the toolbar
* @return int
*/
function atto_html_sort_order() {
return 15;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

+69
View File
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="html.svg" preserveAspectRatio="xMinYMid meet">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="11.313709"
inkscape:cx="-8.155614"
inkscape:cy="13.234355"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1122"
inkscape:window-height="685"
inkscape:window-x="162"
inkscape:window-y="57"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>media/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillMedia" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1036.3622)">
<text
xml:space="preserve"
style="font-size:52.96332169000000079px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#999999;fill-opacity:1;stroke:none;font-family:Comic Sans MS;-inkscape-font-specification:Comic Sans MS"
x="10.191385"
y="836.76996"
id="text2988"
sodipodi:linespacing="125%"
transform="scale(0.79843788,1.2524456)"><tspan
sodipodi:role="line"
id="tspan2990"
x="10.191385"
y="836.76996"
style="font-size:9.93062304999999945px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:FreeMono;-inkscape-font-specification:FreeMono Bold;fill:#999999;fill-opacity:1">&lt;/&gt;</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

+29
View File
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_html
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2013080900; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2013050100; // Requires this Moodle version.
$plugin->component = 'atto_html'; // Full name of the plugin (used for diagnostics).
@@ -0,0 +1,59 @@
YUI.add('moodle-atto_html-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor html plugin.
*
* @package editor-atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.atto_html = M.atto_html || {
/**
* Are we in html editing mode or not?
*/
ishtml : false,
init : function(params) {
var click = function(e, elementid) {
e.preventDefault();
var textarea = Y.one('#' + elementid);
var atto = Y.one('#' + elementid + 'editable');
if (M.atto_html.ishtml) {
M.editor_atto.enable_all_widgets(elementid);
atto.setHTML('');
atto.append(textarea.get('value'));
textarea.hide();
atto.show();
} else {
M.editor_atto.disable_all_widgets(elementid);
M.editor_atto.enable_widget(elementid, 'html');
textarea.set('value', atto.getHTML());
atto.hide();
textarea.show();
}
M.atto_html.ishtml = !M.atto_html.ishtml;
};
M.editor_atto.add_toolbar_button(params.elementid, 'html', params.icon, click);
}
};
}, '@VERSION@', {"requires": ["node"]});
@@ -0,0 +1 @@
YUI.add("moodle-atto_html-button",function(e,t){M.atto_html=M.atto_html||{ishtml:!1,init:function(t){var n=function(t,n){t.preventDefault();var r=e.one("#"+n),i=e.one("#"+n+"editable");M.atto_html.ishtml?(M.editor_atto.enable_all_widgets(n),i.setHTML(""),i.append(r.get("value")),r.hide(),i.show()):(M.editor_atto.disable_all_widgets(n),M.editor_atto.enable_widget(n,"html"),r.set("value",i.getHTML()),i.hide(),r.show()),M.atto_html.ishtml=!M.atto_html.ishtml};M.editor_atto.add_toolbar_button(t.elementid,"html",t.icon,n)}}},"@VERSION@",{requires:["node"]});
@@ -0,0 +1,59 @@
YUI.add('moodle-atto_html-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor html plugin.
*
* @package editor-atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.atto_html = M.atto_html || {
/**
* Are we in html editing mode or not?
*/
ishtml : false,
init : function(params) {
var click = function(e, elementid) {
e.preventDefault();
var textarea = Y.one('#' + elementid);
var atto = Y.one('#' + elementid + 'editable');
if (M.atto_html.ishtml) {
M.editor_atto.enable_all_widgets(elementid);
atto.setHTML('');
atto.append(textarea.get('value'));
textarea.hide();
atto.show();
} else {
M.editor_atto.disable_all_widgets(elementid);
M.editor_atto.enable_widget(elementid, 'html');
textarea.set('value', atto.getHTML());
atto.hide();
textarea.show();
}
M.atto_html.ishtml = !M.atto_html.ishtml;
};
M.editor_atto.add_toolbar_button(params.elementid, 'html', params.icon, click);
}
};
}, '@VERSION@', {"requires": ["node"]});
@@ -0,0 +1,10 @@
{
"name": "moodle-atto_html-button",
"builds": {
"moodle-atto_html-button": {
"jsfiles": [
"button.js"
]
}
}
}
@@ -0,0 +1,54 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor html plugin.
*
* @package editor-atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.atto_html = M.atto_html || {
/**
* Are we in html editing mode or not?
*/
ishtml : false,
init : function(params) {
var click = function(e, elementid) {
e.preventDefault();
var textarea = Y.one('#' + elementid);
var atto = Y.one('#' + elementid + 'editable');
if (M.atto_html.ishtml) {
M.editor_atto.enable_all_widgets(elementid);
atto.setHTML('');
atto.append(textarea.get('value'));
textarea.hide();
atto.show();
} else {
M.editor_atto.disable_all_widgets(elementid);
M.editor_atto.enable_widget(elementid, 'html');
textarea.set('value', atto.getHTML());
atto.hide();
textarea.show();
}
M.atto_html.ishtml = !M.atto_html.ishtml;
};
M.editor_atto.add_toolbar_button(params.elementid, 'html', params.icon, click);
}
};
@@ -0,0 +1,7 @@
{
"moodle-atto_html-button": {
"requires": [
"node"
]
}
}
@@ -0,0 +1,34 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'atto_image', language 'en'.
*
* @package atto_image
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Image';
$string['image'] = 'Image';
$string['createimage'] = 'Insert image';
$string['enterurl'] = 'Enter URL';
$string['enteralt'] = 'Enter alternative text';
$string['browserepositories'] = 'Browse repositories...';
$string['accessibilityhint'] = '<p>Web content accessibility guidelines (WCAG):<br/><ul><li><a href="http://www.w3.org/TR/WCAG20/#text-equiv" target="_blank">Text Alternatives</a></li></ul></p>';
$string['width'] = 'Width';
$string['height'] = 'Height';
$string['preview'] = 'Preview';
+60
View File
@@ -0,0 +1,60 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_image
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Initialise this plugin
* @param string $elementid
*/
function atto_image_init_editor($elementid) {
global $PAGE, $OUTPUT;
$icon = $OUTPUT->pix_icon('image',
get_string('image', 'atto_image'),
'atto_image',
array('class'=>'icon'));
$PAGE->requires->strings_for_js(array('createimage',
'enterurl',
'enteralt',
'preview',
'browserepositories',
'accessibilityhint',
'width',
'height'), 'atto_image');
$PAGE->requires->yui_module('moodle-atto_image-button',
'M.atto_image.init',
array(array('elementid'=>$elementid, 'icon'=>$icon)),
true);
}
/**
* Return the order this plugin should be displayed in the toolbar
* @return int
*/
function atto_image_sort_order() {
return 13;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="image.svg" preserveAspectRatio="xMinYMid meet">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="32"
inkscape:cx="11.577509"
inkscape:cy="9.3016902"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1122"
inkscape:window-height="685"
inkscape:window-x="162"
inkscape:window-y="57"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1036.3622)">
<rect
style="fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#999999;stroke-opacity:1"
id="rect2986"
width="14.142136"
height="13.131983"
x="1.0101526"
y="1.4159228"
transform="translate(0,1036.3622)"
ry="0.053281363" />
<path
style="fill:#999999;fill-opacity:1;stroke:none"
d="m 13.794977,1041.1242 c 0,0.9241 -0.749059,1.6732 -1.673146,1.6732 -0.9241,0 -1.673146,-0.7491 -1.673146,-1.6732 0,-0.924 0.749059,-1.6731 1.673146,-1.6731 0.9241,0 1.673146,0.7491 1.673146,1.6731 z"
id="path3761"
inkscape:connector-curvature="0" />
<path
transform="translate(0,1036.3622)"
style="fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:#999999;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0"
d="m 1.5711386,10.553572 c 1.7890625,-0.125 3.8205354,-3.734909 6.286004,-3.75 2.4654684,-0.015091 3.8591064,3.4375 6.4645754,3.75 0.01465,0.0018 0.02808,0.0059 0.03771,0.01557 0.0096,0.0096 0.01557,0.02295 0.01557,0.03771 l 0,3.018437 c 0,0.02952 -0.02376,0.05328 -0.05328,0.05328 l -12.7505794,0 c -0.029518,0 -0.053281,-0.02376 -0.053281,-0.05328 l 0,-3.018437 c 0,-0.01476 0.00594,-0.02808 0.015572,-0.03771 0.00963,-0.0096 0.022987,-0.01454 0.037709,-0.01557 z"
id="rect3753"
inkscape:connector-curvature="0"
sodipodi:nodetypes="szssssssssss" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

+29
View File
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor integration version file.
*
* @package atto_image
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2013080900; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2013050100; // Requires this Moodle version.
$plugin->component = 'atto_image'; // Full name of the plugin (used for diagnostics).
@@ -0,0 +1,184 @@
YUI.add('moodle-atto_image-button', function (Y, NAME) {
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Atto text editor image plugin.
*
* @package editor-atto
* @copyright 2013 Damyon Wiese <damyon@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
M.atto_image = M.atto_image || {
dialogue : null,
selection : null,
init : function(params) {
var display_chooser = function(e, elementid) {
e.preventDefault();
if (!M.editor_atto.is_active(elementid)) {
M.editor_atto.focus(elementid);
}
M.atto_image.selection = M.editor_atto.get_selection();
if (M.atto_image.selection !== false) {
var dialogue;
if (!M.atto_image.dialogue) {
dialogue = new M.core.dialogue({
visible: false,
modal: true,
close: true,
draggable: true
});
} else {
dialogue = M.atto_image.dialogue;
}
dialogue.set('bodyContent', M.atto_image.get_form_content(elementid));
dialogue.set('headerContent', M.util.get_string('createimage', 'atto_image'));
dialogue.render();
dialogue.centerDialogue();
M.atto_image.dialogue = dialogue;
var selectedText = M.editor_atto.get_selection_text();
var i = 0;
var images = [];
for (i = 0; i < selectedText.childNodes.length; i++) {
var child = selectedText.childNodes[0];
if (images.length === 0) {
if (child.nodeName.toLowerCase() === 'img') {
images[0] = child;
} else {
if (child.getElementsByTagName) {
images = child.getElementsByTagName('img');
}
}
}
}
if (images.length > 0) {
var image = Y.one(images[0]);
var width = image.getAttribute('width');
var height = image.getAttribute('height');
if (width > 0) {
Y.one('#atto_image_widthentry').set('value', width);
}
if (height > 0) {
Y.one('#atto_image_heightentry').set('value', height);
}
Y.one('#atto_image_preview').set('src', image.get('src'));
Y.one('#atto_image_preview').setStyle('display', 'inline');
Y.one('#atto_image_altentry').set('value', image.get('alt'));
Y.one('#atto_image_urlentry').set('value', image.get('src'));
}
dialogue.show();
}
};
M.editor_atto.add_toolbar_button(params.elementid, 'image', params.icon, display_chooser, this);
},
open_filepicker : function(e) {
var elementid = this.getAttribute('data-editor');
e.preventDefault();
M.editor_atto.show_filepicker(elementid, 'image', M.atto_image.filepicker_callback);
},
filepicker_callback : function(params) {
if (params.url !== '') {
var input = Y.one('#atto_image_urlentry');
input.set('value', params.url);
input = Y.one('#atto_image_altentry');
input.set('value', params.file);
// Auto set the width and height.
var image = new Image();
image.onload = function() {
Y.one('#atto_image_widthentry').set('value', this.width);
Y.one('#atto_image_heightentry').set('value', this.height);
Y.one('#atto_image_preview').set('src', this.src);
Y.one('#atto_image_preview').setStyle('display', 'inline');
};
image.src = params.url;
}
},
set_image : function(e) {
e.preventDefault();
M.atto_image.dialogue.hide();
var input = e.currentTarget.get('parentNode').one('#atto_image_urlentry');
var url = input.get('value');
input = e.currentTarget.get('parentNode').one('#atto_image_altentry');
var alt = input.get('value');
input = e.currentTarget.get('parentNode').one('#atto_image_widthentry');
var width = input.get('value');
input = e.currentTarget.get('parentNode').one('#atto_image_heightentry');
var height = input.get('value');
if (url !== '' && alt !== '') {
M.editor_atto.set_selection(M.atto_image.selection);
var imagehtml = '<img src="' + Y.Escape.html(url) + '" alt="' + Y.Escape.html(alt) + '"';
if (width) {
imagehtml += ' width="' + Y.Escape.html(width) + '"';
}
if (height) {
imagehtml += ' height="' + Y.Escape.html(height) + '"';
}
imagehtml += '"/>';
if (document.selection && document.selection.createRange().pasteHTML) {
document.selection.createRange().pasteHTML(imagehtml);
} else {
document.execCommand('insertHTML', false, imagehtml);
}
}
},
get_form_content : function(elementid) {
var content = Y.Node.create('<form class="atto_form">' +
'<label for="atto_image_urlentry">' + M.util.get_string('enterurl', 'atto_image') +
'</label>' +
'<input type="url" value="" id="atto_image_urlentry" size="32"/>' +
'<label for="atto_image_altentry">' + M.util.get_string('enteralt', 'atto_image') +
'</label>' +
'<input type="text" value="" id="atto_image_altentry" size="32" required="true"/>' +
'<label for="atto_image_widthentry">' + M.util.get_string('width', 'atto_image') +
'</label>' +
'<input type="text" value="" id="atto_image_widthentry" size="10"/>' +
'<br/>' +
'<label for="atto_image_heightentry">' + M.util.get_string('height', 'atto_image') +
'</label>' +
'<input type="text" value="" id="atto_image_heightentry" size="10"/>' +
'<label for="atto_image_preview">' + M.util.get_string('preview', 'atto_image') +
'</label>' +
'<img src="#" width="200" id="atto_image_preview" alt="" style="display: none;"/>' +
'<hr/>' +
'<button id="openimagebrowser" data-editor="' + Y.Escape.html(elementid) + '">' +
M.util.get_string('browserepositories', 'atto_image') +
'</button>' +
'<hr/>' +
'<button id="atto_image_urlentrysubmit">' +
M.util.get_string('createimage', 'atto_image') +
'</button>' +
'</form>' +
'<hr/>' + M.util.get_string('accessibilityhint', 'atto_image'));
content.one('#atto_image_urlentrysubmit').on('click', M.atto_image.set_image);
content.one('#openimagebrowser').on('click', M.atto_image.open_filepicker);
return content;
}
};
}, '@VERSION@', {"requires": ["node", "escape"]});

Some files were not shown because too many files have changed in this diff Show More