}
if (isset($admins[$newmain])) {
+ $logstringold = implode(', ', $admins);
+
unset($admins[$newmain]);
array_unshift($admins, $newmain);
+
+ $logstringnew = implode(', ', $admins);
+
set_config('siteadmins', implode(',', $admins));
+ add_to_config_log('siteadmins', $logstringold, $logstringnew, null);
+
redirect($PAGE->url);
}
}
$admins[$admin] = $admin;
}
}
+
+ $logstringold = implode(', ', $admins);
+
$admins[$confirmadd] = $confirmadd;
+
+ $logstringnew = implode(', ', $admins);
+
set_config('siteadmins', implode(',', $admins));
+ add_to_config_log('siteadmins', $logstringold, $logstringnew, 'core');
+
redirect($PAGE->url);
} else if ($confirmdel and confirm_sesskey() and $confirmdel != $USER->id) {
$admins[$admin] = $admin;
}
}
+
+ $logstringold = implode(', ', $admins);
+
unset($admins[$confirmdel]);
+
+ $logstringnew = implode(', ', $admins);
+
set_config('siteadmins', implode(',', $admins));
+ add_to_config_log('siteadmins', $logstringold, $logstringnew, 'core');
+
redirect($PAGE->url);
}
}}>
<td>{{typenameshort}}</td>
<td><a href="{{foruser.profileurl}}" title="{{#str}}viewprofile{{/str}}">{{foruser.fullname}}</a></td>
- <td>{{#userdate}} {{timecreated}}, {{#str}} strftimedate {{/str}} {{/userdate}}</td>
+ <td>{{#userdate}} {{timecreated}}, {{#str}} strftimedatetime {{/str}} {{/userdate}}</td>
<td>
<span class="label {{statuslabelclass}}">{{statuslabel}}</span>
</td>
}} data-status="{{status}}"{{!
}}>
<td>{{typename}}</td>
- <td>{{#userdate}} {{timecreated}}, {{#str}} strftimedate {{/str}} {{/userdate}}</td>
+ <td>{{#userdate}} {{timecreated}}, {{#str}} strftimedatetime {{/str}} {{/userdate}}</td>
<td>
<span class="label {{statuslabelclass}}">{{statuslabel}}</span>
</td>
<a href="mailto:{{foruser.email}}">{{foruser.email}}</a>
<div class="clearfix m-t-1 m-b-1">
<span class="pull-left m-r-1">
- <strong>{{#str}}daterequesteddetail, tool_dataprivacy{{/str}}</strong> {{#userdate}} {{timecreated}}, {{#str}} strftimedate {{/str}} {{/userdate}}
+ <strong>{{#str}}daterequesteddetail, tool_dataprivacy{{/str}}</strong> {{#userdate}} {{timecreated}}, {{#str}} strftimedatetime {{/str}} {{/userdate}}
</span>
<span class="pull-left m-r-1">
<strong>{{#str}}statusdetail, tool_dataprivacy{{/str}}</strong>
// Testing the restore of an overridden grade.
list($course, $assign) = $this->create_course_and_module('assign', []);
$cm = $DB->get_record('course_modules', ['course' => $course->id, 'instance' => $assign->id]);
- $assignobj = new testable_assign(context_module::instance($cm->id), $cm, $course);
+ $assignobj = new mod_assign_testable_assign(context_module::instance($cm->id), $cm, $course);
$submission = $assignobj->get_user_submission($USER->id, true);
$grade = $assignobj->get_user_grade($USER->id, true);
$grade->grade = 75;
'completionusegrade' => 1 // Student must receive a grade to complete this activity.
]);
$cm = $DB->get_record('course_modules', ['course' => $course->id, 'instance' => $assign->id]);
- $assignobj = new testable_assign(context_module::instance($cm->id), $cm, $course);
+ $assignobj = new mod_assign_testable_assign(context_module::instance($cm->id), $cm, $course);
$submission = $assignobj->get_user_submission($USER->id, true);
$grade = $assignobj->get_user_grade($USER->id, true);
$grade->grade = 75;
$this->assertEquals($dates['originaldate'], $dates['restoredate']);
}
}
-}
\ No newline at end of file
+}
if (!empty($info->role_mappings->mappings)) {
$context = context_course::instance($this->ui->get_controller()->get_courseid());
$assignableroles = get_assignable_roles($context, ROLENAME_ALIAS, false);
- $html .= $renderer->role_mappings($info->role_mappings->mappings, $assignableroles);
+
+ // Get current role mappings.
+ $currentroles = role_fix_names(get_all_roles(), $context);
+ // Get backup role mappings.
+ $rolemappings = $info->role_mappings->mappings;
+
+ array_map(function($rolemapping) use ($currentroles) {
+ foreach ($currentroles as $role) {
+ // Find matching archetype to determine the backup's shortname for label display.
+ if ($rolemapping->archetype == $role->archetype) {
+ $rolemapping->name = $rolemapping->shortname;
+ break;
+ }
+ }
+ if ($rolemapping->name == null) {
+ $rolemapping->name = get_string('undefinedrolemapping', 'backup', $rolemapping->archetype);
+ }
+ }, $rolemappings);
+
+ $html .= $renderer->role_mappings($rolemappings, $assignableroles);
}
break;
default:
$string['title'] = 'Title';
$string['totalcategorysearchresults'] = 'Total categories: {$a}';
$string['totalcoursesearchresults'] = 'Total courses: {$a}';
+$string['undefinedrolemapping'] = 'Role mapping undefined for: \'{$a}\' archetype';
$string['unnamedsection'] = 'Unnamed section';
$string['userinfo'] = 'Userinfo';
$string['module'] = 'Module';
*/
$string['application/epub_zip'] = 'EPUB ebook';
+$string['application/json'] = '{$a->MIMETYPE2} text';
$string['application/msword'] = 'Word document';
$string['application/pdf'] = 'PDF document';
$string['application/vnd.moodle.backup'] = 'Moodle backup';
'jpg' => array('type' => 'image/jpeg', 'icon' => 'jpeg', 'groups' => array('image', 'web_image'), 'string' => 'image'),
'jqz' => array('type' => 'text/xml', 'icon' => 'markup'),
'js' => array('type' => 'application/x-javascript', 'icon' => 'text', 'groups' => array('web_file')),
+ 'json' => array('type' => 'application/json', 'icon' => 'text'),
'latex' => array('type' => 'application/x-latex', 'icon' => 'text'),
'm' => array('type' => 'text/plain', 'icon' => 'sourcecode'),
'mbz' => array('type' => 'application/vnd.moodle.backup', 'icon' => 'moodle'),
.atto_charmap_selector button {
- width: 2em;
- padding: 0 3px;
+ width: 2.18rem;
+ margin: 0.1rem;
}
'<div class="{{CSS.CHARMAP}}">' +
'{{#each CHARMAP}}' +
'{{#if this.[2]}}' +
- '<button class="{{../../CSS.BUTTON}}" ' +
+ '<button class="btn btn-default btn-sm {{../../CSS.BUTTON}}" ' +
'aria-label="{{get_string this.[3] ../../component}}" ' +
'title="{{get_string this.[3] ../../component}}" ' +
'data-character="{{this.[0]}}" ' +
-.atto_equation_library .yui3-tabview-list {
- border: none;
-}
-
-.atto_equation_library .yui3-tab-selected .yui3-tab-label,
-.yui3-skin-sam #atto_equation_library .yui3-tab-selected .yui3-tab-label:focus,
-.yui3-skin-sam #atto_equation_library .yui3-tab-selected .yui3-tab-label:hover {
- background: none;
- color: black;
- border-top-left-radius: 4px;
- border-top-right-radius: 4px;
-}
-
.atto_equation_library button {
margin: 0.25%;
min-width: 12%;
'<textarea class="fullwidth {{CSS.EQUATION_TEXT}}" ' +
'id="{{elementid}}_{{CSS.EQUATION_TEXT}}" rows="8"></textarea><br/>' +
'<label for="{{elementid}}_{{CSS.EQUATION_PREVIEW}}">{{get_string "preview" component}}</label>' +
- '<div describedby="{{elementid}}_cursorinfo" class="well well-small fullwidth {{CSS.EQUATION_PREVIEW}}" ' +
+ '<div describedby="{{elementid}}_cursorinfo" class="well well-small p-1 fullwidth {{CSS.EQUATION_PREVIEW}}" ' +
'id="{{elementid}}_{{CSS.EQUATION_PREVIEW}}"></div>' +
'<div id="{{elementid}}_cursorinfo">{{get_string "cursorinfo" component}}</div>' +
'<div class="mdl-align">' +
'<br/>' +
- '<button class="{{CSS.SUBMIT}}">{{get_string "saveequation" component}}</button>' +
+ '<button class="btn btn-default {{CSS.SUBMIT}}">{{get_string "saveequation" component}}</button>' +
'</div>' +
'</form>',
LIBRARY: '' +
'<div class="{{CSS.LIBRARY}}">' +
- '<ul>' +
+ '<ul class="root nav nav-tabs m-b-1" role="tablist">' +
'{{#each library}}' +
- '<li><a href="#{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}">' +
- '{{get_string groupname ../component}}' +
- '</a></li>' +
+ '<li class="nav-item">' +
+ '<a class="nav-link" href="#{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}" ' +
+ ' role="tab" data-toggle="tab">' +
+ '{{get_string groupname ../component}}' +
+ '</a>' +
+ '</li>' +
'{{/each}}' +
'</ul>' +
- '<div class="{{CSS.LIBRARY_GROUPS}}">' +
+ '<div class="tab-content m-b-1 {{CSS.LIBRARY_GROUPS}}">' +
'{{#each library}}' +
- '<div id="{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}">' +
+ '<div data-medium-type="{{CSS.LINK}}" class="tab-pane" ' +
+ 'id="{{../elementid}}_{{../CSS.LIBRARY_GROUP_PREFIX}}_{{@key}}">' +
'<div role="toolbar">' +
'{{#split "\n" elements}}' +
- '<button tabindex="-1" data-tex="{{this}}" aria-label="{{this}}" title="{{this}}">' +
+ '<button class="btn btn-default" tabindex="-1" data-tex="{{this}}"' +
+ 'aria-label="{{this}}" title="{{this}}">' +
'{{../../DELIMITERS.START}}{{this}}{{../../DELIMITERS.END}}' +
'</button>' +
'{{/split}}' +
var content = this._getDialogueContent();
dialogue.set('bodyContent', content);
- var library = content.one(SELECTORS.LIBRARY);
+ content.one('.nav-item:first-child .nav-link').getDOMNode().click();
- var tabview = new Y.TabView({
- srcNode: library
- });
-
- tabview.render();
dialogue.show();
// Notify the filters about the modified nodes.
require(['core/event'], function(event) {
}
.atto_image_preview_box {
- max-height: 200px;
+ max-height: 150px;
margin-bottom: 1em;
overflow: auto;
}
cursor: pointer;
}
-.atto_image_size {
- display: inline-block;
-}
-
-.atto_image_size input[type=checkbox] {
- margin-left: 1em;
- margin-right: 1em;
-}
-
-.atto_image_size input[type=text] {
- width: 3em;
-}
-
-.atto_image_size label {
- display: inline-block;
-}
-
.atto_image_button_text-top {
vertical-align: text-top;
margin: 0 0.5em;
TEMPLATE = '' +
'<form class="atto_form">' +
- '<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
- '<input class="fullwidth {{CSS.INPUTURL}}" type="url" id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
- '<br/>' +
// Add the repository browser button.
'{{#if showFilepicker}}' +
- '<button class="{{CSS.IMAGEBROWSER}}" type="button">{{get_string "browserepositories" component}}</button>' +
+ '<div class="m-b-1">' +
+ '<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
+ '<div class="input-group input-append w-100">' +
+ '<input class="form-control {{CSS.INPUTURL}}" type="url" ' +
+ 'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
+ '<span class="input-group-append">' +
+ '<button class="btn btn-default {{CSS.IMAGEBROWSER}}" type="button">' +
+ '{{get_string "browserepositories" component}}</button>' +
+ '</span>' +
+ '</div>' +
+ '</div>' +
+ '{{else}}' +
+ '<div class="m-b-1">' +
+ '<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
+ '<input class="form-control fullwidth {{CSS.INPUTURL}}" type="url" ' +
+ 'id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
+ '</div>' +
'{{/if}}' +
// Add the Alt box.
- '<div style="display:none" role="alert" class="warning {{CSS.IMAGEALTWARNING}}">' +
+ '<div style="display:none" role="alert" class="alert alert-warning m-b-1 {{CSS.IMAGEALTWARNING}}">' +
'{{get_string "presentationoraltrequired" component}}' +
'</div>' +
+ '<div class="m-b-1">' +
'<label for="{{elementid}}_{{CSS.INPUTALT}}">{{get_string "enteralt" component}}</label>' +
- '<input class="fullwidth {{CSS.INPUTALT}}" type="text" value="" id="{{elementid}}_{{CSS.INPUTALT}}" size="32"/>' +
- '<br/>' +
+ '<input class="form-control fullwidth {{CSS.INPUTALT}}" type="text" value="" ' +
+ 'id="{{elementid}}_{{CSS.INPUTALT}}" size="32"/>' +
// Add the presentation select box.
- '<input type="checkbox" class="{{CSS.IMAGEPRESENTATION}}" id="{{elementid}}_{{CSS.IMAGEPRESENTATION}}"/>' +
- '<label class="sameline" for="{{elementid}}_{{CSS.IMAGEPRESENTATION}}">' +
+ '<div class="form-check">' +
+ '<input type="checkbox" class="form-check-input {{CSS.IMAGEPRESENTATION}}" ' +
+ 'id="{{elementid}}_{{CSS.IMAGEPRESENTATION}}"/>' +
+ '<label class="form-check-label" for="{{elementid}}_{{CSS.IMAGEPRESENTATION}}">' +
'{{get_string "presentation" component}}' +
'</label>' +
- '<br/>' +
+ '</div>' +
+ '</div>' +
// Add the size entry boxes.
- '<label class="sameline" for="{{elementid}}_{{CSS.INPUTSIZE}}">{{get_string "size" component}}</label>' +
- '<div id="{{elementid}}_{{CSS.INPUTSIZE}}" class="{{CSS.INPUTSIZE}}">' +
+ '<div class="m-b-1">' +
+ '<label class="" for="{{elementid}}_{{CSS.INPUTSIZE}}">{{get_string "size" component}}</label>' +
+ '<div id="{{elementid}}_{{CSS.INPUTSIZE}}" class="form-inline {{CSS.INPUTSIZE}}">' +
'<label class="accesshide" for="{{elementid}}_{{CSS.INPUTWIDTH}}">{{get_string "width" component}}</label>' +
- '<input type="text" class="{{CSS.INPUTWIDTH}} input-mini" id="{{elementid}}_{{CSS.INPUTWIDTH}}" size="4"/> x ' +
+ '<input type="text" class="form-control m-r-1 input-mini {{CSS.INPUTWIDTH}}" ' +
+ 'id="{{elementid}}_{{CSS.INPUTWIDTH}}" size="4"/> x' +
// Add the height entry box.
'<label class="accesshide" for="{{elementid}}_{{CSS.INPUTHEIGHT}}">{{get_string "height" component}}</label>' +
- '<input type="text" class="{{CSS.INPUTHEIGHT}} input-mini" id="{{elementid}}_{{CSS.INPUTHEIGHT}}" size="4"/>' +
+ '<input type="text" class="form-control m-l-1 input-mini {{CSS.INPUTHEIGHT}}" ' +
+ 'id="{{elementid}}_{{CSS.INPUTHEIGHT}}" size="4"/>' +
// Add the constrain checkbox.
- '<input type="checkbox" class="{{CSS.INPUTCONSTRAIN}} sameline" id="{{elementid}}_{{CSS.INPUTCONSTRAIN}}"/>' +
- '<label for="{{elementid}}_{{CSS.INPUTCONSTRAIN}}">{{get_string "constrain" component}}</label>' +
+ '<div class="form-check m-l-2">' +
+ '<input type="checkbox" class="form-check-input {{CSS.INPUTCONSTRAIN}}" ' +
+ 'id="{{elementid}}_{{CSS.INPUTCONSTRAIN}}"/>' +
+ '<label class="form-check-label" for="{{elementid}}_{{CSS.INPUTCONSTRAIN}}">' +
+ '{{get_string "constrain" component}}</label>' +
+ '</div>' +
+ '</div>' +
'</div>' +
// Add the alignment selector.
- '<label class="sameline" for="{{elementid}}_{{CSS.INPUTALIGNMENT}}">{{get_string "alignment" component}}</label>' +
- '<select class="{{CSS.INPUTALIGNMENT}}" id="{{elementid}}_{{CSS.INPUTALIGNMENT}}">' +
+ '<div class="form-inline m-b-1">' +
+ '<label class="for="{{elementid}}_{{CSS.INPUTALIGNMENT}}">{{get_string "alignment" component}}</label>' +
+ '<select class="custom-select {{CSS.INPUTALIGNMENT}}" id="{{elementid}}_{{CSS.INPUTALIGNMENT}}">' +
'{{#each alignments}}' +
'<option value="{{value}}">{{get_string str ../component}}</option>' +
'{{/each}}' +
'</select>' +
+ '</div>' +
// Hidden input to store custom styles.
'<input type="hidden" class="{{CSS.INPUTCUSTOMSTYLE}}"/>' +
'<br/>' +
'</div>' +
// Add the submit button and close the form.
- '<button class="{{CSS.INPUTSUBMIT}}" type="submit">{{get_string "saveimage" component}}</button>' +
+ '<button class="btn btn-default {{CSS.INPUTSUBMIT}}" type="submit">{{get_string "saveimage" component}}</button>' +
'</div>' +
'</form>',
},
TEMPLATE = '' +
'<form class="atto_form">' +
- '<label for="{{elementid}}_atto_link_urlentry">{{get_string "enterurl" component}}</label>' +
- '<input class="fullwidth url {{CSS.URLINPUT}}" type="url" id="{{elementid}}_atto_link_urlentry" size="32"/><br/>' +
-
- // Add the repository browser button.
'{{#if showFilepicker}}' +
- '<button class="openlinkbrowser">{{get_string "browserepositories" component}}</button>' +
- '<br/>' +
+ '<label for="{{elementid}}_atto_link_urlentry">{{get_string "enterurl" component}}</label>' +
+ '<div class="input-group input-append w-100 m-b-1">' +
+ '<input class="form-control url {{CSS.URLINPUT}}" type="url" ' +
+ 'id="{{elementid}}_atto_link_urlentry"/>' +
+ '<span class="input-group-append">' +
+ '<button class="btn btn-default openlinkbrowser" type="button">' +
+ '{{get_string "browserepositories" component}}</button>' +
+ '</span>' +
+ '</div>' +
+ '{{else}}' +
+ '<div class="m-b-1">' +
+ '<label for="{{elementid}}_atto_link_urlentry">{{get_string "enterurl" component}}</label>' +
+ '<input class="form-control fullwidth url {{CSS.URLINPUT}}" type="url" ' +
+ 'id="{{elementid}}_atto_link_urlentry" size="32"/>' +
+ '</div>' +
'{{/if}}' +
- '<input type="checkbox" class="newwindow" id="{{elementid}}_{{CSS.NEWWINDOW}}"/>' +
- '<label class="sameline" for="{{elementid}}_{{CSS.NEWWINDOW}}">{{get_string "openinnewwindow" component}}</label>' +
- '<br/>' +
+ '<div class="form-check">' +
+ '<input type="checkbox" class="form-check-input newwindow" id="{{elementid}}_{{CSS.NEWWINDOW}}"/>' +
+ '<label class="form-check-label" for="{{elementid}}_{{CSS.NEWWINDOW}}">' +
+ '{{get_string "openinnewwindow" component}}' +
+ '</label>' +
+ '</div>' +
'<div class="mdl-align">' +
'<br/>' +
- '<button type="submit" class="submit">{{get_string "createlink" component}}</button>' +
+ '<button type="submit" class="btn btn-default submit">{{get_string "createlink" component}}</button>' +
'</div>' +
'</form>';
Y.namespace('M.atto_link').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
-.atto_form.atto_media #video input,
-.atto_form.atto_media #audio input,
-.atto_form.atto_media #link input {
- box-sizing: border-box;
- height: inherit;
-}
-
.atto_form.atto_media > .tab-content {
max-height: 45vh;
overflow-x: hidden;
- padding-left: 20px;
- padding-right: 20px;
- margin-left: -20px;
- margin-right: -21px;
-}
-
-.atto_form.atto_media [id$="-advanced-settings"] label {
- margin-right: 10px;
-}
-
-.atto_form.atto_media label {
- display: inline-block;
-}
-
-.atto_form.atto_media label > span {
- display: inline-block;
- min-width: 6em;
-}
-
-.atto_form.atto_media .atto_media_track_lang_entry,
-.atto_form.atto_media .atto_media_track_label_entry {
- width: 168px;
-}
-
-.atto_form.atto_media .atto_media_track_source {
- margin-bottom: 10px;
-}
-
-.atto_form.atto_media select {
- margin-right: 10px;
-}
-
-.atto_form.atto_media [id$="-tracks"] input[type=checkbox] {
- margin-left: 10px;
-}
-
-.atto_form.atto_media .atto_media_track ~ .atto_media_track {
- margin-top: 5px;
- padding-top: 10px;
- border-top: 1px solid #e5e5e5;
-}
-
-.atto_form.atto_media label.fullwidth {
- width: 100%;
-}
-
-.atto_media_postersize {
- display: inline-block;
-}
-
-.atto_media_postersize input[type=text] {
- width: 3em;
-}
-
-input[size].atto_media_url_entry {
- width: calc(100% - 15px);
-}
-
-.openmediabrowser {
- margin-top: -4px;
-}
-
-.addcomponent,
-.removecomponent {
- font-weight: bold;
- margin-right: 10px;
-}
-
-.trackhelp {
- text-align: right;
-}
-
-.atto_form.atto_media .atto_media_source > label {
- width: calc(100% - 153px);
-}
-
-.atto_form.atto_media .atto_media_track_lang_entry,
-.atto_form.atto_media .atto_media_track_label_entry {
- width: 116px;
-}
-
-.langlabel {
- width: 42%;
-}
-
-.labellabel {
- width: 44%;
-}
-
-.defaultlabel {
- width: 14%;
-}
-
-[data-medium-type=link] label {
- width: 100%;
-}
+}
\ No newline at end of file
TEMPLATES = {
ROOT: '' +
'<form class="mform atto_form atto_media" id="{{elementid}}_atto_media_form">' +
- '<ul class="root nav nav-tabs" role="tablist">' +
+ '<ul class="root nav nav-tabs m-b-1" role="tablist">' +
'<li data-medium-type="{{CSS.LINK}}" class="nav-item">' +
'<a class="nav-link active" href="#{{elementid}}_{{CSS.LINK}}" role="tab" data-toggle="tab">' +
'{{get_string "link" component}}' +
'</div>' +
'<div class="mdl-align">' +
'<br/>' +
- '<button class="submit" type="submit">{{get_string "createmedia" component}}</button>' +
+ '<button class="btn btn-default submit" type="submit">{{get_string "createmedia" component}}</button>' +
'</div>' +
'</form>',
TAB_PANES: {
LINK: '' +
'{{renderPartial "form_components.source" context=this id=CSS.LINK_SOURCE}}' +
- '<label>' +
- 'Enter name' +
- '<input class="fullwidth {{CSS.NAME_INPUT}}" type="text" id="{{elementid}}_link_nameentry"' +
- 'size="32" required="true"/>' +
- '</label>',
+ '<label for="{{elementid}}_link_nameentry">{{get_string "entername" component}}</label>' +
+ '<input class="form-control fullwidth {{CSS.NAME_INPUT}}" type="text" id="{{elementid}}_link_nameentry"' +
+ 'size="32" required="true"/>',
VIDEO: '' +
'{{renderPartial "form_components.source" context=this id=CSS.MEDIA_SOURCE entersourcelabel="videosourcelabel"' +
' addcomponentlabel="addsource" multisource="true" addsourcehelp=helpStrings.addsource}}' +
FORM_COMPONENTS: {
SOURCE: '' +
'<div class="{{CSS.SOURCE}} {{id}}">' +
- '<label>' +
+ '<div class="m-b-1">' +
+ '<label for="url-input">' +
'{{#entersourcelabel}}{{get_string ../entersourcelabel ../component}}{{/entersourcelabel}}' +
- '{{^entersourcelabel}}{{get_string "entersource" ../component}}{{/entersourcelabel}}</a>' +
- '<br/>' +
- '<input class="{{CSS.URL_INPUT}}" type="url" size="32"/>' +
- '</label>' +
- '<button class="openmediabrowser" type="button">{{get_string "browserepositories" component}}</button>' +
+ '{{^entersourcelabel}}{{get_string "entersource" ../component}}{{/entersourcelabel}}' +
+ '</label>' +
+ '<div class="input-group input-append w-100">' +
+ '<input id="url-input" class="form-control {{CSS.URL_INPUT}}" type="url" size="32"/>' +
+ '<span class="input-group-append">' +
+ '<button class="btn btn-default openmediabrowser" type="button">' +
+ '{{get_string "browserepositories" component}}</button>' +
+ '</span>' +
+ '</div>' +
+ '</div>' +
'{{#multisource}}' +
'{{renderPartial "form_components.add_component" context=../this label=../addcomponentlabel ' +
' help=../addsourcehelp}}' +
'</div>',
DISPLAY_OPTIONS: '' +
'<div class="{{CSS.DISPLAY_OPTIONS}}">' +
- '<label>' +
- '{{get_string "size" component}}' +
- '<div class={{CSS.POSTER_SIZE}}>' +
- '<label>' +
- '<span class="accesshide">{{get_string "videowidth" component}}</span>' +
- '<input type="text" class="{{CSS.WIDTH_INPUT}} input-mini" size="4"/>' +
- '</label>' +
+ '<div class="m-b-1">' +
+ '<label>{{get_string "size" component}}</label>' +
+ '<div class="form-inline {{CSS.POSTER_SIZE}}">' +
+ '<label class="accesshide">{{get_string "videowidth" component}}</label>' +
+ '<input type="text" class="form-control m-r-1 {{CSS.WIDTH_INPUT}} input-mini" size="4"/>' +
' x ' +
- '<label>' +
- '<span class="accesshide">{{get_string "videoheight" component}}</span>' +
- '<input type="text" class="{{CSS.HEIGHT_INPUT}} input-mini" size="4"/>' +
- '</label>' +
+ '<label class="accesshide">{{get_string "videoheight" component}}</label>' +
+ '<input type="text" class="form-control m-l-1 {{CSS.HEIGHT_INPUT}} input-mini" size="4"/>' +
'</div>' +
- '</label>' +
+ '</div>' +
'<div class="clearfix"></div>' +
'{{renderPartial "form_components.source" context=this id=CSS.POSTER_SOURCE entersourcelabel="poster"}}' +
'<div>',
ADVANCED_SETTINGS: '' +
'<div class="{{CSS.ADVANCED_SETTINGS}}">' +
- '<label>' +
- '<input type="checkbox" checked="true" class="{{CSS.MEDIA_CONTROLS_TOGGLE}}"/>' +
- '{{get_string "controls" component}}' +
- '</label>' +
- '<label>' +
- '<input type="checkbox" class="{{CSS.MEDIA_AUTOPLAY_TOGGLE}}"/>' +
- '{{get_string "autoplay" component}}' +
- '</label>' +
- '<label>' +
- '<input type="checkbox" class="{{CSS.MEDIA_MUTE_TOGGLE}}"/>' +
- '{{get_string "mute" component}}' +
- '</label>' +
- '<label>' +
- '<input type="checkbox" class="{{CSS.MEDIA_LOOP_TOGGLE}}"/>' +
- '{{get_string "loop" component}}' +
- '</label>' +
+ '<div class="form-check">' +
+ '<input type="checkbox" checked="true" class="form-check-input {{CSS.MEDIA_CONTROLS_TOGGLE}}"' +
+ 'id="media-controls-toggle"/>' +
+ '<label class="form-check-label" for="media-controls-toggle">{{get_string "controls" component}}</label>' +
+ '</div>' +
+ '<div class="form-check">' +
+ '<input type="checkbox" class="form-check-input {{CSS.MEDIA_AUTOPLAY_TOGGLE}}"' +
+ 'id="media-autoplay-toggle"/>' +
+ '<label class="form-check-label" for="media-autoplay-toggle">{{get_string "autoplay" component}}</label>' +
+ '</div>' +
+ '<div class="form-check">' +
+ '<input type="checkbox" class="form-check-input {{CSS.MEDIA_MUTE_TOGGLE}}" id="media-mute-toggle"/>' +
+ '<label class="form-check-label" for="media-mute-toggle">{{get_string "mute" component}}</label>' +
+ '</div>' +
+ '<div class="form-check">' +
+ '<input type="checkbox" class="form-check-input {{CSS.MEDIA_LOOP_TOGGLE}}" id="media-loop-toggle"/>' +
+ '<label class="form-check-label" for="media-loop-toggle">{{get_string "loop" component}}</label>' +
+ '</div>' +
'</div>',
TRACK_TABS: '' +
- '<ul class="nav nav-tabs">' +
+ '<ul class="nav nav-tabs mb-3">' +
'<li data-track-kind="{{CSS.TRACK_SUBTITLES}}" class="nav-item">' +
'<a class="nav-link active" href="#{{elementid}}_{{id}}_{{CSS.TRACK_SUBTITLES}}"' +
' role="tab" data-toggle="tab">' +
'</div>' +
'</div>',
TRACK: '' +
- '<div class="{{CSS.TRACK}}">' +
+ '<div class="m-b-1 {{CSS.TRACK}}">' +
'{{renderPartial "form_components.source" context=this id=CSS.TRACK_SOURCE entersourcelabel=sourcelabel}}' +
- '<label class="langlabel">' +
- '<span>{{get_string "srclang" component}}</span>' +
- '<select class="{{CSS.TRACK_LANG_INPUT}}">' +
+ '<div class="form-group">' +
+ '<label class="w-100" for="lang-input">{{get_string "srclang" component}}</label>' +
+ '<select id="lang-input" class="custom-select {{CSS.TRACK_LANG_INPUT}}">' +
'<optgroup label="{{get_string "languagesinstalled" component}}">' +
'{{#langsinstalled}}' +
'<option value="{{code}}" {{#default}}selected="selected"{{/default}}>{{lang}}</option>' +
'{{#langsavailable}}<option value="{{code}}">{{lang}}</option>{{/langsavailable}}' +
'</optgroup>' +
'</select>' +
- '</label>' +
- '<label class="labellabel">' +
- '<span>{{get_string "label" component}}</span>' +
- '<input class="{{CSS.TRACK_LABEL_INPUT}}" type="text"/>' +
- '</label>' +
- '<label class="defaultlabel">' +
- '<input type="checkbox" class="{{CSS.TRACK_DEFAULT_SELECT}}"/>' +
- '{{get_string "default" component}}' +
- '</label>' +
+ '</div>' +
+ '<div class="form-group">' +
+ '<label class="w-100" for="track-input">{{get_string "label" component}}</label>' +
+ '<input id="track-input" class="form-control {{CSS.TRACK_LABEL_INPUT}}" type="text"/>' +
+ '</div>' +
+ '<div class="form-check">' +
+ '<input type="checkbox" class="form-check-input {{CSS.TRACK_DEFAULT_SELECT}}"/>' +
+ '<label class="form-check-label">{{get_string "default" component}}</label>' +
+ '</div>' +
'{{renderPartial "form_components.add_component" context=this label=addcomponentlabel}}' +
'</div>'
},
// Empty the array containing the previously recorded chunks.
cm.chunks = [];
cm.blobSize = 0;
+ cm.uploadBtn.detach('click');
// Initialize common configurations.
var commonConfig = {
// Empty the array containing the previously recorded chunks.
cm.chunks = [];
cm.blobSize = 0;
+ cm.uploadBtn.detach('click');
// Initialize common configurations.
var commonConfig = {
div.editor_atto_content caption {
border: 1px dashed #bbb;
position: relative;
- min-width: 30px;
- height: 13px;
+ min-width: 2rem;
+ height: 2rem;
}
div.editor_atto_content caption {
}
div.availablecolors {
- max-width: 55%;
- display: inline-block;
- vertical-align: middle;
+ background-color: #eee;
+ padding: 0.5rem;
}
-
-div.availablecolors label:not(.hideborder) {
- border: 1px solid #ddd;
-}
-
-div.availablecolors label {
- border-radius: 4px;
- display: inline-block;
- font-size: 0.1em;
- padding: 2px;
- padding-left: 22px;
-}
-
-div.availablecolors label input[type="radio"] {
- float: none;
- margin: 0;
- margin-left: -15px;
-}
-
-input[name="bordersize"],
-input[name="width"] {
- margin-right: 0.3em;
+div.availablecolors .tablebordercolor,
+div.availablecolors .tablebackgroundcolor {
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ width: 2rem;
+ height: 2rem;
+ text-align: center;
+ vertical-align: sub;
+ border-radius: 25%;
+ margin-right: 0.2rem;
}
And I click on "Table" "button"
When I click on "Edit table" "link"
# Check that the background colour is set correctly.
- Then the field with xpath "//label/input[@name='backgroundColour' and @value='#FFFFFF']" matches value "1"
+ Then the field with xpath "//div[@class='tablebackgroundcolor']/input[@name='backgroundColour' and @value='#FFFFFF']" matches value "1"
And the field "Table width (in %)" matches value "100"
And the field "Borders" matches value "Around table"
And the field "Style of borders" matches value "dashed"
And the field "Size of borders" matches value "2"
# Check that the border colour is set correctly.
- And the field with xpath "//label/input[@name='borderColour' and @value='#FFFFFF']" matches value "1"
+ And the field with xpath "//div[@class='tablebordercolor']/input[@name='borderColour' and @value='#FFFFFF']" matches value "1"
@javascript
Scenario: Create a table with background colour and width with border settings off
},
TEMPLATE = '' +
'<form class="{{CSS.FORM}}">' +
+ '<div class="m-b-1 form-group row-fluid">' +
+ '<div class="col-sm-4 span4">' +
'<label for="{{elementid}}_atto_table_caption">{{get_string "caption" component}}</label>' +
- '<input class="{{CSS.CAPTION}} fullwidth" id="{{elementid}}_atto_table_caption" required />' +
- '<br/>' +
- '<br/>' +
- '<label for="{{elementid}}_atto_table_captionposition" class="sameline">' +
+ '</div><div class="col-sm-8 span8">' +
+ '<input type="text" class="form-control {{CSS.CAPTION}}" id="{{elementid}}_atto_table_caption" required />' +
+ '</div>' +
+ '</div>' +
+ '<div class="m-b-1 form-group row-fluid">' +
+ '<div class="col-sm-4 span4">' +
+ '<label for="{{elementid}}_atto_table_captionposition">' +
'{{get_string "captionposition" component}}</label>' +
- '<select class="{{CSS.CAPTIONPOSITION}}" id="{{elementid}}_atto_table_captionposition">' +
+ '</div><div class="col-sm-8 span8">' +
+ '<select class="custom-select {{CSS.CAPTIONPOSITION}}" id="{{elementid}}_atto_table_captionposition">' +
'<option value=""></option>' +
'<option value="top">{{get_string "top" "editor"}}</option>' +
'<option value="bottom">{{get_string "bottom" "editor"}}</option>' +
'</select>' +
- '<br/>' +
- '<label for="{{elementid}}_atto_table_headers" class="sameline">{{get_string "headers" component}}</label>' +
- '<select class="{{CSS.HEADERS}}" id="{{elementid}}_atto_table_headers">' +
+ '</div>' +
+ '</div>' +
+ '<div class="m-b-1 form-group row-fluid">' +
+ '<div class="col-sm-4 span4">' +
+ '<label for="{{elementid}}_atto_table_headers">{{get_string "headers" component}}</label>' +
+ '</div><div class="col-sm-8 span8">' +
+ '<select class="custom-select {{CSS.HEADERS}}" id="{{elementid}}_atto_table_headers">' +
'<option value="columns">{{get_string "columns" component}}' + '</option>' +
'<option value="rows">{{get_string "rows" component}}' + '</option>' +
'<option value="both">{{get_string "both" component}}' + '</option>' +
'</select>' +
- '<br/>' +
+ '</div>' +
+ '</div>' +
'{{#if nonedit}}' +
- '<label for="{{elementid}}_atto_table_rows" class="sameline">{{get_string "numberofrows" component}}</label>' +
- '<input class="{{CSS.ROWS}}" type="number" value="3" ' +
+ '<div class="m-b-1 form-group row-fluid">' +
+ '<div class="col-sm-4 span4">' +
+ '<label for="{{elementid}}_atto_table_rows">{{get_string "numberofrows" component}}</label>' +
+ '</div><div class="col-sm-8 span8">' +
+ '<input class="form-control w-auto {{CSS.ROWS}}" type="number" value="3" ' +
'id="{{elementid}}_atto_table_rows" size="8" min="1" max="50"/>' +
- '<br/>' +
+ '</div>' +
+ '</div>' +
+ '<div class="m-b-1 form-group row-fluid">' +
+ '<div class="col-sm-4 span4">' +
'<label for="{{elementid}}_atto_table_columns" ' +
- 'class="sameline">{{get_string "numberofcolumns" component}}</label>' +
- '<input class="{{CSS.COLUMNS}}" type="number" value="3" id="{{elementid}}_atto_table_columns"' +
+ '>{{get_string "numberofcolumns" component}}</label>' +
+ '</div><div class="col-sm-8 span8">' +
+ '<input class="form-control w-auto {{CSS.COLUMNS}}" type="number" value="3" ' +
+ 'id="{{elementid}}_atto_table_columns"' +
'size="8" min="1" max="20"/>' +
- '<br/>' +
+ '</div>' +
+ '</div>' +
'{{/if}}' +
'{{#if allowStyling}}' +
'<fieldset>' +
'<legend class="mdl-align">{{get_string "appearance" component}}</legend>' +
'{{#if allowBorders}}' +
- '<label for="{{elementid}}_atto_table_borders" class="sameline">{{get_string "borders" component}}</label>' +
- '<select name="borders" class="{{CSS.BORDERS}}" id="{{elementid}}_atto_table_borders">' +
+ '<div class="m-b-1 form-group row-fluid">' +
+ '<div class="col-sm-4 span4">' +
+ '<label for="{{elementid}}_atto_table_borders">{{get_string "borders" component}}</label>' +
+ '</div><div class="col-sm-8 span8">' +
+ '<select name="borders" class="custom-select {{CSS.BORDERS}}" id="{{elementid}}_atto_table_borders">' +
'<option value="default">{{get_string "themedefault" component}}' + '</option>' +
'<option value="outer">{{get_string "outer" component}}' + '</option>' +
'<option value="all">{{get_string "all" component}}' + '</option>' +
'</select>' +
- '<br>' +
- '<label for="{{elementid}}_atto_table_borderstyle" class="sameline">' +
+ '</div>' +
+ '</div>' +
+ '<div class="m-b-1 form-group row-fluid">' +
+ '<div class="col-sm-4 span4">' +
+ '<label for="{{elementid}}_atto_table_borderstyle">' +
'{{get_string "borderstyles" component}}</label>' +
- '<select name="borderstyles" class="{{CSS.BORDERSTYLE}}" id="{{elementid}}_atto_table_borderstyle">' +
+ '</div><div class="col-sm-8 span8">' +
+ '<select name="borderstyles" class="custom-select {{CSS.BORDERSTYLE}}" ' +
+ 'id="{{elementid}}_atto_table_borderstyle">' +
'{{#each borderStyles}}' +
'<option value="' + '{{this}}' + '">' + '{{get_string this ../component}}' + '</option>' +
'{{/each}}' +
'</select>' +
- '<br>' +
- '<label for="{{elementid}}_atto_table_bordersize" class="sameline">' +
+ '</div>' +
+ '</div>' +
+ '<div class="m-b-1 form-group row-fluid">' +
+ '<div class="col-sm-4 span4">' +
+ '<label for="{{elementid}}_atto_table_bordersize">' +
'{{get_string "bordersize" component}}</label>' +
- '<input name="bordersize" id="{{elementid}}_atto_table_bordersize" class="{{CSS.BORDERSIZE}}"' +
+ '</div><div class="col-sm-8 span8">' +
+ '<div class="form-inline">' +
+ '<input name="bordersize" id="{{elementid}}_atto_table_bordersize" ' +
+ 'class="form-control w-auto m-r-1 {{CSS.BORDERSIZE}}"' +
'type="number" value="1" size="8" min="1" max="50"/>' +
- '<label style="display: inline-block;">{{CSS.BORDERSIZEUNIT}}</label>' +
- '<br>' +
- '<label for="{{elementid}}_atto_table_bordercolour" class="sameline">' +
+ '<label>{{CSS.BORDERSIZEUNIT}}</label>' +
+ '</div>' +
+ '</div>' +
+ '</div>' +
+ '<div class="m-b-1 form-group row-fluid">' +
+ '<div class="col-sm-4 span4">' +
+ '<label for="{{elementid}}_atto_table_bordercolour">' +
'{{get_string "bordercolour" component}}</label>' +
+ '</div><div class="col-sm-8 span8">' +
'<div id="{{elementid}}_atto_table_bordercolour"' +
- 'class="{{CSS.BORDERCOLOUR}} {{CSS.AVAILABLECOLORS}}" size="1">' +
- '<label class="hideborder" for="{{../elementid}}_atto_table_bordercolour_-1"' +
- 'style="background-color:transparent;color:transparent">' +
-
+ 'class="form-inline {{CSS.BORDERCOLOUR}} {{CSS.AVAILABLECOLORS}}" size="1">' +
+ '<div class="tablebordercolor" style="background-color:transparent;color:transparent">' +
'<input id="{{../elementid}}_atto_table_bordercolour_-1"' +
- 'type="radio" name="borderColour" value="none" checked="checked"' +
+ 'type="radio" class="m-0" name="borderColour" value="none" checked="checked"' +
'title="{{get_string "themedefault" component}}"></input>' +
-
- '{{get_string "themedefault" component}}' +
- '</label>' +
+ '<label for="{{../elementid}}_atto_table_bordercolour_-1" class="accesshide">' +
+ '{{get_string "themedefault" component}}</label>' +
+ '</div>' +
'{{#each availableColours}}' +
- '<label for="{{../elementid}}_atto_table_bordercolour_{{@index}}"' +
- 'style="background-color:{{this}};color:{{this}}">' +
-
+ '<div class="tablebordercolor" style="background-color:{{this}};color:{{this}}">' +
'<input id="{{../elementid}}_atto_table_bordercolour_{{@index}}"' +
- 'type="radio" name="borderColour" value="' + '{{this}}' + '" title="{{this}}">' +
-
- '{{this}}' +
- '</label>' +
+ 'type="radio" class="m-0" name="borderColour" value="' + '{{this}}' + '" title="{{this}}">' +
+ '<label for="{{../elementid}}_atto_table_bordercolour_{{@index}}" class="accesshide">' +
+ '{{this}}</label>' +
+ '</div>' +
'{{/each}}' +
'</div>' +
- '<br>' +
+ '</div>' +
+ '</div>' +
'{{/if}}' +
'{{#if allowBackgroundColour}}' +
- '<label for="{{elementid}}_atto_table_backgroundcolour" class="sameline">' +
+ '<div class="m-b-1 form-group row-fluid">' +
+ '<div class="col-sm-4 span4">' +
+ '<label for="{{elementid}}_atto_table_backgroundcolour">' +
'{{get_string "backgroundcolour" component}}</label>' +
+ '</div><div class="col-sm-8 span8">' +
'<div id="{{elementid}}_atto_table_backgroundcolour"' +
- 'class="{{CSS.BACKGROUNDCOLOUR}} {{CSS.AVAILABLECOLORS}}" size="1">' +
- '<label class="hideborder" for="{{../elementid}}_atto_table_backgroundcolour_-1"' +
- 'style="background-color:transparent;color:transparent">' +
-
+ 'class="form-inline {{CSS.BACKGROUNDCOLOUR}} {{CSS.AVAILABLECOLORS}}" size="1">' +
+ '<div class="tablebackgroundcolor" style="background-color:transparent;color:transparent">' +
'<input id="{{../elementid}}_atto_table_backgroundcolour_-1"' +
- 'type="radio" name="backgroundColour" value="none" checked="checked"' +
+ 'type="radio" class="m-0" name="backgroundColour" value="none" checked="checked"' +
'title="{{get_string "themedefault" component}}"></input>' +
-
- '{{get_string "themedefault" component}}' +
- '</label>' +
+ '<label for="{{../elementid}}_atto_table_backgroundcolour_-1" class="accesshide">' +
+ '{{get_string "themedefault" component}}</label>' +
+ '</div>' +
'{{#each availableColours}}' +
- '<label for="{{../elementid}}_atto_table_backgroundcolour_{{@index}}"' +
- 'style="background-color:{{this}};color:{{this}}">' +
-
+ '<div class="tablebackgroundcolor" style="background-color:{{this}};color:{{this}}">' +
'<input id="{{../elementid}}_atto_table_backgroundcolour_{{@index}}"' +
- 'type="radio" name="backgroundColour" value="' + '{{this}}' + '" title="{{this}}">' +
-
- '{{this}}' +
- '</label>' +
+ 'type="radio" class="m-0" name="backgroundColour" value="' + '{{this}}' + '" title="{{this}}">' +
+ '<label for="{{../elementid}}_atto_table_backgroundcolour_{{@index}}" class="accesshide">' +
+ '{{this}}</label>' +
+ '</div>' +
'{{/each}}' +
'</div>' +
- '<br>' +
+ '</div>' +
+ '</div>' +
'{{/if}}' +
'{{#if allowWidth}}' +
- '<label for="{{elementid}}_atto_table_width" class="sameline">' +
+ '<div class="m-b-1 form-group row-fluid">' +
+ '<div class="col-sm-4 span4">' +
+ '<label for="{{elementid}}_atto_table_width">' +
'{{get_string "width" component}}</label>' +
- '<input name="width" id="{{elementid}}_atto_table_width" class="{{CSS.WIDTH}}" size="8" ' +
+ '</div><div class="col-sm-8 span8">' +
+ '<div class="form-inline">' +
+ '<input name="width" id="{{elementid}}_atto_table_width" ' +
+ 'class="form-control w-auto m-r-1 {{CSS.WIDTH}}" size="8" ' +
'type="number" min="0" max="100"/>' +
- '<label style="display: inline-block;">{{CSS.WIDTHUNIT}}</label>' +
- '<br>' +
+ '<label>{{CSS.WIDTHUNIT}}</label>' +
+ '</div>' +
+ '</div>' +
+ '</div>' +
'{{/if}}' +
'</fieldset>' +
'{{/if}}' +
'<div class="mdl-align">' +
'<br/>' +
'{{#if edit}}' +
- '<button class="submit" type="submit">{{get_string "updatetable" component}}</button>' +
+ '<button class="btn btn-default submit" type="submit">{{get_string "updatetable" component}}</button>' +
'{{/if}}' +
'{{#if nonedit}}' +
- '<button class="submit" type="submit">{{get_string "createtable" component}}</button>' +
+ '<button class="btn btn-default submit" type="submit">{{get_string "createtable" component}}</button>' +
'{{/if}}' +
'</div>' +
'</form>',
margin: 4px;
}
-.atto_form label.sameline {
+/*.atto_form label.sameline {
display: inline-block;
min-width: 10em;
-}
+}*/
.atto_form textarea.fullwidth,
.atto_form input.fullwidth {
}
.atto_form {
- padding-left: 30px;
- padding-right: 30px;
+ padding: 0.5rem;
}
-.atto_form label {
+/*.atto_form label {
display: block;
margin: 0 0 5px 0;
-}
+}*/
.atto_control {
position: absolute;
$this->migrate_data($userid, $otheruserid);
$transaction->allow_commit();
} catch (\Throwable $e) {
- $updatepreference = false;
+ throw $e;
+ } finally {
+ $lock->release();
}
-
- $lock->release();
} else {
// Couldn't get a lock, move on to next user but make sure we don't update user preference so
// we still try again.
$tabledata->useridto = $notification->useridto;
$tabledata->subject = $notification->subject;
$tabledata->fullmessage = $notification->fullmessage;
- $tabledata->fullmessageformat = $notification->fullmessageformat;
+ $tabledata->fullmessageformat = $notification->fullmessageformat ?? FORMAT_MOODLE;
$tabledata->fullmessagehtml = $notification->fullmessagehtml;
$tabledata->smallmessage = $notification->smallmessage;
$tabledata->component = $notification->component;
$tabledata->conversationid = $conversationid;
$tabledata->subject = $message->subject;
$tabledata->fullmessage = $message->fullmessage;
- $tabledata->fullmessageformat = $message->fullmessageformat;
+ $tabledata->fullmessageformat = $message->fullmessageformat ?? FORMAT_MOODLE;
$tabledata->fullmessagehtml = $message->fullmessagehtml;
$tabledata->smallmessage = $message->smallmessage;
$tabledata->timecreated = $message->timecreated;
this.initialLoad = false;
// Let's find out how many unread messages there are.
- this.loadUnreadMessageCount();
+ this.unreadCount = this.root.find(SELECTORS.COUNT_CONTAINER).html();
};
/**
this.root.find(SELECTORS.COUNT_CONTAINER).addClass('hidden');
};
- /**
- * Ask the server how many unread messages are left, render the value
- * as a badge on the menu toggle and update the aria labels on the menu
- * toggle.
- *
- * @method loadUnreadMessageCount
- */
- MessagePopoverController.prototype.loadUnreadMessageCount = function() {
- MessageRepo.countUnreadConversations({useridto: this.userId}).then(function(count) {
- this.unreadCount = count;
- this.renderUnreadCount();
- this.updateButtonAriaLabel();
- }.bind(this)).catch(Notification.exception);
- };
-
/**
* Render the message data with the appropriate template and add it to the DOM.
*
this.initialLoad = false;
// Let's find out how many unread notifications there are.
- this.loadUnreadNotificationCount();
+ this.unreadCount = this.root.find(SELECTORS.COUNT_CONTAINER).html();
};
/**
this.root.find(SELECTORS.COUNT_CONTAINER).addClass('hidden');
};
- /**
- * Ask the server how many unread notifications are left, render the value
- * as a badge on the menu toggle and update the aria labels on the menu
- * toggle.
- *
- * @method loadUnreadNotificationCount
- */
- NotificationPopoverController.prototype.loadUnreadNotificationCount = function() {
- NotificationRepo.countUnread({useridto: this.userId}).then(function(count) {
- this.unreadCount = count;
- this.renderUnreadCount();
- this.updateButtonAriaLabel();
- }.bind(this)).catch(DebugNotification.exception);
- };
-
/**
* Find the notification element for the given id.
*
// Link to mark read page before loading the actual link.
notification.contexturl = URL.relativeUrl('message/output/popup/mark_notification_read.php', {
- redirecturl: notification.contexturl,
notificationid: notification.id,
+ redirecturl: notification.contexturl
});
var promise = Templates.render('message_popup/notification_content_item', notification)
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
-defined('MOODLE_INTERNAL') || die();
-
/**
* Contains standard functions for message_popup.
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
+defined('MOODLE_INTERNAL') || die();
+
/**
* Renders the popup.
*
// Add the messages popover.
if (!empty($CFG->messaging)) {
+ $unreadcount = \core_message\api::count_unread_conversations($USER);
$context = [
'userid' => $USER->id,
+ 'unreadcount' => $unreadcount,
'urls' => [
'seeall' => (new moodle_url('/message/index.php'))->out(),
'writeamessage' => (new moodle_url('/message/index.php', ['contactsfirst' => 1]))->out(),
// Add the notifications popover.
$enabled = \core_message\api::is_processor_enabled("popup");
if ($enabled) {
+ $unreadcount = \message_popup\api::count_unread_popup_notifications($USER->id);
$context = [
'userid' => $USER->id,
+ 'unreadcount' => $unreadcount,
'urls' => [
'seeall' => (new moodle_url('/message/output/popup/notifications.php'))->out(),
'preferences' => (new moodle_url('/message/notificationpreferences.php', ['userid' => $USER->id]))->out(),
{{$togglelabel}}{{#str}} showmessagewindownonew, message {{/str}}{{/togglelabel}}
{{$togglecontent}}
{{#pix}} t/message, core, {{#str}} togglemessagemenu, message {{/str}} {{/pix}}
- <div class="count-container hidden" data-region="count-container"></div>
+ <div class="count-container {{^unreadcount}}hidden{{/unreadcount}}" data-region="count-container">{{unreadcount}}</div>
{{/togglecontent}}
{{$containerlabel}}{{#str}} notificationwindow, message {{/str}}{{/containerlabel}}
{{$togglelabel}}{{#str}} shownotificationwindownonew, message {{/str}}{{/togglelabel}}
{{$togglecontent}}
{{#pix}} i/notifications, core, {{#str}} togglenotificationmenu, message {{/str}} {{/pix}}
- <div class="count-container hidden" data-region="count-container"></div>
+ <div class="count-container {{^unreadcount}}hidden{{/unreadcount}}" data-region="count-container">{{unreadcount}}</div>
{{/togglecontent}}
{{$containerlabel}}{{#str}} notificationwindow, message {{/str}}{{/containerlabel}}
}
}
+ /**
+ * Test migrating a legacy message that contains null as the format.
+ */
+ public function test_migrating_message_null_format() {
+ global $DB;
+
+ // Create users to test with.
+ $user1 = $this->getDataGenerator()->create_user();
+ $user2 = $this->getDataGenerator()->create_user();
+
+ $this->create_legacy_message_or_notification($user1->id, $user2->id, null, false, null, null);
+
+ // Now, let's execute the task for user 1.
+ $task = new \core_message\task\migrate_message_data();
+ $task->set_custom_data(
+ [
+ 'userid' => $user1->id
+ ]
+ );
+ $task->execute();
+
+ $messages = $DB->get_records('messages');
+ $this->assertCount(1, $messages);
+
+ $message = reset($messages);
+ $this->assertEquals(FORMAT_MOODLE, $message->fullmessageformat);
+ }
+
+ /**
+ * Test migrating a legacy notification that contains null as the format.
+ */
+ public function test_migrating_notification_null_format() {
+ global $DB;
+
+ // Create users to test with.
+ $user1 = $this->getDataGenerator()->create_user();
+ $user2 = $this->getDataGenerator()->create_user();
+
+ $this->create_legacy_message_or_notification($user1->id, $user2->id, null, true, null, null);
+
+ // Now, let's execute the task for user 1.
+ $task = new \core_message\task\migrate_message_data();
+ $task->set_custom_data(
+ [
+ 'userid' => $user1->id
+ ]
+ );
+ $task->execute();
+
+ $notifications = $DB->get_records('notifications');
+ $this->assertCount(1, $notifications);
+
+ $notification = reset($notifications);
+ $this->assertEquals(FORMAT_MOODLE, $notification->fullmessageformat);
+ }
+
/**
* Creates a legacy message or notification to be used for testing.
*
* @param int $timecreated
* @param bool $notification
* @param int|null $timeread The time the message/notification was read, null if it hasn't been.
+ * @param string|int|null $format The format of the message.
* @return int The id of the message (in either the message or message_read table)
* @throws dml_exception
*/
private function create_legacy_message_or_notification($useridfrom, $useridto, $timecreated = null,
- $notification = false, $timeread = null) {
+ $notification = false, $timeread = null, $format = FORMAT_PLAIN) {
global $DB;
$tabledata = new \stdClass();
$tabledata->useridto = $useridto;
$tabledata->subject = 'Subject ' . $timecreated;
$tabledata->fullmessage = 'Full message ' . $timecreated;
- $tabledata->fullmessageformat = FORMAT_PLAIN;
+ $tabledata->fullmessageformat = $format;
$tabledata->fullmessagehtml = 'Full message HTML ' . $timecreated;
$tabledata->smallmessage = 'Small message ' . $timecreated;
$tabledata->timecreated = $timecreated;
s.status = :submitted AND
(s.timemodified >= g.timemodified OR g.timemodified IS NULL OR g.grade IS NULL';
- if ($this->assignment->get_grade_item()->gradetype == GRADE_TYPE_SCALE) {
+ // Assignment grade is set to the negative grade scale id when scales are used.
+ if ($this->assignment->get_instance()->grade < 0) {
// Scale grades are set to -1 when not graded.
$where .= ' OR g.grade = -1';
}
}
}
+ /**
+ * Test filter by requires grading.
+ *
+ * This is specifically checking an assignment with no grade to make sure we do not
+ * get an exception thrown when rendering the grading table for this type of assignment.
+ */
+ public function test_gradingtable_filter_by_requiresgrading_no_grade() {
+ global $PAGE;
+
+ $this->resetAfterTest();
+
+ $course = $this->getDataGenerator()->create_course();
+ $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
+ $this->setUser($teacher);
+ $assign = $this->create_instance($course, [
+ 'assignsubmission_onlinetext_enabled' => 1,
+ 'assignfeedback_comments_enabled' => 0,
+ 'grade' => GRADE_TYPE_NONE
+ ]);
+
+ $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
+ 'id' => $assign->get_course_module()->id,
+ 'action' => 'grading',
+ )));
+
+ // Render the table with the requires grading filter.
+ $gradingtable = new assign_grading_table($assign, 1, ASSIGN_FILTER_REQUIRE_GRADING, 0, true);
+ $output = $assign->get_renderer()->render($gradingtable);
+
+ // Test that the filter function does not throw errors for assignments with no grade.
+ $this->assertContains(get_string('nothingtodisplay'), $output);
+ }
+
+
/**
* Test submissions with extension date.
*/
$cm = get_coursemodule_from_instance('assign', $assign->get_instance()->id);
$context = context_module::instance($cm->id);
- $assign = new testable_assign($context, $cm, $course);
+ $assign = new mod_assign_testable_assign($context, $cm, $course);
// Check that other teachers can't view this submission.
$this->setUser($otherteacher);
$event->add_record_snapshot('forum_discussions', $discussion);
$event->trigger();
- redirect("view.php?f=$discussion->forum");
+ redirect(new moodle_url('/mod/forum/view.php', ['f' => $discussion->forum]));
} else if (forum_delete_post($post, has_capability('mod/forum:deleteanypost', $modcontext),
$course, $cm, $forum)) {
} else { // Adding a new discussion.
// The location to redirect to after successfully posting.
- $redirectto = new moodle_url('view.php', array('f' => $fromform->forum));
+ $redirectto = new moodle_url('/mod/forum/view.php', array('f' => $fromform->forum));
$fromform->mailnow = empty($fromform->mailnow) ? 0 : 1;
$params['itemid'] = $itemid;
}
if ($userid) {
- $params['userid'] = $userid;
+ $params['tiuserid'] = $userid;
}
$DB->delete_records('tag_instance', $params);
}
$this->assertEquals($expectedtagcount, $DB->count_records('tag_instance'));
}
+ /**
+ * Test method delete_item_tags() with userid.
+ */
+ public function test_delete_item_tags_with_userid() {
+ global $DB;
+
+ $this->resetAfterTest(true);
+ // Create a course to tag.
+ $course = $this->getDataGenerator()->create_course();
+ $context = context_course::instance($course->id);
+
+ // Create a user to perform tagging.
+ $user = $this->getDataGenerator()->create_user();
+ $this->setUser($user);
+
+ // Tag courses.
+ core_tag_tag::set_item_tags('core_course', 'course', $course->id, $context, ['Tag 1', 'Tag 2'], $user->id);
+ $expectedtagcount = $DB->count_records('tag_instance');
+
+ // Delete tags for course. Use wrong userid.
+ core_tag\privacy\provider::delete_item_tags($context, 'core_course', 'course', null, 1);
+ $this->assertEquals($expectedtagcount, $DB->count_records('tag_instance'));
+
+ $expectedtagcount -= 2;
+ // Delete tags for course. Use correct userid.
+ core_tag\privacy\provider::delete_item_tags($context, 'core_course', 'course', null, $user->id);
+ $this->assertEquals($expectedtagcount, $DB->count_records('tag_instance'));
+ }
+
/**
* Test method delete_item_tags_select().
*/
SECTION: '.list-group-item[href*="#section-"]'
};
+ var small = $(document).width() < 768;
+
/**
* Constructor for the Drawer.
*
var hidden = trigger.attr('aria-expanded') == 'false';
var side = trigger.attr('data-side');
var body = $(SELECTORS.BODY);
+ var preference = trigger.attr('data-preference');
+ if (small) {
+ M.util.set_user_preference(preference, 'false');
+ }
drawer.on('mousewheel DOMMouseScroll', this.preventPageScroll);
}.bind(this));
this.registerEventListeners();
- var small = $(document).width() < 768;
if (small) {
this.closeAll();
}
body.removeClass('drawer-open-' + side);
drawer.attr('aria-hidden', 'true');
drawer.addClass('closed');
- M.util.set_user_preference(preference, 'false');
+ if (!small) {
+ M.util.set_user_preference(preference, 'false');
+ }
});
};
var body = $(SELECTORS.BODY);
var side = trigger.attr('data-side');
var preference = trigger.attr('data-preference');
+ if (small) {
+ M.util.set_user_preference(preference, 'false');
+ }
body.addClass('drawer-ease');
var open = trigger.attr('aria-expanded') == 'true';
drawer.focus();
body.addClass('drawer-open-' + side);
drawer.removeClass('closed');
- M.util.set_user_preference(preference, 'true');
+ if (!small) {
+ M.util.set_user_preference(preference, 'true');
+ }
} else {
// Close.
body.removeClass('drawer-open-' + side);
trigger.attr('aria-expanded', 'false');
drawer.attr('aria-hidden', 'true');
drawer.addClass('closed');
- M.util.set_user_preference(preference, 'false');
+ if (!small) {
+ M.util.set_user_preference(preference, 'false');
+ }
}
};
}.bind(this));
$(SELECTORS.SECTION).click(function() {
- var small = $(document).width() < 768;
if (small) {
this.closeAll();
}
namespace theme_boost\privacy;
+use \core_privacy\local\metadata\collection;
+
defined('MOODLE_INTERNAL') || die();
/**
- * The boost theme does not store any data.
+ * The boost theme stores a user preference data.
*
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-class provider implements \core_privacy\local\metadata\null_provider {
+class provider implements
+ // This plugin has data.
+ \core_privacy\local\metadata\provider,
+ // This plugin has some sitewide user preferences to export.
+ \core_privacy\local\request\user_preference_provider {
+
+ /** The user preference for the navigation drawer. */
+ const DRAWER_OPEN_NAV = 'drawer-open-nav';
+
+ /**
+ * Returns meta data about this system.
+ *
+ * @param collection $items The initialised item collection to add items to.
+ * @return collection A listing of user data stored through this system.
+ */
+ public static function get_metadata(collection $items) : collection {
+ $items->add_user_preference(self::DRAWER_OPEN_NAV, 'privacy:metadata:preference:draweropennav');
+ return $items;
+ }
/**
- * Get the language string identifier with the component's language
- * file to explain why this plugin stores no data.
+ * Store all user preferences for the plugin.
*
- * @return string
+ * @param int $userid The userid of the user whose data is to be exported.
*/
- public static function get_reason() : string {
- return 'privacy:metadata';
+ public static function export_user_preferences(int $userid) {
+ $draweropennavpref = get_user_preferences(self::DRAWER_OPEN_NAV, null, $userid);
+
+ if (isset($draweropennavpref)) {
+ $preferencestring = get_string('privacy:drawernavclosed', 'theme_boost');
+ if ($draweropennavpref == 'true') {
+ $preferencestring = get_string('privacy:drawernavopen', 'theme_boost');
+ }
+ \core_privacy\local\request\writer::export_user_preference(
+ 'theme_boost',
+ self::DRAWER_OPEN_NAV,
+ $draweropennavpref,
+ $preferencestring
+ );
+ }
}
}
$string['rawscsspre'] = 'Raw initial SCSS';
$string['rawscsspre_desc'] = 'In this field you can provide initialising SCSS code, it will be injected before everything else. Most of the time you will use this setting to define variables.';
$string['region-side-pre'] = 'Right';
+$string['privacy:metadata:preference:draweropennav'] = 'The user\'s preference for hiding or showing the drawer menu navigation.';
+$string['privacy:drawernavclosed'] = 'The current preference for the navigation drawer is closed.';
+$string['privacy:drawernavopen'] = 'The current preference for the navigation drawer is open.';
background-size: ($input-height / 2) ($input-height / 2);
}
-.form-check-input {
- position: static;
-}
+// .form-check-input {
+// position: static;
+// }
@mixin tag-variant($color) {
background-color: $color;
.open.atto_menu > .dropdown-menu {
display: block;
}
+div.editor_atto_toolbar button .icon {
+ color: $gray-700;
+}
+.w-auto {
+ width: auto;
+}
+
overflow-y: auto;
}
-// Extends bootstrapbase/less/bootstrap/type.less
-// to enable ol lists to use a larger number set.
-
-ol {
- margin: 0 0 $line-height-base / 2 2.5em;
-}
-
// Set menus in the fixed header to scroll vertically when they are longer than the page.
.navbar.fixed-top .dropdown .dropdown-menu {
max-height: calc(100vh - #{$navbar-height});
<div class="fp-license form-group row">
<label class="form-control-label col-4">{{#str}}chooselicense, repository{{/str}}</label>
<div class="col-8">
- <select class="form-control"></select>
+ <select class="custom-select"></select>
</div>
</div>
<div class="fp-path form-group row">
<label class="form-control-label col-4">{{#str}}path, repository{{/str}}</label>
<div class="col-8">
- <select class="form-control"></select>
+ <select class="custom-select"></select>
</div>
</div>
<div class="fp-original form-group row">
</div>
<div class="fp-login-select form-group">
<label class="form-control-label"></label>
- <select class="form-control"></select>
+ <select class="custom-select"></select>
</div>
<div class="fp-login-input form-group">
<label class="form-control-label"></label>
</div>
<div class="fp-setlicense form-group row">
<label class="col-form-label">{{#str}}chooselicense, repository{{/str}}</label>
- <select class="form-control"></select>
+ <select class="custom-select"></select>
</div>
<div class="form-group row">
<div class="fp-select-buttons">
</div>
<div class="fp-setlicense control-group">
<label>{{#str}}chooselicense, repository{{/str}}</label>
- <select class="form-control"></select>
+ <select class="custom-select"></select>
</div>
</div>
</form>
--- /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/>.
+
+/**
+ * Privacy tests for theme_boost.
+ *
+ * @package theme_boost
+ * @category test
+ * @copyright 2018 Adrian Greeve <adriangreeve.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+use \theme_boost\privacy\provider;
+
+/**
+ * Unit tests for theme_boost/classes/privacy/policy
+ *
+ * @copyright 2018 Adrian Greeve <adriangreeve.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class theme_boost_privacy_testcase extends \core_privacy\tests\provider_testcase {
+
+ /**
+ * Test for provider::test_export_user_preferences().
+ */
+ public function test_export_user_preferences() {
+ $this->resetAfterTest();
+
+ // Test setup.
+ $user = $this->getDataGenerator()->create_user();
+ $this->setUser($user);
+
+ // Add a user home page preference for the User.
+ set_user_preference(provider::DRAWER_OPEN_NAV, 'false', $user);
+
+ // Test the user preferences export contains 1 user preference record for the User.
+ provider::export_user_preferences($user->id);
+ $contextuser = context_user::instance($user->id);
+ $writer = \core_privacy\local\request\writer::with_context($contextuser);
+ $this->assertTrue($writer->has_any_data());
+
+ $exportedpreferences = $writer->get_user_preferences('theme_boost');
+ $this->assertCount(1, (array) $exportedpreferences);
+ $this->assertEquals('false', $exportedpreferences->{provider::DRAWER_OPEN_NAV}->value);
+ $this->assertEquals(get_string('privacy:drawernavclosed', 'theme_boost'),
+ $exportedpreferences->{provider::DRAWER_OPEN_NAV}->description);
+
+ // Add a user home page preference for the User.
+ set_user_preference(provider::DRAWER_OPEN_NAV, 'true', $user);
+
+ // Test the user preferences export contains 1 user preference record for the User.
+ provider::export_user_preferences($user->id);
+ $contextuser = context_user::instance($user->id);
+ $writer = \core_privacy\local\request\writer::with_context($contextuser);
+ $this->assertTrue($writer->has_any_data());
+
+ $exportedpreferences = $writer->get_user_preferences('theme_boost');
+ $this->assertCount(1, (array) $exportedpreferences);
+ $this->assertEquals('true', $exportedpreferences->{provider::DRAWER_OPEN_NAV}->value);
+ $this->assertEquals(get_string('privacy:drawernavopen', 'theme_boost'),
+ $exportedpreferences->{provider::DRAWER_OPEN_NAV}->description);
+ }
+}
// Due to lessphp limitations, the following classes must be declared statically without use of a loop.
+.p-0 {
+ padding: 0 !important;
+}
.p-l-1 {
padding-left: 1 * @baseFontSize !important;
}
padding: 1 * @baseFontSize !important;
}
+.m-0 {
+ margin: 0 !important;
+}
+
.m-l-1 {
margin-left: 1 * @baseFontSize !important;
}
.bg-faded {
background-color: @grayLighter;
}
+
+.w-100 {
+ width: 100%;
+}
#page-mod-forum-search .c1 {
.form-horizontal .controls
}
-
+.form-inline label:not(.sr-only):not(.accesshide) + select {
+ margin-left: 0.5rem;
+}
.formsettingheading {
.form-horizontal .help-block
#page-mod-forum-search .c1:first-child {
*padding-left: 200px;
}
+.form-inline label:not(.sr-only):not(.accesshide) + select {
+ margin-left: 0.5rem;
+}
.formsettingheading {
margin-bottom: 0;
}
.card-block {
padding: 1.25rem;
}
+.p-0 {
+ padding: 0 !important;
+}
.p-l-1 {
padding-left: 14px !important;
}
.p-a-1 {
padding: 14px !important;
}
+.m-0 {
+ margin: 0 !important;
+}
.m-l-1 {
margin-left: 14px !important;
}
.bg-faded {
background-color: #eee;
}
+.w-100 {
+ width: 100%;
+}