* @return array
*/
public function get_data_requests_provider() {
- $generator = new testing_data_generator();
- $user1 = $generator->create_user();
- $user2 = $generator->create_user();
- $user3 = $generator->create_user();
- $user4 = $generator->create_user();
- $user5 = $generator->create_user();
- $users = [$user1, $user2, $user3, $user4, $user5];
$completeonly = [api::DATAREQUEST_STATUS_COMPLETE];
$completeandcancelled = [api::DATAREQUEST_STATUS_COMPLETE, api::DATAREQUEST_STATUS_CANCELLED];
return [
// Own data requests.
- [$users, $user1, false, $completeonly],
+ ['user', false, $completeonly],
// Non-DPO fetching all requets.
- [$users, $user2, true, $completeonly],
+ ['user', true, $completeonly],
// Admin fetching all completed and cancelled requests.
- [$users, get_admin(), true, $completeandcancelled],
+ ['dpo', true, $completeandcancelled],
// Admin fetching all completed requests.
- [$users, get_admin(), true, $completeonly],
+ ['dpo', true, $completeonly],
// Guest fetching all requests.
- [$users, guest_user(), true, $completeonly],
+ ['guest', true, $completeonly],
];
}
* Test for api::get_data_requests()
*
* @dataProvider get_data_requests_provider
- * @param stdClass[] $users Array of users to create data requests for.
- * @param stdClass $loggeduser The user logging in.
+ * @param string $usertype The type of the user logging in.
* @param boolean $fetchall Whether to fetch all records.
* @param int[] $statuses Status filters.
*/
- public function test_get_data_requests($users, $loggeduser, $fetchall, $statuses) {
+ public function test_get_data_requests($usertype, $fetchall, $statuses) {
+ $generator = new testing_data_generator();
+ $user1 = $generator->create_user();
+ $user2 = $generator->create_user();
+ $user3 = $generator->create_user();
+ $user4 = $generator->create_user();
+ $user5 = $generator->create_user();
+ $users = [$user1, $user2, $user3, $user4, $user5];
+
+ switch ($usertype) {
+ case 'user':
+ $loggeduser = $user1;
+ break;
+ case 'dpo':
+ $loggeduser = get_admin();
+ break;
+ case 'guest':
+ $loggeduser = guest_user();
+ break;
+ }
+
$comment = 'Data %s request comment by user %d';
$exportstring = helper::get_shortened_request_type_string(api::DATAREQUEST_TYPE_EXPORT);
$deletionstring = helper::get_shortened_request_type_string(api::DATAREQUEST_TYPE_DELETE);
/**
* Legacy log reader.
+ * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
+ * @todo MDL-52805 This is to be removed in Moodle 4.0
*
* @package logstore_legacy
* @copyright 2013 Petr Skoda {@link http://skodak.org}
use \tool_log\helper\store,
\tool_log\helper\reader;
+ /**
+ * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
+ * @todo MDL-52805 This is to be removed in Moodle 4.0
+ *
+ * @param \tool_log\log\manager $manager
+ */
public function __construct(\tool_log\log\manager $manager) {
$this->helper_setup($manager);
}
return array($selectwhere, $params, $sort);
}
+ /**
+ * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
+ * @todo MDL-52805 This will be removed in Moodle 4.0
+ *
+ * @param string $selectwhere
+ * @param array $params
+ * @param string $sort
+ * @param int $limitfrom
+ * @param int $limitnum
+ * @return array
+ */
public function get_events_select($selectwhere, array $params, $sort, $limitfrom, $limitnum) {
global $DB;
/**
* Fetch records using given criteria returning a Traversable object.
+ * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
+ * @todo MDL-52805 This will be removed in Moodle 4.0
*
* Note that the traversable object contains a moodle_recordset, so
* remember that is important that you call close() once you finish
/**
* Returns an event from the log data.
+ * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
+ * @todo MDL-52805 This will be removed in Moodle 4.0
*
* @param stdClass $data Log data
* @return \core\event\base
return \logstore_legacy\event\legacy_logged::restore_legacy($data);
}
+ /**
+ * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
+ * @todo MDL-52805 This will be removed in Moodle 4.0
+ *
+ * @param string $selectwhere
+ * @param array $params
+ * @return int
+ */
public function get_events_select_count($selectwhere, array $params) {
global $DB;
/**
* Are the new events appearing in the reader?
+ * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
+ * @todo MDL-52805 This will be removed in Moodle 4.0
*
* @return bool true means new log events are being added, false means no new data will be added
*/
return (bool)$this->get_config('loglegacy', true);
}
+ /**
+ * @deprecated since Moodle 3.6 MDL-52953 - Please use supported log stores such as "standard" or "external" instead.
+ * @todo MDL-52805 This will be removed in Moodle 4.0
+ */
public function dispose() {
}
--- /dev/null
+This files describes API changes in /admin/tool/log - plugins,
+information provided here is intended especially for developers.
+
+
+=== 3.6 ===
+
+* The legacy log store is in its first stage of deprecation and is due for removal in Moodle 4.0. Please use one of
+ the other log stores such as "standard" and "database".
\ No newline at end of file
--- /dev/null
+<?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/>.
+
+/**
+ * Contains a helper class for the Shibboleth authentication plugin.
+ *
+ * @package auth_shibboleth
+ * @copyright 2018 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace auth_shibboleth;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * The helper class for the Shibboleth authentication plugin.
+ *
+ * @package auth_shibboleth
+ * @copyright 2018 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class helper {
+
+ /**
+ * Delete session of user using file sessions.
+ *
+ * @param string $spsessionid SP-provided Shibboleth Session ID
+ * @return \SoapFault or void if everything was fine
+ */
+ public static function logout_file_session($spsessionid) {
+ global $CFG;
+
+ if (!empty($CFG->session_file_save_path)) {
+ $dir = $CFG->session_file_save_path;
+ } else {
+ $dir = $CFG->dataroot . '/sessions';
+ }
+
+ if (is_dir($dir)) {
+ if ($dh = opendir($dir)) {
+ // Read all session files.
+ while (($file = readdir($dh)) !== false) {
+ // Check if it is a file.
+ if (is_file($dir.'/'.$file)) {
+ // Read session file data.
+ $data = file($dir.'/'.$file);
+ if (isset($data[0])) {
+ $usersession = self::unserializesession($data[0]);
+ // Check if we have found session that shall be deleted.
+ if (isset($usersession['SESSION']) && isset($usersession['SESSION']->shibboleth_session_id)) {
+ // If there is a match, delete file.
+ if ($usersession['SESSION']->shibboleth_session_id == $spsessionid) {
+ // Delete session file.
+ if (!unlink($dir.'/'.$file)) {
+ return new SoapFault('LogoutError', 'Could not delete Moodle session file.');
+ }
+ }
+ }
+ }
+ }
+ }
+ closedir($dh);
+ }
+ }
+ }
+
+ /**
+ * Delete session of user using DB sessions.
+ *
+ * @param string $spsessionid SP-provided Shibboleth Session ID
+ */
+ public static function logout_db_session($spsessionid) {
+ global $CFG, $DB;
+
+ $sessions = $DB->get_records_sql(
+ 'SELECT userid, sessdata FROM {sessions} WHERE timemodified > ?',
+ array(time() - $CFG->sessiontimeout)
+ );
+
+ foreach ($sessions as $session) {
+ // Get user session from DB.
+ if (session_decode(base64_decode($session->sessdata))) {
+ if (isset($_SESSION['SESSION']) && isset($_SESSION['SESSION']->shibboleth_session_id)) {
+ // If there is a match, kill the session.
+ if ($_SESSION['SESSION']->shibboleth_session_id == trim($spsessionid)) {
+ // Delete this user's sessions.
+ \core\session\manager::kill_user_sessions($session->userid);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Unserialize a session string.
+ *
+ * @param string $serializedstring
+ * @return array
+ */
+ private static function unserializesession($serializedstring) {
+ $variables = array();
+ $a = preg_split("/(\w+)\|/", $serializedstring, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
+ $counta = count($a);
+ for ($i = 0; $i < $counta; $i = $i + 2) {
+ $variables[$a[$i]] = unserialize($a[$i + 1]);
+ }
+ return $variables;
+ }
+}
* @return SoapFault or void if everything was fine
*/
function LogoutNotification($spsessionid) {
-
- global $CFG, $SESSION, $DB;
-
- // Delete session of user using $spsessionid.
- if(empty($CFG->dbsessions)) {
-
- // File session
- $dir = $CFG->dataroot .'/sessions';
- if (is_dir($dir)) {
- if ($dh = opendir($dir)) {
- // Read all session files
- while (($file = readdir($dh)) !== false) {
- // Check if it is a file
- if (is_file($dir.'/'.$file)){
- $session_key = preg_replace('/sess_/', '', $file);
-
- // Read session file data
- $data = file($dir.'/'.$file);
- if (isset($data[0])){
- $usersession = unserializesession($data[0]);
-
- // Check if we have found session that shall be deleted
- if (isset($usersession['SESSION']) && isset($usersession['SESSION']->shibboleth_session_id)) {
-
- // If there is a match, delete file
- if ($usersession['SESSION']->shibboleth_session_id == $spsessionid) {
- // Delete session file
- if (!unlink($dir.'/'.$file)){
- return new SoapFault('LogoutError', 'Could not delete Moodle session file.');
- }
- }
- }
- }
- }
- }
- closedir($dh);
- }
- }
- } else {
- // DB Sessions.
- $sessions = $DB->get_records_sql(
- 'SELECT userid, sessdata FROM {sessions} WHERE timemodified > ?',
- array(time() - $CFG->sessiontimeout)
- );
- foreach ($sessions as $session) {
- // Get user session from DB.
- if (session_decode(base64_decode($session->sessdata))) {
- if (isset($_SESSION['SESSION']) && isset($_SESSION['SESSION']->shibboleth_session_id)) {
- // If there is a match, kill the session.
- if ($_SESSION['SESSION']->shibboleth_session_id == trim($spsessionid)) {
- // Delete this user's sessions.
- \core\session\manager::kill_user_sessions($session->userid);
- }
- }
- }
- }
+ $sessionclass = \core\session\manager::get_handler_class();
+ switch ($sessionclass) {
+ case '\core\session\file':
+ return \auth_shibboleth\helper::logout_file_session($spsessionid);
+ case '\core\session\database':
+ return \auth_shibboleth\helper::logout_db_session($spsessionid);
+ default:
+ throw new moodle_exception("Shibboleth logout not implemented for '$sessionclass'");
}
// If no SoapFault was thrown, the function will return OK as the SP assumes.
}
-
-/*****************************************************************************/
-
-// Same function as in adodb, but cannot be used for file session for some reason...
-function unserializesession($serialized_string) {
- $variables = array();
- $a = preg_split("/(\w+)\|/", $serialized_string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
- $counta = count($a);
- for ($i = 0; $i < $counta; $i = $i+2) {
- $variables[$a[$i]] = unserialize($a[$i+1]);
- }
- return $variables;
-}
This files describes API changes in /auth/shibboleth/*,
information provided here is intended especially for developers.
+=== 3.5.2 ===
+
+* Moved the public function unserializesession in auth/shibboleth/logout.php to auth/shibboleth/classes/helper.php and
+ made it private. This function should not have been used outside of this file.
+
=== 3.3 ===
* The config.html file was migrated to use the admin settings API.
// 6b) User cannot, check if we are in some contextlevel with fallback
// 7a) There is fallback, move ALL the qcats to fallback, warn. End qcat loop
// 7b) No fallback, error. End qcat loop
- // 5b) Match, mark q to be mapped
+ // 5b) Random question, must always create new.
+ // 5c) Match, mark q to be mapped
// 8) Check if backup is from Moodle >= 3.5 and error if more than one top-level category in the context.
// Get all the contexts (question banks) in restore for the given contextlevel
break 2; // out from qcat loop (both 7a and 7b), we have decided about ALL categories in context (bank)
}
- // 5b) Match, mark q to be mapped
+ // 5b) Random questions must always be newly created.
+ } else if ($question->qtype == 'random') {
+ // Nothing to mark, newitemid means create
+
+ // 5c) Match, mark q to be mapped.
} else {
self::set_backup_ids_record($restoreid, 'question', $question->id, $matchqid);
}
$string['myprofile_settings'] = 'Visible user information';
$string['pluginname'] = 'Logged in user';
$string['privacy:metadata'] = 'The Logged in user block only shows information about the logged in user and does not store data itself.';
-
-// Deprecated since Moodle 3.2.
-$string['display_un'] = 'Display name';
-display_un,block_myprofile
And I click on "New event" "button"
When I click on "Save" "button"
Then I should see "Required"
- And I am on site homepage
- And I follow "Calendar"
+ And I am on homepage
+ And I follow "This month"
And I click on "New event" "button"
And I set the field "Type of event" to "Course"
When I click on "Save" "button"
// Make sure maxbytes are less then CFG->maxbytes.
if (array_key_exists('maxbytes', $course)) {
- $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']);
+ // We allow updates back to 0 max bytes, a special value denoting the course uses the site limit.
+ // Otherwise, either use the size specified, or cap at the max size for the course.
+ if ($course['maxbytes'] != 0) {
+ $course['maxbytes'] = get_max_upload_file_size($CFG->maxbytes, $course['maxbytes']);
+ }
}
if (!empty($course['courseformatoptions'])) {
/**
* Checks if the activity type has multiple items in the activity chooser.
- * This may happen as a result of defining callback modulename_get_shortcuts()
- * or [deprecated] modulename_get_types() - TODO MDL-53697 remove this line.
+ * This may happen as a result of defining callback modulename_get_shortcuts().
*
* @return bool|null (null if the check is not possible)
*/
}
$defaultmodule->archetype = plugin_supports('mod', $modname, FEATURE_MOD_ARCHETYPE, MOD_ARCHETYPE_OTHER);
- // Legacy support for callback get_types() - do not use any more, use get_shortcuts() instead!
- $typescallbackexists = component_callback_exists($modname, 'get_types');
-
// Each module can implement callback modulename_get_shortcuts() in its lib.php and return the list
// of elements to be added to activity chooser.
$items = component_callback($modname, 'get_shortcuts', array($defaultmodule), null);
$modlist[$course->id][$modname][$item->name] = $item;
}
$return += $modlist[$course->id][$modname];
- if ($typescallbackexists) {
- debugging('Both callbacks get_shortcuts() and get_types() are found in module ' . $modname .
- '. Callback get_types() will be completely ignored', DEBUG_DEVELOPER);
- }
// If get_shortcuts() callback is defined, the default module action is not added.
// It is a responsibility of the callback to add it to the return value unless it is not needed.
continue;
}
- if ($typescallbackexists) {
- debugging('Callback get_types() is found in module ' . $modname . ', this functionality is deprecated, ' .
- 'please use callback get_shortcuts() instead', DEBUG_DEVELOPER);
- }
- $types = component_callback($modname, 'get_types', array(), MOD_SUBTYPE_NO_CHILDREN);
- if ($types !== MOD_SUBTYPE_NO_CHILDREN) {
- // Legacy support for deprecated callback get_types(). To be removed in Moodle 3.5. TODO MDL-53697.
- if (is_array($types) && count($types) > 0) {
- $grouptitle = $modnamestr;
- $icon = $OUTPUT->pix_icon('icon', '', $modname, array('class' => 'icon'));
- foreach($types as $type) {
- if ($type->typestr === '--') {
- continue;
- }
- if (strpos($type->typestr, '--') === 0) {
- $grouptitle = str_replace('--', '', $type->typestr);
- continue;
- }
- // Set the Sub Type metadata.
- $subtype = new stdClass();
- $subtype->title = get_string('activitytypetitle', '',
- (object)['activity' => $grouptitle, 'type' => $type->typestr]);
- $subtype->type = str_replace('&', '&', $type->type);
- $typename = preg_replace('/.*type=/', '', $subtype->type);
- $subtype->archetype = $type->modclass;
-
- if (!empty($type->help)) {
- $subtype->help = $type->help;
- } else if (get_string_manager()->string_exists('help' . $subtype->name, $modname)) {
- $subtype->help = get_string('help' . $subtype->name, $modname);
- }
- $subtype->link = new moodle_url($urlbase, array('add' => $modname, 'type' => $typename));
- $subtype->name = $modname . ':' . $subtype->link;
- $subtype->icon = $icon;
- $modlist[$course->id][$modname][$subtype->name] = $subtype;
- }
- $return += $modlist[$course->id][$modname];
- }
- } else {
- // Neither get_shortcuts() nor get_types() callbacks found, use the default item for the activity chooser.
- $modlist[$course->id][$modname][$modname] = $defaultmodule;
- $return[$modname] = $defaultmodule;
- }
+ // The callback get_shortcuts() was not found, use the default item for the activity chooser.
+ $modlist[$course->id][$modname][$modname] = $defaultmodule;
+ $return[$modname] = $defaultmodule;
}
core_collator::asort_objects_by_property($return, 'title');
// Invalidate the course groups cache seeing as we've changed it.
cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($courseid));
+ // Invalidate the user_group_groupings cache, too.
+ cache_helper::purge_by_definition('core', 'user_group_groupings');
} else if (isset($frm->remove) and !empty($frm->removeselect)) {
foreach ($frm->removeselect as $groupid) {
// Ask this method not to purge the cache, we'll do it ourselves afterwards.
}
// Invalidate the course groups cache seeing as we've changed it.
cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($courseid));
+
+ // Invalidate the user_group_groupings cache, too.
+ cache_helper::purge_by_definition('core', 'user_group_groupings');
}
}
defined('MOODLE_INTERNAL') || die();
-$string['admindirname'] = 'Rendszergazdakönyvtár';
+$string['admindirname'] = 'Rendszergazda-könyvtár';
$string['availablelangs'] = 'Elérhető nyelvek listája';
$string['chooselanguagehead'] = 'Nyelv kiválasztása';
$string['chooselanguagesub'] = 'Válasszon nyelvet a telepítéshez! Ez lesz a portál alapbeállítás szerinti nyelve, de később módosíthatja.';
$string['dirroot'] = 'Moodleディレクトリ';
$string['environmenthead'] = 'あなたの環境を確認しています ...';
$string['environmentsub2'] = 'それぞれのMoodleリリースにはPHPバージョンの最小必要条件および多くの必須PHP拡張モジュールがあります。完全な環境チェックはインストールおよびアップグレードごとに実行されます。新しいPHPバージョンのインストールまたはPHP拡張モジュールの有効化に関して分からない場合、あなたのサーバ管理者にご連絡ください。';
-$string['errorsinenvironment'] = 'ç\92°å¢\83ã\83\81ã\82§ã\83\83ã\82¯ã\81\8c失敗しました!';
+$string['errorsinenvironment'] = 'ç\92°å¢\83ã\83\81ã\82§ã\83\83ã\82¯ã\81«失敗しました!';
$string['installation'] = 'インストレーション';
$string['langdownloaderror'] = '残念ですが、言語「 {$a} 」をダウンロードできませんでした。インストール処理は英語で継続されます。';
$string['memorylimithelp'] = '<p>現在、サーバのPHPメモリ制限が {$a} に設定されています。</p>
$string['cachesessionhelp'] = 'User specific cache that expires when the user\'s session ends. Designed to alleviate session bloat/strain.';
$string['cacheapplication'] = 'Application cache';
$string['cacheapplicationhelp'] = 'Cached items are shared among all users and expire by a determined time to live (ttl).';
-// Deprecated since Moodle 3.2.
-$string['mobile'] = 'Mobile';
+
// Deprecated since Moodle 3.3.
$string['loginpasswordautocomplete'] = 'Prevent password autocompletion on login form';
$string['loginpasswordautocomplete_help'] = 'If enabled, users are not allowed to save their account password in their browser.';
$string['yesterday'] = 'Yesterday';
$string['youcandeleteallrepeats'] = 'This event is part of a repeating event series. You can delete this event only, or all {$a} events in the series at once.';
-// Deprecated since Moodle 3.2.
-$string['for'] = 'for';
-
// Deprecated since Moodle 3.4.
$string['quickdownloadcalendar'] = 'Quick download / subscribe to calendar';
$string['ical'] = 'iCal';
$string['usercompetencystatus_inreview'] = 'In review';
$string['usercompetencystatus_waitingforreview'] = 'Waiting for review';
$string['userplans'] = 'Learning plans';
-
-// Deprecated since Moodle 3.2.
-$string['invalidpersistent'] = 'Invalid persistent';
mypreferences,core_grades
myprofile,core
viewallmyentries,core_blog
-modchooserenable,core
-modchooserdisable,core
-invalidpersistent,core_competency
revealpassword,core_form
-mediasettings,core_media
-legacyheading,core_media
-legacyheading_desc,core_media
-mobile,core_admin
-for,core_calendar
-context,core_message
-discussion,core_message
-emptysearchstring,core_message
-formorethan,core_message
-keywords,core_message
-messagehistory,core_message
-newsearch,core_message
-nosearchresults,core_message
-onlymycourses,core_message
-pagerefreshes,core_message
-page-message-x,core_message
-recent,core_message
-savemysettings,core_message
-search,core_message
-settingssaved,core_message
-strftimedaydatetime,core_message
-timenosee,core_message
-timesent,core_message
-userssearchresults,core_message
loginpasswordautocomplete,core_admin
loginpasswordautocomplete_help,core_admin
deletecomment,core
$string['privacy:metadata'] = 'Media embedding does not store any personal data.';
$string['supports'] = 'Supports';
$string['videoextensions'] = 'Video: {$a}';
-
-// Deprecated since Moodle 3.2.
-$string['mediasettings'] = 'Media embedding';
-$string['legacyheading'] = 'Legacy media players';
-$string['legacyheading_desc'] = 'These players are not frequently used on the Web and require browser plugins that are less widely installed.';
$string['newonlymsg'] = 'Show only new';
$string['newmessage'] = 'New message';
$string['newmessagesearch'] = 'Select or search for a contact to send a new message.';
-$string['newsearch'] = 'New search';
$string['noframesjs'] = 'Use more accessible interface';
$string['nocontacts'] = 'No contacts';
$string['nomessages'] = 'No messages';
$string['viewunreadmessageswith'] = 'View unread messages with {$a}';
$string['writeamessage'] = 'Write a message...';
$string['you'] = 'You:';
-
-// Deprecated since Moodle 3.2.
-$string['context'] = 'context';
-$string['discussion'] = 'Discussion';
-$string['emptysearchstring'] = 'You must search for something';
-$string['formorethan'] = 'For more than';
-$string['keywords'] = 'Keywords';
-$string['messagehistory'] = 'Message history';
-$string['newsearch'] = 'New search';
-$string['nosearchresults'] = 'There were no results from your search';
-$string['onlymycourses'] = 'Only in my courses';
-$string['pagerefreshes'] = 'This page refreshes automatically every {$a} seconds';
-$string['page-message-x'] = 'Any message pages';
-$string['recent'] = 'Recent';
-$string['savemysettings'] = 'Save my settings';
-$string['search'] = 'Search';
-$string['settingssaved'] = 'Your settings have been saved';
-$string['strftimedaydatetime'] = '%A, %d %B %Y, %I:%M %p';
-$string['timenosee'] = 'Minutes since I was last seen online';
-$string['timesent'] = 'Time sent';
-$string['userssearchresults'] = 'Users found: {$a}';
$string['zippingbackup'] = 'Zipping backup';
$string['deprecatedeventname'] = '{$a} (no longer in use)';
-// Deprecated since Moodle 3.2.
-$string['modchooserenable'] = 'Activity chooser on';
-$string['modchooserdisable'] = 'Activity chooser off';
-
// Deprecated since Moodle 3.3.
$string['deletecomment'] = 'Delete this comment';
$string['sectionusedefaultname'] = 'Use default section name';
}
/**
- * Create handler instance.
+ * Get fully qualified name of session handler class.
+ *
+ * @return string The name of the handler class
*/
- protected static function load_handler() {
+ public static function get_handler_class() {
global $CFG, $DB;
- if (self::$handler) {
- return;
- }
-
- // Find out which handler to use.
if (PHPUNIT_TEST) {
- $class = '\core\session\file';
-
+ return '\core\session\file';
} else if (!empty($CFG->session_handler_class)) {
- $class = $CFG->session_handler_class;
-
+ return $CFG->session_handler_class;
} else if (!empty($CFG->dbsessions) and $DB->session_lock_supported()) {
- $class = '\core\session\database';
+ return '\core\session\database';
+ }
- } else {
- $class = '\core\session\file';
+ return '\core\session\file';
+ }
+
+ /**
+ * Create handler instance.
+ */
+ protected static function load_handler() {
+ if (self::$handler) {
+ return;
}
+
+ // Find out which handler to use.
+ $class = self::get_handler_class();
self::$handler = new $class();
}
'simplekeys' => true,
'simpledata' => true,
'ttl' => 1800,
+ 'invalidationevents' => array(
+ 'createduser',
+ )
),
);
}
/**
- * Returns detailed function information
- *
- * @deprecated since Moodle 3.1
- * @param string|object $function name of external function or record from external_function
- * @param int $strictness IGNORE_MISSING means compatible mode, false returned if record not found, debug message if more found;
- * MUST_EXIST means throw exception if no record or multiple records found
- * @return stdClass description or false if not found or exception thrown
- * @since Moodle 2.0
+ * @deprecated since Moodle 3.1. Use external_api::external_function_info().
*/
function external_function_info($function, $strictness=MUST_EXIST) {
- debugging('external_function_info() is deprecated. Please use external_api::external_function_info() instead.',
- DEBUG_DEVELOPER);
- return external_api::external_function_info($function, $strictness);
+ throw new coding_exception('external_function_info() can not be used any'.
+ 'more. Please use external_api::external_function_info() instead.');
}
/**
--- /dev/null
+@editor @editor_atto @atto @editor_moodleform
+Feature: Atto with enable/disable function.
+ In order to test enable/disable function
+ I create a sample page to test this feature.
+ As a user
+ I need to enable/disable all buttons/plugins and content of editor if "enable/disable" feature enabled.
+
+ Background:
+ Given the following "courses" exist:
+ | fullname | shortname | format |
+ | Course 1 | C1 | topics |
+ And the following "activities" exist:
+ | activity | name | intro | course | idnumber |
+ | label | L1 | <a href="../lib/editor/tests/fixtures/disable_control_example.php">Control Enable/Disable Atto</a> | C1 | label1 |
+ And I log in as "admin"
+ And I am on "Course 1" course homepage
+ And I follow "Control Enable/Disable Atto"
+
+ @javascript
+ Scenario: Check disable Atto editor.
+ When I set the field "mycontrol" to "Disable"
+ Then the "disabled" attribute of "button.atto_collapse_button" "css_element" should contain "disabled"
+ And the "disabled" attribute of "button.atto_title_button" "css_element" should contain "disabled"
+ And the "disabled" attribute of "button.atto_bold_button_bold" "css_element" should contain "disabled"
+ And the "disabled" attribute of "button.atto_italic_button_italic" "css_element" should contain "disabled"
+ And the "disabled" attribute of "button.atto_unorderedlist_button_insertUnorderedList" "css_element" should contain "disabled"
+ And the "disabled" attribute of "button.atto_orderedlist_button_insertOrderedList" "css_element" should contain "disabled"
+ And the "disabled" attribute of "button.atto_link_button" "css_element" should contain "disabled"
+ And the "disabled" attribute of "button.atto_link_button_unlink" "css_element" should contain "disabled"
+ And the "disabled" attribute of "button.atto_image_button" "css_element" should contain "disabled"
+ And the "contenteditable" attribute of "div#id_myeditoreditable" "css_element" should contain "false"
+
+ @javascript
+ Scenario: Check enable Atto editor.
+ When I set the field "mycontrol" to "Enable"
+ Then "button.atto_collapse_button[disabled]" "css_element" should not exist
+ And "button.atto_title_button[disabled]" "css_element" should not exist
+ And "button.atto_bold_button_bold[disabled]" "css_element" should not exist
+ And "button.atto_italic_button_italic[disabled]" "css_element" should not exist
+ And "button.atto_unorderedlist_button_insertUnorderedList[disabled]" "css_element" should not exist
+ And "button.atto_orderedlist_button_insertOrderedList[disabled]" "css_element" should not exist
+ And "button.atto_link_button[disabled]" "css_element" should not exist
+ And "button.atto_link_button_unlink[disabled]" "css_element" should not exist
+ And "button.atto_image_button[disabled]" "css_element" should not exist
+ And the "contenteditable" attribute of "div#id_myeditoreditable" "css_element" should contain "true"
// Hide the old textarea.
this.textarea.hide();
+ // Set up custom event for editor updated.
+ Y.mix(Y.Node.DOM_EVENTS, {'form:editorUpdated': true});
+ this.textarea.on('form:editorUpdated', function() {
+ this.updateEditorState();
+ }, this);
+
// Copy the text to the contenteditable div.
this.updateFromTextArea();
}
},
+ /**
+ * Update the state of the editor.
+ */
+ updateEditorState: function() {
+ var disabled = this.textarea.hasAttribute('readonly'),
+ editorfield = Y.one('#' + this.get('elementid') + 'editable');
+ // Enable/Disable all plugins.
+ this._setPluginState(!disabled);
+ // Enable/Disable content of editor.
+ if (editorfield) {
+ editorfield.setAttribute('contenteditable', !disabled);
+ }
+ },
+
/**
* Register an event handle for disposal in the destructor.
*
--- /dev/null
+<?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/>.
+
+/**
+ * Demonstrates use of editor with enable/disable function.
+ *
+ * This fixture is only used by the Behat test.
+ *
+ * @package core_editor
+ * @copyright 2018 Jake Hau <phuchau1509@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require(__DIR__ . '/../../../../config.php');
+require_once('./editor_form.php');
+
+// Behat test fixture only.
+defined('BEHAT_SITE_RUNNING') || die('Only available on Behat test server');
+
+// Require login.
+require_login();
+
+$PAGE->set_url('/lib/editor/tests/fixtures/disable_control_example.php');
+$PAGE->set_context(context_system::instance());
+
+// Create moodle form.
+$mform = new editor_form();
+
+echo $OUTPUT->header();
+
+// Display moodle form.
+$mform->display();
+
+echo $OUTPUT->footer();
--- /dev/null
+<?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/>.
+
+/**
+ * Provides {@link lib/editor/tests/fixtures/editor_form} class.
+ *
+ * @package core_editor
+ * @copyright 2018 Jake Hau <phuchau1509@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->libdir.'/formslib.php');
+
+/**
+ * Class editor_form
+ *
+ * Demonstrates use of editor with disabledIf function.
+ * This fixture is only used by the Behat test.
+ *
+ * @package core_editor
+ * @copyright 2018 Jake Hau <phuchau1509@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class editor_form extends moodleform {
+
+ /**
+ * Form definition. Abstract method - always override!
+ */
+ protected function definition() {
+ $mform = $this->_form;
+ $editoroptions = $this->_customdata['editoroptions'];
+
+ // Add header.
+ $mform->addElement('header', 'myheader', 'Editor in Moodle form');
+
+ // Add element control.
+ $mform->addElement('select', 'mycontrol', 'My control', ['Enable', 'Disable']);
+
+ // Add editor.
+ $mform->addElement('editor', 'myeditor', 'My Editor', null, $editoroptions);
+ $mform->setType('myeditor', PARAM_RAW);
+
+ // Add control.
+ $mform->disabledIf('myeditor', 'mycontrol', 'eq', 1);
+ }
+}
--- /dev/null
+@editor @editor_textarea @texarea @editor_moodleform
+Feature: Text area with enable/disable function.
+ In order to test enable/disable function
+ I set default editor is Text area editor, and I create a sample page to test this feature.
+ As a user
+ I need to enable/disable content of editor if "enable/disable" feature enabled.
+
+ Background:
+ Given the following "courses" exist:
+ | fullname | shortname | format |
+ | Course 1 | C1 | topics |
+ And the following "activities" exist:
+ | activity | name | intro | course | idnumber |
+ | label | L1 | <a href="../lib/editor/tests/fixtures/disable_control_example.php">Control Enable/Disable Text area</a> | C1 | label1 |
+ And I log in as "admin"
+ And I follow "Preferences" in the user menu
+ And I follow "Editor preferences"
+ And I set the field "Text editor" to "Plain text area"
+ And I press "Save changes"
+ And I am on "Course 1" course homepage
+ And I follow "Control Enable/Disable Text area"
+
+ @javascript
+ Scenario: Check disable Text area editor.
+ When I set the field "mycontrol" to "Disable"
+ Then the "readonly" attribute of "textarea#id_myeditor" "css_element" should contain "readonly"
+
+ @javascript
+ Scenario: Check enable Text area editor.
+ When I set the field "mycontrol" to "Enable"
+ Then "textarea#id_myeditor[readonly]" "css_element" should not exist
if (item) {
item.parentNode.removeChild(item);
}
+
+ document.getElementById(editorid).addEventListener('form:editorUpdated', function() {
+ M.editor_tinymce.updateEditorState(editorid);
+ });
};
M.editor_tinymce.init_callback = function() {
tinyMCE.execCommand('mceToggleEditor', false, id);
};
+/**
+ * Update the state of the editor.
+ * @param {String} id
+ */
+M.editor_tinymce.updateEditorState = function(id) {
+ var instance = window.tinyMCE.get(id),
+ content = instance.getBody(),
+ controls = instance.controlManager.controls,
+ disabled = instance.getElement().readOnly;
+ // Enable/Disable all plugins.
+ for (var key in controls) {
+ if (controls.hasOwnProperty(key)) {
+ controls[key].setDisabled(disabled);
+ }
+ }
+ // Enable/Disable body content.
+ content.setAttribute('contenteditable', !disabled);
+};
+
M.editor_tinymce.filepicker_callback = function(args) {
};
--- /dev/null
+@editor @editor_tinymce @tinymce @editor_moodleform
+Feature: Tinymce with enable/disable function.
+ In order to test enable/disable function
+ I set default editor is Tinymce editor, and I create a sample page to test this feature.
+ As a user
+ I need to enable/disable all buttons/plugins and content of editor if "enable/disable" feature enabled.
+
+ Background:
+ Given the following "courses" exist:
+ | fullname | shortname | format |
+ | Course 1 | C1 | topics |
+ And the following "activities" exist:
+ | activity | name | intro | course | idnumber |
+ | label | L1 | <a href="../lib/editor/tests/fixtures/disable_control_example.php">Control Enable/Disable Tinymce</a> | C1 | label1 |
+ And I log in as "admin"
+ And I follow "Preferences" in the user menu
+ And I follow "Editor preferences"
+ And I set the field "Text editor" to "TinyMCE HTML editor"
+ And I press "Save changes"
+ And I am on "Course 1" course homepage
+ And I follow "Control Enable/Disable Tinymce"
+
+ @javascript
+ Scenario: Check disable Tinymce editor.
+ When I click on "option[value=1]" "css_element" in the "select#id_mycontrol" "css_element"
+ Then the "class" attribute of "a#id_myeditor_pdw_toggle" "css_element" should contain "mceButtonDisabled"
+ And the "class" attribute of "table#id_myeditor_formatselect" "css_element" should contain "mceListBoxDisabled"
+ And the "class" attribute of "a#id_myeditor_bold" "css_element" should contain "mceButtonDisabled"
+ And the "class" attribute of "a#id_myeditor_italic" "css_element" should contain "mceButtonDisabled"
+ And the "class" attribute of "a#id_myeditor_bullist" "css_element" should contain "mceButtonDisabled"
+ And the "class" attribute of "a#id_myeditor_numlist" "css_element" should contain "mceButtonDisabled"
+ And the "class" attribute of "a#id_myeditor_link" "css_element" should contain "mceButtonDisabled"
+ And the "class" attribute of "a#id_myeditor_unlink" "css_element" should contain "mceButtonDisabled"
+ And the "class" attribute of "a#id_myeditor_moodlenolink" "css_element" should contain "mceButtonDisabled"
+ And the "class" attribute of "a#id_myeditor_image" "css_element" should contain "mceButtonDisabled"
+ And the "class" attribute of "a#id_myeditor_moodlemedia" "css_element" should contain "mceButtonDisabled"
+ And I switch to "id_myeditor_ifr" iframe
+ And the "contenteditable" attribute of "body" "css_element" should contain "false"
+
+ @javascript
+ Scenario: Check enable Tinymce editor.
+ When I click on "option[value=0]" "css_element" in the "select#id_mycontrol" "css_element"
+ Then the "class" attribute of "a#id_myeditor_pdw_toggle" "css_element" should contain "mceButtonEnabled"
+ And the "class" attribute of "table#id_myeditor_formatselect" "css_element" should contain "mceListBoxEnabled"
+ And the "class" attribute of "a#id_myeditor_bold" "css_element" should contain "mceButtonEnabled"
+ And the "class" attribute of "a#id_myeditor_italic" "css_element" should contain "mceButtonEnabled"
+ And the "class" attribute of "a#id_myeditor_bullist" "css_element" should contain "mceButtonEnabled"
+ And the "class" attribute of "a#id_myeditor_numlist" "css_element" should contain "mceButtonEnabled"
+ And the "class" attribute of "a#id_myeditor_link" "css_element" should contain "mceButtonDisabled"
+ And the "class" attribute of "a#id_myeditor_unlink" "css_element" should contain "mceButtonDisabled"
+ And the "class" attribute of "a#id_myeditor_moodlenolink" "css_element" should contain "mceButtonDisabled"
+ And the "class" attribute of "a#id_myeditor_image" "css_element" should contain "mceButtonEnabled"
+ And the "class" attribute of "a#id_myeditor_moodlemedia" "css_element" should contain "mceButtonEnabled"
+ And I switch to "id_myeditor_ifr" iframe
+ And the "contenteditable" attribute of "body" "css_element" should contain "true"
return $data;
}
+/**
+ * Returns all of the files in the draftarea.
+ *
+ * @param int $draftitemid The draft item ID
+ * @param string $filepath path for the uploaded files.
+ * @return array An array of files associated with this draft item id.
+ */
+function file_get_all_files_in_draftarea(int $draftitemid, string $filepath = '/') : array {
+ $files = [];
+ $draftfiles = file_get_drafarea_files($draftitemid, $filepath);
+ file_get_drafarea_folders($draftitemid, $filepath, $draftfiles);
+
+ if (!empty($draftfiles)) {
+ foreach ($draftfiles->list as $draftfile) {
+ if ($draftfile->type == 'file') {
+ $files[] = $draftfile;
+ }
+ }
+
+ if (isset($draftfiles->children)) {
+ foreach ($draftfiles->children as $draftfile) {
+ $files = array_merge($files, file_get_all_files_in_draftarea($draftitemid, $draftfile->filepath));
+ }
+ }
+ }
+ return $files;
+}
+
/**
* Returns draft area itemid for a given element.
*
return;
}
- $draftfiles = file_get_drafarea_files($value);
+ $draftfiles = file_get_all_files_in_draftarea($value);
$wrongfiles = array();
if (empty($draftfiles)) {
return;
}
- foreach ($draftfiles->list as $file) {
+ foreach ($draftfiles as $file) {
if (!$filetypesutil->is_allowed_file_type($file->filename, $whitelist)) {
$wrongfiles[] = $file->filename;
}
* @param {Boolean} disabled True to disable, false to enable.
*/
_disableElement: function(name, disabled) {
- var els = this.elementsByName(name);
- var filepicker = this.isFilePicker(name);
+ var els = this.elementsByName(name),
+ filepicker = this.isFilePicker(name),
+ editors = this.get('form').all('.fitem [data-fieldtype="editor"] textarea[name="' + name + '[text]"]');
+
els.each(function(node) {
if (disabled) {
node.setAttribute('disabled', 'disabled');
}
}
});
+ editors.each(function(editor) {
+ if (disabled) {
+ editor.setAttribute('readonly', 'readonly');
+ } else {
+ editor.removeAttribute('readonly', 'readonly');
+ }
+ editor.getDOMNode().dispatchEvent(new Event('form:editorUpdated'));
+ });
},
/**
* Hides or shows all form elements with the given name.
/** System (not user-addable) module archetype */
define('MOD_ARCHETYPE_SYSTEM', 3);
-/**
- * Return this from modname_get_types callback to use default display in activity chooser.
- * Deprecated, will be removed in 3.5, TODO MDL-53697.
- * @deprecated since Moodle 3.1
- */
-define('MOD_SUBTYPE_NO_CHILDREN', 'modsubtypenochildren');
-
/**
* Security token used for allowing access
* from external application such as web services.
$this->assertContains($file->get_filename(), $expected);
}
}
+
+ /**
+ * Test that all files in the draftarea are returned.
+ */
+ public function test_file_get_all_files_in_draftarea() {
+ $this->resetAfterTest();
+ $this->setAdminUser();
+
+ $filerecord = ['filename' => 'basepic.jpg'];
+ $file = self::create_draft_file($filerecord);
+
+ $secondrecord = [
+ 'filename' => 'infolder.jpg',
+ 'filepath' => '/assignment/',
+ 'itemid' => $file->get_itemid()
+ ];
+ $file = self::create_draft_file($secondrecord);
+
+ $thirdrecord = [
+ 'filename' => 'deeperfolder.jpg',
+ 'filepath' => '/assignment/pics/',
+ 'itemid' => $file->get_itemid()
+ ];
+ $file = self::create_draft_file($thirdrecord);
+
+ $fourthrecord = [
+ 'filename' => 'differentimage.jpg',
+ 'filepath' => '/secondfolder/',
+ 'itemid' => $file->get_itemid()
+ ];
+ $file = self::create_draft_file($fourthrecord);
+
+ // This record has the same name as the last record, but it's in a different folder.
+ // Just checking this is also returned.
+ $fifthrecord = [
+ 'filename' => 'differentimage.jpg',
+ 'filepath' => '/assignment/pics/',
+ 'itemid' => $file->get_itemid()
+ ];
+ $file = self::create_draft_file($fifthrecord);
+
+ $allfiles = file_get_all_files_in_draftarea($file->get_itemid());
+ $this->assertCount(5, $allfiles);
+ $this->assertEquals($filerecord['filename'], $allfiles[0]->filename);
+ $this->assertEquals($secondrecord['filename'], $allfiles[1]->filename);
+ $this->assertEquals($thirdrecord['filename'], $allfiles[2]->filename);
+ $this->assertEquals($fourthrecord['filename'], $allfiles[3]->filename);
+ $this->assertEquals($fifthrecord['filename'], $allfiles[4]->filename);
+ }
}
/**
$this->assertFalse($stringman->string_deprecated('hidden', 'grades'));
// Check deprecated string.
- $this->assertTrue($stringman->string_deprecated('modchooserenable', 'core'));
- $this->assertTrue($stringman->string_exists('modchooserenable', 'core'));
+ $this->assertTrue($stringman->string_deprecated('groupextendenrol', 'core'));
+ $this->assertTrue($stringman->string_exists('groupextendenrol', 'core'));
$this->assertDebuggingNotCalled();
- $this->assertEquals('Activity chooser on', get_string('modchooserenable', 'core'));
- $this->assertDebuggingCalled('String [modchooserenable,core] is deprecated. '.
+ $this->assertEquals('Extend enrolment (common)', get_string('groupextendenrol', 'core'));
+ $this->assertDebuggingCalled('String [groupextendenrol,core] is deprecated. '.
'Either you should no longer be using that string, or the string has been incorrectly deprecated, in which case you should report this as a bug. '.
'Please refer to https://docs.moodle.org/dev/String_deprecation');
}
This files describes API changes in core libraries and APIs,
information provided here is intended especially for developers.
+=== 3.6 ===
+
+* The following functions have been finally deprecated and can not be used any more:
+
+- external_function_info()
+
=== 3.5 ===
* There is a new privacy API that every subsystem and plugin has to implement so that the site can become GDPR
});
// Link to mark read page before loading the actual link.
- notification.contexturl = URL.relativeUrl('message/output/popup/mark_notification_read.php', {
- notificationid: notification.id,
- redirecturl: notification.contexturl
- });
+ var notificationurlparams = {
+ notificationid: notification.id
+ };
+ if (notification.contexturl) {
+ notificationurlparams.redirecturl = notification.contexturl;
+ }
+ notification.contexturl = URL.relativeUrl('message/output/popup/mark_notification_read.php', notificationurlparams);
var promise = Templates.render('message_popup/notification_content_item', notification)
.then(function(html, js) {
}
$notificationid = required_param('notificationid', PARAM_INT);
-$redirecturl = optional_param('redirecturl', $CFG->wwwroot, PARAM_LOCALURL);
+$redirecturl = optional_param('redirecturl', '', PARAM_URL);
$notification = $DB->get_record('notifications', array('id' => $notificationid));
+// If the redirect URL after filtering is empty, or it was never passed, then redirect to the notification page.
+if (empty($redirecturl)) {
+ $redirecturl = new moodle_url('/message/output/popup/notifications.php', ['notificationid' => $notificationid]);
+}
+
// Check notification belongs to this user.
if ($USER->id != $notification->useridto) {
redirect($CFG->wwwroot);
}
\core_message\api::mark_notification_as_read($notification);
-redirect($redirecturl);
+redirect(new moodle_url($redirecturl));
$string['viewrevealidentitiesconfirm'] = 'View reveal student identities confirmation page.';
$string['workflowfilter'] = 'Workflow filter';
$string['xofy'] = '{$a->x} of {$a->y}';
-
-// Deprecated since Moodle 3.2.
-$string['changegradewarning'] = 'This assignment has graded submissions and changing the grade will not automatically re-calculate existing submission grades. You must re-grade all existing submissions, if you wish to change the grade.';
-changegradewarning,mod_assign
\ No newline at end of file
* @return bool
*/
public function submission_is_empty(stdClass $data) {
- $files = file_get_drafarea_files($data->files_filemanager);
- return count($files->list) == 0;
+ global $USER;
+ $fs = get_file_storage();
+ // Get a count of all the draft files, excluding any directories.
+ $files = $fs->get_area_files(context_user::instance($USER->id)->id,
+ 'user',
+ 'draft',
+ $data->files_filemanager,
+ 'id',
+ false);
+ return count($files) == 0;
}
/**
$this->assertTrue($result === $expected);
}
+ /**
+ * Test that an empty directory is is not detected as a valid submission by submission_is_empty.
+ */
+ public function test_submission_is_empty_directory_only() {
+ $this->resetAfterTest();
+ $course = $this->getDataGenerator()->create_course();
+ $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
+ $assign = $this->create_instance($course, [
+ 'assignsubmission_file_enabled' => 1,
+ 'assignsubmission_file_maxfiles' => 12,
+ 'assignsubmission_file_maxsizebytes' => 10,
+ ]);
+ $this->setUser($student->id);
+ $itemid = file_get_unused_draft_itemid();
+ $submission = (object)['files_filemanager' => $itemid];
+ $plugin = $assign->get_submission_plugin_by_type('file');
+ $fs = get_file_storage();
+ $fs->create_directory(
+ context_user::instance($student->id)->id,
+ 'user',
+ 'draft',
+ $itemid,
+ '/subdirectory/'
+ );
+
+ $this->assertTrue($plugin->submission_is_empty($submission));
+ }
+
/**
* Test new_submission_empty
*
$this->assertTrue($result === $expected);
}
+ /**
+ * Test that an empty directory is is not detected as a valid submission by new_submission_is_empty.
+ */
+ public function test_new_submission_empty_directory_only() {
+ $this->resetAfterTest();
+ $course = $this->getDataGenerator()->create_course();
+ $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
+ $assign = $this->create_instance($course, [
+ 'assignsubmission_file_enabled' => 1,
+ 'assignsubmission_file_maxfiles' => 12,
+ 'assignsubmission_file_maxsizebytes' => 10,
+ ]);
+ $this->setUser($student->id);
+ $itemid = file_get_unused_draft_itemid();
+ $submission = (object)['files_filemanager' => $itemid];
+ $plugin = $assign->get_submission_plugin_by_type('file');
+ $fs = get_file_storage();
+ $fs->create_directory(
+ context_user::instance($student->id)->id,
+ 'user',
+ 'draft',
+ $itemid,
+ '/subdirectory/'
+ );
+
+ $this->assertTrue($assign->new_submission_empty($submission));
+ }
+
/**
* Dataprovider for the test_submission_is_empty testcase
*
],
false
],
+ 'With file in directory' => [
+ [
+ 'component' => 'user',
+ 'filearea' => 'draft',
+ 'filepath' => '/subdir/',
+ 'filename' => 'not_a_virus.exe'
+ ],
+ false
+ ],
'Without file' => [null, true]
];
}
$string['viewtodate'] = 'Read only to';
$string['viewtodatevalidation'] = 'The read only to date cannot be before the read only from date.';
$string['wrongdataid'] = 'Wrong data id provided';
-
-// Deprecated since Moodle 3.2.
-$string['namedate'] = 'Date field';
-$string['namefile'] = 'File field';
-$string['namecheckbox'] = 'Checkbox field';
-$string['namelatlong'] = 'Latitude/longitude field';
-$string['namemenu'] = 'Menu field';
-$string['namemultimenu'] = 'Multiple-selection menu field';
-$string['namenumber'] = 'Number field';
-$string['namepicture'] = 'Picture field';
-$string['nameradiobutton'] = 'Radio button field';
-$string['nametext'] = 'Text field';
-$string['nametextarea'] = 'Textarea field';
-$string['nameurl'] = 'URL field';
-namedate,mod_data
-namefile,mod_data
-namecheckbox,mod_data
-namelatlong,mod_data
-namemenu,mod_data
-namemultimenu,mod_data
-namenumber,mod_data
-namepicture,mod_data
-nameradiobutton,mod_data
-nametext,mod_data
-nametextarea,mod_data
-nameurl,mod_data
\ No newline at end of file
-start,mod_feedback
-stop,mod_feedback
\ No newline at end of file
$string['use_this_template'] = 'Use this template';
$string['using_templates'] = 'Use a template';
$string['vertical'] = 'Vertical';
-// Deprecated since Moodle 3.2.
-$string['start'] = 'Start';
-$string['stop'] = 'End';
////////////////////////////////////////////////
/**
- * returns the context-id related to the given coursemodule-id
- *
* @deprecated since 3.1
- * @staticvar object $context
- * @param int $cmid the coursemodule-id
- * @return object $context
*/
-function feedback_get_context($cmid) {
- debugging('Function feedback_get_context() is deprecated because it was not used.',
- DEBUG_DEVELOPER);
- static $context;
-
- if (isset($context)) {
- return $context;
- }
-
- $context = context_module::instance($cmid);
- return $context;
+function feedback_get_context() {
+ throw new coding_exception('feedback_get_context() can not be used anymore.');
}
/**
}
/**
- * creates a new item-record
- *
* @deprecated since 3.1
- * @param object $data the data from edit_item_form
- * @return int the new itemid
*/
-function feedback_create_item($data) {
- debugging('Function feedback_create_item() is deprecated because it was not used.',
- DEBUG_DEVELOPER);
- global $DB;
-
- $item = new stdClass();
- $item->feedback = $data->feedbackid;
-
- $item->template=0;
- if (isset($data->templateid)) {
- $item->template = intval($data->templateid);
- }
-
- $itemname = trim($data->itemname);
- $item->name = ($itemname ? $data->itemname : get_string('no_itemname', 'feedback'));
-
- if (!empty($data->itemlabel)) {
- $item->label = trim($data->itemlabel);
- } else {
- $item->label = get_string('no_itemlabel', 'feedback');
- }
-
- $itemobj = feedback_get_item_class($data->typ);
- $item->presentation = ''; //the date comes from postupdate() of the itemobj
-
- $item->hasvalue = $itemobj->get_hasvalue();
-
- $item->typ = $data->typ;
- $item->position = $data->position;
-
- $item->required=0;
- if (!empty($data->required)) {
- $item->required = $data->required;
- }
-
- $item->id = $DB->insert_record('feedback_item', $item);
-
- //move all itemdata to the data
- $data->id = $item->id;
- $data->feedback = $item->feedback;
- $data->name = $item->name;
- $data->label = $item->label;
- $data->required = $item->required;
- return $itemobj->postupdate($data);
+function feedback_create_item() {
+ throw new coding_exception('feedback_create_item() can not be used anymore.');
}
/**
}
/**
- * prints the given item as a preview.
- * each item-class has an own print_item_preview function implemented.
- *
* @deprecated since Moodle 3.1
- * @global object
- * @param object $item the item what we want to print out
- * @return void
*/
-function feedback_print_item_preview($item) {
- debugging('Function feedback_print_item_preview() is deprecated and does nothing. '
- . 'Items must implement complete_form_element()', DEBUG_DEVELOPER);
+function feedback_print_item_preview() {
+ throw new coding_exception('feedback_print_item_preview() can not be used anymore. '
+ . 'Items must implement complete_form_element().');
}
/**
- * prints the given item in the completion form.
- * each item-class has an own print_item_complete function implemented.
- *
* @deprecated since Moodle 3.1
- * @param object $item the item what we want to print out
- * @param mixed $value the value
- * @param boolean $highlightrequire if this set true and the value are false on completing so the item will be highlighted
- * @return void
*/
-function feedback_print_item_complete($item, $value = false, $highlightrequire = false) {
- debugging('Function feedback_print_item_complete() is deprecated and does nothing. '
- . 'Items must implement complete_form_element()', DEBUG_DEVELOPER);
+function feedback_print_item_complete() {
+ throw new coding_exception('feedback_print_item_complete() can not be used anymore. '
+ . 'Items must implement complete_form_element().');
}
/**
- * prints the given item in the show entries page.
- * each item-class has an own print_item_show_value function implemented.
- *
* @deprecated since Moodle 3.1
- * @param object $item the item what we want to print out
- * @param mixed $value
- * @return void
*/
-function feedback_print_item_show_value($item, $value = false) {
- debugging('Function feedback_print_item_show_value() is deprecated and does nothing. '
- . 'Items must implement complete_form_element()', DEBUG_DEVELOPER);
+function feedback_print_item_show_value() {
+ throw new coding_exception('feedback_print_item_show_value() can not be used anymore. '
+ . 'Items must implement complete_form_element().');
}
/**
}
/**
- * deletes the given temporary completed and all related temporary values
- *
* @deprecated since Moodle 3.1
- *
- * @param int $tmpcplid
- * @return void
*/
-function feedback_delete_completedtmp($tmpcplid) {
- global $DB;
-
- debugging('Function feedback_delete_completedtmp() is deprecated because it is no longer used',
- DEBUG_DEVELOPER);
+function feedback_delete_completedtmp() {
+ throw new coding_exception('feedback_delete_completedtmp() can not be used anymore.');
- $DB->delete_records('feedback_valuetmp', array('completed'=>$tmpcplid));
- $DB->delete_records('feedback_completedtmp', array('id'=>$tmpcplid));
}
////////////////////////////////////////////////
}
/**
- * this returns the position where the user can continue the completing.
- *
* @deprecated since Moodle 3.1
- * @global object
- * @global object
- * @global object
- * @param int $feedbackid
- * @param int $courseid
- * @param string $guestid this id will be saved temporary and is unique
- * @return int the position to continue
*/
-function feedback_get_page_to_continue($feedbackid, $courseid = false, $guestid = false) {
- global $CFG, $USER, $DB;
-
- debugging('Function feedback_get_page_to_continue() is deprecated and since it is '
- . 'no longer used in mod_feedback', DEBUG_DEVELOPER);
-
- //is there any break?
-
- if (!$allbreaks = feedback_get_all_break_positions($feedbackid)) {
- return false;
- }
-
- $params = array();
- if ($courseid) {
- $courseselect = "AND fv.course_id = :courseid";
- $params['courseid'] = $courseid;
- } else {
- $courseselect = '';
- }
-
- if ($guestid) {
- $userselect = "AND fc.guestid = :guestid";
- $usergroup = "GROUP BY fc.guestid";
- $params['guestid'] = $guestid;
- } else {
- $userselect = "AND fc.userid = :userid";
- $usergroup = "GROUP BY fc.userid";
- $params['userid'] = $USER->id;
- }
-
- $sql = "SELECT MAX(fi.position)
- FROM {feedback_completedtmp} fc, {feedback_valuetmp} fv, {feedback_item} fi
- WHERE fc.id = fv.completed
- $userselect
- AND fc.feedback = :feedbackid
- $courseselect
- AND fi.id = fv.item
- $usergroup";
- $params['feedbackid'] = $feedbackid;
-
- $lastpos = $DB->get_field_sql($sql, $params);
-
- //the index of found pagebreak is the searched pagenumber
- foreach ($allbreaks as $pagenr => $br) {
- if ($lastpos < $br) {
- return $pagenr;
- }
- }
- return count($allbreaks);
+function feedback_get_page_to_continue() {
+ throw new coding_exception('feedback_get_page_to_continue() can not be used anymore.');
}
////////////////////////////////////////////////
////////////////////////////////////////////////
/**
- * cleans the userinput while submitting the form.
- *
* @deprecated since Moodle 3.1
- * @param mixed $value
- * @return mixed
*/
-function feedback_clean_input_value($item, $value) {
- debugging('Function feedback_clean_input_value() is deprecated and does nothing. '
- . 'Items must implement complete_form_element()', DEBUG_DEVELOPER);
+function feedback_clean_input_value() {
+ throw new coding_exception('feedback_clean_input_value() can not be used anymore. '
+ . 'Items must implement complete_form_element().');
+
}
/**
- * this saves the values of an completed.
- * if the param $tmp is set true so the values are saved temporary in table feedback_valuetmp.
- * if there is already a completed and the userid is set so the values are updated.
- * on all other things new value records will be created.
- *
* @deprecated since Moodle 3.1
- *
- * @param int $usrid
- * @param boolean $tmp
- * @return mixed false on error or the completeid
*/
-function feedback_save_values($usrid, $tmp = false) {
- global $DB;
-
- debugging('Function feedback_save_values() was deprecated because it did not have '.
- 'enough arguments, was not suitable for non-temporary table and was taking '.
- 'data directly from input', DEBUG_DEVELOPER);
-
- $completedid = optional_param('completedid', 0, PARAM_INT);
- $tmpstr = $tmp ? 'tmp' : '';
- $time = time();
- $timemodified = mktime(0, 0, 0, date('m', $time), date('d', $time), date('Y', $time));
-
- if ($usrid == 0) {
- return feedback_create_values($usrid, $timemodified, $tmp);
- }
- $completed = $DB->get_record('feedback_completed'.$tmpstr, array('id'=>$completedid));
- if (!$completed) {
- return feedback_create_values($usrid, $timemodified, $tmp);
- } else {
- $completed->timemodified = $timemodified;
- return feedback_update_values($completed, $tmp);
- }
+function feedback_save_values() {
+ throw new coding_exception('feedback_save_values() can not be used anymore.');
}
/**
- * this saves the values from anonymous user such as guest on the main-site
- *
* @deprecated since Moodle 3.1
- *
- * @param string $guestid the unique guestidentifier
- * @return mixed false on error or the completeid
*/
-function feedback_save_guest_values($guestid) {
- global $DB;
-
- debugging('Function feedback_save_guest_values() was deprecated because it did not have '.
- 'enough arguments, was not suitable for non-temporary table and was taking '.
- 'data directly from input', DEBUG_DEVELOPER);
-
- $completedid = optional_param('completedid', false, PARAM_INT);
-
- $timemodified = time();
- if (!$completed = $DB->get_record('feedback_completedtmp', array('id'=>$completedid))) {
- return feedback_create_values(0, $timemodified, true, $guestid);
- } else {
- $completed->timemodified = $timemodified;
- return feedback_update_values($completed, true);
- }
+function feedback_save_guest_values() {
+ throw new coding_exception('feedback_save_guest_values() can not be used anymore.');
}
/**
}
/**
- * this function checks the correctness of values.
- * the rules for this are implemented in the class of each item.
- * it can be the required attribute or the value self e.g. numeric.
- * the params first/lastitem are given to determine the visible range between pagebreaks.
- *
- * @global object
- * @param int $firstitem the position of firstitem for checking
- * @param int $lastitem the position of lastitem for checking
- * @return boolean
+ * @deprecated since Moodle 3.1
*/
-function feedback_check_values($firstitem, $lastitem) {
- debugging('Function feedback_check_values() is deprecated and does nothing. '
- . 'Items must implement complete_form_element()', DEBUG_DEVELOPER);
- return true;
+function feedback_check_values() {
+ throw new coding_exception('feedback_check_values() can not be used anymore. '
+ . 'Items must implement complete_form_element().');
}
/**
- * this function create a complete-record and the related value-records.
- * depending on the $tmp (true/false) the values are saved temporary or permanently
- *
* @deprecated since Moodle 3.1
- *
- * @param int $userid
- * @param int $timemodified
- * @param boolean $tmp
- * @param string $guestid a unique identifier to save temporary data
- * @return mixed false on error or the completedid
*/
-function feedback_create_values($usrid, $timemodified, $tmp = false, $guestid = false) {
- global $DB;
-
- debugging('Function feedback_create_values() was deprecated because it did not have '.
- 'enough arguments, was not suitable for non-temporary table and was taking '.
- 'data directly from input', DEBUG_DEVELOPER);
-
- $tmpstr = $tmp ? 'tmp' : '';
- //first we create a new completed record
- $completed = new stdClass();
- $completed->feedback = $feedbackid;
- $completed->userid = $usrid;
- $completed->guestid = $guestid;
- $completed->timemodified = $timemodified;
- $completed->anonymous_response = $anonymous_response;
-
- $completedid = $DB->insert_record('feedback_completed'.$tmpstr, $completed);
-
- $completed = $DB->get_record('feedback_completed'.$tmpstr, array('id'=>$completedid));
-
- //the keys are in the form like abc_xxx
- //with explode we make an array with(abc, xxx) and (abc=typ und xxx=itemnr)
-
- //get the items of the feedback
- if (!$allitems = $DB->get_records('feedback_item', array('feedback'=>$completed->feedback))) {
- return false;
- }
- foreach ($allitems as $item) {
- if (!$item->hasvalue) {
- continue;
- }
- //get the class of item-typ
- $itemobj = feedback_get_item_class($item->typ);
-
- $keyname = $item->typ.'_'.$item->id;
-
- if ($item->typ === 'multichoice') {
- $itemvalue = optional_param_array($keyname, null, PARAM_INT);
- } else {
- $itemvalue = optional_param($keyname, null, PARAM_NOTAGS);
- }
-
- if (is_null($itemvalue)) {
- continue;
- }
-
- $value = new stdClass();
- $value->item = $item->id;
- $value->completed = $completed->id;
- $value->course_id = $courseid;
-
- //the kind of values can be absolutely different
- //so we run create_value directly by the item-class
- $value->value = $itemobj->create_value($itemvalue);
- $DB->insert_record('feedback_value'.$tmpstr, $value);
- }
- return $completed->id;
+function feedback_create_values() {
+ throw new coding_exception('feedback_create_values() can not be used anymore.');
}
/**
- * this function updates a complete-record and the related value-records.
- * depending on the $tmp (true/false) the values are saved temporary or permanently
- *
- * @global object
- * @param object $completed
- * @param boolean $tmp
- * @return int the completedid
+ * @deprecated since Moodle 3.1
*/
-function feedback_update_values($completed, $tmp = false) {
- global $DB;
-
- debugging('Function feedback_update_values() was deprecated because it did not have '.
- 'enough arguments, was not suitable for non-temporary table and was taking '.
- 'data directly from input', DEBUG_DEVELOPER);
-
- $courseid = optional_param('courseid', false, PARAM_INT);
- $tmpstr = $tmp ? 'tmp' : '';
-
- $DB->update_record('feedback_completed'.$tmpstr, $completed);
- //get the values of this completed
- $values = $DB->get_records('feedback_value'.$tmpstr, array('completed'=>$completed->id));
-
- //get the items of the feedback
- if (!$allitems = $DB->get_records('feedback_item', array('feedback'=>$completed->feedback))) {
- return false;
- }
- foreach ($allitems as $item) {
- if (!$item->hasvalue) {
- continue;
- }
- //get the class of item-typ
- $itemobj = feedback_get_item_class($item->typ);
-
- $keyname = $item->typ.'_'.$item->id;
-
- if ($item->typ === 'multichoice') {
- $itemvalue = optional_param_array($keyname, null, PARAM_INT);
- } else {
- $itemvalue = optional_param($keyname, null, PARAM_NOTAGS);
- }
-
- //is the itemvalue set (could be a subset of items because pagebreak)?
- if (is_null($itemvalue)) {
- continue;
- }
-
- $newvalue = new stdClass();
- $newvalue->item = $item->id;
- $newvalue->completed = $completed->id;
- $newvalue->course_id = $courseid;
-
- //the kind of values can be absolutely different
- //so we run create_value directly by the item-class
- $newvalue->value = $itemobj->create_value($itemvalue);
-
- //check, if we have to create or update the value
- $exist = false;
- foreach ($values as $value) {
- if ($value->item == $newvalue->item) {
- $newvalue->id = $value->id;
- $exist = true;
- break;
- }
- }
- if ($exist) {
- $DB->update_record('feedback_value'.$tmpstr, $newvalue);
- } else {
- $DB->insert_record('feedback_value'.$tmpstr, $newvalue);
- }
- }
-
- return $completed->id;
+function feedback_update_values() {
+ throw new coding_exception('feedback_update_values() can not be used anymore.');
}
/**
}
/**
- * if the completion of a feedback will be continued eg.
- * by pagebreak or by multiple submit so the complete must be found.
- * if the param $tmp is set true so all things are related to temporary completeds
- *
- * @deprecated since Moodle 3.1
- * @param int $feedbackid
- * @param boolean $tmp
- * @param int $courseid
- * @param string $guestid
- * @return int the id of the found completed
+ * @deprecated since Moodle 3.1. Use feedback_get_current_completed_tmp() or feedback_get_last_completed.
*/
-function feedback_get_current_completed($feedbackid,
- $tmp = false,
- $courseid = false,
- $guestid = false) {
-
- debugging('Function feedback_get_current_completed() is deprecated. Please use either '.
- 'feedback_get_current_completed_tmp() or feedback_get_last_completed()',
- DEBUG_DEVELOPER);
-
- global $USER, $CFG, $DB;
-
- $tmpstr = $tmp ? 'tmp' : '';
-
- if (!$courseid) {
- if ($guestid) {
- $params = array('feedback'=>$feedbackid, 'guestid'=>$guestid);
- return $DB->get_record('feedback_completed'.$tmpstr, $params);
- } else {
- $params = array('feedback'=>$feedbackid, 'userid'=>$USER->id);
- return $DB->get_record('feedback_completed'.$tmpstr, $params);
- }
- }
-
- $params = array();
-
- if ($guestid) {
- $userselect = "AND fc.guestid = :guestid";
- $params['guestid'] = $guestid;
- } else {
- $userselect = "AND fc.userid = :userid";
- $params['userid'] = $USER->id;
- }
- //if courseid is set the feedback is global.
- //there can be more than one completed on one feedback
- $sql = "SELECT DISTINCT fc.*
- FROM {feedback_value{$tmpstr}} fv, {feedback_completed{$tmpstr}} fc
- WHERE fv.course_id = :courseid
- AND fv.completed = fc.id
- $userselect
- AND fc.feedback = :feedbackid";
- $params['courseid'] = intval($courseid);
- $params['feedbackid'] = $feedbackid;
-
- if (!$sqlresult = $DB->get_records_sql($sql, $params)) {
- return false;
- }
- foreach ($sqlresult as $r) {
- return $DB->get_record('feedback_completed'.$tmpstr, array('id'=>$r->id));
- }
+function feedback_get_current_completed() {
+ throw new coding_exception('feedback_get_current_completed() can not be used anymore. Please ' .
+ 'use either feedback_get_current_completed_tmp() or feedback_get_last_completed()');
}
/**
////////////////////////////////////////////////
/**
- * checks if the course and the feedback is in the table feedback_sitecourse_map.
- *
* @deprecated since 3.1
- * @param int $feedbackid
- * @param int $courseid
- * @return int the count of records
*/
-function feedback_is_course_in_sitecourse_map($feedbackid, $courseid) {
- debugging('Function feedback_is_course_in_sitecourse_map() is deprecated because it was not used.',
- DEBUG_DEVELOPER);
- global $DB;
- $params = array('feedbackid'=>$feedbackid, 'courseid'=>$courseid);
- return $DB->count_records('feedback_sitecourse_map', $params);
+function feedback_is_course_in_sitecourse_map() {
+ throw new coding_exception('feedback_is_course_in_sitecourse_map() can not be used anymore.');
}
/**
- * checks if the feedback is in the table feedback_sitecourse_map.
- *
* @deprecated since 3.1
- * @param int $feedbackid
- * @return boolean
*/
-function feedback_is_feedback_in_sitecourse_map($feedbackid) {
- debugging('Function feedback_is_feedback_in_sitecourse_map() is deprecated because it was not used.',
- DEBUG_DEVELOPER);
- global $DB;
- return $DB->record_exists('feedback_sitecourse_map', array('feedbackid'=>$feedbackid));
+function feedback_is_feedback_in_sitecourse_map() {
+ throw new coding_exception('feedback_is_feedback_in_sitecourse_map() can not be used anymore.');
}
/**
}
/**
- * removes non existing courses or feedbacks from sitecourse_map.
- * it shouldn't be called all too often
- * a good place for it could be the mapcourse.php or unmapcourse.php
- *
* @deprecated since 3.1
- * @global object
- * @return void
*/
function feedback_clean_up_sitecourse_map() {
- global $DB;
- debugging('Function feedback_clean_up_sitecourse_map() is deprecated because it was not used.',
- DEBUG_DEVELOPER);
-
- $maps = $DB->get_records('feedback_sitecourse_map');
- foreach ($maps as $map) {
- if (!$DB->get_record('course', array('id'=>$map->courseid))) {
- $params = array('courseid'=>$map->courseid, 'feedbackid'=>$map->feedbackid);
- $DB->delete_records('feedback_sitecourse_map', $params);
- continue;
- }
- if (!$DB->get_record('feedback', array('id'=>$map->feedbackid))) {
- $params = array('courseid'=>$map->courseid, 'feedbackid'=>$map->feedbackid);
- $DB->delete_records('feedback_sitecourse_map', $params);
- continue;
- }
-
- }
+ throw new coding_exception('feedback_clean_up_sitecourse_map() can not be used anymore.');
}
////////////////////////////////////////////////
////////////////////////////////////////////////
/**
- * prints the option items of a selection-input item (dropdownlist).
* @deprecated since 3.1
- * @param int $startval the first value of the list
- * @param int $endval the last value of the list
- * @param int $selectval which item should be selected
- * @param int $interval the stepsize from the first to the last value
- * @return void
*/
-function feedback_print_numeric_option_list($startval, $endval, $selectval = '', $interval = 1) {
- debugging('Function feedback_print_numeric_option_list() is deprecated because it was not used.',
- DEBUG_DEVELOPER);
- for ($i = $startval; $i <= $endval; $i += $interval) {
- if ($selectval == ($i)) {
- $selected = 'selected="selected"';
- } else {
- $selected = '';
- }
- echo '<option '.$selected.'>'.$i.'</option>';
- }
+function feedback_print_numeric_option_list() {
+ throw new coding_exception('feedback_print_numeric_option_list() can not be used anymore.');
}
/**
+=== 3.6 ===
+
+* The following functions have been finally deprecated and can not be used anymore:
+ * feedback_print_item_preview()
+ * feedback_print_item_complete()
+ * feedback_print_item_show_value()
+ * feedback_check_values()
+ * feedback_clean_input_value()
+ * feedback_get_context()
+ * feedback_create_item()
+ * feedback_delete_completedtmp()
+ * feedback_get_page_to_continue()
+ * feedback_save_values()
+ * feedback_save_guest_values()
+ * feedback_create_values()
+ * feedback_update_values()
+ * feedback_get_current_completed()
+ * feedback_is_course_in_sitecourse_map()
+ * feedback_is_feedback_in_sitecourse_map()
+ * feedback_clean_up_sitecourse_map()
+ * feedback_print_numeric_option_list()
+
=== 3.5 ===
* The following functions, previously used (exclusively) by upgrade steps are not available
if ($displayformat == 'header') {
echo '<table cellspacing="0" class="forumheaderlist">';
- echo '<thead>';
+ echo '<thead class="text-left">';
echo '<tr>';
echo '<th class="header topic" scope="col">'.get_string('discussion', 'forum').'</th>';
echo '<th class="header author" scope="col">'.get_string('startedby', 'forum').'</th>';
$progress = $lesson->calculate_progress();
}
- // print out the Progress Bar. Attempted to put as much as possible in the style sheets.
- $content = '<br />' . html_writer::tag('div', $progress . '%', array('class' => 'progress_bar_completed', 'style' => 'width: '. $progress . '%;'));
- $printprogress = html_writer::tag('div', get_string('progresscompleted', 'lesson', $progress) . $content, array('class' => 'progress_bar'));
-
+ $content = html_writer::start_tag('div');
+ $content .= html_writer::start_tag('div', array('class' => 'progress'));
+ $content .= html_writer::start_tag('div', array('class' => 'progress-bar bar', 'role' => 'progressbar',
+ 'style' => 'width: ' . $progress .'%', 'aria-valuenow' => $progress, 'aria-valuemin' => 0, 'aria-valuemax' => 100));
+ $content .= $progress . "%";
+ $content .= html_writer::end_tag('div');
+ $content .= html_writer::end_tag('div');
+ $printprogress = html_writer::tag('div', get_string('progresscompleted', 'lesson', $progress) . $content);
return $this->output->box($printprogress, 'progress_bar');
}
// Add items defined in ltisource plugins.
foreach (core_component::get_plugin_list('ltisource') as $pluginname => $dir) {
- if ($moretypes = component_callback("ltisource_$pluginname", 'get_types')) {
- // Callback 'get_types()' in 'ltisource' plugins is deprecated in 3.1 and will be removed in 3.5, TODO MDL-53697.
- debugging('Deprecated callback get_types() is found in ltisource_' . $pluginname .
- ', use get_shortcuts() instead', DEBUG_DEVELOPER);
- $grouptitle = get_string('modulenameplural', 'mod_lti');
- foreach ($moretypes as $subtype) {
- // Instead of adding subitems combine the name of the group with the name of the subtype.
- $subtype->title = get_string('activitytypetitle', '',
- (object)['activity' => $grouptitle, 'type' => $subtype->typestr]);
- // Re-implement the logic of get_module_metadata() in Moodle 3.0 and below for converting
- // subtypes into items in activity chooser.
- $subtype->type = str_replace('&', '&', $subtype->type);
- $subtype->name = preg_replace('/.*type=/', '', $subtype->type);
- $subtype->link = new moodle_url($defaultitem->link, array('type' => $subtype->name));
- if (empty($subtype->help) && !empty($subtype->name) &&
- get_string_manager()->string_exists('help' . $subtype->name, $pluginname)) {
- $subtype->help = get_string('help' . $subtype->name, $pluginname);
- }
- unset($subtype->typestr);
- $types[] = $subtype;
- }
- }
// LTISOURCE plugins can also implement callback get_shortcuts() to add items to the activity chooser.
// The return values are the same as of the 'mod' callbacks except that $defaultitem is only passed for reference and
// should not be added to the return value.
$questions = quiz_report_get_significant_questions($quiz);
// Only load main question not sub questions.
- $questionstatistics = $DB->get_records_select('question_statistics', 'hashcode = ? AND slot IS NOT NULL',
+ $questionstatistics = $DB->get_records_select('question_statistics',
+ 'hashcode = ? AND slot IS NOT NULL AND variant IS NULL',
[$qubaids->get_hash_code()]);
// Configure what to display.
}
var correct_responses = {
- 'true-false':{'pre':'', 'max':1, 'delimiter':'', 'unique':false, 'duplicate':false,
+ 'true-false':{'max':1, 'delimiter':'', 'unique':false, 'duplicate':false,
'format':'^true$|^false$',
'limit':1},
- 'choice':{'pre':'', 'max':36, 'delimiter':'[,]', 'unique':true, 'duplicate':false,
+ 'choice':{'max':36, 'delimiter':'[,]', 'unique':true, 'duplicate':false,
'format':CMIShortIdentifier},
-// 'fill-in':{'pre':'^(((\{case_matters=(true|false)\})(\{order_matters=(true|false)\})?)|((\{order_matters=(true|false)\})(\{case_matters=(true|false)\})?))(.*?)$',
- 'fill-in':{'pre':'',
- 'max':10, 'delimiter':'[,]', 'unique':false, 'duplicate':false,
+ 'fill-in':{'max':10, 'delimiter':'[,]', 'unique':false, 'duplicate':false,
'format':CMILangString250cr},
- 'long-fill-in':{'pre':'^(\{case_matters=(true|false)\})?', 'max':1, 'delimiter':'', 'unique':false, 'duplicate':true,
+ 'long-fill-in':{'max':1, 'delimiter':'', 'unique':false, 'duplicate':true,
'format':CMILangString4000},
- 'matching':{'pre':'', 'max':36, 'delimiter':'[,]', 'delimiter2':'[.]', 'unique':false, 'duplicate':false,
+ 'matching':{'max':36, 'delimiter':'[,]', 'delimiter2':'[.]', 'unique':false, 'duplicate':false,
'format':CMIShortIdentifier, 'format2':CMIShortIdentifier},
- 'performance':{'pre':'^(\{order_matters=(true|false)\})?',
- 'max':250, 'delimiter':'[,]', 'delimiter2':'[.]', 'unique':false, 'duplicate':false,
+ 'performance':{'max':250, 'delimiter':'[,]', 'delimiter2':'[.]', 'unique':false, 'duplicate':false,
'format':'^$|' + CMIShortIdentifier, 'format2':CMIDecimal + '|^$|' + CMIShortIdentifier},
- 'sequencing':{'pre':'', 'max':36, 'delimiter':'[,]', 'unique':false, 'duplicate':false,
+ 'sequencing':{'max':36, 'delimiter':'[,]', 'unique':false, 'duplicate':false,
'format':CMIShortIdentifier},
- 'likert':{'pre':'', 'max':1, 'delimiter':'', 'unique':false, 'duplicate':false,
+ 'likert':{'max':1, 'delimiter':'', 'unique':false, 'duplicate':false,
'format':CMIShortIdentifier,
'limit':1},
- 'numeric':{'pre':'', 'max':2, 'delimiter':'[:]', 'unique':false, 'duplicate':false,
+ 'numeric':{'max':2, 'delimiter':'[:]', 'unique':false, 'duplicate':false,
'format':CMIDecimal,
'limit':1},
- 'other':{'pre':'', 'max':1, 'delimiter':'', 'unique':false, 'duplicate':false,
+ 'other':{'max':1, 'delimiter':'', 'unique':false, 'duplicate':false,
'format':CMIString4000,
'limit':1}
}
nodes[i] = result.node;
}
- // check for prefix on each node
- if (correct_responses[interactiontype].pre != '') {
- matches = nodes[i].match(correct_responses[interactiontype].pre);
- if (matches != null) {
- nodes[i] = nodes[i].substr(matches[1].length);
- }
- }
-
if (correct_responses[interactiontype].delimiter2 != undefined) {
values = nodes[i].split(correct_responses[interactiontype].delimiter2);
if (values.length == 2) {
This files describes API changes in /mod/* - activity modules,
information provided here is intended especially for developers.
+=== 3.6 ===
+
+* The final deprecation of xxx_get_types() callback means that this function will no longer be called.
+ Please use get_shortcuts() instead.
+* lti_get_shortcuts has been deprecated. Please use get_shortcuts() instead to add items to the activity chooser.
+
=== 3.5 ===
* There is a new privacy API that every subsystem and plugin has to implement so that the site can become GDPR
echo '<a title="' . $title . '" href="' . $url . '">' . $OUTPUT->pix_icon($icon, $title) . '</a>';
}
+ public function get_extra_joins() {
+ return array('qc' => 'JOIN {question_categories} qc ON qc.id = q.category');
+ }
+
public function get_required_fields() {
// Createdby is required for permission checks.
- return array('q.id', 'q.createdby');
+ return array('q.id', 'q.createdby', 'qc.contextid');
}
}
/**
* Constructor
- * @param question_edit_contexts $contexts
- * @param moodle_url $pageurl
+ * @param \question_edit_contexts $contexts
+ * @param \moodle_url $pageurl
* @param object $course course settings
* @param object $cm (optional) activity settings.
*/
return $question;
case 'multichoice':
+ // "Temporary" solution to enable choice of answernumbering on GIFT import
+ // by respecting default set for multichoice questions (MDL-59447)
+ $question->answernumbering = get_config('qtype_multichoice', 'answernumbering');
+
if (strpos($answertext, "=") === false) {
$question->single = 0; // Multiple answers are enabled if no single answer is 100% correct.
} else {
$this->assert_same_gift($expectedgift, $gift);
}
- public function test_import_multichoice() {
+ /**
+ * Test import of multichoice question in GIFT format
+ *
+ * @dataProvider numberingstyle_provider
+ *
+ * @param string $numberingstyle multichoice numbering style to set for qtype_multichoice
+ *
+ */
+ public function test_import_multichoice($numberingstyle) {
+ $this->resetAfterTest(true);
+
+ set_config('answernumbering', $numberingstyle, 'qtype_multichoice');
$gift = "
// multiple choice with specified feedback for right and wrong answers
::Q2:: What's between orange and green in the spectrum?
'length' => 1,
'single' => 1,
'shuffleanswers' => '1',
- 'answernumbering' => 'abc',
+ 'answernumbering' => $numberingstyle,
'correctfeedback' => array(
'text' => '',
'format' => FORMAT_MOODLE,
$this->assert(new question_check_specified_fields_expectation($expectedq), $q);
}
+ /**
+ * Return a list of numbering styles (see question/type/multichoice/questiontype.php
+ * for valid choices)
+ *
+ * @return array Array of 1-element arrays of qtype_multichoice numbering styles
+ */
+ public function numberingstyle_provider() {
+ return [
+ ['abc'],
+ ['ABCD'],
+ ['123'],
+ ['iii'],
+ ['IIII'],
+ ['none']
+ ];
+ }
+
public function test_import_multichoice_multi() {
$gift = "
// multiple choice, multiple response with specified feedback for right and wrong answers
--- /dev/null
+<?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 the question bank view class.
+ *
+ * @package core_question
+ * @category test
+ * @copyright 2018 the Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot . '/question/editlib.php');
+
+
+/**
+ * Unit tests for the question bank view class.
+ *
+ * @copyright 2018 the Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class core_question_bank_view_testcase extends advanced_testcase {
+
+ public function test_viewing_question_bank_should_not_load_individual_questions() {
+ $this->resetAfterTest();
+ $this->setAdminUser();
+ $generator = $this->getDataGenerator();
+ /** @var core_question_generator $questiongenerator */
+ $questiongenerator = $generator->get_plugin_generator('core_question');
+
+ // Cerate a course.
+ $course = $generator->create_course();
+ $context = context_course::instance($course->id);
+
+ // Create a question in the default category.
+ $contexts = new question_edit_contexts($context);
+ $cat = question_make_default_categories($contexts->all());
+ $questiondata = $questiongenerator->create_question('numerical', null,
+ ['name' => 'Example question', 'category' => $cat->id]);
+
+ // Ensure the qusetion is not in the cache.
+ $cache = cache::make('core', 'questiondata');
+ $cache->delete($questiondata->id);
+
+ // Generate the view.
+ $view = new core_question\bank\view($contexts, new moodle_url('/'), $course);
+ ob_start();
+ $view->display('editq', 0, 20, $cat->id . ',' . $context->id, false, false, false);
+ $html = ob_get_clean();
+
+ // Verify the output includes the expected question.
+ $this->assertContains('Example question', $html);
+
+ // Verify the qusetion has not been loaded into the cache.
+ $this->assertFalse($cache->has($questiondata->id));
+ }
+}
// Run the delete functions as default user.
$this->setUser();
+ // Find out how many questions are in the question bank to start with.
+ $questioncount = $DB->count_records('question');
+
// The delete functions should do nothing here.
- $this->assertCount(6, $DB->get_records('question'));
// Delete for all users in context.
provider::delete_data_for_all_users_in_context($expectedcontext);
- $this->assertCount(6, $DB->get_records('question'));
+ $this->assertEquals($questioncount, $DB->count_records('question'));
provider::delete_data_for_user($approvedcontextlist);
- $this->assertCount(6, $DB->get_records('question'));
+ $this->assertEquals($questioncount, $DB->count_records('question'));
}
/**
[$context->id]
);
+ // Find out how many questions are in the question bank to start with.
+ $questioncount = $DB->count_records('question');
+
// Delete the data and check it is removed.
$this->setUser();
provider::delete_data_for_user($approvedcontextlist);
- $this->assertCount(5, $DB->get_records('question'));
+ $this->assertEquals($questioncount, $DB->count_records('question'));
$qrecord = $DB->get_record('question', ['id' => $q1->id]);
$this->assertEquals(0, $qrecord->createdby);
$questiongenerator->update_question($q3);
$q5 = $questiongenerator->create_question('shortanswer', null, array('category' => $othercat->id));
+ // Find out how many questions are in the question bank to start with.
+ $questioncount = $DB->count_records('question');
+
// Delete the data and check it is removed.
$this->setUser();
provider::delete_data_for_all_users_in_context($context);
- $this->assertCount(5, $DB->get_records('question'));
+ $this->assertEquals($questioncount, $DB->count_records('question'));
$qrecord = $DB->get_record('question', ['id' => $q1->id]);
$this->assertEquals(0, $qrecord->createdby);
#page-mod-lesson-view .branchbuttoncontainer .singlebutton button[type="submit"] {
white-space: normal;
}
+#page-mod-lesson-view {
+ .vertical .singlebutton {
+ display: block;
+ + .singlebutton {
+ margin-left: 0;
+ margin-top: 1rem;
+ }
+ }
+}
.path-mod-lesson .generaltable td {
vertical-align: middle;
label {
#page-mod-lesson-view .branchbuttoncontainer .singlebutton button[type="submit"] {
white-space: normal; }
+#page-mod-lesson-view .vertical .singlebutton {
+ display: block; }
+ #page-mod-lesson-view .vertical .singlebutton + .singlebutton {
+ margin-left: 0;
+ margin-top: 1rem; }
+
.path-mod-lesson .generaltable td {
vertical-align: middle; }
.path-mod-lesson .generaltable td label {
+++ /dev/null
-// 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/>.
-
-/**
- * Name and page filter JS module for the course participants page.
- *
- * @module core_user/name_page_filter
- * @package core_user
- * @copyright 2017 Mihail Geshoski
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-define(['jquery', 'core_user/unified_filter'],
- function($, UnifiedFilter) {
-
- /**
- * Selectors.
- *
- * @access private
- * @type {{NAME_FILTERS: string, PAGE_FILTERS: string}}
- */
- var SELECTORS = {
- NAME_FILTERS: 'a.letter',
- PAGE_FILTERS: 'a.page-link'
- };
-
- /**
- * Init function.
- *
- * @method init
- * @private
- */
- var init = function() {
- $(SELECTORS.NAME_FILTERS + ', ' + SELECTORS.PAGE_FILTERS).on('click', function(e) {
- e.preventDefault();
- var href = $(this).attr('href');
- UnifiedFilter.getForm().attr('action', href);
- UnifiedFilter.getForm().submit();
- });
- };
-
- return /** @alias module:core/form-autocomplete */ {
- // Public variables and functions.
- /**
- * Initialise the name and page user filter.
- *
- * @method init
- */
- 'init': function() {
- init();
- }
- };
-});
echo '<div class="userlist">';
+// Add filters to the baseurl after creating unified_filter to avoid losing them.
+foreach ($filtersapplied as $filter) {
+ $baseurl->param('unified-filters[]', $filter);
+}
$participanttable = new \core_user\participants_table($course->id, $groupid, $lastaccess, $roleid, $enrolid, $status,
$searchkeywords, $bulkoperations, $selectall);
$participanttable->define_baseurl($baseurl);
echo $participanttablehtml;
-$PAGE->requires->js_call_amd('core_user/name_page_filter', 'init');
-
$perpageurl = clone($baseurl);
$perpageurl->remove_params('perpage');
if ($perpage == SHOW_ALL_PAGE_SIZE && $participanttable->totalrows > DEFAULT_PAGE_SIZE) {
\core\event\user_created::create_from_userid($newuserid)->trigger();
}
+ // Purge the associated caches.
+ cache_helper::purge_by_event('createduser');
+
return $newuserid;
}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-define ('PROFILE_VISIBLE_ALL', '2'); // Only visible for users with moodle/user:update capability.
-define ('PROFILE_VISIBLE_PRIVATE', '1'); // Either we are viewing our own profile or we have moodle/user:update capability.
-define ('PROFILE_VISIBLE_NONE', '0'); // Only visible for moodle/user:update capability.
+/**
+ * Visible to anyone who can view the user.
+ * Editable by the profile owner if they have the moodle/user:editownprofile capability
+ * or any user with the moodle/user:update capability.
+ */
+define('PROFILE_VISIBLE_ALL', '2');
+/**
+ * Visible to the profile owner or anyone with the moodle/user:viewalldetails capability.
+ * Editable by the profile owner if they have the moodle/user:editownprofile capability
+ * or any user with moodle/user:viewalldetails and moodle/user:update capabilities.
+ */
+define('PROFILE_VISIBLE_PRIVATE', '1');
+/**
+ * Only visible to users with the moodle/user:viewalldetails capability.
+ * Only editable by users with the moodle/user:viewalldetails and moodle/user:update capabilities.
+ */
+define('PROFILE_VISIBLE_NONE', '0');
/**
* Base class for the customisable profile fields.
* @return bool
*/
public function edit_field($mform) {
- if ($this->field->visible != PROFILE_VISIBLE_NONE
- or has_capability('moodle/user:update', context_system::instance())) {
-
- $this->edit_field_add($mform);
- $this->edit_field_set_default($mform);
- $this->edit_field_set_required($mform);
- return true;
+ if (!$this->is_editable()) {
+ return false;
}
- return false;
+
+ $this->edit_field_add($mform);
+ $this->edit_field_set_default($mform);
+ $this->edit_field_set_required($mform);
+ return true;
}
/**
* @return bool
*/
public function edit_after_data($mform) {
- if ($this->field->visible != PROFILE_VISIBLE_NONE
- or has_capability('moodle/user:update', context_system::instance())) {
- $this->edit_field_set_locked($mform);
- return true;
+ if (!$this->is_editable()) {
+ return false;
}
- return false;
+
+ $this->edit_field_set_locked($mform);
+ return true;
}
/**
}
}
+ /**
+ * Check if the field data is editable for the current user
+ * This method should not generally be overwritten by child classes.
+ * @return bool
+ */
+ public function is_editable() {
+ global $USER;
+
+ if (!$this->is_visible()) {
+ return false;
+ }
+
+ $systemcontext = context_system::instance();
+
+ if ($this->userid == $USER->id && has_capability('moodle/user:editownprofile', $systemcontext)) {
+ return true;
+ }
+
+ if (has_capability('moodle/user:update', $systemcontext)) {
+ return true;
+ }
+
+ return false;
+ }
+
/**
* Check if the field data is considered empty
* @internal This method should not generally be overwritten by child classes.
* @param int $userid id of user whose profile is being edited.
*/
function profile_definition($mform, $userid = 0) {
- global $CFG, $DB;
-
- // If user is "admin" fields are displayed regardless.
- $update = has_capability('moodle/user:update', context_system::instance());
-
$categories = profile_get_user_fields_with_data_by_category($userid);
foreach ($categories as $categoryid => $fields) {
// Check first if *any* fields will be displayed.
- $display = false;
+ $fieldstodisplay = [];
+
foreach ($fields as $formfield) {
- if ($formfield->is_visible()) {
- $display = true;
+ if ($formfield->is_editable()) {
+ $fieldstodisplay[] = $formfield;
}
}
+ if (empty($fieldstodisplay)) {
+ continue;
+ }
+
// Display the header and the fields.
- if ($display or $update) {
- $mform->addElement('header', 'category_'.$categoryid, format_string($formfield->get_category_name()));
- foreach ($fields as $formfield) {
- $formfield->edit_field($mform);
- }
+ $mform->addElement('header', 'category_'.$categoryid, format_string($fields[0]->get_category_name()));
+ foreach ($fieldstodisplay as $formfield) {
+ $formfield->edit_field($mform);
}
}
}
--- /dev/null
+@core @core_user
+Feature: Custom profile fields should be visible and editable by those with the correct permissions.
+
+ Background: Attempting to self-register as a new user with empty names
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | userwithinformation | userwithinformation | 1 | userwithinformation@example.com |
+ And the following "courses" exist:
+ | fullname | shortname | category | groupmode |
+ | Course 1 | C1 | 0 | 1 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | userwithinformation | C1 | student |
+
+ And I log in as "admin"
+ And I navigate to "User profile fields" node in "Site administration > Users > Accounts"
+ And I set the field "datatype" to "Text input"
+ And I set the following fields to these values:
+ | Short name | notvisible_field |
+ | Name | notvisible_field |
+ | Who is this field visible to? | Not visible |
+ And I click on "Save changes" "button"
+
+ And I set the field "datatype" to "Text input"
+ And I set the following fields to these values:
+ | Short name | uservisible_field |
+ | Name | uservisible_field |
+ | Who is this field visible to? | Visible to user |
+ And I click on "Save changes" "button"
+
+ And I set the field "datatype" to "Text input"
+ And I set the following fields to these values:
+ | Short name | everyonevisible_field |
+ | Name | everyonevisible_field |
+ | Who is this field visible to? | Visible to everyone |
+ And I click on "Save changes" "button"
+
+ And I navigate to "Browse list of users" node in "Site administration > Users > Accounts"
+ And I click on ".icon[title=Edit]" "css_element" in the "userwithinformation@example.com" "table_row"
+ And I expand all fieldsets
+ And I set the field "notvisible_field" to "notvisible_field_information"
+ And I set the field "uservisible_field" to "uservisible_field_information"
+ And I set the field "everyonevisible_field" to "everyonevisible_field_information"
+ And I click on "Update profile" "button"
+ And I log out
+
+ @javascript
+ Scenario: User with moodle/user:update but without moodle/user:viewalldetails can only update visible profile fields.
+ Given the following "roles" exist:
+ | name | shortname | description | archetype |
+ | Update Users | updateusers | updateusers | |
+ And the following "permission overrides" exist:
+ | capability | permission | role | contextlevel | reference |
+ | moodle/user:update | Allow | updateusers | System | |
+ And the following "users" exist:
+ | username | firstname | lastname | email |
+ | user_updateusers | updateusers | 1 | updateusers@example.com |
+ And the following "role assigns" exist:
+ | user | role | contextlevel | reference |
+ | user_updateusers | updateusers | System | |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | user_updateusers | C1 | editingteacher |
+ And I log in as "user_updateusers"
+ And I am on "Course 1" course homepage
+ And I navigate to course participants
+ And I follow "userwithinformation 1"
+
+ Then I should see "everyonevisible_field"
+ And I should see "everyonevisible_field_information"
+ And I should not see "uservisible_field"
+ And I should not see "uservisible_field_information"
+ And I should not see "notvisible_field"
+ And I should not see "notvisible_field_information"
+ And I follow "Edit profile"
+ And the following fields match these values:
+ | everyonevisible_field | everyonevisible_field_information |
+ And I should not see "uservisible_field"
+ And I should not see "notvisible_field"
+
+ @javascript
+ Scenario: User with moodle/user:viewalldetails but without moodle/user:update can view all profile fields.
+ Given the following "roles" exist:
+ | name | shortname | description | archetype |
+ | View All Details | viewalldetails | viewalldetails | |
+ And the following "permission overrides" exist:
+ | capability | permission | role | contextlevel | reference |
+ | moodle/user:viewalldetails | Allow | viewalldetails | System | |
+ And the following "users" exist:
+ | username | firstname | lastname | email |
+ | user_viewalldetails | viewalldetails | 1 | viewalldetails@example.com |
+ And the following "role assigns" exist:
+ | user | role | contextlevel | reference |
+ | user_viewalldetails | viewalldetails | System | |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | user_viewalldetails | C1 | editingteacher |
+ And I log in as "user_viewalldetails"
+ And I am on "Course 1" course homepage
+ And I navigate to course participants
+ And I follow "userwithinformation 1"
+
+ Then I should see "everyonevisible_field"
+ And I should see "everyonevisible_field_information"
+ And I should see "uservisible_field"
+ And I should see "uservisible_field_information"
+ And I should see "notvisible_field"
+ And I should see "notvisible_field_information"
+ And I should not see "Edit profile"
+
+ @javascript
+ Scenario: User with moodle/user:viewalldetails and moodle/user:update capabilities can view and edit all profile fields.
+ Given the following "roles" exist:
+ | name | shortname | description | archetype |
+ | View All Details and Update Users | viewalldetailsandupdateusers | viewalldetailsandupdateusers | |
+ And the following "permission overrides" exist:
+ | capability | permission | role | contextlevel | reference |
+ | moodle/user:viewalldetails | Allow | viewalldetailsandupdateusers | System | |
+ | moodle/user:update | Allow | viewalldetailsandupdateusers | System | |
+ And the following "users" exist:
+ | username | firstname | lastname | email |
+ | user_viewalldetailsandupdateusers | viewalldetailsandupdateusers | 1 | viewalldetailsandupdateusers@example.com |
+ And the following "role assigns" exist:
+ | user | role | contextlevel | reference |
+ | user_viewalldetailsandupdateusers | viewalldetailsandupdateusers | System | |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | user_viewalldetailsandupdateusers | C1 | editingteacher |
+ And I log in as "user_viewalldetailsandupdateusers"
+ And I am on "Course 1" course homepage
+ And I navigate to course participants
+ And I follow "userwithinformation 1"
+
+ Then I should see "everyonevisible_field"
+ And I should see "everyonevisible_field_information"
+ And I should see "uservisible_field"
+ And I should see "uservisible_field_information"
+ And I should see "notvisible_field"
+ And I should see "notvisible_field_information"
+ And I follow "Edit profile"
+ And the following fields match these values:
+ | everyonevisible_field | everyonevisible_field_information |
+ | uservisible_field | uservisible_field_information |
+ | notvisible_field | notvisible_field_information |
+
+ @javascript
+ Scenario: Users can view and edit custom profile fields except those marked as not visible.
+ Given I log in as "userwithinformation"
+ And I follow "Profile" in the user menu
+
+ Then I should see "everyonevisible_field"
+ And I should see "everyonevisible_field_information"
+ And I should see "uservisible_field"
+ And I should see "uservisible_field_information"
+ And I should not see "notvisible_field"
+ And I should not see "notvisible_field_information"
+
+ And I click on "Edit profile" "link" in the "region-main" "region"
+ Then the following fields match these values:
+ | everyonevisible_field | everyonevisible_field_information |
+ | uservisible_field | uservisible_field_information |
+ And I should not see "notvisible_field"
+ And I should not see "notvisible_field_information"
+
+ @javascript
+ Scenario: Users can view but not edit custom profile fields when denied the edit own profile capability.
+ Given the following "roles" exist:
+ | name | shortname | description | archetype |
+ | Deny editownprofile | denyeditownprofile | denyeditownprofile | |
+
+ And the following "permission overrides" exist:
+ | capability | permission | role | contextlevel | reference |
+ | moodle/user:editownprofile | Prohibit | denyeditownprofile | System | |
+ And the following "role assigns" exist:
+ | user | role | contextlevel | reference |
+ | userwithinformation | denyeditownprofile | System | |
+
+ And I log in as "userwithinformation"
+ And I follow "Profile" in the user menu
+
+ Then I should see "everyonevisible_field"
+ And I should see "everyonevisible_field_information"
+ And I should see "uservisible_field"
+ And I should see "uservisible_field_information"
+ And I should not see "notvisible_field"
+ And I should not see "notvisible_field_information"
+
+ And I should not see "Edit profile"
\ No newline at end of file
And I should see "Student 4" in the "participants" "table"
And I should not see "Teacher 1" in the "participants" "table"
+ @javascript
+ Scenario: Reorder users without losing filter
+ Given I log in as "teacher1"
+ And I am on "Course 1" course homepage
+ And I navigate to course participants
+ And I open the autocomplete suggestions list
+ And I click on "Role: Student" item in the autocomplete list
+ When I click on "Surname" "link"
+ Then I should see "Role: Student"
+ And I should see "Student 1" in the "participants" "table"
+ And I should see "Student 2" in the "participants" "table"
+ And I should see "Student 3" in the "participants" "table"
+ And I should see "Student 4" in the "participants" "table"
+ And I should not see "Teacher 1" in the "participants" "table"
+
@javascript
Scenario: Rendering filter options for teachers in a course that don't support groups
Given I log in as "teacher1"
--- /dev/null
+@core @core_user
+Feature: Course participants can be filtered to display all the users
+ In order to filter the list of course participants
+ As a user
+ I need to visit the course participants page, apply the appropriate filters and show all users per page
+
+ Background:
+ Given the following "courses" exist:
+ | fullname | shortname | groupmode |
+ | Course 1 | C1 | 1 |
+ | Course 2 | C2 | 0 |
+ And the following "users" exist:
+ | username | firstname | lastname | email |
+ | student1 | Student | 1 | student1@example.com |
+ | student2 | Student | 2 | student2@example.com |
+ | student3 | Student | 3 | student3@example.com |
+ | student4 | Student | 4 | student4@example.com |
+ | student5 | Student | 5 | student5@example.com |
+ | student6 | Student | 6 | student6@example.com |
+ | student7 | Student | 7 | student7@example.com |
+ | student8 | Student | 8 | student8@example.com |
+ | student9 | Student | 9 | student9@example.com |
+ | student10 | Student | 10 | student10@example.com |
+ | student11 | Student | 11 | student11@example.com |
+ | student12 | Student | 12 | student12@example.com |
+ | student13 | Student | 13 | student13@example.com |
+ | student14 | Student | 14 | student14@example.com |
+ | student15 | Student | 15 | student15@example.com |
+ | student16 | Student | 16 | student16@example.com |
+ | student17 | Student | 17 | student17@example.com |
+ | student18 | Student | 18 | student18@example.com |
+ | student19 | Student | 19 | student19@example.com |
+ | student20 | Student | 20 | student20@example.com |
+ | student21 | Student | 21 | student21@example.com |
+ | student22 | Student | 22 | student22@example.com |
+ | student23 | Student | 23 | student23@example.com |
+ | student24 | Student | 24 | student24@example.com |
+ | teacher1 | Teacher | 1 | teacher1@example.com |
+ And the following "course enrolments" exist:
+ | user | course | role | status | timeend |
+ | student1 | C1 | student | 0 | |
+ | student2 | C1 | student | 1 | |
+ | student3 | C1 | student | 0 | |
+ | student4 | C1 | student | 0 | |
+ | student5 | C1 | student | 0 | |
+ | student6 | C1 | student | 0 | |
+ | student7 | C1 | student | 0 | |
+ | student8 | C1 | student | 0 | |
+ | student9 | C1 | student | 0 | |
+ | student10 | C1 | student | 0 | |
+ | student11 | C1 | student | 0 | |
+ | student12 | C1 | student | 0 | |
+ | student13 | C1 | student | 0 | |
+ | student14 | C1 | student | 0 | |
+ | student15 | C1 | student | 0 | |
+ | student16 | C1 | student | 0 | |
+ | student17 | C1 | student | 0 | |
+ | student18 | C1 | student | 0 | |
+ | student19 | C1 | student | 0 | |
+ | student20 | C1 | student | 0 | |
+ | student21 | C1 | student | 0 | |
+ | student22 | C1 | student | 0 | |
+ | student23 | C1 | student | 0 | |
+ | student24 | C1 | student | 0 | |
+ | student1 | C2 | student | 0 | |
+ | student2 | C2 | student | 0 | |
+ | student3 | C2 | student | 0 | |
+ | teacher1 | C1 | editingteacher | 0 | |
+ | teacher1 | C2 | editingteacher | 0 | |
+ And the following "groups" exist:
+ | name | course | idnumber |
+ | Group 1 | C1 | G1 |
+ | Group 2 | C1 | G2 |
+ And the following "group members" exist:
+ | user | group |
+ | student2 | G1 |
+ | student2 | G2 |
+ | student3 | G2 |
+
+ @javascript
+ Scenario: Show all filtered users for a course
+ Given I log in as "teacher1"
+ And I am on "Course 1" course homepage
+ And I navigate to course participants
+ When I open the autocomplete suggestions list
+ And I click on "Role: Student" item in the autocomplete list
+ And I click on "Show all 24" "link"
+ Then I should see "Role: Student"
+ And I should see "Number of participants: 24" in the "//div[@class='userlist']" "xpath_element"
+ And I should see "Show 20 per page"
defined('MOODLE_INTERNAL') || die();
-$version = 2018071300.00; // YYYYMMDD = weekly release date of this DEV branch.
+$version = 2018072000.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.
-$release = '3.6dev (Build: 20180713)'; // Human-friendly version name
+$release = '3.6dev (Build: 20180720)'; // Human-friendly version name
$branch = '36'; // This version's branch.
$maturity = MATURITY_ALPHA; // This version's maturity level.