From 644671430525d19ec2aecf748f6ef68f4f1492e5 Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Mon, 13 Nov 2023 09:27:06 +0000 Subject: [PATCH 01/62] MDL-80088 mod_imscp: better uploaded package filetype validation. --- lib/classes/filetypes.php | 1 + mod/imscp/mod_form.php | 18 ++++-------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/lib/classes/filetypes.php b/lib/classes/filetypes.php index cd91993f6cd4a..3b0be4b591b17 100644 --- a/lib/classes/filetypes.php +++ b/lib/classes/filetypes.php @@ -127,6 +127,7 @@ protected static function get_default_types() { 'ico' => array('type' => 'image/vnd.microsoft.icon', 'icon' => 'image', 'groups' => array('image'), 'string' => 'image'), 'ics' => array('type' => 'text/calendar', 'icon' => 'text'), + 'imscc' => array('type' => 'application/zip', 'icon' => 'archive', 'string' => 'archive'), 'isf' => array('type' => 'application/inspiration', 'icon' => 'isf'), 'ist' => array('type' => 'application/inspiration.template', 'icon' => 'isf'), 'java' => array('type' => 'text/plain', 'icon' => 'sourcecode'), diff --git a/mod/imscp/mod_form.php b/mod/imscp/mod_form.php index d93baf90add2e..a8a0eabb3945f 100644 --- a/mod/imscp/mod_form.php +++ b/mod/imscp/mod_form.php @@ -59,7 +59,9 @@ public function definition() { // IMS-CP file upload. $mform->addElement('header', 'content', get_string('contentheader', 'imscp')); $mform->setExpanded('content', true); - $mform->addElement('filepicker', 'package', get_string('packagefile', 'imscp')); + + $mform->addElement('filepicker', 'package', get_string('packagefile', 'imscp'), null, + ['accepted_types' => ['application/zip', '.imscc']]); $options = array('-1' => get_string('all'), '0' => get_string('no'), '1' => '1', '2' => '2', '5' => '5', '10' => '10', '20' => '20'); @@ -78,27 +80,15 @@ public function definition() { * @param array $files */ public function validation($data, $files) { - global $USER; - if ($errors = parent::validation($data, $files)) { return $errors; } - $usercontext = context_user::instance($USER->id); - $fs = get_file_storage(); - - if (!$files = $fs->get_area_files($usercontext->id, 'user', 'draft', $data['package'], 'id', false)) { + if (!$this->get_draft_files('package')) { if (!$this->current->instance) { $errors['package'] = get_string('required'); return $errors; } - } else { - $file = reset($files); - if ($file->get_mimetype() != 'application/zip') { - $errors['package'] = get_string('invalidfiletype', 'error', '', $file); - // Better delete current file, it is not usable anyway. - $fs->delete_area_files($usercontext->id, 'user', 'draft', $data['package']); - } } return $errors; From 26df8c5b90d18bfe71043231e72951f2c4fd862b Mon Sep 17 00:00:00 2001 From: Stevani Andolo Date: Tue, 27 Feb 2024 12:09:24 +0800 Subject: [PATCH 02/62] MDL-80827 tool_xmldb: Fixed xmldb editor broken with PHP 8.1 --- .../xmldb/actions/edit_field_save/edit_field_save.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/tool/xmldb/actions/edit_field_save/edit_field_save.class.php b/admin/tool/xmldb/actions/edit_field_save/edit_field_save.class.php index 40bbeee44c910..67ddf045edf19 100644 --- a/admin/tool/xmldb/actions/edit_field_save/edit_field_save.class.php +++ b/admin/tool/xmldb/actions/edit_field_save/edit_field_save.class.php @@ -91,12 +91,12 @@ function invoke() { $comment = trim($comment); $type = required_param('type', PARAM_INT); - $length = strtolower(optional_param('length', NULL, PARAM_ALPHANUM)); + $length = optional_param('length', null, PARAM_INT); $decimals = optional_param('decimals', NULL, PARAM_INT); $notnull = optional_param('notnull', false, PARAM_BOOL); $sequence = optional_param('sequence', false, PARAM_BOOL); $default = optional_param('default', NULL, PARAM_PATH); - $default = trim($default); + $default = is_null($default) ? $default : trim($default); $editeddir = $XMLDB->editeddirs[$dirpath]; $structure = $editeddir->xml_file->getStructure(); From 9497db441a4178f4b5a97579a6ba230039ae7493 Mon Sep 17 00:00:00 2001 From: Laurent David Date: Thu, 1 Feb 2024 09:35:26 +0100 Subject: [PATCH 03/62] MDL-79829 core: Reactive drag and drop cursor * When dragging and dropping an item, the cursor should be set to "move" and not copy. --- lib/amd/build/local/reactive/dragdrop.min.js | 2 +- lib/amd/build/local/reactive/dragdrop.min.js.map | 2 +- lib/amd/src/local/reactive/dragdrop.js | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/amd/build/local/reactive/dragdrop.min.js b/lib/amd/build/local/reactive/dragdrop.min.js index 168c501e59ff5..7b98c099689da 100644 --- a/lib/amd/build/local/reactive/dragdrop.min.js +++ b/lib/amd/build/local/reactive/dragdrop.min.js @@ -62,6 +62,6 @@ define("core/local/reactive/dragdrop",["exports","core/local/reactive/basecompon * @class core/local/reactive/dragdrop * @copyright 2021 Ferran Recio * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_basecomponent=(obj=_basecomponent)&&obj.__esModule?obj:{default:obj};let activeDropData=new Map,dragStartPoint={};class _default extends _basecomponent.default{create(parent){var _parent$name,_parent$classes,_this$parent$draggabl,_this$parent$relative;this.name="".concat(null!==(_parent$name=parent.name)&&void 0!==_parent$name?_parent$name:"unkown","_dragdrop"),this.classes=Object.assign({BODYDRAGGING:"dragging",DRAGGABLEREADY:"draggable",DROPREADY:"dropready",DRAGOVER:"dragover",DRAGGING:"dragging",DROPUP:"drop-up",DROPDOWN:"drop-down",DROPZONE:"drop-zone",DRAGICON:"dragicon"},null!==(_parent$classes=null==parent?void 0:parent.classes)&&void 0!==_parent$classes?_parent$classes:{}),this.fullregion=parent.fullregion,this.parent=parent,this.autoconfigDraggable=null===(_this$parent$draggabl=this.parent.draggable)||void 0===_this$parent$draggabl||_this$parent$draggabl,this.relativeDrag=null!==(_this$parent$relative=this.parent.relativeDrag)&&void 0!==_this$parent$relative&&_this$parent$relative,this.entercount=0,this.dropzonevisible=!1,this.ismouseover=!1}getClasses(){return this.classes}isDropzoneVisible(){return this.dropzonevisible}stateReady(){"function"==typeof this.parent.validateDropData&&(this.element.classList.add(this.classes.DROPREADY),this.addEventListener(this.element,"dragenter",this._dragEnter),this.addEventListener(this.element,"dragleave",this._dragLeave),this.addEventListener(this.element,"dragover",this._dragOver),this.addEventListener(this.element,"drop",this._drop),this.addEventListener(this.element,"mouseover",this._mouseOver),this.addEventListener(this.element,"mouseleave",this._mouseLeave)),this.autoconfigDraggable&&"function"==typeof this.parent.getDraggableData&&this.setDraggable(!0)}setDraggable(value){if("function"!=typeof this.parent.getDraggableData)throw new Error("Draggable components must have a getDraggableData method");this.element.setAttribute("draggable",value),value?(this.addEventListener(this.element,"dragstart",this._dragStart),this.addEventListener(this.element,"dragend",this._dragEnd),this.element.classList.add(this.classes.DRAGGABLEREADY)):(this.removeEventListener(this.element,"dragstart",this._dragStart),this.removeEventListener(this.element,"dragend",this._dragEnd),this.element.classList.remove(this.classes.DRAGGABLEREADY))}_mouseOver(){this.ismouseover=!0}_mouseLeave(){this.ismouseover=!1}_dragStart(event){var _this$fullregion;if(document.activeElement.matches("textarea, input"))return void event.preventDefault();const dropdata=this.parent.getDraggableData();if(!dropdata)return;dragStartPoint={pageX:event.pageX,pageY:event.pageY},event.stopPropagation(),activeDropData.set(this.reactive,dropdata),document.body.classList.add(this.classes.BODYDRAGGING),this.element.classList.add(this.classes.DRAGGING),null===(_this$fullregion=this.fullregion)||void 0===_this$fullregion||_this$fullregion.classList.add(this.classes.DRAGGING);let dragImage=this.element;if(void 0!==this.parent.setDragImage){const customImage=this.parent.setDragImage(dropdata,event);customImage&&(dragImage=customImage)}const position={x:0,y:0};this.relativeDrag&&(position.x=event.offsetX,position.y=event.offsetY),event.dataTransfer.setDragImage(dragImage,position.x,position.y),this._callParentMethod("dragStart",dropdata,event)}_dragEnd(event){var _this$fullregion2;const dropdata=activeDropData.get(this.reactive);dropdata&&(activeDropData.delete(this.reactive),document.body.classList.remove(this.classes.BODYDRAGGING),this.element.classList.remove(this.classes.DRAGGING),null===(_this$fullregion2=this.fullregion)||void 0===_this$fullregion2||_this$fullregion2.classList.remove(this.classes.DRAGGING),this.removeAllOverlays(),this._addEventTotalMovement(event),this._callParentMethod("dragEnd",dropdata,event))}_dragEnter(event){const dropdata=this._processEvent(event);dropdata&&(this.entercount++,this.element.classList.add(this.classes.DRAGOVER),1!=this.entercount||this.dropzonevisible||(this.dropzonevisible=!0,this.element.classList.add(this.classes.DRAGOVER),this._callParentMethod("showDropZone",dropdata,event)))}_dragOver(event){const dropdata=this._processEvent(event);dropdata&&!this.dropzonevisible&&(this.dropzonevisible=!0,this.element.classList.add(this.classes.DRAGOVER),this._callParentMethod("showDropZone",dropdata,event))}_dragLeave(event){const dropdata=this._processEvent(event);dropdata&&(this.entercount--,this.entercount<=0&&this.dropzonevisible&&(this.dropzonevisible=!1,this.element.classList.remove(this.classes.DRAGOVER),this._callParentMethod("hideDropZone",dropdata,event)))}_drop(event){const dropdata=this._processEvent(event);dropdata&&(this.entercount=0,this.dropzonevisible&&(this.dropzonevisible=!1,this._callParentMethod("hideDropZone",dropdata,event)),this.element.classList.remove(this.classes.DRAGOVER),this.removeAllOverlays(),this._callParentMethod("drop",dropdata,event),dragStartPoint={})}_processEvent(event){const dropdata=this._getDropData(event);return dropdata&&this.parent.validateDropData(dropdata)?(event.preventDefault(),event.stopPropagation(),this._addEventTotalMovement(event),dropdata):null}_addEventTotalMovement(event){if(void 0===dragStartPoint.pageX||void 0===event.pageX)return;event.fixedMovementX=event.pageX-dragStartPoint.pageX,event.fixedMovementY=event.pageY-dragStartPoint.pageY,event.initialPageX=dragStartPoint.pageX,event.initialPageY=dragStartPoint.pageY;const current=this.element.getBoundingClientRect();if(event.newFixedTop=current.top+event.fixedMovementY,event.newFixedLeft=current.left+event.fixedMovementX,void 0!==this.fullregion){const current=this.fullregion.getBoundingClientRect();event.newRegionFixedxTop=current.top+event.fixedMovementY,event.newRegionFixedxLeft=current.left+event.fixedMovementX}}_callParentMethod(methodname,dropdata,event){"function"==typeof this.parent[methodname]&&this.parent[methodname](dropdata,event)}_getDropData(event){return this._isOnlyFilesDragging=this._containsOnlyFiles(event),this._isOnlyFilesDragging?void 0!==this.reactive.getFilesDraggableData&&"function"==typeof this.reactive.getFilesDraggableData?this.reactive.getFilesDraggableData(event.dataTransfer):void 0:activeDropData.get(this.reactive)}_containsOnlyFiles(event){return!!event.dataTransfer.types.includes("Files")&&event.dataTransfer.types.every((type=>"text/uri-list"!=type.toLowerCase()&&"text/html"!=type.toLowerCase()&&"text/plain"!=type.toLowerCase()))}}return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_basecomponent=(obj=_basecomponent)&&obj.__esModule?obj:{default:obj};let activeDropData=new Map,dragStartPoint={};class _default extends _basecomponent.default{create(parent){var _parent$name,_parent$classes,_this$parent$draggabl,_this$parent$relative;this.name="".concat(null!==(_parent$name=parent.name)&&void 0!==_parent$name?_parent$name:"unkown","_dragdrop"),this.classes=Object.assign({BODYDRAGGING:"dragging",DRAGGABLEREADY:"draggable",DROPREADY:"dropready",DRAGOVER:"dragover",DRAGGING:"dragging",DROPUP:"drop-up",DROPDOWN:"drop-down",DROPZONE:"drop-zone",DRAGICON:"dragicon"},null!==(_parent$classes=null==parent?void 0:parent.classes)&&void 0!==_parent$classes?_parent$classes:{}),this.fullregion=parent.fullregion,this.parent=parent,this.autoconfigDraggable=null===(_this$parent$draggabl=this.parent.draggable)||void 0===_this$parent$draggabl||_this$parent$draggabl,this.relativeDrag=null!==(_this$parent$relative=this.parent.relativeDrag)&&void 0!==_this$parent$relative&&_this$parent$relative,this.entercount=0,this.dropzonevisible=!1,this.ismouseover=!1}getClasses(){return this.classes}isDropzoneVisible(){return this.dropzonevisible}stateReady(){"function"==typeof this.parent.validateDropData&&(this.element.classList.add(this.classes.DROPREADY),this.addEventListener(this.element,"dragenter",this._dragEnter),this.addEventListener(this.element,"dragleave",this._dragLeave),this.addEventListener(this.element,"dragover",this._dragOver),this.addEventListener(this.element,"drop",this._drop),this.addEventListener(this.element,"mouseover",this._mouseOver),this.addEventListener(this.element,"mouseleave",this._mouseLeave)),this.autoconfigDraggable&&"function"==typeof this.parent.getDraggableData&&this.setDraggable(!0)}setDraggable(value){if("function"!=typeof this.parent.getDraggableData)throw new Error("Draggable components must have a getDraggableData method");this.element.setAttribute("draggable",value),value?(this.addEventListener(this.element,"dragstart",this._dragStart),this.addEventListener(this.element,"dragend",this._dragEnd),this.element.classList.add(this.classes.DRAGGABLEREADY)):(this.removeEventListener(this.element,"dragstart",this._dragStart),this.removeEventListener(this.element,"dragend",this._dragEnd),this.element.classList.remove(this.classes.DRAGGABLEREADY))}_mouseOver(){this.ismouseover=!0}_mouseLeave(){this.ismouseover=!1}_dragStart(event){var _this$fullregion;if(document.activeElement.matches("textarea, input"))return void event.preventDefault();const dropdata=this.parent.getDraggableData();if(!dropdata)return;dragStartPoint={pageX:event.pageX,pageY:event.pageY},event.stopPropagation(),activeDropData.set(this.reactive,dropdata),document.body.classList.add(this.classes.BODYDRAGGING),this.element.classList.add(this.classes.DRAGGING),null===(_this$fullregion=this.fullregion)||void 0===_this$fullregion||_this$fullregion.classList.add(this.classes.DRAGGING);let dragImage=this.element;if(void 0!==this.parent.setDragImage){const customImage=this.parent.setDragImage(dropdata,event);customImage&&(dragImage=customImage)}const position={x:0,y:0};this.relativeDrag&&(position.x=event.offsetX,position.y=event.offsetY),event.dataTransfer.setDragImage(dragImage,position.x,position.y),event.dataTransfer.effectAllowed="copyMove",this._callParentMethod("dragStart",dropdata,event)}_dragEnd(event){var _this$fullregion2;const dropdata=activeDropData.get(this.reactive);dropdata&&(activeDropData.delete(this.reactive),document.body.classList.remove(this.classes.BODYDRAGGING),this.element.classList.remove(this.classes.DRAGGING),null===(_this$fullregion2=this.fullregion)||void 0===_this$fullregion2||_this$fullregion2.classList.remove(this.classes.DRAGGING),this.removeAllOverlays(),this._addEventTotalMovement(event),this._callParentMethod("dragEnd",dropdata,event))}_dragEnter(event){const dropdata=this._processEvent(event);dropdata&&(this.entercount++,this.element.classList.add(this.classes.DRAGOVER),1!=this.entercount||this.dropzonevisible||(this.dropzonevisible=!0,this.element.classList.add(this.classes.DRAGOVER),this._callParentMethod("showDropZone",dropdata,event)))}_dragOver(event){const dropdata=this._processEvent(event);event.dataTransfer.dropEffect=event.altKey?"copy":"move",dropdata&&!this.dropzonevisible&&(this.dropzonevisible=!0,this.element.classList.add(this.classes.DRAGOVER),this._callParentMethod("showDropZone",dropdata,event))}_dragLeave(event){const dropdata=this._processEvent(event);dropdata&&(this.entercount--,this.entercount<=0&&this.dropzonevisible&&(this.dropzonevisible=!1,this.element.classList.remove(this.classes.DRAGOVER),this._callParentMethod("hideDropZone",dropdata,event)))}_drop(event){const dropdata=this._processEvent(event);dropdata&&(this.entercount=0,this.dropzonevisible&&(this.dropzonevisible=!1,this._callParentMethod("hideDropZone",dropdata,event)),this.element.classList.remove(this.classes.DRAGOVER),this.removeAllOverlays(),this._callParentMethod("drop",dropdata,event),dragStartPoint={})}_processEvent(event){const dropdata=this._getDropData(event);return dropdata&&this.parent.validateDropData(dropdata)?(event.preventDefault(),event.stopPropagation(),this._addEventTotalMovement(event),dropdata):null}_addEventTotalMovement(event){if(void 0===dragStartPoint.pageX||void 0===event.pageX)return;event.fixedMovementX=event.pageX-dragStartPoint.pageX,event.fixedMovementY=event.pageY-dragStartPoint.pageY,event.initialPageX=dragStartPoint.pageX,event.initialPageY=dragStartPoint.pageY;const current=this.element.getBoundingClientRect();if(event.newFixedTop=current.top+event.fixedMovementY,event.newFixedLeft=current.left+event.fixedMovementX,void 0!==this.fullregion){const current=this.fullregion.getBoundingClientRect();event.newRegionFixedxTop=current.top+event.fixedMovementY,event.newRegionFixedxLeft=current.left+event.fixedMovementX}}_callParentMethod(methodname,dropdata,event){"function"==typeof this.parent[methodname]&&this.parent[methodname](dropdata,event)}_getDropData(event){return this._isOnlyFilesDragging=this._containsOnlyFiles(event),this._isOnlyFilesDragging?void 0!==this.reactive.getFilesDraggableData&&"function"==typeof this.reactive.getFilesDraggableData?this.reactive.getFilesDraggableData(event.dataTransfer):void 0:activeDropData.get(this.reactive)}_containsOnlyFiles(event){return!!event.dataTransfer.types.includes("Files")&&event.dataTransfer.types.every((type=>"text/uri-list"!=type.toLowerCase()&&"text/html"!=type.toLowerCase()&&"text/plain"!=type.toLowerCase()))}}return _exports.default=_default,_exports.default})); //# sourceMappingURL=dragdrop.min.js.map \ No newline at end of file diff --git a/lib/amd/build/local/reactive/dragdrop.min.js.map b/lib/amd/build/local/reactive/dragdrop.min.js.map index 0a814846d6888..7d80dbc80dcfd 100644 --- a/lib/amd/build/local/reactive/dragdrop.min.js.map +++ b/lib/amd/build/local/reactive/dragdrop.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dragdrop.min.js","sources":["../../../src/local/reactive/dragdrop.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Drag and drop helper component.\n *\n * This component is used to delegate drag and drop handling.\n *\n * To delegate the logic to this particular element the component should create a new instance\n * passing \"this\" as param. The component will use all the necessary callbacks and add all the\n * necessary listeners to the component element.\n *\n * Component attributes used by dragdrop module:\n * - element: the draggable or dropzone element.\n * - (optional) classes: object with alternative CSS classes\n * - (optional) fullregion: page element affeted by the elementy dragging. Use this attribute if\n * the draggable element affects a bigger region (for example a draggable\n * title).\n * - (optional) autoconfigDraggable: by default, the component will be draggable if it has a\n * getDraggableData method. If this value is false draggable\n * property must be defined using setDraggable method.\n * - (optional) relativeDrag: by default the drag image is located at point (0,0) relative to the\n * mouse position to prevent the mouse from covering it. If this attribute\n * is true the drag image will be located at the click offset.\n *\n * Methods the parent component should have for making it draggable:\n *\n * - getDraggableData(): Object|data\n * Return the data that will be passed to any valid dropzone while it is dragged.\n * If the component has this method, the dragdrop module will enable the dragging,\n * this is the only required method for dragging.\n * If at the dragging moment this method returns a false|null|undefined, the dragging\n * actions won't be captured.\n *\n * - (optional) dragStart(Object dropdata, Event event): void\n * - (optional) dragEnd(Object dropdata, Event event): void\n * Callbacks dragdrop will call when the element is dragged and getDraggableData\n * return some data.\n *\n * Methods the parent component should have for enabling it as a dropzone:\n *\n * - validateDropData(Object dropdata): boolean\n * If that method exists, the dragdrop module will automathically configure the element as dropzone.\n * This method will return true if the dropdata is accepted. In case it returns false, no drag and\n * drop event will be listened for this specific dragged dropdata.\n *\n * - (Optional) showDropZone(Object dropdata, Event event): void\n * - (Optional) hideDropZone(Object dropdata, Event event): void\n * Methods called when a valid dragged data pass over the element.\n *\n * - (Optional) drop(Object dropdata, Event event): void\n * Called when a valid dragged element is dropped over the element.\n *\n * Note that none of this methods will be called if validateDropData\n * returns a false value.\n *\n * This module will also add or remove several CSS classes from both dragged elements and dropzones.\n * See the \"this.classes\" in the create method for more details. In case the parent component wants\n * to use the same classes, it can use the getClasses method. On the other hand, if the parent\n * component has an alternative \"classes\" attribute, this will override the default drag and drop\n * classes.\n *\n * @module core/local/reactive/dragdrop\n * @class core/local/reactive/dragdrop\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BaseComponent from 'core/local/reactive/basecomponent';\n\n// Map with the dragged element generate by an specific reactive applications.\n// Potentially, any component can generate a draggable element to interact with other\n// page elements. However, the dragged data is specific and could only interact with\n// components of the same reactive instance.\nlet activeDropData = new Map();\n\n// Drag & Drop API provides the final drop point and incremental movements but we can\n// provide also starting points and displacements. Absolute displacements simplifies\n// moving components with aboslute position around the page.\nlet dragStartPoint = {};\n\nexport default class extends BaseComponent {\n\n /**\n * Constructor hook.\n *\n * @param {BaseComponent} parent the parent component.\n */\n create(parent) {\n // Optional component name for debugging.\n this.name = `${parent.name ?? 'unkown'}_dragdrop`;\n\n // Default drag and drop classes.\n this.classes = Object.assign(\n {\n // This class indicate a dragging action is active at a page level.\n BODYDRAGGING: 'dragging',\n\n // Added when draggable and drop are ready.\n DRAGGABLEREADY: 'draggable',\n DROPREADY: 'dropready',\n\n // When a valid drag element is over the element.\n DRAGOVER: 'dragover',\n // When a the component is dragged.\n DRAGGING: 'dragging',\n\n // Dropzones classes names.\n DROPUP: 'drop-up',\n DROPDOWN: 'drop-down',\n DROPZONE: 'drop-zone',\n\n // Drag icon class.\n DRAGICON: 'dragicon',\n },\n parent?.classes ?? {}\n );\n\n // Add the affected region if any.\n this.fullregion = parent.fullregion;\n\n // Keep parent to execute drap and drop handlers.\n this.parent = parent;\n\n // Check if parent handle draggable manually.\n this.autoconfigDraggable = this.parent.draggable ?? true;\n\n // Drag image relative position.\n this.relativeDrag = this.parent.relativeDrag ?? false;\n\n // Sub HTML elements will trigger extra dragEnter and dragOver all the time.\n // To prevent that from affecting dropzones, we need to count the enters and leaves.\n this.entercount = 0;\n\n // Stores if the droparea is shown or not.\n this.dropzonevisible = false;\n\n // Stores if the mouse is over the element or not.\n this.ismouseover = false;\n }\n\n /**\n * Return the component drag and drop CSS classes.\n *\n * @returns {Object} the dragdrop css classes\n */\n getClasses() {\n return this.classes;\n }\n\n /**\n * Return the current drop-zone visible of the element.\n *\n * @returns {boolean} if the dropzone should be visible or not\n */\n isDropzoneVisible() {\n return this.dropzonevisible;\n }\n\n /**\n * Initial state ready method.\n *\n * This method will add all the necessary event listeners to the component depending on the\n * parent methods.\n * - Add drop events to the element if the parent component has validateDropData method.\n * - Configure the elements draggable if the parent component has getDraggableData method.\n */\n stateReady() {\n // Add drop events to the element if the parent component has dropable types.\n if (typeof this.parent.validateDropData === 'function') {\n this.element.classList.add(this.classes.DROPREADY);\n this.addEventListener(this.element, 'dragenter', this._dragEnter);\n this.addEventListener(this.element, 'dragleave', this._dragLeave);\n this.addEventListener(this.element, 'dragover', this._dragOver);\n this.addEventListener(this.element, 'drop', this._drop);\n this.addEventListener(this.element, 'mouseover', this._mouseOver);\n this.addEventListener(this.element, 'mouseleave', this._mouseLeave);\n }\n\n // Configure the elements draggable if the parent component has dragable data.\n if (this.autoconfigDraggable && typeof this.parent.getDraggableData === 'function') {\n this.setDraggable(true);\n }\n }\n\n /**\n * Enable or disable the draggable property.\n *\n * @param {bool} value the new draggable value\n */\n setDraggable(value) {\n if (typeof this.parent.getDraggableData !== 'function') {\n throw new Error(`Draggable components must have a getDraggableData method`);\n }\n this.element.setAttribute('draggable', value);\n if (value) {\n this.addEventListener(this.element, 'dragstart', this._dragStart);\n this.addEventListener(this.element, 'dragend', this._dragEnd);\n this.element.classList.add(this.classes.DRAGGABLEREADY);\n } else {\n this.removeEventListener(this.element, 'dragstart', this._dragStart);\n this.removeEventListener(this.element, 'dragend', this._dragEnd);\n this.element.classList.remove(this.classes.DRAGGABLEREADY);\n }\n }\n\n /**\n * Mouse over handle.\n */\n _mouseOver() {\n this.ismouseover = true;\n }\n\n /**\n * Mouse leave handler.\n */\n _mouseLeave() {\n this.ismouseover = false;\n }\n\n /**\n * Drag start event handler.\n *\n * This method will generate the current dropable data. This data is the one used to determine\n * if a droparea accepts the dropping or not.\n *\n * @param {Event} event the event.\n */\n _dragStart(event) {\n // Cancel dragging if any editable form element is focussed.\n if (document.activeElement.matches(`textarea, input`)) {\n event.preventDefault();\n return;\n }\n\n const dropdata = this.parent.getDraggableData();\n if (!dropdata) {\n return;\n }\n\n // Save the starting point.\n dragStartPoint = {\n pageX: event.pageX,\n pageY: event.pageY,\n };\n\n // If the drag event is accepted we prevent any other draggable element from interfiering.\n event.stopPropagation();\n\n // Save the drop data of the current reactive intance.\n activeDropData.set(this.reactive, dropdata);\n\n // Add some CSS classes to indicate the state.\n document.body.classList.add(this.classes.BODYDRAGGING);\n this.element.classList.add(this.classes.DRAGGING);\n this.fullregion?.classList.add(this.classes.DRAGGING);\n\n // Force the drag image. This makes the UX more consistent in case the\n // user dragged an internal element like a link or some other element.\n let dragImage = this.element;\n if (this.parent.setDragImage !== undefined) {\n const customImage = this.parent.setDragImage(dropdata, event);\n if (customImage) {\n dragImage = customImage;\n }\n }\n // Define the image position relative to the mouse.\n const position = {x: 0, y: 0};\n if (this.relativeDrag) {\n position.x = event.offsetX;\n position.y = event.offsetY;\n }\n event.dataTransfer.setDragImage(dragImage, position.x, position.y);\n\n this._callParentMethod('dragStart', dropdata, event);\n }\n\n /**\n * Drag end event handler.\n *\n * @param {Event} event the event.\n */\n _dragEnd(event) {\n const dropdata = activeDropData.get(this.reactive);\n if (!dropdata) {\n return;\n }\n\n // Remove the current dropdata.\n activeDropData.delete(this.reactive);\n\n // Remove the dragging classes.\n document.body.classList.remove(this.classes.BODYDRAGGING);\n this.element.classList.remove(this.classes.DRAGGING);\n this.fullregion?.classList.remove(this.classes.DRAGGING);\n this.removeAllOverlays();\n\n // We add the total movement to the event in case the component\n // wants to move its absolute position.\n this._addEventTotalMovement(event);\n\n this._callParentMethod('dragEnd', dropdata, event);\n }\n\n /**\n * Drag enter event handler.\n *\n * The JS drag&drop API triggers several dragenter events on the same element because it bubbles the\n * child events as well. To prevent this form affecting the dropzones display, this methods use\n * \"entercount\" to determine if it's one extra child event or a valid one.\n *\n * @param {Event} event the event.\n */\n _dragEnter(event) {\n const dropdata = this._processEvent(event);\n if (dropdata) {\n this.entercount++;\n this.element.classList.add(this.classes.DRAGOVER);\n if (this.entercount == 1 && !this.dropzonevisible) {\n this.dropzonevisible = true;\n this.element.classList.add(this.classes.DRAGOVER);\n this._callParentMethod('showDropZone', dropdata, event);\n }\n }\n }\n\n /**\n * Drag over event handler.\n *\n * We only use dragover event when a draggable action starts inside a valid dropzone. In those cases\n * the API won't trigger any dragEnter because the dragged alement was already there. We use the\n * dropzonevisible to determine if the component needs to display the dropzones or not.\n *\n * @param {Event} event the event.\n */\n _dragOver(event) {\n const dropdata = this._processEvent(event);\n if (dropdata && !this.dropzonevisible) {\n this.dropzonevisible = true;\n this.element.classList.add(this.classes.DRAGOVER);\n this._callParentMethod('showDropZone', dropdata, event);\n }\n }\n\n /**\n * Drag over leave handler.\n *\n * The JS drag&drop API triggers several dragleave events on the same element because it bubbles the\n * child events as well. To prevent this form affecting the dropzones display, this methods use\n * \"entercount\" to determine if it's one extra child event or a valid one.\n *\n * @param {Event} event the event.\n */\n _dragLeave(event) {\n const dropdata = this._processEvent(event);\n if (dropdata) {\n this.entercount--;\n if (this.entercount <= 0 && this.dropzonevisible) {\n this.dropzonevisible = false;\n this.element.classList.remove(this.classes.DRAGOVER);\n this._callParentMethod('hideDropZone', dropdata, event);\n }\n }\n }\n\n /**\n * Drop event handler.\n *\n * This method will call both hideDropZones and drop methods on the parent component.\n *\n * @param {Event} event the event.\n */\n _drop(event) {\n const dropdata = this._processEvent(event);\n if (dropdata) {\n this.entercount = 0;\n if (this.dropzonevisible) {\n this.dropzonevisible = false;\n this._callParentMethod('hideDropZone', dropdata, event);\n }\n this.element.classList.remove(this.classes.DRAGOVER);\n this.removeAllOverlays();\n this._callParentMethod('drop', dropdata, event);\n // An accepted drop resets the initial position.\n // Save the starting point.\n dragStartPoint = {};\n }\n }\n\n /**\n * Process a drag and drop event and delegate logic to the parent component.\n *\n * @param {Event} event the drag and drop event\n * @return {Object|false} the dropdata or null if the event should not be processed\n */\n _processEvent(event) {\n const dropdata = this._getDropData(event);\n if (!dropdata) {\n return null;\n }\n if (this.parent.validateDropData(dropdata)) {\n // All accepted drag&drop event must prevent bubbling and defaults, otherwise\n // parent dragdrop instances could capture it by mistake.\n event.preventDefault();\n event.stopPropagation();\n this._addEventTotalMovement(event);\n return dropdata;\n }\n return null;\n }\n\n /**\n * Add the total amout of movement to a mouse event.\n *\n * @param {MouseEvent} event\n */\n _addEventTotalMovement(event) {\n if (dragStartPoint.pageX === undefined || event.pageX === undefined) {\n return;\n }\n event.fixedMovementX = event.pageX - dragStartPoint.pageX;\n event.fixedMovementY = event.pageY - dragStartPoint.pageY;\n event.initialPageX = dragStartPoint.pageX;\n event.initialPageY = dragStartPoint.pageY;\n // The element possible new top.\n const current = this.element.getBoundingClientRect();\n // Add the new position fixed position.\n event.newFixedTop = current.top + event.fixedMovementY;\n event.newFixedLeft = current.left + event.fixedMovementX;\n // The affected region possible new top.\n if (this.fullregion !== undefined) {\n const current = this.fullregion.getBoundingClientRect();\n event.newRegionFixedxTop = current.top + event.fixedMovementY;\n event.newRegionFixedxLeft = current.left + event.fixedMovementX;\n }\n }\n\n /**\n * Convenient method for calling parent component functions if present.\n *\n * @param {string} methodname the name of the method\n * @param {Object} dropdata the current drop data object\n * @param {Event} event the original event\n */\n _callParentMethod(methodname, dropdata, event) {\n if (typeof this.parent[methodname] === 'function') {\n this.parent[methodname](dropdata, event);\n }\n }\n\n /**\n * Get the current dropdata for a specific event.\n *\n * The browser can generate drag&drop events related to several user interactions:\n * - Drag a page elements: this case is registered in the activeDropData map\n * - Drag some HTML selections: ignored for now\n * - Drag a file over the browser: file drag may appear in the future but for now they are ignored.\n *\n * @param {Event} event the original event.\n * @returns {Object|undefined} with the dragged data (or undefined if none)\n */\n _getDropData(event) {\n this._isOnlyFilesDragging = this._containsOnlyFiles(event);\n if (this._isOnlyFilesDragging) {\n // Check if the reactive instance can provide a files draggable data.\n if (this.reactive.getFilesDraggableData !== undefined && typeof this.reactive.getFilesDraggableData === 'function') {\n return this.reactive.getFilesDraggableData(event.dataTransfer);\n }\n return undefined;\n }\n return activeDropData.get(this.reactive);\n }\n\n /**\n * Check if the dragged event contains only files.\n *\n * Files dragging does not generate drop data because they came from outside the page and the component\n * must check it before validating the event.\n *\n * Some browsers like Firefox add extra types to file dragging. To discard the false positives\n * a double check is necessary.\n *\n * @param {Event} event the original event.\n * @returns {boolean} if the drag dataTransfers contains files.\n */\n _containsOnlyFiles(event) {\n if (!event.dataTransfer.types.includes('Files')) {\n return false;\n }\n return event.dataTransfer.types.every((type) => {\n return (type.toLowerCase() != 'text/uri-list'\n && type.toLowerCase() != 'text/html'\n && type.toLowerCase() != 'text/plain'\n );\n });\n }\n}\n"],"names":["activeDropData","Map","dragStartPoint","BaseComponent","create","parent","name","classes","Object","assign","BODYDRAGGING","DRAGGABLEREADY","DROPREADY","DRAGOVER","DRAGGING","DROPUP","DROPDOWN","DROPZONE","DRAGICON","fullregion","autoconfigDraggable","this","draggable","relativeDrag","entercount","dropzonevisible","ismouseover","getClasses","isDropzoneVisible","stateReady","validateDropData","element","classList","add","addEventListener","_dragEnter","_dragLeave","_dragOver","_drop","_mouseOver","_mouseLeave","getDraggableData","setDraggable","value","Error","setAttribute","_dragStart","_dragEnd","removeEventListener","remove","event","document","activeElement","matches","preventDefault","dropdata","pageX","pageY","stopPropagation","set","reactive","body","dragImage","undefined","setDragImage","customImage","position","x","y","offsetX","offsetY","dataTransfer","_callParentMethod","get","delete","removeAllOverlays","_addEventTotalMovement","_processEvent","_getDropData","fixedMovementX","fixedMovementY","initialPageX","initialPageY","current","getBoundingClientRect","newFixedTop","top","newFixedLeft","left","newRegionFixedxTop","newRegionFixedxLeft","methodname","_isOnlyFilesDragging","_containsOnlyFiles","getFilesDraggableData","types","includes","every","type","toLowerCase"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"dragdrop.min.js","sources":["../../../src/local/reactive/dragdrop.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Drag and drop helper component.\n *\n * This component is used to delegate drag and drop handling.\n *\n * To delegate the logic to this particular element the component should create a new instance\n * passing \"this\" as param. The component will use all the necessary callbacks and add all the\n * necessary listeners to the component element.\n *\n * Component attributes used by dragdrop module:\n * - element: the draggable or dropzone element.\n * - (optional) classes: object with alternative CSS classes\n * - (optional) fullregion: page element affeted by the elementy dragging. Use this attribute if\n * the draggable element affects a bigger region (for example a draggable\n * title).\n * - (optional) autoconfigDraggable: by default, the component will be draggable if it has a\n * getDraggableData method. If this value is false draggable\n * property must be defined using setDraggable method.\n * - (optional) relativeDrag: by default the drag image is located at point (0,0) relative to the\n * mouse position to prevent the mouse from covering it. If this attribute\n * is true the drag image will be located at the click offset.\n *\n * Methods the parent component should have for making it draggable:\n *\n * - getDraggableData(): Object|data\n * Return the data that will be passed to any valid dropzone while it is dragged.\n * If the component has this method, the dragdrop module will enable the dragging,\n * this is the only required method for dragging.\n * If at the dragging moment this method returns a false|null|undefined, the dragging\n * actions won't be captured.\n *\n * - (optional) dragStart(Object dropdata, Event event): void\n * - (optional) dragEnd(Object dropdata, Event event): void\n * Callbacks dragdrop will call when the element is dragged and getDraggableData\n * return some data.\n *\n * Methods the parent component should have for enabling it as a dropzone:\n *\n * - validateDropData(Object dropdata): boolean\n * If that method exists, the dragdrop module will automathically configure the element as dropzone.\n * This method will return true if the dropdata is accepted. In case it returns false, no drag and\n * drop event will be listened for this specific dragged dropdata.\n *\n * - (Optional) showDropZone(Object dropdata, Event event): void\n * - (Optional) hideDropZone(Object dropdata, Event event): void\n * Methods called when a valid dragged data pass over the element.\n *\n * - (Optional) drop(Object dropdata, Event event): void\n * Called when a valid dragged element is dropped over the element.\n *\n * Note that none of this methods will be called if validateDropData\n * returns a false value.\n *\n * This module will also add or remove several CSS classes from both dragged elements and dropzones.\n * See the \"this.classes\" in the create method for more details. In case the parent component wants\n * to use the same classes, it can use the getClasses method. On the other hand, if the parent\n * component has an alternative \"classes\" attribute, this will override the default drag and drop\n * classes.\n *\n * @module core/local/reactive/dragdrop\n * @class core/local/reactive/dragdrop\n * @copyright 2021 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BaseComponent from 'core/local/reactive/basecomponent';\n\n// Map with the dragged element generate by an specific reactive applications.\n// Potentially, any component can generate a draggable element to interact with other\n// page elements. However, the dragged data is specific and could only interact with\n// components of the same reactive instance.\nlet activeDropData = new Map();\n\n// Drag & Drop API provides the final drop point and incremental movements but we can\n// provide also starting points and displacements. Absolute displacements simplifies\n// moving components with aboslute position around the page.\nlet dragStartPoint = {};\n\nexport default class extends BaseComponent {\n\n /**\n * Constructor hook.\n *\n * @param {BaseComponent} parent the parent component.\n */\n create(parent) {\n // Optional component name for debugging.\n this.name = `${parent.name ?? 'unkown'}_dragdrop`;\n\n // Default drag and drop classes.\n this.classes = Object.assign(\n {\n // This class indicate a dragging action is active at a page level.\n BODYDRAGGING: 'dragging',\n\n // Added when draggable and drop are ready.\n DRAGGABLEREADY: 'draggable',\n DROPREADY: 'dropready',\n\n // When a valid drag element is over the element.\n DRAGOVER: 'dragover',\n // When a the component is dragged.\n DRAGGING: 'dragging',\n\n // Dropzones classes names.\n DROPUP: 'drop-up',\n DROPDOWN: 'drop-down',\n DROPZONE: 'drop-zone',\n\n // Drag icon class.\n DRAGICON: 'dragicon',\n },\n parent?.classes ?? {}\n );\n\n // Add the affected region if any.\n this.fullregion = parent.fullregion;\n\n // Keep parent to execute drap and drop handlers.\n this.parent = parent;\n\n // Check if parent handle draggable manually.\n this.autoconfigDraggable = this.parent.draggable ?? true;\n\n // Drag image relative position.\n this.relativeDrag = this.parent.relativeDrag ?? false;\n\n // Sub HTML elements will trigger extra dragEnter and dragOver all the time.\n // To prevent that from affecting dropzones, we need to count the enters and leaves.\n this.entercount = 0;\n\n // Stores if the droparea is shown or not.\n this.dropzonevisible = false;\n\n // Stores if the mouse is over the element or not.\n this.ismouseover = false;\n }\n\n /**\n * Return the component drag and drop CSS classes.\n *\n * @returns {Object} the dragdrop css classes\n */\n getClasses() {\n return this.classes;\n }\n\n /**\n * Return the current drop-zone visible of the element.\n *\n * @returns {boolean} if the dropzone should be visible or not\n */\n isDropzoneVisible() {\n return this.dropzonevisible;\n }\n\n /**\n * Initial state ready method.\n *\n * This method will add all the necessary event listeners to the component depending on the\n * parent methods.\n * - Add drop events to the element if the parent component has validateDropData method.\n * - Configure the elements draggable if the parent component has getDraggableData method.\n */\n stateReady() {\n // Add drop events to the element if the parent component has dropable types.\n if (typeof this.parent.validateDropData === 'function') {\n this.element.classList.add(this.classes.DROPREADY);\n this.addEventListener(this.element, 'dragenter', this._dragEnter);\n this.addEventListener(this.element, 'dragleave', this._dragLeave);\n this.addEventListener(this.element, 'dragover', this._dragOver);\n this.addEventListener(this.element, 'drop', this._drop);\n this.addEventListener(this.element, 'mouseover', this._mouseOver);\n this.addEventListener(this.element, 'mouseleave', this._mouseLeave);\n }\n\n // Configure the elements draggable if the parent component has dragable data.\n if (this.autoconfigDraggable && typeof this.parent.getDraggableData === 'function') {\n this.setDraggable(true);\n }\n }\n\n /**\n * Enable or disable the draggable property.\n *\n * @param {bool} value the new draggable value\n */\n setDraggable(value) {\n if (typeof this.parent.getDraggableData !== 'function') {\n throw new Error(`Draggable components must have a getDraggableData method`);\n }\n this.element.setAttribute('draggable', value);\n if (value) {\n this.addEventListener(this.element, 'dragstart', this._dragStart);\n this.addEventListener(this.element, 'dragend', this._dragEnd);\n this.element.classList.add(this.classes.DRAGGABLEREADY);\n } else {\n this.removeEventListener(this.element, 'dragstart', this._dragStart);\n this.removeEventListener(this.element, 'dragend', this._dragEnd);\n this.element.classList.remove(this.classes.DRAGGABLEREADY);\n }\n }\n\n /**\n * Mouse over handle.\n */\n _mouseOver() {\n this.ismouseover = true;\n }\n\n /**\n * Mouse leave handler.\n */\n _mouseLeave() {\n this.ismouseover = false;\n }\n\n /**\n * Drag start event handler.\n *\n * This method will generate the current dropable data. This data is the one used to determine\n * if a droparea accepts the dropping or not.\n *\n * @param {Event} event the event.\n */\n _dragStart(event) {\n // Cancel dragging if any editable form element is focussed.\n if (document.activeElement.matches(`textarea, input`)) {\n event.preventDefault();\n return;\n }\n\n const dropdata = this.parent.getDraggableData();\n if (!dropdata) {\n return;\n }\n\n // Save the starting point.\n dragStartPoint = {\n pageX: event.pageX,\n pageY: event.pageY,\n };\n\n // If the drag event is accepted we prevent any other draggable element from interfiering.\n event.stopPropagation();\n\n // Save the drop data of the current reactive intance.\n activeDropData.set(this.reactive, dropdata);\n\n // Add some CSS classes to indicate the state.\n document.body.classList.add(this.classes.BODYDRAGGING);\n this.element.classList.add(this.classes.DRAGGING);\n this.fullregion?.classList.add(this.classes.DRAGGING);\n\n // Force the drag image. This makes the UX more consistent in case the\n // user dragged an internal element like a link or some other element.\n let dragImage = this.element;\n if (this.parent.setDragImage !== undefined) {\n const customImage = this.parent.setDragImage(dropdata, event);\n if (customImage) {\n dragImage = customImage;\n }\n }\n // Define the image position relative to the mouse.\n const position = {x: 0, y: 0};\n if (this.relativeDrag) {\n position.x = event.offsetX;\n position.y = event.offsetY;\n }\n event.dataTransfer.setDragImage(dragImage, position.x, position.y);\n event.dataTransfer.effectAllowed = 'copyMove';\n this._callParentMethod('dragStart', dropdata, event);\n }\n\n /**\n * Drag end event handler.\n *\n * @param {Event} event the event.\n */\n _dragEnd(event) {\n const dropdata = activeDropData.get(this.reactive);\n if (!dropdata) {\n return;\n }\n\n // Remove the current dropdata.\n activeDropData.delete(this.reactive);\n\n // Remove the dragging classes.\n document.body.classList.remove(this.classes.BODYDRAGGING);\n this.element.classList.remove(this.classes.DRAGGING);\n this.fullregion?.classList.remove(this.classes.DRAGGING);\n this.removeAllOverlays();\n\n // We add the total movement to the event in case the component\n // wants to move its absolute position.\n this._addEventTotalMovement(event);\n\n this._callParentMethod('dragEnd', dropdata, event);\n }\n\n /**\n * Drag enter event handler.\n *\n * The JS drag&drop API triggers several dragenter events on the same element because it bubbles the\n * child events as well. To prevent this form affecting the dropzones display, this methods use\n * \"entercount\" to determine if it's one extra child event or a valid one.\n *\n * @param {Event} event the event.\n */\n _dragEnter(event) {\n const dropdata = this._processEvent(event);\n if (dropdata) {\n this.entercount++;\n this.element.classList.add(this.classes.DRAGOVER);\n if (this.entercount == 1 && !this.dropzonevisible) {\n this.dropzonevisible = true;\n this.element.classList.add(this.classes.DRAGOVER);\n this._callParentMethod('showDropZone', dropdata, event);\n }\n }\n }\n\n /**\n * Drag over event handler.\n *\n * We only use dragover event when a draggable action starts inside a valid dropzone. In those cases\n * the API won't trigger any dragEnter because the dragged alement was already there. We use the\n * dropzonevisible to determine if the component needs to display the dropzones or not.\n *\n * @param {Event} event the event.\n */\n _dragOver(event) {\n const dropdata = this._processEvent(event);\n event.dataTransfer.dropEffect = (event.altKey) ? 'copy' : 'move';\n if (dropdata && !this.dropzonevisible) {\n this.dropzonevisible = true;\n this.element.classList.add(this.classes.DRAGOVER);\n this._callParentMethod('showDropZone', dropdata, event);\n }\n }\n\n /**\n * Drag over leave handler.\n *\n * The JS drag&drop API triggers several dragleave events on the same element because it bubbles the\n * child events as well. To prevent this form affecting the dropzones display, this methods use\n * \"entercount\" to determine if it's one extra child event or a valid one.\n *\n * @param {Event} event the event.\n */\n _dragLeave(event) {\n const dropdata = this._processEvent(event);\n if (dropdata) {\n this.entercount--;\n if (this.entercount <= 0 && this.dropzonevisible) {\n this.dropzonevisible = false;\n this.element.classList.remove(this.classes.DRAGOVER);\n this._callParentMethod('hideDropZone', dropdata, event);\n }\n }\n }\n\n /**\n * Drop event handler.\n *\n * This method will call both hideDropZones and drop methods on the parent component.\n *\n * @param {Event} event the event.\n */\n _drop(event) {\n const dropdata = this._processEvent(event);\n if (dropdata) {\n this.entercount = 0;\n if (this.dropzonevisible) {\n this.dropzonevisible = false;\n this._callParentMethod('hideDropZone', dropdata, event);\n }\n this.element.classList.remove(this.classes.DRAGOVER);\n this.removeAllOverlays();\n this._callParentMethod('drop', dropdata, event);\n // An accepted drop resets the initial position.\n // Save the starting point.\n dragStartPoint = {};\n }\n }\n\n /**\n * Process a drag and drop event and delegate logic to the parent component.\n *\n * @param {Event} event the drag and drop event\n * @return {Object|false} the dropdata or null if the event should not be processed\n */\n _processEvent(event) {\n const dropdata = this._getDropData(event);\n if (!dropdata) {\n return null;\n }\n if (this.parent.validateDropData(dropdata)) {\n // All accepted drag&drop event must prevent bubbling and defaults, otherwise\n // parent dragdrop instances could capture it by mistake.\n event.preventDefault();\n event.stopPropagation();\n this._addEventTotalMovement(event);\n return dropdata;\n }\n return null;\n }\n\n /**\n * Add the total amout of movement to a mouse event.\n *\n * @param {MouseEvent} event\n */\n _addEventTotalMovement(event) {\n if (dragStartPoint.pageX === undefined || event.pageX === undefined) {\n return;\n }\n event.fixedMovementX = event.pageX - dragStartPoint.pageX;\n event.fixedMovementY = event.pageY - dragStartPoint.pageY;\n event.initialPageX = dragStartPoint.pageX;\n event.initialPageY = dragStartPoint.pageY;\n // The element possible new top.\n const current = this.element.getBoundingClientRect();\n // Add the new position fixed position.\n event.newFixedTop = current.top + event.fixedMovementY;\n event.newFixedLeft = current.left + event.fixedMovementX;\n // The affected region possible new top.\n if (this.fullregion !== undefined) {\n const current = this.fullregion.getBoundingClientRect();\n event.newRegionFixedxTop = current.top + event.fixedMovementY;\n event.newRegionFixedxLeft = current.left + event.fixedMovementX;\n }\n }\n\n /**\n * Convenient method for calling parent component functions if present.\n *\n * @param {string} methodname the name of the method\n * @param {Object} dropdata the current drop data object\n * @param {Event} event the original event\n */\n _callParentMethod(methodname, dropdata, event) {\n if (typeof this.parent[methodname] === 'function') {\n this.parent[methodname](dropdata, event);\n }\n }\n\n /**\n * Get the current dropdata for a specific event.\n *\n * The browser can generate drag&drop events related to several user interactions:\n * - Drag a page elements: this case is registered in the activeDropData map\n * - Drag some HTML selections: ignored for now\n * - Drag a file over the browser: file drag may appear in the future but for now they are ignored.\n *\n * @param {Event} event the original event.\n * @returns {Object|undefined} with the dragged data (or undefined if none)\n */\n _getDropData(event) {\n this._isOnlyFilesDragging = this._containsOnlyFiles(event);\n if (this._isOnlyFilesDragging) {\n // Check if the reactive instance can provide a files draggable data.\n if (this.reactive.getFilesDraggableData !== undefined && typeof this.reactive.getFilesDraggableData === 'function') {\n return this.reactive.getFilesDraggableData(event.dataTransfer);\n }\n return undefined;\n }\n return activeDropData.get(this.reactive);\n }\n\n /**\n * Check if the dragged event contains only files.\n *\n * Files dragging does not generate drop data because they came from outside the page and the component\n * must check it before validating the event.\n *\n * Some browsers like Firefox add extra types to file dragging. To discard the false positives\n * a double check is necessary.\n *\n * @param {Event} event the original event.\n * @returns {boolean} if the drag dataTransfers contains files.\n */\n _containsOnlyFiles(event) {\n if (!event.dataTransfer.types.includes('Files')) {\n return false;\n }\n return event.dataTransfer.types.every((type) => {\n return (type.toLowerCase() != 'text/uri-list'\n && type.toLowerCase() != 'text/html'\n && type.toLowerCase() != 'text/plain'\n );\n });\n }\n}\n"],"names":["activeDropData","Map","dragStartPoint","BaseComponent","create","parent","name","classes","Object","assign","BODYDRAGGING","DRAGGABLEREADY","DROPREADY","DRAGOVER","DRAGGING","DROPUP","DROPDOWN","DROPZONE","DRAGICON","fullregion","autoconfigDraggable","this","draggable","relativeDrag","entercount","dropzonevisible","ismouseover","getClasses","isDropzoneVisible","stateReady","validateDropData","element","classList","add","addEventListener","_dragEnter","_dragLeave","_dragOver","_drop","_mouseOver","_mouseLeave","getDraggableData","setDraggable","value","Error","setAttribute","_dragStart","_dragEnd","removeEventListener","remove","event","document","activeElement","matches","preventDefault","dropdata","pageX","pageY","stopPropagation","set","reactive","body","dragImage","undefined","setDragImage","customImage","position","x","y","offsetX","offsetY","dataTransfer","effectAllowed","_callParentMethod","get","delete","removeAllOverlays","_addEventTotalMovement","_processEvent","dropEffect","altKey","_getDropData","fixedMovementX","fixedMovementY","initialPageX","initialPageY","current","getBoundingClientRect","newFixedTop","top","newFixedLeft","left","newRegionFixedxTop","newRegionFixedxLeft","methodname","_isOnlyFilesDragging","_containsOnlyFiles","getFilesDraggableData","types","includes","every","type","toLowerCase"],"mappings":"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"} \ No newline at end of file diff --git a/lib/amd/src/local/reactive/dragdrop.js b/lib/amd/src/local/reactive/dragdrop.js index 44c8ef5b0d4fc..5abd9c1c87406 100644 --- a/lib/amd/src/local/reactive/dragdrop.js +++ b/lib/amd/src/local/reactive/dragdrop.js @@ -283,7 +283,7 @@ export default class extends BaseComponent { position.y = event.offsetY; } event.dataTransfer.setDragImage(dragImage, position.x, position.y); - + event.dataTransfer.effectAllowed = 'copyMove'; this._callParentMethod('dragStart', dropdata, event); } @@ -347,6 +347,7 @@ export default class extends BaseComponent { */ _dragOver(event) { const dropdata = this._processEvent(event); + event.dataTransfer.dropEffect = (event.altKey) ? 'copy' : 'move'; if (dropdata && !this.dropzonevisible) { this.dropzonevisible = true; this.element.classList.add(this.classes.DRAGOVER); From cbe47c1cf41e43aabb9f677156af99d98c7c0010 Mon Sep 17 00:00:00 2001 From: Laurent David Date: Thu, 1 Feb 2024 09:51:10 +0100 Subject: [PATCH 04/62] MDL-79829 theme: Fix Course index draggable cursor * Make the cursor consistent for drag and drop operation to be the same in the course content and the course index --- theme/boost/scss/moodle/courseindex.scss | 3 --- theme/boost/style/moodle.css | 3 --- theme/classic/style/moodle.css | 3 --- 3 files changed, 9 deletions(-) diff --git a/theme/boost/scss/moodle/courseindex.scss b/theme/boost/scss/moodle/courseindex.scss index a97f0d396078b..fbb098eb33ded 100644 --- a/theme/boost/scss/moodle/courseindex.scss +++ b/theme/boost/scss/moodle/courseindex.scss @@ -30,9 +30,6 @@ $courseindex-item-current: $primary !default; color: $courseindex-link-hover-color; } } - &.draggable { - cursor: pointer; - } } } diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 316487b30620f..7254e7658fa3e 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -38105,9 +38105,6 @@ div.editor_atto_toolbar button .icon { .courseindex .courseindex-item:focus.dimmed .courseindex-chevron { color: black; } -.courseindex .courseindex-item:hover.draggable, .courseindex .courseindex-item:focus.draggable { - cursor: pointer; -} .courseindex .courseindex-item.dragging { border: 1px solid #b8dce2; background-color: #e0f0f2; diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index d28086a753b9b..3617c63cc4425 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -38039,9 +38039,6 @@ div.editor_atto_toolbar button .icon { .courseindex .courseindex-item:focus.dimmed .courseindex-chevron { color: black; } -.courseindex .courseindex-item:hover.draggable, .courseindex .courseindex-item:focus.draggable { - cursor: pointer; -} .courseindex .courseindex-item.dragging { border: 1px solid #b8dce2; background-color: #e0f0f2; From 3bf8846639a566bbc15fee5e172fba4fc2255342 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Thu, 14 Mar 2024 12:08:02 +0800 Subject: [PATCH 05/62] MDL-81233 core: Include phpstorm meta info This allows IDEs such as phpstorm, and vscode, to determine class information for classes returned from the DI Container. --- .gitignore | 1 + .phpstorm.meta.php | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 .phpstorm.meta.php diff --git a/.gitignore b/.gitignore index d74f914f79f50..2ab8b0a66666e 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ /.patches/ /.idea/ .phpstorm.* +!.phpstorm.meta.php /nbproject/ CVS .DS_Store diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php new file mode 100644 index 0000000000000..1f9f6c3000b18 --- /dev/null +++ b/.phpstorm.meta.php @@ -0,0 +1,40 @@ +. + +/** + * Helper file for PhpStorm, and other IDEs, to provide better code completion. + * + * @package core + * @copyright 2024 Andrew Lyons + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace PHPSTORM_META; + +// Assume that anything returned by \core\di::get('...') is an instance of the first argument. +override(\core\di::get(0), map([ + '' => '@', +])); + +// Assume that anything returned by \Psr\Container\ContainerInterface::get('...') is an instance of the first argument. +override(\Psr\Container\ContainerInterface::get(0), map([ + '' => '@', +])); + +// Assume that anything returned by \DI\Container::get('...') is an instance of the first argument. +override(\DI\Container::get(0), map([ + '' => '@', +])); From 2c6a746578fc08d50347f4a66ac4d86c3649284b Mon Sep 17 00:00:00 2001 From: Matthew Hilton Date: Mon, 5 Feb 2024 10:06:35 +1000 Subject: [PATCH 06/62] MDL-80300 mod_quiz: Add override manager class --- mod/quiz/classes/cache/overrides.php | 5 + mod/quiz/classes/local/override_cache.php | 127 +++ mod/quiz/classes/local/override_manager.php | 604 +++++++++++ mod/quiz/classes/privacy/provider.php | 20 +- mod/quiz/classes/quiz_settings.php | 12 + mod/quiz/db/caches.php | 3 + mod/quiz/deprecatedlib.php | 34 + mod/quiz/lang/en/quiz.php | 13 +- mod/quiz/lib.php | 95 +- mod/quiz/locallib.php | 26 +- mod/quiz/overridedelete.php | 9 +- mod/quiz/overrideedit.php | 100 +- mod/quiz/tests/event/events_test.php | 6 +- mod/quiz/tests/override_cache_test.php | 86 ++ mod/quiz/tests/override_manager_test.php | 1071 +++++++++++++++++++ mod/quiz/tests/privacy/provider_test.php | 2 +- mod/quiz/upgrade.txt | 6 + mod/quiz/version.php | 2 +- 18 files changed, 2004 insertions(+), 217 deletions(-) create mode 100644 mod/quiz/classes/local/override_cache.php create mode 100644 mod/quiz/classes/local/override_manager.php create mode 100644 mod/quiz/tests/override_cache_test.php create mode 100644 mod/quiz/tests/override_manager_test.php diff --git a/mod/quiz/classes/cache/overrides.php b/mod/quiz/classes/cache/overrides.php index 5a69313978b24..346d38d8ed7f9 100644 --- a/mod/quiz/classes/cache/overrides.php +++ b/mod/quiz/classes/cache/overrides.php @@ -64,6 +64,11 @@ public static function get_instance_for_cache(cache_definition $definition): ove public function load_for_cache($key) { global $DB; + // Ignore getting data if this is a cache invalidation - {@see \cache_helper::purge_by_event()}. + if ($key == 'lastinvalidation') { + return null; + } + [$quizid, $ug, $ugid] = explode('_', $key); $quizid = (int) $quizid; diff --git a/mod/quiz/classes/local/override_cache.php b/mod/quiz/classes/local/override_cache.php new file mode 100644 index 0000000000000..e1d5452412f6c --- /dev/null +++ b/mod/quiz/classes/local/override_cache.php @@ -0,0 +1,127 @@ +. + +namespace mod_quiz\local; + +/** + * Cache manager for quiz overrides + * + * Override cache data is set via its data source, {@see \mod_quiz\cache\overrides} + * @package mod_quiz + * @copyright 2024 Matthew Hilton + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class override_cache { + + /** @var string invalidation event used to purge data when reset_userdata is called, {@see \cache_helper::purge_by_event()}**/ + public const INVALIDATION_USERDATARESET = 'userdatareset'; + + /** + * Create override_cache object and link to quiz + * + * @param int $quizid The quiz to link this cache to + */ + public function __construct( + /** @var int $quizid ID of quiz cache is being operated on **/ + protected readonly int $quizid + ) { + } + + /** + * Returns the override cache + * + * @return cache + */ + protected function get_cache(): \cache { + return \cache::make('mod_quiz', 'overrides'); + } + + /** + * Returns group cache key + * + * @param int $groupid + * @return string the group cache key + */ + protected function get_group_cache_key(int $groupid): string { + return "{$this->quizid}_g_{$groupid}"; + } + + /** + * Returns user cache key + * + * @param int $userid + * @return string the user cache key + */ + protected function get_user_cache_key(int $userid): string { + return "{$this->quizid}_u_{$userid}"; + } + + /** + * Returns the override value in the cache for the given group + * + * @param int $groupid group to get cached override data for + * @return ?\stdClass override value in the cache for the given group, or null if there is none. + */ + public function get_cached_group_override(int $groupid): ?\stdClass { + $raw = $this->get_cache()->get($this->get_group_cache_key($groupid)); + return empty($raw) || !is_object($raw) ? null : (object) $raw; + } + + /** + * Returns the override value in the cache for the given user + * + * @param int $userid user to get cached override data for + * @return ?\stdClass the override value in the cache for the given user, or null if there is none. + */ + public function get_cached_user_override(int $userid): ?\stdClass { + $raw = $this->get_cache()->get($this->get_user_cache_key($userid)); + return empty($raw) || !is_object($raw) ? null : (object) $raw; + } + + /** + * Deletes the cached override data for a given group + * + * @param int $groupid group to delete data for + */ + public function clear_for_group(int $groupid): void { + $this->get_cache()->delete($this->get_group_cache_key($groupid)); + } + + /** + * Deletes the cached override data for the given user + * + * @param int $userid user to delete data for + */ + public function clear_for_user(int $userid): void { + $this->get_cache()->delete($this->get_user_cache_key($userid)); + } + + /** + * Clears the cache for the given user and/or group. + * + * @param ?int $userid user to delete data for, or null. + * @param ?int $groupid group to delete data for, or null. + */ + public function clear_for(?int $userid = null, ?int $groupid = null): void { + if (!empty($userid)) { + $this->clear_for_user($userid); + } + + if (!empty($groupid)) { + $this->clear_for_group($groupid); + } + } +} diff --git a/mod/quiz/classes/local/override_manager.php b/mod/quiz/classes/local/override_manager.php new file mode 100644 index 0000000000000..f5b6aaa157cc1 --- /dev/null +++ b/mod/quiz/classes/local/override_manager.php @@ -0,0 +1,604 @@ +. + +namespace mod_quiz\local; + +use mod_quiz\event\group_override_created; +use mod_quiz\event\group_override_deleted; +use mod_quiz\event\group_override_updated; +use mod_quiz\event\user_override_created; +use mod_quiz\event\user_override_deleted; +use mod_quiz\event\user_override_updated; + +/** + * Manager class for quiz overrides + * + * @package mod_quiz + * @copyright 2024 Matthew Hilton + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class override_manager { + + /** @var array quiz setting keys that can be overwritten **/ + private const OVERRIDEABLE_QUIZ_SETTINGS = ['timeopen', 'timeclose', 'timelimit', 'attempts', 'password']; + + /** + * Create override manager + * + * @param \stdClass $quiz The quiz to link the manager to. + * @param \context_module $context Context being operated in + */ + public function __construct( + /** @var \stdClass The quiz linked to this manager instance **/ + protected readonly \stdClass $quiz, + /** @var \context_module The context being operated in **/ + public readonly \context_module $context + ) { + global $CFG; + // Required for quiz_* methods. + require_once($CFG->dirroot . '/mod/quiz/locallib.php'); + + // Sanity check that the context matches the quiz. + if (empty($quiz->cmid) || $quiz->cmid != $context->instanceid) { + throw new \coding_exception("Given context does not match the quiz object"); + } + } + + /** + * Returns all overrides for the linked quiz. + * + * @return array of quiz_override records + */ + public function get_all_overrides(): array { + global $DB; + return $DB->get_records('quiz_overrides', ['quiz' => $this->quiz->id]); + } + + /** + * Validates the data, usually from a moodleform or a webservice call. + * If it contains an 'id' property, additional validation is performed against the existing record. + * + * @param array $formdata data from moodleform or webservice call. + * @return array array where the keys are error elements, and the values are lists of errors for each element. + */ + public function validate_data(array $formdata): array { + global $DB; + + // Because this can be called directly (e.g. via edit_override_form) + // and not just through save_override, we must ensure the data + // is parsed in the same way. + $formdata = $this->parse_formdata($formdata); + + $formdata = (object) $formdata; + + $errors = []; + + // Ensure at least one of the overrideable settings is set. + $keysthatareset = array_map(function($key) use ($formdata) { + return isset($formdata->$key) && !is_null($formdata->$key); + } , self::OVERRIDEABLE_QUIZ_SETTINGS); + + if (!in_array(true, $keysthatareset)) { + $errors['general'][] = new \lang_string('nooverridedata', 'quiz'); + } + + // Ensure quiz is a valid quiz. + if (empty($formdata->quiz) || empty(get_coursemodule_from_instance('quiz', $formdata->quiz))) { + $errors['quiz'][] = new \lang_string('overrideinvalidquiz', 'quiz'); + } + + // Ensure either userid or groupid is set. + if (empty($formdata->userid) && empty($formdata->groupid)) { + $errors['general'][] = new \lang_string('overridemustsetuserorgroup', 'quiz'); + } + + // Ensure not both userid and groupid are set. + if (!empty($formdata->userid) && !empty($formdata->groupid)) { + $errors['general'][] = new \lang_string('overridecannotsetbothgroupanduser', 'quiz'); + } + + // If group is set, ensure it is a real group. + if (!empty($formdata->groupid) && empty(groups_get_group($formdata->groupid))) { + $errors['groupid'][] = new \lang_string('overrideinvalidgroup', 'quiz'); + } + + // If user is set, ensure it is a valid user. + if (!empty($formdata->userid) && !\core_user::is_real_user($formdata->userid, true)) { + $errors['userid'][] = new \lang_string('overrideinvaliduser', 'quiz'); + } + + // Ensure timeclose is later than timeopen, if both are set. + if (!empty($formdata->timeclose) && !empty($formdata->timeopen) && $formdata->timeclose <= $formdata->timeopen) { + $errors['timeclose'][] = new \lang_string('closebeforeopen', 'quiz'); + } + + // Ensure attempts is a integer greater than or equal to 0 (0 is unlimited attempts). + if (isset($formdata->attempts) && ((int) $formdata->attempts < 0)) { + $errors['attempts'][] = new \lang_string('overrideinvalidattempts', 'quiz'); + } + + // Ensure timelimit is greather than zero. + if (!empty($formdata->timelimit) && $formdata->timelimit <= 0) { + $errors['timelimit'][] = new \lang_string('overrideinvalidtimelimit', 'quiz'); + } + + // Ensure other records do not exist with the same group or user. + if (!empty($formdata->quiz) && (!empty($formdata->userid) || !empty($formdata->groupid))) { + $existingrecordparams = ['quiz' => $formdata->quiz, 'groupid' => $formdata->groupid ?? null, + 'userid' => $formdata->userid ?? null, ]; + $records = $DB->get_records('quiz_overrides', $existingrecordparams, '', 'id'); + + // Ignore self if updating. + if (!empty($formdata->id)) { + unset($records[$formdata->id]); + } + + // If count is not zero, it means existing records exist already for this user/group. + if (!empty($records)) { + $errors['general'][] = new \lang_string('overridemultiplerecordsexist', 'quiz'); + } + } + + // If is existing record, validate it against the existing record. + if (!empty($formdata->id)) { + $existingrecorderrors = self::validate_against_existing_record($formdata->id, $formdata); + $errors = array_merge($errors, $existingrecorderrors); + } + + // Implode each value (array of error strings) into a single error string. + foreach ($errors as $key => $value) { + $errors[$key] = implode(",", $value); + } + + return $errors; + } + + /** + * Returns the existing quiz override record with the given ID or null if does not exist. + * + * @param int $id existing quiz override id + * @return ?\stdClass record, if exists + */ + private static function get_existing(int $id): ?\stdClass { + global $DB; + return $DB->get_record('quiz_overrides', ['id' => $id]) ?: null; + } + + /** + * Validates the formdata against an existing record. + * + * @param int $existingid id of existing quiz override record + * @param \stdClass $formdata formdata, usually from moodleform or webservice call. + * @return array array where the keys are error elements, and the values are lists of errors for each element. + */ + private static function validate_against_existing_record(int $existingid, \stdClass $formdata): array { + $existingrecord = self::get_existing($existingid); + $errors = []; + + // Existing record must exist. + if (empty($existingrecord)) { + $errors['general'][] = new \lang_string('overrideinvalidexistingid', 'quiz'); + } + + // Group value must match existing record if it is set in the formdata. + if (!empty($existingrecord) && !empty($formdata->groupid) && $existingrecord->groupid != $formdata->groupid) { + $errors['groupid'][] = new \lang_string('overridecannotchange', 'quiz'); + } + + // User value must match existing record if it is set in the formdata. + if (!empty($existingrecord) && !empty($formdata->userid) && $existingrecord->userid != $formdata->userid) { + $errors['userid'][] = new \lang_string('overridecannotchange', 'quiz'); + } + + return $errors; + } + + /** + * Parses the formdata by finding only the OVERRIDEABLE_QUIZ_SETTINGS, + * clearing any values that match the existing quiz, and re-adds the user or group id. + * + * @param array $formdata data usually from moodleform or webservice call. + * @return array array containing parsed formdata, with keys as the properties and values as the values. + * Any values set the same as the existing quiz are set to null. + */ + public function parse_formdata(array $formdata): array { + // Get the data from the form that we want to update. + $settings = array_intersect_key($formdata, array_flip(self::OVERRIDEABLE_QUIZ_SETTINGS)); + + // Remove values that are the same as currently in the quiz. + $settings = $this->clear_unused_values($settings); + + // Add the user / group back as applicable. + $userorgroupdata = array_intersect_key($formdata, array_flip(['userid', 'groupid', 'quiz', 'id'])); + + return array_merge($settings, $userorgroupdata); + } + + /** + * Saves the given override. If an id is given, it updates, otherwise it creates a new one. + * Note, capabilities are not checked, {@see require_manage_capability()} + * + * @param array $formdata data usually from moodleform or webservice call. + * @return int updated/inserted record id + */ + public function save_override(array $formdata): int { + global $DB; + + // Extract only the necessary data. + $datatoset = $this->parse_formdata($formdata); + $datatoset['quiz'] = $this->quiz->id; + + // Validate the data is OK. + $errors = $this->validate_data($datatoset); + if (!empty($errors)) { + $errorstr = implode(',', $errors); + throw new \invalid_parameter_exception($errorstr); + } + + // Insert or update. + $id = $datatoset['id'] ?? 0; + if (!empty($id)) { + $DB->update_record('quiz_overrides', $datatoset); + } else { + $id = $DB->insert_record('quiz_overrides', $datatoset); + } + + $userid = $datatoset['userid'] ?? null; + $groupid = $datatoset['groupid'] ?? null; + + // Clear the cache. + $cache = new override_cache($this->quiz->id); + $cache->clear_for($userid, $groupid); + + // Trigger moodle events. + if (empty($formdata['id'])) { + $this->fire_created_event($id, $userid, $groupid); + } else { + $this->fire_updated_event($id, $userid, $groupid); + } + + // Update open events. + quiz_update_open_attempts(['quizid' => $this->quiz->id]); + + // Update calendar events. + $isgroup = !empty($datatoset['groupid']); + if ($isgroup) { + // If is group, must update the entire quiz calendar events. + quiz_update_events($this->quiz); + } else { + // If is just a user, can update only their calendar event. + quiz_update_events($this->quiz, (object) $datatoset); + } + + return $id; + } + + /** + * Deletes all the overrides for the linked quiz + * + * @param bool $shouldlog If true, will log a override_deleted event + */ + public function delete_all_overrides(bool $shouldlog = true): void { + global $DB; + $overrides = $DB->get_records('quiz_overrides', ['quiz' => $this->quiz->id], '', 'id,userid,groupid'); + $this->delete_overrides($overrides, $shouldlog); + } + + /** + * Deletes overrides given just their ID. + * Note, the given IDs must exist otherwise an exception will be thrown. + * Also note, capabilities are not checked, {@see require_manage_capability()} + * + * @param array $ids IDs of overrides to delete + * @param bool $shouldlog If true, will log a override_deleted event + */ + public function delete_overrides_by_id(array $ids, bool $shouldlog = true): void { + global $DB; + [$sql, $params] = self::get_override_in_sql($this->quiz->id, $ids); + $records = $DB->get_records_select('quiz_overrides', $sql, $params, '', 'id,userid,groupid'); + + // Ensure all the given ids exist, so the user is aware if they give a dodgy id. + $missingids = array_diff($ids, array_keys($records)); + + if (!empty($missingids)) { + throw new \invalid_parameter_exception(get_string('overridemissingdelete', 'quiz', implode(',', $missingids))); + } + + $this->delete_overrides($records, $shouldlog); + } + + + /** + * Builds sql and parameters to find overrides in quiz with the given ids + * + * @param int $quizid id of quiz + * @param array $ids array of quiz override ids + * @return array sql and params + */ + private static function get_override_in_sql(int $quizid, array $ids): array { + global $DB; + + [$insql, $inparams] = $DB->get_in_or_equal($ids, SQL_PARAMS_NAMED); + $params = array_merge($inparams, ['quizid' => $quizid]); + $sql = 'id ' . $insql . ' AND quiz = :quizid'; + return [$sql, $params]; + } + + /** + * Deletes the given overrides in the quiz linked to the override manager. + * Note - capabilities are not checked, {@see require_manage_capability()} + * + * @param array $overrides override to delete. Must specify an id, quizid, and either a userid or groupid. + * @param bool $shouldlog If true, will log a override_deleted event + */ + public function delete_overrides(array $overrides, bool $shouldlog = true): void { + global $DB; + + foreach ($overrides as $override) { + if (empty($override->id)) { + throw new \coding_exception("All overrides must specify an ID"); + } + + // Sanity check that user xor group is specified. + // User or group is required to clear the cache. + self::ensure_userid_xor_groupid_set($override->userid ?? null, $override->groupid ?? null); + } + + if (empty($overrides)) { + // Exit early, since delete select requires at least 1 record. + return; + } + + // Match id and quiz. + [$sql, $params] = self::get_override_in_sql($this->quiz->id, array_column($overrides, 'id')); + $DB->delete_records_select('quiz_overrides', $sql, $params); + + $cache = new override_cache($this->quiz->id); + + // Perform other cleanup. + foreach ($overrides as $override) { + $userid = $override->userid ?? null; + $groupid = $override->groupid ?? null; + + $cache->clear_for($userid, $groupid); + $this->delete_override_events($userid, $groupid); + + if ($shouldlog) { + $this->fire_deleted_event($override->id, $userid, $groupid); + } + } + } + + /** + * Ensures either userid or groupid is set, but not both. + * If neither or both are set, a coding exception is thrown. + * + * @param ?int $userid user for the record, or null + * @param ?int $groupid group for the record, or null + */ + private static function ensure_userid_xor_groupid_set(?int $userid = null, ?int $groupid = null): void { + $groupset = !empty($groupid); + $userset = !empty($userid); + + // If either set, but not both (xor). + $xorset = $groupset ^ $userset; + + if (!$xorset) { + throw new \coding_exception("Either userid or groupid must be specified, but not both."); + } + } + + /** + * Deletes the events associated with the override. + * + * @param ?int $userid or null if groupid is specified + * @param ?int $groupid or null if the userid is specified + */ + private function delete_override_events(?int $userid = null, ?int $groupid = null): void { + global $DB; + + // Sanity check. + self::ensure_userid_xor_groupid_set($userid, $groupid); + + $eventssearchparams = ['modulename' => 'quiz', 'instance' => $this->quiz->id]; + + if (!empty($userid)) { + $eventssearchparams['userid'] = $userid; + } + + if (!empty($groupid)) { + $eventssearchparams['groupid'] = $groupid; + } + + $events = $DB->get_records('event', $eventssearchparams); + foreach ($events as $event) { + $eventold = \calendar_event::load($event); + $eventold->delete(); + } + } + + /** + * Requires the user has the override management capability + */ + public function require_manage_capability(): void { + require_capability('mod/quiz:manageoverrides', $this->context); + } + + /** + * Requires the user has the override viewing capability + */ + public function require_read_capability(): void { + // If user can manage, they can also view. + // It would not make sense to be able to create and edit overrides without being able to view them. + if (!has_any_capability(['mod/quiz:viewoverrides', 'mod/quiz:manageoverrides'], $this->context)) { + throw new \required_capability_exception($this->context, 'mod/quiz:viewoverrides', 'nopermissions', ''); + } + } + + /** + * Builds common event data + * + * @param int $id override id + * @return array of data to add as parameters to an event. + */ + private function get_base_event_params(int $id): array { + return [ + 'context' => $this->context, + 'other' => [ + 'quizid' => $this->quiz->id, + ], + 'objectid' => $id, + ]; + } + + /** + * Log that a given override was deleted + * + * @param int $id of quiz override that was just deleted + * @param ?int $userid user attached to override record, or null + * @param ?int $groupid group attached to override record, or null + */ + private function fire_deleted_event(int $id, ?int $userid = null, ?int $groupid = null): void { + // Sanity check. + self::ensure_userid_xor_groupid_set($userid, $groupid); + + $params = $this->get_base_event_params($id); + $params['objectid'] = $id; + + if (!empty($userid)) { + $params['relateduserid'] = $userid; + user_override_deleted::create($params)->trigger(); + } + + if (!empty($groupid)) { + $params['other']['groupid'] = $groupid; + group_override_deleted::create($params)->trigger(); + } + } + + + /** + * Log that a given override was created + * + * @param int $id of quiz override that was just created + * @param ?int $userid user attached to override record, or null + * @param ?int $groupid group attached to override record, or null + */ + private function fire_created_event(int $id, ?int $userid = null, ?int $groupid = null): void { + // Sanity check. + self::ensure_userid_xor_groupid_set($userid, $groupid); + + $params = $this->get_base_event_params($id); + + if (!empty($userid)) { + $params['relateduserid'] = $userid; + user_override_created::create($params)->trigger(); + } + + if (!empty($groupid)) { + $params['other']['groupid'] = $groupid; + group_override_created::create($params)->trigger(); + } + } + + /** + * Log that a given override was updated + * + * @param int $id of quiz override that was just updated + * @param ?int $userid user attached to override record, or null + * @param ?int $groupid group attached to override record, or null + */ + private function fire_updated_event(int $id, ?int $userid = null, ?int $groupid = null): void { + // Sanity check. + self::ensure_userid_xor_groupid_set($userid, $groupid); + + $params = $this->get_base_event_params($id); + + if (!empty($userid)) { + $params['relateduserid'] = $userid; + user_override_updated::create($params)->trigger(); + } + + if (!empty($groupid)) { + $params['other']['groupid'] = $groupid; + group_override_updated::create($params)->trigger(); + } + } + + /** + * Clears any overrideable settings in the formdata, where the value matches what is already in the quiz + * If they match, the data is set to null. + * + * @param array $formdata data usually from moodleform or webservice call. + * @return array formdata with same values cleared + */ + private function clear_unused_values(array $formdata): array { + foreach (self::OVERRIDEABLE_QUIZ_SETTINGS as $key) { + // If the formdata is the same as the current quiz object data, clear it. + if (isset($formdata[$key]) && $formdata[$key] == $this->quiz->$key) { + $formdata[$key] = null; + } + + // Ensure these keys always are set (even if null). + $formdata[$key] = $formdata[$key] ?? null; + + // If the formdata is empty, set it to null. + // This avoids putting 0, false, or '' into the DB since the override logic expects null. + // Attempts is the exception, it can have a integer value of '0', so we use is_numeric instead. + if ($key != 'attempts' && empty($formdata[$key])) { + $formdata[$key] = null; + } + + if ($key == 'attempts' && !is_numeric($formdata[$key])) { + $formdata[$key] = null; + } + } + + return $formdata; + } + + /** + * Deletes orphaned group overrides in a given course. + * Note - permissions are not checked and events are not logged for performance reasons. + * + * @param int $courseid ID of course to delete orphaned group overrides in + * @return array array of quizzes that had orphaned group overrides. + */ + public static function delete_orphaned_group_overrides_in_course(int $courseid): array { + global $DB; + + // It would be nice if we got the groupid that was deleted. + // Instead, we just update all quizzes with orphaned group overrides. + $sql = "SELECT o.id, o.quiz, o.groupid + FROM {quiz_overrides} o + JOIN {quiz} quiz ON quiz.id = o.quiz + LEFT JOIN {groups} grp ON grp.id = o.groupid + WHERE quiz.course = :courseid + AND o.groupid IS NOT NULL + AND grp.id IS NULL"; + $params = ['courseid' => $courseid]; + $records = $DB->get_records_sql($sql, $params); + + $DB->delete_records_list('quiz_overrides', 'id', array_keys($records)); + + // Purge cache for each record. + foreach ($records as $record) { + $cache = new override_cache($record->quiz); + $cache->clear_for_group($record->groupid); + } + return array_unique(array_column($records, 'quiz')); + } +} diff --git a/mod/quiz/classes/privacy/provider.php b/mod/quiz/classes/privacy/provider.php index 6d910511a48b5..8d77a18f684d8 100644 --- a/mod/quiz/classes/privacy/provider.php +++ b/mod/quiz/classes/privacy/provider.php @@ -365,8 +365,8 @@ public static function delete_data_for_all_users_in_context(\context $context) { [$quizobj] ); - // Delete all overrides - do not log. - quiz_delete_all_overrides($quiz, false); + // Delete all overrides. + $quizobj->get_override_manager()->delete_all_overrides(shouldlog: false); // This will delete all question attempts, quiz attempts, and quiz grades for this quiz. quiz_delete_all_attempts($quiz); @@ -411,9 +411,11 @@ public static function delete_data_for_user(approved_contextlist $contextlist) { 'userid' => $user->id, ]); - foreach ($overrides as $override) { - quiz_delete_override($quiz, $override->id, false); - } + $manager = $quizobj->get_override_manager(); + $manager->delete_overrides( + overrides: $overrides, + shouldlog: false, + ); // This will delete all question attempts, quiz attempts, and quiz grades for this quiz. quiz_delete_user_attempts($quizobj, $user); @@ -461,9 +463,11 @@ public static function delete_data_for_users(approved_userlist $userlist) { 'userid' => $userid, ]); - foreach ($overrides as $override) { - quiz_delete_override($quiz, $override->id, false); - } + $manager = $quizobj->get_override_manager(); + $manager->delete_overrides( + overrides: $overrides, + shouldlog: false, + ); // This will delete all question attempts, quiz attempts, and quiz grades for this user in the given quiz. quiz_delete_user_attempts($quizobj, (object)['id' => $userid]); diff --git a/mod/quiz/classes/quiz_settings.php b/mod/quiz/classes/quiz_settings.php index 48ab0d6575a22..2b1c925bb5a8c 100644 --- a/mod/quiz/classes/quiz_settings.php +++ b/mod/quiz/classes/quiz_settings.php @@ -622,4 +622,16 @@ public function get_all_question_types_used($includepotential = false) { return $questiontypes; } + + /** + * Returns an override manager instance with context and quiz loaded. + * + * @return \mod_quiz\local\override_manager + */ + public function get_override_manager(): \mod_quiz\local\override_manager { + return new \mod_quiz\local\override_manager( + quiz: $this->quiz, + context: $this->context + ); + } } diff --git a/mod/quiz/db/caches.php b/mod/quiz/db/caches.php index 0b0e541c79c07..74964ca8f17fb 100644 --- a/mod/quiz/db/caches.php +++ b/mod/quiz/db/caches.php @@ -31,5 +31,8 @@ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'datasource' => '\mod_quiz\cache\overrides', + 'invalidationevents' => [ + \mod_quiz\local\override_cache::INVALIDATION_USERDATARESET, + ], ], ]; diff --git a/mod/quiz/deprecatedlib.php b/mod/quiz/deprecatedlib.php index 45f2fb4101f26..2e1f624256c61 100644 --- a/mod/quiz/deprecatedlib.php +++ b/mod/quiz/deprecatedlib.php @@ -311,3 +311,37 @@ function quiz_calculate_best_attempt($quiz, $attempts) { return $maxattempt; } } + +/** + * Deletes a quiz override from the database and clears any corresponding calendar events + * + * @deprecated since Moodle 4.4 + * @todo MDL-80944 Final deprecation in Moodle 4.8 + * @param stdClass $quiz The quiz object. + * @param int $overrideid The id of the override being deleted + * @param bool $log Whether to trigger logs. + * @return bool true on success + */ +function quiz_delete_override($quiz, $overrideid, $log = true) { + debugging('quiz_delete_override is deprecated. Please use override_manager::delete_override_by_id instead.', DEBUG_DEVELOPER); + $quizsettings = quiz_settings::create($quiz->id); + $quizsettings->get_override_manager()->delete_overrides_by_id( + ids: [$overrideid], + shouldlog: $log, + ); +} + +/** + * Deletes all quiz overrides from the database and clears any corresponding calendar events + * + * @deprecated since Moodle 4.4 + * @todo MDL-80944 Final deprecation in Moodle 4.8 + * @param stdClass $quiz The quiz object. + * @param bool $log Whether to trigger logs. + */ +function quiz_delete_all_overrides($quiz, $log = true) { + debugging('quiz_delete_all_overrides is deprecated. Please use override_manager::delete_all_overrides instead.', + DEBUG_DEVELOPER); + $quizsettings = quiz_settings::create($quiz->id); + $quizsettings->get_override_manager()->delete_all_overrides(shouldlog: $log); +} diff --git a/mod/quiz/lang/en/quiz.php b/mod/quiz/lang/en/quiz.php index d8b5b082bf657..58ec84b447cbd 100644 --- a/mod/quiz/lang/en/quiz.php +++ b/mod/quiz/lang/en/quiz.php @@ -176,7 +176,7 @@ $string['categoryupdated'] = 'The category was successfully updated'; $string['close'] = 'Close window'; $string['closed'] = 'Closed'; -$string['closebeforeopen'] = 'Could not update the quiz. You have specified a close date before the open date.'; +$string['closebeforeopen'] = 'The close date cannot be before or equal to the open date.'; $string['closepreview'] = 'Close preview'; $string['closereview'] = 'Close review'; $string['comment'] = 'Comment'; @@ -650,10 +650,21 @@ $string['overduehandlingautoabandon'] = 'Attempts must be submitted before time expires, or they are not counted'; $string['overduemustbesubmittedby'] = 'This attempt is now overdue. It should already have been submitted. If you would like this quiz to be graded, you must submit it by {$a}. If you do not submit it by then, no marks from this attempt will be counted.'; $string['override'] = 'Override'; +$string['overridecannotchange'] = 'The user or group cannot be changed after an override is created'; +$string['overridecannotsetbothgroupanduser'] = 'Both group and user cannot be set at the same time'; $string['overridedeletegroupsure'] = 'Are you sure you want to delete the override for group {$a}?'; $string['overridedeleteusersure'] = 'Are you sure you want to delete the override for user {$a}?'; $string['overridegroup'] = 'Override group'; $string['overridegroupeventname'] = '{$a->quiz} - {$a->group}'; +$string['overrideinvalidattempts'] = 'Attempts value must be greater than zero'; +$string['overrideinvalidexistingid'] = 'Existing override does not exist'; +$string['overrideinvalidgroup'] = 'Group given does not exist'; +$string['overrideinvalidquiz'] = 'Quiz id set does not exist'; +$string['overrideinvalidtimelimit'] = 'Time limit must be greater than zero'; +$string['overrideinvaliduser'] = 'User given does not exist'; +$string['overridemissingdelete'] = 'Override id(s) {$a} could not be deleted because they do not exist or are not a part of the given quiz'; +$string['overridemultiplerecordsexist'] = 'Multiple overrides cannot be made for the same user/group'; +$string['overridemustsetuserorgroup'] = 'A user or group must be set'; $string['overrides'] = 'Overrides'; $string['overridesforquiz'] = 'Settings overrides: {$a}'; $string['overridesnoneforgroups'] = 'No group settings overrides have been created for this quiz.'; diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php index cbdafc4f08db6..36a613b7aa439 100644 --- a/mod/quiz/lib.php +++ b/mod/quiz/lib.php @@ -38,6 +38,7 @@ use mod_quiz\question\qubaids_for_quiz; use mod_quiz\question\qubaids_for_users_attempts; use core_question\statistics\questions\all_calculated_for_qubaid_condition; +use mod_quiz\local\override_cache; use mod_quiz\quiz_attempt; use mod_quiz\quiz_settings; @@ -190,7 +191,11 @@ function quiz_delete_instance($id) { $quiz = $DB->get_record('quiz', ['id' => $id], '*', MUST_EXIST); quiz_delete_all_attempts($quiz); - quiz_delete_all_overrides($quiz); + + // Delete all overrides, and for performance do not log or check permissions. + $quizobj = quiz_settings::create($quiz->id); + $quizobj->get_override_manager()->delete_all_overrides(shouldlog: false); + quiz_delete_references($quiz->id); // We need to do the following deletes before we try and delete randoms, otherwise they would still be 'in use'. @@ -214,86 +219,6 @@ function quiz_delete_instance($id) { return true; } -/** - * Deletes a quiz override from the database and clears any corresponding calendar events - * - * @param stdClass $quiz The quiz object. - * @param int $overrideid The id of the override being deleted - * @param bool $log Whether to trigger logs. - * @return bool true on success - */ -function quiz_delete_override($quiz, $overrideid, $log = true) { - global $DB; - - if (!isset($quiz->cmid)) { - $cm = get_coursemodule_from_instance('quiz', $quiz->id, $quiz->course); - $quiz->cmid = $cm->id; - } - - $override = $DB->get_record('quiz_overrides', ['id' => $overrideid], '*', MUST_EXIST); - - // Delete the events. - if (isset($override->groupid)) { - // Create the search array for a group override. - $eventsearcharray = ['modulename' => 'quiz', - 'instance' => $quiz->id, 'groupid' => (int)$override->groupid]; - $cachekey = "{$quiz->id}_g_{$override->groupid}"; - } else { - // Create the search array for a user override. - $eventsearcharray = ['modulename' => 'quiz', - 'instance' => $quiz->id, 'userid' => (int)$override->userid]; - $cachekey = "{$quiz->id}_u_{$override->userid}"; - } - $events = $DB->get_records('event', $eventsearcharray); - foreach ($events as $event) { - $eventold = calendar_event::load($event); - $eventold->delete(); - } - - $DB->delete_records('quiz_overrides', ['id' => $overrideid]); - cache::make('mod_quiz', 'overrides')->delete($cachekey); - - if ($log) { - // Set the common parameters for one of the events we will be triggering. - $params = [ - 'objectid' => $override->id, - 'context' => context_module::instance($quiz->cmid), - 'other' => [ - 'quizid' => $override->quiz - ] - ]; - // Determine which override deleted event to fire. - if (!empty($override->userid)) { - $params['relateduserid'] = $override->userid; - $event = \mod_quiz\event\user_override_deleted::create($params); - } else { - $params['other']['groupid'] = $override->groupid; - $event = \mod_quiz\event\group_override_deleted::create($params); - } - - // Trigger the override deleted event. - $event->add_record_snapshot('quiz_overrides', $override); - $event->trigger(); - } - - return true; -} - -/** - * Deletes all quiz overrides from the database and clears any corresponding calendar events - * - * @param stdClass $quiz The quiz object. - * @param bool $log Whether to trigger logs. - */ -function quiz_delete_all_overrides($quiz, $log = true) { - global $DB; - - $overrides = $DB->get_records('quiz_overrides', ['quiz' => $quiz->id], 'id'); - foreach ($overrides as $override) { - quiz_delete_override($quiz, $override->id, $log); - } -} - /** * Updates a quiz object with override information for a user. * @@ -1620,7 +1545,7 @@ function quiz_reset_userdata($data) { } if ($purgeoverrides) { - cache::make('mod_quiz', 'overrides')->purge(); + \cache_helper::purge_by_event(\mod_quiz\local\override_cache::INVALIDATION_USERDATARESET); } return $status; @@ -2158,8 +2083,8 @@ function quiz_get_coursemodule_info($coursemodule) { function mod_quiz_cm_info_dynamic(cm_info $cm) { global $USER; - $cache = cache::make('mod_quiz', 'overrides'); - $override = $cache->get("{$cm->instance}_u_{$USER->id}"); + $cache = new override_cache($cm->instance); + $override = $cache->get_cached_user_override($USER->id); if (!$override) { $override = (object) [ @@ -2174,7 +2099,7 @@ function mod_quiz_cm_info_dynamic(cm_info $cm) { $closes = []; $groupings = groups_get_user_groups($cm->course, $USER->id); foreach ($groupings[0] as $groupid) { - $groupoverride = $cache->get("{$cm->instance}_g_{$groupid}"); + $groupoverride = $cache->get_cached_group_override($groupid); if (isset($groupoverride->timeopen)) { $opens[] = $groupoverride->timeopen; } diff --git a/mod/quiz/locallib.php b/mod/quiz/locallib.php index aa5167d68afeb..d35cd1c2682c7 100644 --- a/mod/quiz/locallib.php +++ b/mod/quiz/locallib.php @@ -39,6 +39,7 @@ use mod_quiz\access_manager; use mod_quiz\event\attempt_submitted; use mod_quiz\grade_calculator; +use mod_quiz\local\override_manager; use mod_quiz\question\bank\qbank_helper; use mod_quiz\question\display_options; use mod_quiz\quiz_attempt; @@ -1585,28 +1586,11 @@ function quiz_send_notify_manual_graded_message(quiz_attempt $attemptobj, object * @return void */ function quiz_process_group_deleted_in_course($courseid) { - global $DB; + $affectedquizzes = override_manager::delete_orphaned_group_overrides_in_course($courseid); - // It would be nice if we got the groupid that was deleted. - // Instead, we just update all quizzes with orphaned group overrides. - $sql = "SELECT o.id, o.quiz, o.groupid - FROM {quiz_overrides} o - JOIN {quiz} quiz ON quiz.id = o.quiz - LEFT JOIN {groups} grp ON grp.id = o.groupid - WHERE quiz.course = :courseid - AND o.groupid IS NOT NULL - AND grp.id IS NULL"; - $params = ['courseid' => $courseid]; - $records = $DB->get_records_sql($sql, $params); - if (!$records) { - return; // Nothing to do. - } - $DB->delete_records_list('quiz_overrides', 'id', array_keys($records)); - $cache = cache::make('mod_quiz', 'overrides'); - foreach ($records as $record) { - $cache->delete("{$record->quiz}_g_{$record->groupid}"); - } - quiz_update_open_attempts(['quizid' => array_unique(array_column($records, 'quiz'))]); + if (!empty($affectedquizzes)) { + quiz_update_open_attempts(['quizid' => $affectedquizzes]); + } } /** diff --git a/mod/quiz/overridedelete.php b/mod/quiz/overridedelete.php index 2e70db0b07387..aa42d2c7a89c2 100644 --- a/mod/quiz/overridedelete.php +++ b/mod/quiz/overridedelete.php @@ -38,11 +38,12 @@ $cm = $quizobj->get_cm(); $course = $quizobj->get_course(); $context = $quizobj->get_context(); +$manager = $quizobj->get_override_manager(); require_login($course, false, $cm); // Check the user has the required capabilities to modify an override. -require_capability('mod/quiz:manageoverrides', $context); +$manager->require_manage_capability(); if ($override->groupid) { if (!groups_group_visible($override->groupid, $course, $cm)) { @@ -65,11 +66,7 @@ // If confirm is set (PARAM_BOOL) then we have confirmation of intention to delete. if ($confirm) { require_sesskey(); - - // Set the course module id before calling quiz_delete_override(). - $quiz->cmid = $cm->id; - quiz_delete_override($quiz, $override->id); - + $manager->delete_overrides(overrides: [$override]); redirect($cancelurl); } diff --git a/mod/quiz/overrideedit.php b/mod/quiz/overrideedit.php index bf2cb378c7e05..878e263770562 100644 --- a/mod/quiz/overrideedit.php +++ b/mod/quiz/overrideedit.php @@ -46,6 +46,7 @@ $cm = $quizobj->get_cm(); $course = $quizobj->get_course(); $context = $quizobj->get_context(); +$manager = $quizobj->get_override_manager(); $url = new moodle_url('/mod/quiz/overrideedit.php'); if ($action) { @@ -65,7 +66,7 @@ require_login($course, false, $cm); // Add or edit an override. -require_capability('mod/quiz:manageoverrides', $context); +$manager->require_manage_capability(); if ($overrideid) { // Editing an override. @@ -121,98 +122,13 @@ redirect($url); } else if ($fromform = $mform->get_data()) { - // Process the data. - $fromform->quiz = $quiz->id; - - // Replace unchanged values with null. - foreach ($keys as $key) { - if ($fromform->{$key} == $quiz->{$key}) { - $fromform->{$key} = null; - } - } - - // See if we are replacing an existing override. - $userorgroupchanged = false; - if (empty($override->id)) { - $userorgroupchanged = true; - } else if (!empty($fromform->userid)) { - $userorgroupchanged = $fromform->userid !== $override->userid; - } else { - $userorgroupchanged = $fromform->groupid !== $override->groupid; + // Only include id when editing (i.e. action is empty). + if (empty($action) && !empty($overrideid)) { + $fromform->id = $overrideid; } - if ($userorgroupchanged) { - $conditions = [ - 'quiz' => $quiz->id, - 'userid' => empty($fromform->userid) ? null : $fromform->userid, - 'groupid' => empty($fromform->groupid) ? null : $fromform->groupid]; - if ($oldoverride = $DB->get_record('quiz_overrides', $conditions)) { - // There is an old override, so we merge any new settings on top of - // the older override. - foreach ($keys as $key) { - if (is_null($fromform->{$key})) { - $fromform->{$key} = $oldoverride->{$key}; - } - } - // Set the course module id before calling quiz_delete_override(). - $quiz->cmid = $cm->id; - quiz_delete_override($quiz, $oldoverride->id); - } - } - - // Set the common parameters for one of the events we may be triggering. - $params = [ - 'context' => $context, - 'other' => [ - 'quizid' => $quiz->id - ] - ]; - if (!empty($override->id)) { - $fromform->id = $override->id; - $DB->update_record('quiz_overrides', $fromform); - $cachekey = $groupmode ? "{$fromform->quiz}_g_{$fromform->groupid}" : "{$fromform->quiz}_u_{$fromform->userid}"; - cache::make('mod_quiz', 'overrides')->delete($cachekey); - - // Determine which override updated event to fire. - $params['objectid'] = $override->id; - if (!$groupmode) { - $params['relateduserid'] = $fromform->userid; - $event = \mod_quiz\event\user_override_updated::create($params); - } else { - $params['other']['groupid'] = $fromform->groupid; - $event = \mod_quiz\event\group_override_updated::create($params); - } - - // Trigger the override updated event. - $event->trigger(); - } else { - unset($fromform->id); - $fromform->id = $DB->insert_record('quiz_overrides', $fromform); - $cachekey = $groupmode ? "{$fromform->quiz}_g_{$fromform->groupid}" : "{$fromform->quiz}_u_{$fromform->userid}"; - cache::make('mod_quiz', 'overrides')->delete($cachekey); - - // Determine which override created event to fire. - $params['objectid'] = $fromform->id; - if (!$groupmode) { - $params['relateduserid'] = $fromform->userid; - $event = \mod_quiz\event\user_override_created::create($params); - } else { - $params['other']['groupid'] = $fromform->groupid; - $event = \mod_quiz\event\group_override_created::create($params); - } - - // Trigger the override created event. - $event->trigger(); - } - - quiz_update_open_attempts(['quizid' => $quiz->id]); - if ($groupmode) { - // Priorities may have shifted, so we need to update all of the calendar events for group overrides. - quiz_update_events($quiz); - } else { - // User override. We only need to update the calendar event for this user override. - quiz_update_events($quiz, $fromform); - } + // Process the data. + $id = $manager->save_override((array) $fromform); if (!empty($fromform->submitbutton)) { redirect($overridelisturl); @@ -221,7 +137,7 @@ // The user pressed the 'again' button, so redirect back to this page. $url->remove_params('cmid'); $url->param('action', 'duplicate'); - $url->param('id', $fromform->id); + $url->param('id', $id); redirect($url); } diff --git a/mod/quiz/tests/event/events_test.php b/mod/quiz/tests/event/events_test.php index 983f6af336144..fe2895d7b61d6 100644 --- a/mod/quiz/tests/event/events_test.php +++ b/mod/quiz/tests/event/events_test.php @@ -630,6 +630,7 @@ public function test_user_override_deleted() { $this->setAdminUser(); $course = $this->getDataGenerator()->create_course(); $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]); + $quizsettings = quiz_settings::create($quiz->id); // Create an override. $override = new \stdClass(); @@ -639,7 +640,7 @@ public function test_user_override_deleted() { // Trigger and capture the event. $sink = $this->redirectEvents(); - quiz_delete_override($quiz, $override->id); + $quizsettings->get_override_manager()->delete_overrides(overrides: [$override]); $events = $sink->get_events(); $event = reset($events); @@ -660,6 +661,7 @@ public function test_group_override_deleted() { $this->setAdminUser(); $course = $this->getDataGenerator()->create_course(); $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]); + $quizsettings = quiz_settings::create($quiz->id); // Create an override. $override = new \stdClass(); @@ -669,7 +671,7 @@ public function test_group_override_deleted() { // Trigger and capture the event. $sink = $this->redirectEvents(); - quiz_delete_override($quiz, $override->id); + $quizsettings->get_override_manager()->delete_overrides(overrides: [$override]); $events = $sink->get_events(); $event = reset($events); diff --git a/mod/quiz/tests/override_cache_test.php b/mod/quiz/tests/override_cache_test.php new file mode 100644 index 0000000000000..57882b487a8fe --- /dev/null +++ b/mod/quiz/tests/override_cache_test.php @@ -0,0 +1,86 @@ +. + +namespace mod_quiz; + +use mod_quiz\local\override_cache; + +/** + * Cache manager tests for quiz overrides + * + * @package mod_quiz + * @copyright 2024 Matthew Hilton + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \mod_quiz\local\override_cache + */ +final class override_cache_test extends \advanced_testcase { + + /** + * Tests CRUD functions of the override_cache + */ + public function test_crud(): void { + // Cache is normally protected, but for testing we reflect it and put test data into it. + $overridecache = new override_cache(0); + $reflection = new \ReflectionClass($overridecache); + + $getcache = $reflection->getMethod('get_cache'); + $getcache->setAccessible(true); + $cache = $getcache->invoke($overridecache); + + $getuserkey = $reflection->getMethod('get_user_cache_key'); + $getuserkey->setAccessible(true); + + $getgroupkey = $reflection->getMethod('get_group_cache_key'); + $getgroupkey->setAccessible(true); + + $dummydata = (object)[ + 'userid' => 1234, + ]; + + // Set some data. + $cache->set($getuserkey->invoke($overridecache, 123), $dummydata); + $cache->set($getgroupkey->invoke($overridecache, 456), $dummydata); + + // Get the data back. + $this->assertEquals($dummydata, $overridecache->get_cached_user_override(123)); + $this->assertEquals($dummydata, $overridecache->get_cached_group_override(456)); + + // Delete. + $overridecache->clear_for_user(123); + $overridecache->clear_for_group(456); + + $this->assertEmpty($overridecache->get_cached_user_override(123)); + $this->assertEmpty($overridecache->get_cached_group_override(456)); + + // Put some data back. + $cache->set($getuserkey->invoke($overridecache, 123), $dummydata); + $cache->set($getgroupkey->invoke($overridecache, 456), $dummydata); + + // Clear it. + $overridecache->clear_for(123, 456); + $this->assertEmpty($overridecache->get_cached_user_override(123)); + $this->assertEmpty($overridecache->get_cached_group_override(456)); + + // Put some data back. + $cache->set($getuserkey->invoke($overridecache, 123), 'testuser'); + $cache->set($getgroupkey->invoke($overridecache, 456), 'testgroup'); + + // Purge it. + \cache_helper::purge_by_event(\mod_quiz\local\override_cache::INVALIDATION_USERDATARESET); + $this->assertEmpty($overridecache->get_cached_user_override(123)); + $this->assertEmpty($overridecache->get_cached_group_override(456)); + } +} diff --git a/mod/quiz/tests/override_manager_test.php b/mod/quiz/tests/override_manager_test.php new file mode 100644 index 0000000000000..8d53ecdbebebc --- /dev/null +++ b/mod/quiz/tests/override_manager_test.php @@ -0,0 +1,1071 @@ +. + +namespace mod_quiz; + +use mod_quiz\event\group_override_created; +use mod_quiz\event\group_override_updated; +use mod_quiz\event\user_override_created; +use mod_quiz\event\user_override_updated; +use mod_quiz\local\override_manager; +use mod_quiz\event\user_override_deleted; +use mod_quiz\local\override_cache; + +/** + * Test for override_manager class + * + * @package mod_quiz + * @copyright 2024 Matthew Hilton + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \mod_quiz\local\override_manager + */ +final class override_manager_test extends \advanced_testcase { + + /** @var array Default quiz settings **/ + private const TEST_QUIZ_SETTINGS = [ + 'attempts' => 5, + 'timeopen' => 100000000, + 'timeclose' => 10000001, + 'timelimit' => 10, + ]; + + /** + * Create quiz and course for test + * + * @return array containing quiz object and course + */ + private function create_quiz_and_course(): array { + $course = $this->getDataGenerator()->create_course(['groupmode' => SEPARATEGROUPS]); + $quizparams = array_merge(self::TEST_QUIZ_SETTINGS, ['course' => $course->id]); + $quiz = $this->getDataGenerator()->create_module('quiz', $quizparams); + $quizobj = quiz_settings::create($quiz->id); + return [$quizobj, $course]; + } + + /** + * Utility function that replaces the placeholders in the given data. + * + * @param array $data + * @param array $placeholdervalues + * @return array the $data with the placeholders replaced + */ + private function replace_placeholders(array $data, array $placeholdervalues) { + foreach ($data as $key => $value) { + $replacement = $placeholdervalues[$value] ?? null; + + if (!empty($replacement)) { + $data[$key] = $replacement; + } + } + + return $data; + } + + /** + * Utility function that sets up data for tests testing CRUD operations. + * Placeholders such as ':userid' and ':groupid' can be used in the data to replace with the relevant id. + * + * @param array $existingdata Data used to setup a preexisting quiz override record. + * @param array $formdata submitted formdata + * @return \stdClass containing formdata (after placeholders replaced), quizobj, user and group + */ + private function setup_existing_and_testing_data(array $existingdata, array $formdata): \stdClass { + global $DB; + + [$quizobj, $course] = $this->create_quiz_and_course(); + + $user = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user->id, $course->id); + + $user2 = $this->getDataGenerator()->create_user(); + $this->getDataGenerator()->enrol_user($user2->id, $course->id); + + $groupid = groups_create_group((object) ['courseid' => $course->id, 'name' => 'test']); + $group2id = groups_create_group((object) ['courseid' => $course->id, 'name' => 'test2']); + + // Replace any userid or groupid placeholders in the form data or existing data. + $placeholdervalues = [ + ':userid' => $user->id, + ':user2id' => $user2->id, + ':groupid' => $groupid, + ':group2id' => $group2id, + ]; + + if (!empty($existingdata)) { + // Raw insert the existing data for the test into the DB. + // We assume it is valid for the test. + $existingdata['quiz'] = $quizobj->get_quizid(); + $existingid = $DB->insert_record('quiz_overrides', $this->replace_placeholders($existingdata, $placeholdervalues)); + $placeholdervalues[':existingid'] = $existingid; + } + + $formdata = $this->replace_placeholders($formdata, $placeholdervalues); + + // Add quiz id to formdata. + $formdata['quiz'] = $quizobj->get_quizid(); + + return (object) [ + 'quizobj' => $quizobj, + 'formdata' => $formdata, + 'user1' => $user, + 'groupid1' => $groupid, + ]; + } + + /** + * Provides values to test_save_and_get_override + * + * @return array + */ + public static function save_and_get_override_provider(): array { + return [ + 'create user override - no existing data' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 1, + 'attempts' => 999, + 'password' => 'test', + ], + 'expectedrecordscreated' => 1, + 'expectedevent' => user_override_created::class, + ], + 'create user override - no calendar events should be created' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => null, + 'timeclose' => null, + 'timelimit' => 1, + 'attempts' => 999, + 'password' => 'test', + ], + 'expectedrecordscreated' => 1, + 'expectedevent' => user_override_created::class, + ], + 'create user override - only timeopen' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => null, + 'timelimit' => null, + 'attempts' => null, + 'password' => 'test', + ], + 'expectedrecordscreated' => 1, + 'expectedevent' => user_override_created::class, + ], + 'create group override - no existing data' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => null, + 'groupid' => ':groupid', + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 1, + 'attempts' => 999, + 'password' => 'test', + ], + 'expectedrecordscreated' => 1, + 'expectedevent' => group_override_created::class, + ], + 'create group override - no calendar events should be created' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => null, + 'groupid' => ':groupid', + 'timeopen' => null, + 'timeclose' => null, + 'timelimit' => 1, + 'attempts' => 999, + 'password' => 'test', + ], + 'expectedrecordscreated' => 1, + 'expectedevent' => group_override_created::class, + ], + 'create group override - only timeopen' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => null, + 'groupid' => ':groupid', + 'timeopen' => 50, + 'timeclose' => null, + 'timelimit' => null, + 'attempts' => null, + 'password' => null, + ], + 'expectedrecordscreated' => 1, + 'expectedevent' => group_override_created::class, + ], + 'update user override - updating existing data' => [ + 'existingdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'formdata' => [ + 'id' => ':existingid', + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => 52, + 'timeclose' => 53, + 'timelimit' => 1, + 'attempts' => 999, + 'password' => 'test', + ], + 'expectedrecordscreated' => 0, + 'expectedevent' => user_override_updated::class, + ], + 'update group override - updating existing data' => [ + 'existingdata' => [ + 'userid' => null, + 'groupid' => ':groupid', + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'formdata' => [ + 'id' => ':existingid', + 'userid' => null, + 'groupid' => ':groupid', + 'timeopen' => 52, + 'timeclose' => 53, + 'timelimit' => 1, + 'attempts' => 999, + 'password' => 'test', + ], + 'expectedrecordscreated' => 0, + 'expectedevent' => group_override_updated::class, + ], + 'attempts is set to unlimited (i.e. 0)' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => null, + 'timeclose' => null, + 'timelimit' => null, + // This checks we are using empty() carefully, since this is valid. + 'attempts' => 0, + 'password' => null, + ], + 'expectedrecordscreated' => 1, + 'expectedevent' => user_override_created::class, + ], + 'some settings submitted are the same as what is in the quiz (valid)' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => null, + // Make these the same, they should be ignored. + 'timeopen' => self::TEST_QUIZ_SETTINGS['timeopen'], + 'timeclose' => self::TEST_QUIZ_SETTINGS['timeclose'], + 'attempts' => self::TEST_QUIZ_SETTINGS['attempts'], + // However change this, this should still get updated. + 'timelimit' => self::TEST_QUIZ_SETTINGS['timelimit'] + 5, + 'password' => null, + ], + 'expectedrecordscreated' => 1, + 'expectedevent' => user_override_created::class, + ], + ]; + } + + /** + * Tests save_override function + * + * @param array $existingdata If given, an existing override will be created. + * @param array $formdata The data being tested, simulating being submitted + * @param int $expectedrecordscreated The number of records that are expected to be created by upsert + * @param string $expectedeventclass an event class, which is expected to the emitted by upsert + * @dataProvider save_and_get_override_provider + */ + public function test_save_and_get_override(array $existingdata, array $formdata, int $expectedrecordscreated, + string $expectedeventclass): void { + global $DB; + + $this->setAdminUser(); + $this->resetAfterTest(); + + $test = $this->setup_existing_and_testing_data($existingdata, $formdata); + $manager = $test->quizobj->get_override_manager(); + + // Get the count before. + $beforecount = $DB->count_records('quiz_overrides'); + + $sink = $this->redirectEvents(); + + // Submit the form data. + $id = $manager->save_override($test->formdata); + + // Get the count after and compare to the expected. + $aftercount = $DB->count_records('quiz_overrides'); + $this->assertEquals($expectedrecordscreated, $aftercount - $beforecount); + + // Read back the created/updated value, and compare it to the formdata. + $readback = $DB->get_record('quiz_overrides', ['id' => $id]); + $this->assertNotEmpty($readback); + + foreach ($test->formdata as $key => $value) { + // If the value is the same as the quiz, we expect it to be null. + if (!empty(self::TEST_QUIZ_SETTINGS[$key]) && $value == self::TEST_QUIZ_SETTINGS[$key]) { + $this->assertNull($readback->{$key}); + } else { + // Else we expect the value to have been set. + $this->assertEquals($value, $readback->{$key}); + } + } + + // Check the get_all_overrides function returns this data as well. + $alloverrideids = array_column($manager->get_all_overrides(), 'id'); + + $this->assertCount($aftercount, $alloverrideids); + $this->assertTrue(in_array($id, $alloverrideids)); + + // Check that the calendar events are created as well. + // This is only if the times were set, and they were set differently to the default. + $expectedcount = 0; + + if (!empty($formdata['timeopen']) && $formdata['timeopen'] != self::TEST_QUIZ_SETTINGS['timeopen']) { + $expectedcount += 1; + } + + if (!empty($formdata['timeclose']) && $formdata['timeclose'] != self::TEST_QUIZ_SETTINGS['timeclose']) { + $expectedcount += 1; + } + + // Find all events. We assume the test event times do not exceed a time of 999. + $events = calendar_get_events(0, 999, [$test->user1->id], [$test->groupid1], false); + $this->assertCount($expectedcount, $events); + + // Check the expected event was also emitted. + if (!empty($expectedeventclass)) { + $events = $sink->get_events(); + $eventclasses = array_map(fn($event) => get_class($event), $events); + $this->assertTrue(in_array($expectedeventclass, $eventclasses)); + } + } + + /** + * Tests that when saving an override, validation is performed and an exception is thrown if this fails. + * Note - this does not test every validation scenario, for that {@see validate_data_provider} + */ + public function test_save_override_validation_throws_exception(): void { + $this->setAdminUser(); + $this->resetAfterTest(); + + [$quizobj] = $this->create_quiz_and_course(); + $manager = $quizobj->get_override_manager(); + + // Submit empty (bad data). + $this->expectException(\invalid_parameter_exception::class); + $this->expectExceptionMessage(get_string('nooverridedata', 'quiz')); + $manager->save_override([]); + } + + /** + * Provides values to test_validate_data + * + * @return array + */ + public static function validate_data_provider(): array { + return [ + 'valid create for user' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'expectedreturn' => [], + ], + 'valid create for group' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => null, + 'groupid' => ':groupid', + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'expectedreturn' => [], + ], + 'valid update for user' => [ + 'existingdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'formdata' => [ + 'id' => ':existingid', + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'expectedreturn' => [], + ], + 'valid update for group' => [ + 'existingdata' => [ + 'userid' => null, + 'groupid' => ':groupid', + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'formdata' => [ + 'id' => ':existingid', + 'userid' => null, + 'groupid' => ':groupid', + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'expectedreturn' => [], + ], + 'update but without user or group specified in update' => [ + 'existingdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'formdata' => [ + 'id' => ':existingid', + 'userid' => null, + 'groupid' => null, + 'timeopen' => 52, + 'timeclose' => 53, + 'timelimit' => 1, + 'attempts' => 999, + 'password' => 'test', + ], + 'expectedreturn' => [ + 'general' => get_string('overridemustsetuserorgroup', 'quiz'), + ], + ], + 'both userid and groupid specified' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => ':groupid', + 'timeopen' => 50, + 'timeclose' => 100, + 'timelimit' => null, + 'attempts' => null, + 'password' => null, + ], + 'expectedreturn' => [ + 'general' => get_string('overridecannotsetbothgroupanduser', 'quiz'), + ], + ], + 'neither userid nor groupid specified' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => null, + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 100, + 'timelimit' => null, + 'attempts' => null, + 'password' => null, + ], + 'expectedreturn' => [ + 'general' => get_string('overridemustsetuserorgroup', 'quiz'), + ], + ], + 'empty data' => [ + 'existingdata' => [], + 'formdata' => [], + 'expectedreturn' => [ + 'general' => get_string('nooverridedata', 'quiz'), + ], + ], + 'all nulls' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => null, + 'groupid' => null, + 'timeopen' => null, + 'timeclose' => null, + 'timelimit' => null, + 'attempts' => null, + 'password' => null, + ], + 'expectedreturn' => [ + 'general' => get_string('nooverridedata', 'quiz'), + ], + ], + 'user given, rest are nulls' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => null, + 'timeclose' => null, + 'timelimit' => null, + 'attempts' => null, + 'password' => null, + ], + 'expectedreturn' => [ + 'general' => get_string('nooverridedata', 'quiz'), + ], + ], + 'all submitted data was the same as the existing quiz' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => self::TEST_QUIZ_SETTINGS['timeopen'], + 'timeclose' => self::TEST_QUIZ_SETTINGS['timeclose'], + 'attempts' => self::TEST_QUIZ_SETTINGS['attempts'], + 'timelimit' => self::TEST_QUIZ_SETTINGS['timelimit'], + 'password' => null, + ], + 'expectedreturn' => [ + 'general' => get_string('nooverridedata', 'quiz'), + ], + ], + 'userid is invalid' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => -1, + 'groupid' => null, + 'timeopen' => null, + 'timeclose' => null, + 'timelimit' => null, + 'attempts' => null, + 'password' => 'mypass', + ], + 'expectedreturn' => [ + 'userid' => get_string('overrideinvaliduser', 'quiz'), + ], + ], + 'groupid is invalid' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => null, + 'groupid' => -1, + 'timeopen' => null, + 'timeclose' => null, + 'timelimit' => null, + 'attempts' => null, + 'password' => 'mypass', + ], + 'expectedreturn' => [ + 'groupid' => get_string('overrideinvalidgroup', 'quiz'), + ], + ], + 'timeclose is before timeopen' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 10, + 'timelimit' => null, + 'attempts' => null, + 'password' => null, + ], + 'expectedreturn' => [ + 'timeclose' => get_string('closebeforeopen', 'quiz'), + ], + ], + 'timeclose is same as timeopen' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 50, + 'timelimit' => null, + 'attempts' => null, + 'password' => null, + ], + 'expectedreturn' => [ + 'timeclose' => get_string('closebeforeopen', 'quiz'), + ], + ], + 'timelimit is negative' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => null, + 'timeclose' => null, + 'timelimit' => -1, + 'attempts' => null, + 'password' => null, + ], + 'expectedreturn' => [ + 'timelimit' => get_string('overrideinvalidtimelimit', 'quiz'), + ], + ], + 'attempts is negative' => [ + 'existingdata' => [], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => null, + 'timeclose' => null, + 'timelimit' => null, + 'attempts' => -1, + 'password' => null, + ], + 'expectedreturn' => [ + 'attempts' => get_string('overrideinvalidattempts', 'quiz'), + ], + ], + 'existing id given to update is invalid' => [ + 'existingdata' => [], + 'formdata' => [ + 'id' => 999999999999, + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => null, + 'attempts' => null, + 'password' => null, + ], + 'expectedreturn' => [ + 'general' => get_string('overrideinvalidexistingid', 'quiz'), + ], + ], + 'userid changed after creation' => [ + 'existingdata' => [ + 'userid' => ":userid", + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'formdata' => [ + 'id' => ':existingid', + 'userid' => ":user2id", + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => null, + 'attempts' => null, + 'password' => null, + ], + 'expectedreturn' => [ + 'userid' => get_string('overridecannotchange', 'quiz'), + ], + ], + 'groupid changed after creation' => [ + 'existingdata' => [ + 'userid' => null, + 'groupid' => ':groupid', + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'formdata' => [ + 'id' => ':existingid', + 'userid' => null, + 'groupid' => ':group2id', + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => null, + 'attempts' => null, + 'password' => null, + ], + 'expectedreturn' => [ + 'groupid' => get_string('overridecannotchange', 'quiz'), + ], + ], + 'create multiple for the same user' => [ + 'existingdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'formdata' => [ + 'userid' => ':userid', + 'groupid' => null, + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'expectedreturn' => [ + 'general' => get_string('overridemultiplerecordsexist', 'quiz'), + ], + ], + 'create multiple for the same group' => [ + 'existingdata' => [ + 'userid' => null, + 'groupid' => ':groupid', + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'formdata' => [ + 'userid' => null, + 'groupid' => ':groupid', + 'timeopen' => 50, + 'timeclose' => 51, + 'timelimit' => 2, + 'attempts' => 2, + 'password' => 'test2', + ], + 'expectedreturn' => [ + 'general' => get_string('overridemultiplerecordsexist', 'quiz'), + ], + ], + ]; + } + + /** + * Tests validate_data function + * + * @param array $existingdata If given, an existing override will be created. + * @param array $formdata The data being tested, simulating being submitted + * @param array $expectedreturn expected keys and associated values expected to be returned from validate_data + * @dataProvider validate_data_provider + */ + public function test_validate_data(array $existingdata, array $formdata, array $expectedreturn): void { + $this->setAdminUser(); + $this->resetAfterTest(); + + // Setup the test. + $test = $this->setup_existing_and_testing_data($existingdata, $formdata); + + // Validate. + $manager = $test->quizobj->get_override_manager(); + $result = $manager->validate_data($test->formdata); + + // Ensure all expected errors appear in the return. + foreach ($expectedreturn as $key => $error) { + // Ensure it is set. + $this->assertContains($key, array_keys($result)); + + // Ensure the message contains the expected error. + $this->assertStringContainsString($error, $result[$key]); + } + + // Ensure there are no extra returned errors than what was expected. + $extra = array_diff_key($result, $expectedreturn); + $this->assertEmpty($extra, 'More validation errors were returned than expected'); + } + + /** + * Provide delete functions to test + * + * @return array + */ + public static function delete_override_provider(): array { + return [ + 'delete by id (no events logged)' => [ + 'function' => fn($manager, $override) => $manager->delete_overrides_by_id([$override->id], false, false), + 'checkeventslogged' => false, + ], + 'delete single (no events logged)' => [ + 'function' => fn($manager, $override) => $manager->delete_overrides([$override], false, false), + 'checkeventslogged' => false, + ], + 'delete all (no events logged)' => [ + 'function' => fn($manager, $override) => $manager->delete_all_overrides(false, false), + 'checkeventslogged' => false, + ], + 'delete by id (events logged)' => [ + 'function' => fn($manager, $override) => $manager->delete_overrides_by_id([$override->id], true, false), + 'checkeventslogged' => true, + ], + 'delete single (events logged)' => [ + 'function' => fn($manager, $override) => $manager->delete_overrides([$override], true, false), + 'checkeventslogged' => true, + ], + 'delete all (events logged)' => [ + 'function' => fn($manager, $override) => $manager->delete_all_overrides(true, false), + 'checkeventslogged' => true, + ], + 'delete all in database (events logged)' => [ + 'function' => fn($manager, $override) => $manager->delete_all_overrides(true, false), + 'checkeventslogged' => true, + ], + ]; + } + + /** + * Tests deleting override functions + * + * @param \Closure $deletefunction delete function to be called. + * @param bool $checkeventslogged if true, will check that events were logged. + * @dataProvider delete_override_provider + */ + public function test_delete_override(\Closure $deletefunction, bool $checkeventslogged): void { + $this->setAdminUser(); + $this->resetAfterTest(); + [$quizobj] = $this->create_quiz_and_course(); + $user = $this->getDataGenerator()->create_user(); + + // Create an override. + $data = [ + 'userid' => $user->id, + 'timeopen' => 500, + ]; + $manager = $quizobj->get_override_manager(); + $id = $manager->save_override($data); + + // Check the calendar event was made. + $this->assertCount(1, calendar_get_events(0, 999, [$user->id], false, false)); + + // Check that the cache was made. + $overridecache = new override_cache($quizobj->get_quizid()); + $this->assertNotEmpty($overridecache->get_cached_user_override($user->id)); + + // Capture events. + $sink = $this->redirectEvents(); + + $override = (object) [ + 'id' => $id, + 'userid' => $user->id, + ]; + + // Delete the override. + $deletefunction($manager, $override); + + // Check the calendar event was deleted. + $this->assertCount(0, calendar_get_events(0, 999, [$user->id], false, false)); + + // Check that the cache was cleared. + $this->assertEmpty($overridecache->get_cached_user_override($user->id)); + + // Check the event was logged. + if ($checkeventslogged) { + $events = $sink->get_events(); + $eventclasses = array_map(fn($e) => get_class($e), $events); + $this->assertTrue(in_array(user_override_deleted::class, $eventclasses)); + } + } + + /** + * Creates a role with the given capabilities and assigns it to the user. + * + * @param int $userid user to assign role to + * @param array $capabilities array of $capname => $permission to add to role + */ + private function give_user_role_with_capabilities(int $userid, array $capabilities): void { + // Setup the role and permissions. + $roleid = $this->getDataGenerator()->create_role(); + foreach ($capabilities as $capname => $permission) { + role_change_permission($roleid, \context_system::instance(), $capname, $permission); + } + + $user = $this->getDataGenerator()->create_user(); + role_assign($roleid, $userid, \context_system::instance()->id); + } + + /** + * Provides values to test_require_read_capability + * + * @return array + */ + public static function require_read_capability_provider(): array { + $readfunc = fn($manager) => $manager->require_read_capability(); + $managefunc = fn($manager) => $manager->require_manage_capability(); + + return [ + 'reading - cannot read' => [ + 'capabilitiestogive' => [], + 'expectedallowed' => false, + 'functionbeingtested' => $readfunc, + ], + 'reading - can read' => [ + 'capabilitiestogive' => ['mod/quiz:viewoverrides' => CAP_ALLOW], + 'expectedallowed' => true, + 'functionbeingtested' => $readfunc, + ], + 'reading - can manage (so can also read)' => [ + 'capabilitiestogive' => ['mod/quiz:manageoverrides' => CAP_ALLOW], + 'expectedallowed' => true, + 'functionbeingtested' => $readfunc, + ], + 'manage - cannot manage' => [ + 'capabilitiestogive' => [], + 'expectedallowed' => false, + 'functionbeingtested' => $managefunc, + ], + 'manage - can only read' => [ + 'capabilitiestogive' => ['mod/quiz:viewoverrides' => CAP_ALLOW], + 'expectedallowed' => false, + 'functionbeingtested' => $managefunc, + ], + 'manage - can manage' => [ + 'capabilitiestogive' => ['mod/quiz:manageoverrides' => CAP_ALLOW], + 'expectedallowed' => true, + 'functionbeingtested' => $managefunc, + ], + ]; + } + + /** + * Tests require_read_capability + * + * @param array $capabilitiestogive array of capability => value to give to test user + * @param bool $expectedallowed if false, will expect required_capability_exception to be thrown + * @param \Closure $functionbeingtested is passed the manager and calls the function being tested (usually require_*_capability) + * @dataProvider require_read_capability_provider + */ + public function test_require_read_capability(array $capabilitiestogive, bool $expectedallowed, + \Closure $functionbeingtested): void { + $this->resetAfterTest(); + [$quizobj] = $this->create_quiz_and_course(); + $user = $this->getDataGenerator()->create_user(); + $this->give_user_role_with_capabilities($user->id, $capabilitiestogive); + $this->setUser($user); + + if (!$expectedallowed) { + $this->expectException(\required_capability_exception::class); + } + $functionbeingtested($quizobj->get_override_manager()); + } + + /** + * Tests delete_orphaned_group_overrides_in_course + */ + public function test_delete_orphaned_group_overrides_in_course(): void { + global $DB; + + $this->setAdminUser(); + $this->resetAfterTest(); + [$quizobj, $course] = $this->create_quiz_and_course(); + + // Create a two group and one user overrides. + $groupid = groups_create_group((object) ['courseid' => $course->id, 'name' => 'test']); + $groupdata = [ + 'quiz' => $quizobj->get_quizid(), + 'groupid' => $groupid, + 'password' => 'test', + ]; + + $group2id = groups_create_group((object) ['courseid' => $course->id, 'name' => 'test2']); + $group2data = [ + 'quiz' => $quizobj->get_quizid(), + 'groupid' => $group2id, + 'password' => 'test', + ]; + + $userid = $this->getDataGenerator()->create_user()->id; + $userdata = [ + 'quiz' => $quizobj->get_quizid(), + 'userid' => $userid, + 'password' => 'test', + ]; + + $manager = $quizobj->get_override_manager(); + $manager->save_override($groupdata); + $useroverrideid = $manager->save_override($userdata); + $group2overrideid = $manager->save_override($group2data); + + $this->assertCount(3, $manager->get_all_overrides()); + + // Delete the first group (via the DB, so that the callbacks are not run). + $DB->delete_records('groups', ['id' => $groupid]); + + // Confirm the overrides still exist (no callback has been run yet). + $this->assertCount(3, $manager->get_all_overrides()); + + // Run orphaned record callback. + override_manager::delete_orphaned_group_overrides_in_course($course->id); + + // Confirm it has now been deleted (but user and other group override still exists). + $overrides = $manager->get_all_overrides(); + $this->assertCount(2, $overrides); + $this->assertArrayHasKey($useroverrideid, $overrides); + $this->assertArrayHasKey($group2overrideid, $overrides); + } + + /** + * Tests deleting by id but providing an invalid id + */ + public function test_delete_by_id_invalid_id(): void { + $this->setAdminUser(); + $this->resetAfterTest(); + [$quizobj] = $this->create_quiz_and_course(); + + $this->expectException(\invalid_parameter_exception::class); + $this->expectExceptionMessage(get_string('overridemissingdelete', 'quiz', '0,1')); + + // These ids do not exist, so this should throw an error. + $quizobj->get_override_manager()->delete_overrides_by_id([0, 1]); + } + + /** + * Tests that constructing a override manager with mismatching quiz and context throws an exception + */ + public function test_quiz_context_mismatch(): void { + $this->resetAfterTest(); + + // Create one quiz for context, but make the quiz given have an incorrect cmid. + [$quizobj] = $this->create_quiz_and_course(); + $context = \context_module::instance($quizobj->get_cmid()); + + $quiz = (object)[ + 'cmid' => $context->instanceid + 1, + ]; + + $this->expectException(\coding_exception::class); + $this->expectExceptionMessage("Given context does not match the quiz object"); + new override_manager($quiz, $context); + } +} diff --git a/mod/quiz/tests/privacy/provider_test.php b/mod/quiz/tests/privacy/provider_test.php index 742cb6c672634..793d93eed5936 100644 --- a/mod/quiz/tests/privacy/provider_test.php +++ b/mod/quiz/tests/privacy/provider_test.php @@ -535,7 +535,7 @@ public function test_delete_data_for_users() { // Delete the data for user1 and user3 in course1 and check it is removed. $quiz1context = $quiz1obj->get_context(); $approveduserlist = new \core_privacy\local\request\approved_userlist($quiz1context, 'mod_quiz', - [$user1->id, $user3->id]); + [$user1->id, $user3->id]); provider::delete_data_for_users($approveduserlist); // Only the attempt of user2 should be remained in quiz1. diff --git a/mod/quiz/upgrade.txt b/mod/quiz/upgrade.txt index e91e37fd1fc5e..f5ce83ccf2316 100644 --- a/mod/quiz/upgrade.txt +++ b/mod/quiz/upgrade.txt @@ -22,6 +22,12 @@ This file describes API changes in the quiz code. * The following previously deprecated methods have been removed and can no longer be used: - `get_slot_tags_for_slot_id` - `quiz_retrieve_tags_for_slot_ids` +* Quiz override logic has been refactored and put in a new override_manager class. +* Quiz override cache should now be accessed using the new override_cache class. +* The quiz_delete_overrides and quiz_delete_all_overrides functions are now deprecated. Please instead use: + - override_manager::delete_override_by_id + - override_manager::delete_overrides + - override_manager::delete_all_overrides === 4.3 === diff --git a/mod/quiz/version.php b/mod/quiz/version.php index 33a399048fe87..17af57b3c3c71 100644 --- a/mod/quiz/version.php +++ b/mod/quiz/version.php @@ -24,6 +24,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2023112300; +$plugin->version = 2023112303; $plugin->requires = 2023100400; $plugin->component = 'mod_quiz'; From 68dcb5d736cb42db74845309708b475fe002b5d1 Mon Sep 17 00:00:00 2001 From: Matthew Hilton Date: Mon, 5 Feb 2024 10:08:17 +1000 Subject: [PATCH 07/62] MDL-80300 mod_quiz: Add quiz override webservices --- .../classes/external/delete_overrides.php | 77 ++++++ mod/quiz/classes/external/get_overrides.php | 82 +++++++ mod/quiz/classes/external/save_overrides.php | 90 +++++++ mod/quiz/db/services.php | 24 ++ mod/quiz/tests/external/override_test.php | 223 ++++++++++++++++++ 5 files changed, 496 insertions(+) create mode 100644 mod/quiz/classes/external/delete_overrides.php create mode 100644 mod/quiz/classes/external/get_overrides.php create mode 100644 mod/quiz/classes/external/save_overrides.php create mode 100644 mod/quiz/tests/external/override_test.php diff --git a/mod/quiz/classes/external/delete_overrides.php b/mod/quiz/classes/external/delete_overrides.php new file mode 100644 index 0000000000000..17ba19f991009 --- /dev/null +++ b/mod/quiz/classes/external/delete_overrides.php @@ -0,0 +1,77 @@ +. + +namespace mod_quiz\external; + +use core_external\external_api; +use core_external\external_function_parameters; +use core_external\external_multiple_structure; +use core_external\external_single_structure; +use core_external\external_value; +use mod_quiz\quiz_settings; + +/** + * Webservice for deleting quiz overrides. + * + * @package mod_quiz + * @copyright 2024 Matthew Hilton + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class delete_overrides extends external_api { + /** + * Defines parameters + * + * @return external_function_parameters + */ + public static function execute_parameters(): external_function_parameters { + return new external_function_parameters([ + // This must be nested in a single structure, because the ids structure does not play nicely at the top level. + 'data' => new external_single_structure([ + 'quizid' => new external_value(PARAM_INT, "ID of quiz to delete overrides in"), + 'ids' => new external_multiple_structure(new external_value(PARAM_INT, 'ID of override to delete')), + ]), + ]); + } + + /** + * Executes webservice function, deleting given overrides. + * + * @param array $params array of override parameters + * @return array with ids key, which contains the ids of the overrides successfully deleted. + */ + public static function execute($params): array { + $params = self::validate_parameters(self::execute_parameters(), ['data' => $params])['data']; + + $quizsettings = quiz_settings::create($params['quizid']); + $manager = $quizsettings->get_override_manager(); + self::validate_context($manager->context); + $manager->require_manage_capability(); + $manager->delete_overrides_by_id($params['ids']); + + return ['ids' => $params['ids']]; + } + + /** + * Defines return type + * + * @return external_single_structure + */ + public static function execute_returns(): external_single_structure { + return new external_single_structure([ + 'ids' => new external_multiple_structure(new external_value(PARAM_INT, 'ID of deleted override')), + ]); + } +} diff --git a/mod/quiz/classes/external/get_overrides.php b/mod/quiz/classes/external/get_overrides.php new file mode 100644 index 0000000000000..373106f928bad --- /dev/null +++ b/mod/quiz/classes/external/get_overrides.php @@ -0,0 +1,82 @@ +. + +namespace mod_quiz\external; + +use core_external\external_api; +use core_external\external_function_parameters; +use core_external\external_multiple_structure; +use core_external\external_single_structure; +use core_external\external_value; +use mod_quiz\quiz_settings; + +/** + * Webservice for searching overrides. + * + * @package mod_quiz + * @copyright 2024 Matthew Hilton + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class get_overrides extends external_api { + /** + * Defines parameters for getting quiz overrides. + * + * @return external_function_parameters + */ + public static function execute_parameters(): external_function_parameters { + return new external_function_parameters([ + 'quizid' => new external_value(PARAM_INT, 'ID of quiz to get overrides for'), + ]); + } + + /** + * Executes webservice function, returning quiz overrides. + * + * @param int $quizid + * @return array with overrides key which contains the overrides for the given quiz. + */ + public static function execute($quizid): array { + $params = self::validate_parameters(self::execute_parameters(), ['quizid' => $quizid]); + $manager = quiz_settings::create($params['quizid'])->get_override_manager(); + self::validate_context($manager->context); + $manager->require_read_capability(); + $overrides = $manager->get_all_overrides(); + return ['overrides' => $overrides]; + } + + /** + * Defines return type + * + * @return external_single_structure + */ + public static function execute_returns(): external_single_structure { + $overridedatastructure = new external_single_structure([ + 'id' => new external_value(PARAM_INT, 'Override ID'), + 'quiz' => new external_value(PARAM_INT, 'Quiz ID'), + 'userid' => new external_value(PARAM_INT, 'User ID', VALUE_DEFAULT, null), + 'groupid' => new external_value(PARAM_INT, 'Group ID', VALUE_DEFAULT, null), + 'timeopen' => new external_value(PARAM_INT, 'Override time open value', VALUE_DEFAULT, null), + 'timeclose' => new external_value(PARAM_INT, 'Override time close value', VALUE_DEFAULT, null), + 'timelimit' => new external_value(PARAM_INT, 'Override time limit value', VALUE_DEFAULT, null), + 'attempts' => new external_value(PARAM_INT, 'Override attempts value', VALUE_DEFAULT, null), + 'password' => new external_value(PARAM_TEXT, 'Override password', VALUE_DEFAULT, null), + ]); + + return new external_single_structure([ + 'overrides' => new external_multiple_structure($overridedatastructure), + ]); + } +} diff --git a/mod/quiz/classes/external/save_overrides.php b/mod/quiz/classes/external/save_overrides.php new file mode 100644 index 0000000000000..40c5029d7ba42 --- /dev/null +++ b/mod/quiz/classes/external/save_overrides.php @@ -0,0 +1,90 @@ +. + +namespace mod_quiz\external; + +use core_external\external_api; +use core_external\external_function_parameters; +use core_external\external_multiple_structure; +use core_external\external_single_structure; +use core_external\external_value; +use mod_quiz\quiz_settings; + +/** + * Webservice for upserting quiz overrides. + * + * @package mod_quiz + * @copyright 2024 Matthew Hilton + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class save_overrides extends external_api { + /** + * Defines parameters + * + * @return external_function_parameters + */ + public static function execute_parameters(): external_function_parameters { + $overridestructure = new external_single_structure([ + 'id' => new external_value(PARAM_INT, 'ID of existing override (if updating)', VALUE_DEFAULT, null), + 'groupid' => new external_value(PARAM_INT, 'ID of group', VALUE_DEFAULT, null), + 'userid' => new external_value(PARAM_INT, 'ID of user', VALUE_DEFAULT, null), + 'timeopen' => new external_value(PARAM_INT, 'Quiz override opening timestamp', VALUE_DEFAULT, null), + 'timeclose' => new external_value(PARAM_INT, 'Quiz override closing timestamp', VALUE_OPTIONAL, null), + 'timelimit' => new external_value(PARAM_INT, 'Quiz override time limit', VALUE_DEFAULT, null), + 'attempts' => new external_value(PARAM_INT, 'Quiz override attempt count', VALUE_DEFAULT, null), + 'password' => new external_value(PARAM_TEXT, 'Quiz override password', VALUE_DEFAULT, null), + ]); + + return new external_function_parameters([ + // This must be nested in a single structure, because the overrides structure does not play nicely at the top level. + 'data' => new external_single_structure([ + 'quizid' => new external_value(PARAM_INT, 'ID of quiz to save overrides to'), + 'overrides' => new external_multiple_structure($overridestructure), + ]), + ]); + } + + /** + * Executes webservice function, saving the requested overrides. + * + * @param array $data array with quizid key and overrides key containing list of overrides to save. + * @return array with ids key which contains ids of created/updated overrides. + */ + public static function execute($data): array { + $params = self::validate_parameters(self::execute_parameters(), ['data' => $data])['data']; + + $quizsettings = quiz_settings::create($params['quizid']); + $manager = $quizsettings->get_override_manager(); + self::validate_context($manager->context); + $manager->require_manage_capability(); + + // Iterate over and save all overrides. + $ids = array_map(fn($override) => $manager->save_override($override), $params['overrides']); + + return ['ids' => $ids]; + } + + /** + * Defines return type + * + * @return external_single_structure + */ + public static function execute_returns(): external_single_structure { + return new external_single_structure([ + 'ids' => new external_multiple_structure(new external_value(PARAM_INT, 'ID of created/updated override')), + ]); + } +} diff --git a/mod/quiz/db/services.php b/mod/quiz/db/services.php index 2d6545bce49a4..bf366fdb3de09 100644 --- a/mod/quiz/db/services.php +++ b/mod/quiz/db/services.php @@ -231,4 +231,28 @@ 'capabilities' => 'mod/quiz:manage', 'ajax' => true, ], + + 'mod_quiz_save_overrides' => [ + 'classname' => 'mod_quiz\external\save_overrides', + 'description' => 'Update or insert quiz overrides', + 'type' => 'write', + 'capabilities' => 'mod/quiz:manageoverrides', + 'ajax' => true, + ], + + 'mod_quiz_delete_overrides' => [ + 'classname' => 'mod_quiz\external\delete_overrides', + 'description' => 'Delete quiz overrides', + 'type' => 'write', + 'capabilities' => 'mod/quiz:manageoverrides', + 'ajax' => true, + ], + + 'mod_quiz_get_overrides' => [ + 'classname' => 'mod_quiz\external\get_overrides', + 'description' => 'Get quiz overrides', + 'type' => 'read', + 'capabilities' => 'mod/quiz:manageoverrides', + 'ajax' => true, + ], ]; diff --git a/mod/quiz/tests/external/override_test.php b/mod/quiz/tests/external/override_test.php new file mode 100644 index 0000000000000..4cd6b332945da --- /dev/null +++ b/mod/quiz/tests/external/override_test.php @@ -0,0 +1,223 @@ +. + +namespace mod_quiz\external; + +defined('MOODLE_INTERNAL') || die(); + +require_once(__DIR__ . '/../../../../webservice/tests/helpers.php'); + +/** + * Tests for override webservices + * + * @package mod_quiz + * @copyright 2024 Matthew Hilton + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @covers \mod_quiz\external\get_overrides + * @covers \mod_quiz\external\save_overrides + * @covers \mod_quiz\external\delete_overrides + */ +final class override_test extends \externallib_advanced_testcase { + /** + * Creates a quiz for testing. + * + * @return object $quiz + */ + private function create_quiz(): object { + $course = $this->getDataGenerator()->create_course(); + return $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]); + } + + /** + * Provides values to test_get_overrides + * + * @return array + */ + public static function get_override_provider(): array { + return [ + 'quiz that exists' => [ + 'quizid' => ':quizid', + ], + 'quiz that does not exist' => [ + 'quizid' => -1, + 'expectedexception' => \dml_missing_record_exception::class, + ], + ]; + } + + /** + * Tests get_overrides + * + * @param int|string $quizid + * @param string $expectedexception + * @dataProvider get_override_provider + */ + public function test_get_overrides(int|string $quizid, string $expectedexception = ''): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + $quiz = $this->create_quiz(); + + // Create an override. + $DB->insert_record('quiz_overrides', ['quiz' => $quiz->id]); + + // Replace placeholders. + if ($quizid == ":quizid") { + $quizid = $quiz->id; + } + + if (!empty($expectedexception)) { + $this->expectException($expectedexception); + } + + $result = get_overrides::execute($quizid); + $this->assertNotEmpty($result); + } + + /** + * Provides values to test_save_overrides + * + * @return array + */ + public static function save_overrides_provider(): array { + return [ + 'good insert' => [ + 'data' => [ + 'timeopen' => 999, + ], + ], + 'bad insert' => [ + 'data' => [ + 'id' => ':existingid', + 'timeopen' => -1, + ], + 'expectedexception' => \invalid_parameter_exception::class, + ], + 'good update' => [ + 'data' => [ + 'timeopen' => 999, + ], + ], + 'bad update' => [ + 'data' => [ + 'id' => ':existingid', + 'timeopen' => -1, + ], + 'expectedexception' => \invalid_parameter_exception::class, + ], + ]; + } + + /** + * Tests save_overrides + * + * @dataProvider save_overrides_provider + * @param array $data + * @param string $expectedexception + */ + public function test_save_overrides(array $data, string $expectedexception = ''): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + $quiz = $this->create_quiz(); + $user = $this->getDataGenerator()->create_user(); + + if (!empty($data['id'])) { + $data['id'] = $DB->insert_record('quiz_overrides', ['quiz' => $quiz->id, 'userid' => $user->id]); + } + + // Make a new user to insert a new override for. + $user = $this->getDataGenerator()->create_user(); + $data = array_merge($data, ['userid' => $user->id]); + + $payload = [ + 'quizid' => $quiz->id, + 'overrides' => [ + $data, + ], + ]; + + if (!empty($expectedexception)) { + $this->expectException($expectedexception); + } + + $result = save_overrides::execute($payload); + + // If has reached here, but not thrown exception and was expected to, fail the test. + if ($expectedexception) { + $this->fail("Expected exception " . $expectedexception . " was not thrown"); + } + + $this->assertNotEmpty($result['ids']); + $this->assertCount(1, $result['ids']); + } + + /** + * Provides values to test_delete_overrides + * + * @return array + */ + public static function delete_overrides_provider(): array { + return [ + 'delete existing override' => [ + 'id' => ':existingid', + ], + 'delete override that does not exist' => [ + 'id' => -1, + 'expectedexception' => \invalid_parameter_exception::class, + ], + ]; + } + + /** + * Tests delete_overrides + * + * @dataProvider delete_overrides_provider + * @param int|string $id + * @param string $expectedexception + */ + public function test_delete_overrides(int|string $id, string $expectedexception = ''): void { + global $DB; + + $this->resetAfterTest(); + $this->setAdminUser(); + + $quiz = $this->create_quiz(); + $user = $this->getDataGenerator()->create_user(); + + if ($id == ':existingid') { + $id = $DB->insert_record('quiz_overrides', ['quiz' => $quiz->id, 'userid' => $user->id]); + } + + if (!empty($expectedexception)) { + $this->expectException($expectedexception); + } + + $result = delete_overrides::execute(['quizid' => $quiz->id, 'ids' => [$id]]); + + // If has reached here, but not thrown exception and was expected to, fail the test. + if ($expectedexception) { + $this->fail("Expected exception " . $expectedexception . " was not thrown"); + } + + $this->assertNotEmpty($result['ids']); + $this->assertContains($id, $result['ids']); + } +} From 3a1ea0c945eee5983576cfb1ef3389c5c39022e8 Mon Sep 17 00:00:00 2001 From: Matthew Hilton Date: Tue, 13 Feb 2024 12:59:05 +1000 Subject: [PATCH 08/62] MDL-80300 mod_quiz: Updated override edit form validation --- mod/quiz/classes/form/edit_override_form.php | 52 ++++++++------------ 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/mod/quiz/classes/form/edit_override_form.php b/mod/quiz/classes/form/edit_override_form.php index c227e5e0f40bd..d15d71cc54c07 100644 --- a/mod/quiz/classes/form/edit_override_form.php +++ b/mod/quiz/classes/form/edit_override_form.php @@ -56,6 +56,9 @@ class edit_override_form extends moodleform { /** @var int userid, if provided. */ protected $userid; + /** @var int overrideid, if provided. */ + protected $overrideid; + /** * Constructor. * @@ -76,6 +79,7 @@ public function __construct(moodle_url $submiturl, $this->groupmode = $groupmode; $this->groupid = empty($override->groupid) ? 0 : $override->groupid; $this->userid = empty($override->userid) ? 0 : $override->userid; + $this->overrideid = $override->id ?? 0; parent::__construct($submiturl); } @@ -261,43 +265,29 @@ public static function display_user_name(stdClass $user, array $extrauserfields) return $username; } + /** + * Validate the data from the form. + * + * @param array $data form data + * @param array $files form files + * @return array An array of error messages, where the key is is the mform element name and the value is the error. + */ public function validation($data, $files): array { $errors = parent::validation($data, $files); + $data['id'] = $this->overrideid; + $data['quiz'] = $this->quiz->id; - $mform =& $this->_form; - $quiz = $this->quiz; + $manager = new \mod_quiz\local\override_manager($this->quiz, $this->context); + $errors = array_merge($errors, $manager->validate_data($data)); - if ($mform->elementExists('userid')) { - if (empty($data['userid'])) { - $errors['userid'] = get_string('required'); - } - } - - if ($mform->elementExists('groupid')) { - if (empty($data['groupid'])) { - $errors['groupid'] = get_string('required'); - } - } - - // Ensure that the dates make sense. - if (!empty($data['timeopen']) && !empty($data['timeclose'])) { - if ($data['timeclose'] < $data['timeopen'] ) { - $errors['timeclose'] = get_string('closebeforeopen', 'quiz'); - } - } - - // Ensure that at least one quiz setting was changed. - $changed = false; - $keys = ['timeopen', 'timeclose', 'timelimit', 'attempts', 'password']; - foreach ($keys as $key) { - if ($data[$key] != $quiz->{$key}) { - $changed = true; - break; + // Any 'general' errors we merge with the group/user selector element. + if (!empty($errors['general'])) { + if ($this->groupmode) { + $errors['groupid'] = $errors['groupid'] ?? "" . $errors['general']; + } else { + $errors['userid'] = $errors['userid'] ?? "" . $errors['general']; } } - if (!$changed) { - $errors['timeopen'] = get_string('nooverridedata', 'quiz'); - } return $errors; } From 388cc303f9df28fd6a1f8a686a0a379ec47e0f42 Mon Sep 17 00:00:00 2001 From: Huong Nguyen Date: Fri, 15 Mar 2024 11:07:51 +0700 Subject: [PATCH 09/62] MDL-81246 dml: Check the statement resource before freeing it --- lib/dml/sqlsrv_native_moodle_database.php | 4 +++- lib/dml/sqlsrv_native_moodle_recordset.php | 10 ++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/dml/sqlsrv_native_moodle_database.php b/lib/dml/sqlsrv_native_moodle_database.php index d7c899fd2092c..bf189e2a10c7c 100644 --- a/lib/dml/sqlsrv_native_moodle_database.php +++ b/lib/dml/sqlsrv_native_moodle_database.php @@ -680,9 +680,11 @@ protected function normalise_value($column, $value) { * @return bool */ private function free_result($resource) { - if (!is_bool($resource)) { // true/false resources cannot be freed + if (!is_bool($resource) && is_resource($resource)) { + // We need to make sure that the statement resource is in the correct type before freeing it. return sqlsrv_free_stmt($resource); } + return false; } /** diff --git a/lib/dml/sqlsrv_native_moodle_recordset.php b/lib/dml/sqlsrv_native_moodle_recordset.php index 9653a87bec43f..aa4a646e1b98d 100644 --- a/lib/dml/sqlsrv_native_moodle_recordset.php +++ b/lib/dml/sqlsrv_native_moodle_recordset.php @@ -85,7 +85,10 @@ private function fetch_next() { return false; } if (!$row = sqlsrv_fetch_array($this->rsrc, SQLSRV_FETCH_ASSOC)) { - sqlsrv_free_stmt($this->rsrc); + if (is_resource($this->rsrc)) { + // We need to make sure that the statement resource is in the correct type before freeing it. + sqlsrv_free_stmt($this->rsrc); + } $this->rsrc = null; $this->unregister(); return false; @@ -133,7 +136,10 @@ public function valid(): bool { public function close() { if ($this->rsrc) { - sqlsrv_free_stmt($this->rsrc); + if (is_resource($this->rsrc)) { + // We need to make sure that the statement resource is in the correct type before freeing it. + sqlsrv_free_stmt($this->rsrc); + } $this->rsrc = null; } $this->current = null; From 4a1544cfdca6451f8e02af2ab81c7eb91997e5bd Mon Sep 17 00:00:00 2001 From: "Shamiso.Jaravaza" <33659194+ssj365@users.noreply.github.com> Date: Mon, 11 Mar 2024 12:36:56 -0600 Subject: [PATCH 10/62] MDL-79239 mod_bigbluebuttonbn: Remove DPA * Removed dpa * Plugin remains disabled by default --- mod/bigbluebuttonbn/classes/local/config.php | 2 - mod/bigbluebuttonbn/classes/settings.php | 19 ------ ...luebutton_module_disabled_notification.php | 23 ++----- .../lang/en/bigbluebuttonbn.php | 15 +++-- mod/bigbluebuttonbn/lang/en/deprecated.txt | 7 ++ mod/bigbluebuttonbn/lib.php | 22 ------ .../tests/behat/add_instance.feature | 2 +- .../tests/behat/behat_mod_bigbluebuttonbn.php | 10 --- .../tests/behat/completion.feature | 2 +- .../tests/behat/edit_instance.feature | 2 +- .../tests/behat/end_meeting.feature | 2 +- .../tests/behat/group_mode.feature | 2 +- .../tests/behat/guest_access.feature | 2 +- .../tests/behat/join_meeting.feature | 2 +- .../tests/behat/join_meeting_as_guest.feature | 2 +- .../tests/behat/lock_settings.feature | 2 +- .../tests/behat/meeting_roles.feature | 2 +- .../tests/behat/recordings.feature | 2 +- .../tests/behat/recordings_import.feature | 2 +- mod/bigbluebuttonbn/tests/behat/roles.feature | 2 +- mod/bigbluebuttonbn/tests/behat/room.feature | 2 +- .../tests/behat/start_meeting.feature | 2 +- .../tests/behat/subplugins.feature | 2 +- mod/bigbluebuttonbn/tests/generator/lib.php | 1 - mod/bigbluebuttonbn/tests/lib_test.php | 67 ------------------- .../tests/local/extension_test.php | 1 - 26 files changed, 36 insertions(+), 163 deletions(-) diff --git a/mod/bigbluebuttonbn/classes/local/config.php b/mod/bigbluebuttonbn/classes/local/config.php index 9a27d3fc56fbe..c209a37f1bc55 100644 --- a/mod/bigbluebuttonbn/classes/local/config.php +++ b/mod/bigbluebuttonbn/classes/local/config.php @@ -36,8 +36,6 @@ class config { /** @var string Default bigbluebutton server shared secret */ public const DEFAULT_SHARED_SECRET = '0b21fcaf34673a8c3ec8ed877d76ae34'; - /** @var string Default bigbluebutton data processing agreement url */ - public const DEFAULT_DPA_URL = 'https://blindsidenetworks.com/dpa-moodle-free-tier'; /** @var string the default bigbluebutton checksum algorithm */ public const DEFAULT_CHECKSUM_ALGORITHM = 'SHA256'; diff --git a/mod/bigbluebuttonbn/classes/settings.php b/mod/bigbluebuttonbn/classes/settings.php index 79f9a86ed0dab..ae5584df96b85 100644 --- a/mod/bigbluebuttonbn/classes/settings.php +++ b/mod/bigbluebuttonbn/classes/settings.php @@ -167,15 +167,6 @@ protected function add_general_settings(): admin_settingpage { ); $settingsgeneral->add($item); - if (empty($CFG->bigbluebuttonbn_default_dpa_accepted)) { - $settingsgeneral->add(new admin_setting_configcheckbox( - 'bigbluebuttonbn_default_dpa_accepted', - get_string('acceptdpa', 'mod_bigbluebuttonbn'), - get_string('enablingbigbluebuttondpainfo', 'mod_bigbluebuttonbn', config::DEFAULT_DPA_URL), - 0 - )); - } - $item = new admin_setting_configtext( 'bigbluebuttonbn_server_url', get_string('config_server_url', 'bigbluebuttonbn'), @@ -220,16 +211,6 @@ function() { $settingsgeneral ); - $item = new \admin_setting_description( - 'bigbluebuttonbn_dpa_info', - '', - get_string('config_dpa_note', 'bigbluebuttonbn', config::DEFAULT_DPA_URL), - ); - $this->add_conditional_element( - 'dpa_info', - $item, - $settingsgeneral - ); $item = new admin_setting_configtext( 'bigbluebuttonbn_poll_interval', get_string('config_poll_interval', 'bigbluebuttonbn'), diff --git a/mod/bigbluebuttonbn/classes/task/send_bigbluebutton_module_disabled_notification.php b/mod/bigbluebuttonbn/classes/task/send_bigbluebutton_module_disabled_notification.php index 0da79bad316d7..cb5fffcdcbdf7 100644 --- a/mod/bigbluebuttonbn/classes/task/send_bigbluebutton_module_disabled_notification.php +++ b/mod/bigbluebuttonbn/classes/task/send_bigbluebutton_module_disabled_notification.php @@ -23,7 +23,7 @@ use mod_bigbluebuttonbn\local\config; /** - * Ad-hoc task to send a notification related to the disabling of the BigBlueButton activity module. + * Deprecated Ad-hoc task to send a notification related to the disabling of the BigBlueButton activity module. * * The ad-hoc tasks sends a notification to the administrator informing that the BigBlueButton activity module has * been disabled and they are required to confirm their acceptance of the data processing agreement prior to @@ -34,25 +34,12 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class send_bigbluebutton_module_disabled_notification extends adhoc_task { - /** * Execute the task. */ - public function execute(): void { - $message = new message(); - $message->component = 'moodle'; - $message->name = 'notices'; - $message->userfrom = \core_user::get_noreply_user(); - $message->userto = get_admin(); - $message->notification = 1; - $message->contexturl = (new \moodle_url('/admin/modules.php'))->out(false); - $message->contexturlname = get_string('modsettings', 'admin'); - $message->subject = get_string('bigbluebuttondisablednotification_subject', 'mod_bigbluebuttonbn'); - $message->fullmessageformat = FORMAT_HTML; - $message->fullmessagehtml = get_string('bigbluebuttondisablednotification', 'mod_bigbluebuttonbn', - config::DEFAULT_DPA_URL); - $message->smallmessage = strip_tags($message->fullmessagehtml); - - message_send($message); + public function execute() { + // Log the debug message. + $message = "Attempted to run deprecated send_bigbluebutton_module_disabled_notification task."; + debugging($message, DEBUG_DEVELOPER); } } diff --git a/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php b/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php index e47c59ce9d86c..2c87c32c0ceb0 100644 --- a/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php +++ b/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php @@ -26,7 +26,6 @@ defined('MOODLE_INTERNAL') || die(); $string['activityoverview'] = 'You have upcoming BigBlueButton sessions'; -$string['acceptdpa'] = 'I understand and accept the data processing agreement'; $string['bbbduetimeoverstartingtime'] = 'The close time must be later than the open time.'; $string['bbbdurationwarning'] = 'The maximum duration for this session is %duration% minutes.'; $string['bbbrecordwarning'] = 'This session may be recorded.'; @@ -45,12 +44,7 @@ $string['bigbluebuttonbn:importrecordings'] = 'Import recordings'; $string['bigbluebuttonbn:viewallrecordingformats'] = 'View all recording formats'; $string['bigbluebuttonbn'] = 'BigBlueButton'; -$string['bigbluebuttondisablednotification_subject'] = 'BigBlueButton activity module disabled.'; -$string['bigbluebuttondisablednotification'] = 'The BigBlueButton activity module has been disabled and any existing BigBlueButton course activities are currently not accessible. Prior to re-enabling this plugin, please ensure that you have read and accepted the data processing agreement with Blindside Networks Inc.'; $string['cannotperformaction'] = 'Cannot perform action {$a} on this recording'; -$string['enablingbigbluebutton'] = 'Enabling BigBlueButton activity'; -$string['enablingbigbluebuttondpainfo'] = 'In order to meet your data protection obligations, prior to enabling this plugin, you may need to ensure that you have read and accepted the Blindside Networks data processing agreement. Please consult with your own privacy professionals for advice.'; -$string['dpainfonotsigned'] = 'Before enabling this plugin, you must confirm that you have read and accepted the Blindside Networks data processing agreement.'; $string['indicator:cognitivedepth'] = 'BigBlueButton cognitive'; $string['indicator:cognitivedepth_help'] = 'This indicator is based on the cognitive depth reached by the student in a BigBlueButton activity.'; $string['indicator:socialbreadth'] = 'BigBlueButton social'; @@ -127,7 +121,6 @@ $string['minute'] = 'minute'; $string['minutes'] = 'minutes'; -$string['config_dpa_note'] = 'Note: In order to meet your data protection obligations, before using a service provider for this plugin, you must ensure that you have read and accepted the service provider\'s data processing agreement. For the default free BigBlueButton service, this is the Blindside Networks data processing agreement. Please consult with your own privacy professionals for advice.'; $string['config_guestaccess_enabled'] = 'External guest access'; $string['config_guestaccess_enabled_description'] = 'Allow users without an account on your site to access the room.'; @@ -667,3 +660,11 @@ $string['completionview_desc'] = 'View the room'; $string['completionattendancegroup_help'] = 'Attending the meeting for (n) minutes is required for completion.'; $string['completionengagementgroup_help'] = 'Active participation during the session is required for completion.'; +// Deprecated since Moodle 4.4 +$string['acceptdpa'] = 'I understand and accept the data processing agreement'; +$string['bigbluebuttondisablednotification_subject'] = 'BigBlueButton activity module disabled.'; +$string['bigbluebuttondisablednotification'] = 'The BigBlueButton activity module has been disabled and any existing BigBlueButton course activities are currently not accessible. Prior to re-enabling this plugin, please ensure that you have read and accepted the data processing agreement with Blindside Networks Inc.'; +$string['enablingbigbluebutton'] = 'Enabling BigBlueButton activity'; +$string['enablingbigbluebuttondpainfo'] = 'In order to meet your data protection obligations, prior to enabling this plugin, you may need to ensure that you have read and accepted the Blindside Networks data processing agreement. Please consult with your own privacy professionals for advice.'; +$string['dpainfonotsigned'] = 'Before enabling this plugin, you must confirm that you have read and accepted the Blindside Networks data processing agreement.'; +$string['config_dpa_note'] = 'Note: In order to meet your data protection obligations, before using a service provider for this plugin, you must ensure that you have read and accepted the service provider\'s data processing agreement. For the default free BigBlueButton service, this is the Blindside Networks data processing agreement. Please consult with your own privacy professionals for advice.'; diff --git a/mod/bigbluebuttonbn/lang/en/deprecated.txt b/mod/bigbluebuttonbn/lang/en/deprecated.txt index d9ce987c4e95a..ae08997abc186 100644 --- a/mod/bigbluebuttonbn/lang/en/deprecated.txt +++ b/mod/bigbluebuttonbn/lang/en/deprecated.txt @@ -2,3 +2,10 @@ completionview,mod_bigbluebuttonbn completionview_desc,mod_bigbluebuttonbn completionattendancegroup_help,mod_bigbluebuttonbn completionengagementgroup_help,mod_bigbluebuttonbn +acceptdpa,mod_bigbluebuttonbn +bigbluebuttondisablednotification_subject,mod_bigbluebuttonbn +bigbluebuttondisablednotification,mod_bigbluebuttonbn +enablingbigbluebutton,mod_bigbluebuttonbn +enablingbigbluebuttondpainfo,mod_bigbluebuttonbn +dpainfonotsigned,mod_bigbluebuttonbn +config_dpa_note,mod_bigbluebuttonbn diff --git a/mod/bigbluebuttonbn/lib.php b/mod/bigbluebuttonbn/lib.php index b9ad514bca63c..e7c649ba2714b 100644 --- a/mod/bigbluebuttonbn/lib.php +++ b/mod/bigbluebuttonbn/lib.php @@ -719,28 +719,6 @@ function bigbluebuttonbn_print_recent_activity(object $course, bool $viewfullnam return true; } -/** - * Callback method executed prior to enabling the activity module. - * - * @return bool Whether to proceed and enable the plugin or not. - */ -function bigbluebuttonbn_pre_enable_plugin_actions(): bool { - global $PAGE; - - // If the default server configuration is used and the administrator has not accepted the default data processing - // agreement, do not enable the plugin. Instead, display a dynamic form where the administrator can confirm that he - // accepts the DPA prior to enabling the plugin. - if (config::get('server_url') === config::DEFAULT_SERVER_URL && !config::get('default_dpa_accepted')) { - $url = new moodle_url('/admin/category.php', ['category' => 'modbigbluebuttonbnfolder']); - \core\notification::add( - get_string('dpainfonotsigned', 'mod_bigbluebuttonbn', $url->out(false)), - \core\notification::ERROR - ); - return false; - } - // Otherwise, continue and enable the plugin. - return true; -} /** * Creates a number of BigblueButtonBN activities. diff --git a/mod/bigbluebuttonbn/tests/behat/add_instance.feature b/mod/bigbluebuttonbn/tests/behat/add_instance.feature index 602f593725c72..2e84cca6a3cdc 100644 --- a/mod/bigbluebuttonbn/tests/behat/add_instance.feature +++ b/mod/bigbluebuttonbn/tests/behat/add_instance.feature @@ -6,7 +6,7 @@ Feature: bigbluebuttonbn instance Background: Make sure that a course is created Given a BigBlueButton mock server is configured - And I accept dpa and enable bigbluebuttonbn plugin + And I enable "bigbluebuttonbn" "mod" plugin And the following "courses" exist: | fullname | shortname | category | | Test course | Test course | 0 | diff --git a/mod/bigbluebuttonbn/tests/behat/behat_mod_bigbluebuttonbn.php b/mod/bigbluebuttonbn/tests/behat/behat_mod_bigbluebuttonbn.php index ffe5bd6951363..7b6b4b5432559 100644 --- a/mod/bigbluebuttonbn/tests/behat/behat_mod_bigbluebuttonbn.php +++ b/mod/bigbluebuttonbn/tests/behat/behat_mod_bigbluebuttonbn.php @@ -59,16 +59,6 @@ public function before_scenario(BeforeScenarioScope $scope) { } } - /** - * Accept dpa and enable bigbluebuttonbn plugin. - * - * @When /^I accept dpa and enable bigbluebuttonbn plugin$/ - */ - public function i_accept_dpa_and_enable_bigbluebuttonbn_plugin(): void { - set_config('bigbluebuttonbn_default_dpa_accepted', true); - $this->execute('behat_general::i_enable_plugin', ['bigbluebuttonbn', 'mod']); - } - /** * Check that the TEST_MOD_BIGBLUEBUTTONBN_MOCK_SERVER is defined, so we can connect to the mock server. * diff --git a/mod/bigbluebuttonbn/tests/behat/completion.feature b/mod/bigbluebuttonbn/tests/behat/completion.feature index fe208aa3573bd..26df1b2a604f2 100644 --- a/mod/bigbluebuttonbn/tests/behat/completion.feature +++ b/mod/bigbluebuttonbn/tests/behat/completion.feature @@ -2,7 +2,7 @@ Feature: As a user I can complete a BigblueButtonBN activity by usual or custom criteria Background: - Given I accept dpa and enable bigbluebuttonbn plugin + Given I enable "bigbluebuttonbn" "mod" plugin And the following "courses" exist: | fullname | shortname | category | enablecompletion | | Test course | C1 | 0 | 1 | diff --git a/mod/bigbluebuttonbn/tests/behat/edit_instance.feature b/mod/bigbluebuttonbn/tests/behat/edit_instance.feature index d303818dc06bc..58a6c9f134405 100644 --- a/mod/bigbluebuttonbn/tests/behat/edit_instance.feature +++ b/mod/bigbluebuttonbn/tests/behat/edit_instance.feature @@ -4,7 +4,7 @@ Feature: I can edit a bigbluebutton instance Background: Make sure that a course is created Given a BigBlueButton mock server is configured - And I accept dpa and enable bigbluebuttonbn plugin + And I enable "bigbluebuttonbn" "mod" plugin And the following config values are set as admin: | bigbluebuttonbn_voicebridge_editable | 1 | And the following "courses" exist: diff --git a/mod/bigbluebuttonbn/tests/behat/end_meeting.feature b/mod/bigbluebuttonbn/tests/behat/end_meeting.feature index 8090997a61b30..7a6d91a5db308 100644 --- a/mod/bigbluebuttonbn/tests/behat/end_meeting.feature +++ b/mod/bigbluebuttonbn/tests/behat/end_meeting.feature @@ -6,7 +6,7 @@ Feature: Test the ability to end a meeting Background: Given a BigBlueButton mock server is configured - And I accept dpa and enable bigbluebuttonbn plugin + And I enable "bigbluebuttonbn" "mod" plugin Scenario Outline: Only a BigBlueButton moderator can end a session Given the following course exists: diff --git a/mod/bigbluebuttonbn/tests/behat/group_mode.feature b/mod/bigbluebuttonbn/tests/behat/group_mode.feature index 837b42f8767ee..fb4b21c2a1d32 100644 --- a/mod/bigbluebuttonbn/tests/behat/group_mode.feature +++ b/mod/bigbluebuttonbn/tests/behat/group_mode.feature @@ -5,7 +5,7 @@ Feature: Test the module in group mode. # groupmode 1 = separate groups, we force the group # groupmode 2 = visible group Given a BigBlueButton mock server is configured - And I accept dpa and enable bigbluebuttonbn plugin + And I enable "bigbluebuttonbn" "mod" plugin And the following "courses" exist: | fullname | shortname | category | groupmode | groupmodeforce | | Test Course 1 | C1 | 0 | 1 | 1 | diff --git a/mod/bigbluebuttonbn/tests/behat/guest_access.feature b/mod/bigbluebuttonbn/tests/behat/guest_access.feature index b371cff41eba2..b26b486954a64 100644 --- a/mod/bigbluebuttonbn/tests/behat/guest_access.feature +++ b/mod/bigbluebuttonbn/tests/behat/guest_access.feature @@ -3,7 +3,7 @@ Feature: Guest access allows external users to connect to a meeting Background: Given a BigBlueButton mock server is configured - And I accept dpa and enable bigbluebuttonbn plugin + And I enable "bigbluebuttonbn" "mod" plugin And the following "courses" exist: | fullname | shortname | category | | Test Course 1 | C1 | 0 | diff --git a/mod/bigbluebuttonbn/tests/behat/join_meeting.feature b/mod/bigbluebuttonbn/tests/behat/join_meeting.feature index 6eb1deeff326e..5b180e8af220d 100644 --- a/mod/bigbluebuttonbn/tests/behat/join_meeting.feature +++ b/mod/bigbluebuttonbn/tests/behat/join_meeting.feature @@ -4,7 +4,7 @@ Feature: Test the ability to run the full meeting lifecycle (start to end) Background: Given a BigBlueButton mock server is configured - And I accept dpa and enable bigbluebuttonbn plugin + And I enable "bigbluebuttonbn" "mod" plugin And the following config values are set as admin: | bigbluebuttonbn_userlimit_editable | 1 | And the following course exists: diff --git a/mod/bigbluebuttonbn/tests/behat/join_meeting_as_guest.feature b/mod/bigbluebuttonbn/tests/behat/join_meeting_as_guest.feature index 4a2c4edae472f..b1bdc4a085741 100644 --- a/mod/bigbluebuttonbn/tests/behat/join_meeting_as_guest.feature +++ b/mod/bigbluebuttonbn/tests/behat/join_meeting_as_guest.feature @@ -3,7 +3,7 @@ Feature: Test the ability to run the full meeting lifecycle (start to end) for g Background: Given a BigBlueButton mock server is configured - And I accept dpa and enable bigbluebuttonbn plugin + And I enable "bigbluebuttonbn" "mod" plugin And the following config values are set as admin: | bigbluebuttonbn_guestaccess_enabled | 1 | And the following course exists: diff --git a/mod/bigbluebuttonbn/tests/behat/lock_settings.feature b/mod/bigbluebuttonbn/tests/behat/lock_settings.feature index 9f6a6f1b6d893..b037e6f58517c 100644 --- a/mod/bigbluebuttonbn/tests/behat/lock_settings.feature +++ b/mod/bigbluebuttonbn/tests/behat/lock_settings.feature @@ -6,7 +6,7 @@ Feature: Test that the meeting has the right lock setting. Background: Given a BigBlueButton mock server is configured - And I accept dpa and enable bigbluebuttonbn plugin + And I enable "bigbluebuttonbn" "mod" plugin @javascript Scenario Outline: Teacher should be able to set the right lock feature in a given meeting diff --git a/mod/bigbluebuttonbn/tests/behat/meeting_roles.feature b/mod/bigbluebuttonbn/tests/behat/meeting_roles.feature index 37d0a7509a7b1..22ef21354c60b 100644 --- a/mod/bigbluebuttonbn/tests/behat/meeting_roles.feature +++ b/mod/bigbluebuttonbn/tests/behat/meeting_roles.feature @@ -6,7 +6,7 @@ Feature: Test that meeting roles are sent to the server Background: Given a BigBlueButton mock server is configured - And I accept dpa and enable bigbluebuttonbn plugin + And I enable "bigbluebuttonbn" "mod" plugin @javascript Scenario Outline: Users should receive the appropriate role when joining the meeting diff --git a/mod/bigbluebuttonbn/tests/behat/recordings.feature b/mod/bigbluebuttonbn/tests/behat/recordings.feature index 91ede6f5c1a0d..d516919a8bc1d 100644 --- a/mod/bigbluebuttonbn/tests/behat/recordings.feature +++ b/mod/bigbluebuttonbn/tests/behat/recordings.feature @@ -4,7 +4,7 @@ Feature: The recording can be managed through the room page Background: Make sure that import recording is enabled and course, activities and recording exists Given a BigBlueButton mock server is configured - And I accept dpa and enable bigbluebuttonbn plugin + And I enable "bigbluebuttonbn" "mod" plugin And the following "courses" exist: | fullname | shortname | category | | Test Course 1 | C1 | 0 | diff --git a/mod/bigbluebuttonbn/tests/behat/recordings_import.feature b/mod/bigbluebuttonbn/tests/behat/recordings_import.feature index e5f442bce3f09..851c9d68d7300 100644 --- a/mod/bigbluebuttonbn/tests/behat/recordings_import.feature +++ b/mod/bigbluebuttonbn/tests/behat/recordings_import.feature @@ -7,7 +7,7 @@ Feature: Manage and list recordings And the following config values are set as admin: | bigbluebuttonbn_importrecordings_enabled | 1 | | bigbluebuttonbn_importrecordings_from_deleted_enabled | 1 | - And I accept dpa and enable bigbluebuttonbn plugin + And I enable "bigbluebuttonbn" "mod" plugin And the following "courses" exist: | fullname | shortname | category | | Test Course 1 | C1 | 0 | diff --git a/mod/bigbluebuttonbn/tests/behat/roles.feature b/mod/bigbluebuttonbn/tests/behat/roles.feature index 9e83ca6915b5a..a6eace3731db9 100644 --- a/mod/bigbluebuttonbn/tests/behat/roles.feature +++ b/mod/bigbluebuttonbn/tests/behat/roles.feature @@ -5,7 +5,7 @@ Feature: Set role as Bigbluebuttonbn moderator I need to see the list of roles Background: - Given I accept dpa and enable bigbluebuttonbn plugin + Given I enable "bigbluebuttonbn" "mod" plugin And the following "course" exist: | fullname | shortname | | Course 1 | C1 | diff --git a/mod/bigbluebuttonbn/tests/behat/room.feature b/mod/bigbluebuttonbn/tests/behat/room.feature index 484a417b43b21..4b6f8435a06b2 100644 --- a/mod/bigbluebuttonbn/tests/behat/room.feature +++ b/mod/bigbluebuttonbn/tests/behat/room.feature @@ -3,7 +3,7 @@ Feature: The recording can be managed through the room page and as a user I can Background: Make sure that import recording is enabled and course, activities and recording exists Given a BigBlueButton mock server is configured - And I accept dpa and enable bigbluebuttonbn plugin + And I enable "bigbluebuttonbn" "mod" plugin And the following "courses" exist: | fullname | shortname | category | | Test Course 1 | C1 | 0 | diff --git a/mod/bigbluebuttonbn/tests/behat/start_meeting.feature b/mod/bigbluebuttonbn/tests/behat/start_meeting.feature index 3b1d7cd4171f6..f8f2d783e3c0e 100644 --- a/mod/bigbluebuttonbn/tests/behat/start_meeting.feature +++ b/mod/bigbluebuttonbn/tests/behat/start_meeting.feature @@ -6,7 +6,7 @@ Feature: Test the ability to start a meeting Background: Given a BigBlueButton mock server is configured - And I accept dpa and enable bigbluebuttonbn plugin + And I enable "bigbluebuttonbn" "mod" plugin Scenario Outline: Users should be able to join a session depending on the Wait for moderator to join setting Given the following course exists: diff --git a/mod/bigbluebuttonbn/tests/behat/subplugins.feature b/mod/bigbluebuttonbn/tests/behat/subplugins.feature index d098b395a19d8..227904dc17a2b 100644 --- a/mod/bigbluebuttonbn/tests/behat/subplugins.feature +++ b/mod/bigbluebuttonbn/tests/behat/subplugins.feature @@ -5,7 +5,7 @@ Feature: BigBlueButtonBN Subplugins test I can see the additional settings coming from the subplugins in the edit form Background: Make sure that the BigBlueButtonBN plugin is enabled - Given I accept dpa and enable bigbluebuttonbn plugin + Given I enable "bigbluebuttonbn" "mod" plugin And the following "courses" exist: | fullname | shortname | category | enablecompletion | | Test course | Test course | 0 | 1 | diff --git a/mod/bigbluebuttonbn/tests/generator/lib.php b/mod/bigbluebuttonbn/tests/generator/lib.php index 5328ba9ca40da..c6970e6909d21 100644 --- a/mod/bigbluebuttonbn/tests/generator/lib.php +++ b/mod/bigbluebuttonbn/tests/generator/lib.php @@ -50,7 +50,6 @@ class mod_bigbluebuttonbn_generator extends \testing_module_generator { */ public function create_instance($record = null, array $options = null) { // Prior to creating the instance, make sure that the BigBlueButton module is enabled. - set_config('bigbluebuttonbn_default_dpa_accepted', true); $modules = \core_plugin_manager::instance()->get_plugins_of_type('mod'); if (!$modules['bigbluebuttonbn']->is_enabled()) { mod::enable_plugin('bigbluebuttonbn', true); diff --git a/mod/bigbluebuttonbn/tests/lib_test.php b/mod/bigbluebuttonbn/tests/lib_test.php index ae24f29bba881..f91121d54c2e7 100644 --- a/mod/bigbluebuttonbn/tests/lib_test.php +++ b/mod/bigbluebuttonbn/tests/lib_test.php @@ -698,71 +698,4 @@ public function test_mod_bigbluebuttonbn_core_calendar_is_event_visible() { $this->assertFalse(mod_bigbluebuttonbn_core_calendar_is_event_visible($event)); } - /** - * Check the bigbluebuttonbn_pre_enable_plugin_actions function. - * - * @covers ::bigbluebuttonbn_pre_enable_plugin_actions - * @dataProvider bigbluebuttonbn_pre_enable_plugin_actions_provider - * @param bool $initialstate - * @param bool $expected - * @param int $notificationcount - */ - public function test_bigbluebuttonbn_pre_enable_plugin_actions( - ?bool $initialstate, - bool $expected, - int $notificationcount - ): void { - $this->resetAfterTest(true); - - set_config('bigbluebuttonbn_default_dpa_accepted', $initialstate); - - $this->assertEquals($expected, bigbluebuttonbn_pre_enable_plugin_actions()); - $this->assertCount($notificationcount, \core\notification::fetch()); - } - - /** - * Check the bigbluebuttonbn_pre_enable_plugin_actions function. - * - * @covers ::bigbluebuttonbn_pre_enable_plugin_actions - * @dataProvider bigbluebuttonbn_pre_enable_plugin_actions_provider - * @param bool $initialstate - * @param bool $expected - * @param int $notificationcount - */ - public function test_enable_plugin( - ?bool $initialstate, - bool $expected, - int $notificationcount - ): void { - $this->resetAfterTest(true); - - set_config('bigbluebuttonbn_default_dpa_accepted', $initialstate); - $this->assertEquals($expected, \core\plugininfo\mod::enable_plugin('bigbluebuttonbn', 1)); - $this->assertCount($notificationcount, \core\notification::fetch()); - } - - /** - * Data provider for bigbluebuttonbn_pre_enable_plugin_actions tests. - * - * @return array - */ - public function bigbluebuttonbn_pre_enable_plugin_actions_provider(): array { - return [ - 'Initially unset' => [ - null, - false, - 1, - ], - 'Set to false' => [ - false, - false, - 1, - ], - 'Initially set' => [ - true, - true, - 0, - ], - ]; - } } diff --git a/mod/bigbluebuttonbn/tests/local/extension_test.php b/mod/bigbluebuttonbn/tests/local/extension_test.php index 02561cb7bd563..a342441468af3 100644 --- a/mod/bigbluebuttonbn/tests/local/extension_test.php +++ b/mod/bigbluebuttonbn/tests/local/extension_test.php @@ -397,7 +397,6 @@ public function classes_implementing_class(): array { */ private function enable_plugins(bool $bbbenabled) { // First make sure that either BBB is enabled or not. - set_config('bigbluebuttonbn_default_dpa_accepted', $bbbenabled); \core\plugininfo\mod::enable_plugin('bigbluebuttonbn', $bbbenabled ? 1 : 0); $plugin = extension::BBB_EXTENSION_PLUGIN_NAME . '_simple'; if ($bbbenabled) { From b88cf030a356b0bb0fad53aa4f69a0e1747784ca Mon Sep 17 00:00:00 2001 From: "Shamiso.Jaravaza" <33659194+ssj365@users.noreply.github.com> Date: Wed, 20 Mar 2024 08:51:30 -0600 Subject: [PATCH 11/62] MDL-79239 mod_bigbluebuttonbn: Server cred warning * Added warning messages about credentials * Activity still accessible when using test-moodle/test-install --- mod/bigbluebuttonbn/classes/local/config.php | 25 +++++++++- .../classes/output/view_page.php | 25 ---------- mod/bigbluebuttonbn/classes/settings.php | 15 ++++-- .../classes/test/testcase_helper_trait.php | 3 ++ .../lang/en/bigbluebuttonbn.php | 33 +++++++------ mod/bigbluebuttonbn/lang/en/deprecated.txt | 1 + .../behat/add_instance_in_empty_state.feature | 48 +++++++++++++++++++ .../tests/behat/behat_mod_bigbluebuttonbn.php | 4 ++ .../tests/external/can_join_test.php | 1 + .../external/completion_validate_test.php | 1 + .../tests/external/end_meeting_test.php | 1 + .../get_bigbluebuttons_by_courses_test.php | 1 + .../tests/external/get_join_url_test.php | 1 + .../tests/external/get_recordings_test.php | 1 + .../external/view_bigbluebuttonbn_test.php | 1 + mod/bigbluebuttonbn/view.php | 12 +++++ 16 files changed, 126 insertions(+), 47 deletions(-) create mode 100644 mod/bigbluebuttonbn/tests/behat/add_instance_in_empty_state.feature diff --git a/mod/bigbluebuttonbn/classes/local/config.php b/mod/bigbluebuttonbn/classes/local/config.php index c209a37f1bc55..9f47b5fdcc73e 100644 --- a/mod/bigbluebuttonbn/classes/local/config.php +++ b/mod/bigbluebuttonbn/classes/local/config.php @@ -65,8 +65,8 @@ protected static function get_moodle_version_major(): string { */ protected static function defaultvalues(): array { return [ - 'server_url' => self::DEFAULT_SERVER_URL, - 'shared_secret' => self::DEFAULT_SHARED_SECRET, + 'server_url' => '', + 'shared_secret' => '', 'voicebridge_editable' => false, 'importrecordings_enabled' => false, 'importrecordings_from_deleted_enabled' => false, @@ -175,6 +175,27 @@ public static function importrecordings_enabled(): bool { return (boolean) self::get('importrecordings_enabled'); } + /** + * Check if bbb server credentials are invalid. + * + * @return bool + */ + public static function server_credentials_invalid(): bool { + // Test server credentials across all versions of the plugin are flagged. + $parsedurl = parse_url(self::get('server_url')); + $defaultserverurl = parse_url(self::DEFAULT_SERVER_URL); + if (!isset($parsedurl['host'])) { + return false; + } + if (strpos($parsedurl['host'], $defaultserverurl['host']) === 0) { + return true; + } + if (strpos($parsedurl['host'], 'test-install.blindsidenetworks.com') === 0) { + return true; + } + return false; + } + /** * Wraps current settings in an array. * diff --git a/mod/bigbluebuttonbn/classes/output/view_page.php b/mod/bigbluebuttonbn/classes/output/view_page.php index 90848202b7593..d2662d03ade25 100644 --- a/mod/bigbluebuttonbn/classes/output/view_page.php +++ b/mod/bigbluebuttonbn/classes/output/view_page.php @@ -66,14 +66,6 @@ public function export_for_template(renderer_base $output): stdClass { 'joinurl' => $this->instance->get_join_url(), ]; - if ($this->show_default_server_warning()) { - $templatedata->serverwarning = (new notification( - get_string('view_warning_default_server', 'mod_bigbluebuttonbn'), - notification::NOTIFY_WARNING, - false - ))->export_for_template($output); - } - $viewwarningmessage = config::get('general_warning_message'); if ($this->show_view_warning() && !empty($viewwarningmessage)) { $templatedata->sitenotification = (object) [ @@ -127,23 +119,6 @@ public function export_for_template(renderer_base $output): stdClass { return $templatedata; } - /** - * Whether to show the default server warning. - * - * @return bool - */ - protected function show_default_server_warning(): bool { - if (!$this->instance->is_admin()) { - return false; - } - - if (config::DEFAULT_SERVER_URL != config::get('server_url')) { - return false; - } - - return true; - } - /** * Whether to show the view warning. * diff --git a/mod/bigbluebuttonbn/classes/settings.php b/mod/bigbluebuttonbn/classes/settings.php index ae5584df96b85..e874d9b1bbc04 100644 --- a/mod/bigbluebuttonbn/classes/settings.php +++ b/mod/bigbluebuttonbn/classes/settings.php @@ -152,7 +152,7 @@ protected function add_conditional_element(string $name, admin_setting $item, ad * @throws \coding_exception */ protected function add_general_settings(): admin_settingpage { - global $CFG; + global $CFG, $OUTPUT; $settingsgeneral = new admin_settingpage( $this->section, get_string('config_general', 'bigbluebuttonbn'), @@ -167,11 +167,20 @@ protected function add_general_settings(): admin_settingpage { ); $settingsgeneral->add($item); + if (config::server_credentials_invalid()) { + // A notification should appear when default credentials are used. + $settingsgeneral->add(new admin_setting_heading( + 'bigbluebuttonbn_notification', + '', + $OUTPUT->notification(get_string('credentials_warning', 'mod_bigbluebuttonbn'), 'error') + )); + } + $item = new admin_setting_configtext( 'bigbluebuttonbn_server_url', get_string('config_server_url', 'bigbluebuttonbn'), get_string('config_server_url_description', 'bigbluebuttonbn'), - config::DEFAULT_SERVER_URL, + '', PARAM_RAW ); $item->set_updatedcallback( @@ -190,7 +199,7 @@ function() { 'bigbluebuttonbn_shared_secret', get_string('config_shared_secret', 'bigbluebuttonbn'), get_string('config_shared_secret_description', 'bigbluebuttonbn'), - config::DEFAULT_SHARED_SECRET + '' ); $this->add_conditional_element( 'shared_secret', diff --git a/mod/bigbluebuttonbn/classes/test/testcase_helper_trait.php b/mod/bigbluebuttonbn/classes/test/testcase_helper_trait.php index df44bc746696c..b1a0778d91bfe 100644 --- a/mod/bigbluebuttonbn/classes/test/testcase_helper_trait.php +++ b/mod/bigbluebuttonbn/classes/test/testcase_helper_trait.php @@ -26,6 +26,7 @@ use context_module; use mod_bigbluebuttonbn\instance; +use mod_bigbluebuttonbn\local\config; use mod_bigbluebuttonbn\local\proxy\recording_proxy; use mod_bigbluebuttonbn\meeting; use stdClass; @@ -158,6 +159,8 @@ protected function initialise_mock_server(): void { } try { $this->getDataGenerator()->get_plugin_generator('mod_bigbluebuttonbn')->reset_mock(); + // Mock server expects a value. By default this field is empty. + set_config('bigbluebuttonbn_shared_secret', config::DEFAULT_SHARED_SECRET); } catch (\moodle_exception $e) { $this->markTestSkipped( 'Cannot connect to the mock server for this test. Make sure that TEST_MOD_BIGBLUEBUTTONBN_MOCK_SERVER points diff --git a/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php b/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php index 2c87c32c0ceb0..6423bcbdff7d2 100644 --- a/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php +++ b/mod/bigbluebuttonbn/lang/en/bigbluebuttonbn.php @@ -26,6 +26,7 @@ defined('MOODLE_INTERNAL') || die(); $string['activityoverview'] = 'You have upcoming BigBlueButton sessions'; +$string['credentials_warning'] = 'The use of default server credentials will soon expire (see note above to obtain new credentials).'; $string['bbbduetimeoverstartingtime'] = 'The close time must be later than the open time.'; $string['bbbdurationwarning'] = 'The maximum duration for this session is %duration% minutes.'; $string['bbbrecordwarning'] = 'This session may be recorded.'; @@ -73,6 +74,10 @@ $string['search:activity'] = 'BigBlueButton - activity information'; $string['search:tags'] = 'BigBlueButton - tags information'; $string['settings'] = 'BigBlueButton settings'; + +$string['settings_credential_warning_no_capability'] = 'The use of default server credentials will soon expire. To use BigBlueButton your site will require new server credentials. Please contact your site administrator for help with this.'; +$string['settings_credential_warning'] = 'Default BigBlueButton plugin credentials will soon expire. See BigBlueButton plugin settings (opens in a new window) for more information.'; + $string['privacy:metadata:bigbluebuttonbn'] = 'BigBlueButton session configuration'; $string['privacy:metadata:bigbluebuttonbn:participants'] = 'A list of rules that define the role users will have in the BigBlueButton session. A user ID may be stored as permissions can be granted per role or per user.'; $string['privacy:metadata:bigbluebuttonbn_logs'] = 'Stores events triggered when using the plugin.'; @@ -126,19 +131,13 @@ $string['config_guestaccess_enabled_description'] = 'Allow users without an account on your site to access the room.'; $string['config_general'] = 'General settings'; -$string['config_general_description'] = 'These settings are always used.'; +$string['config_general_description'] = 'To set up BigBlueButton, you can either use your own BigBlueButton server and credentials, or obtain credentials through the Blindside Networks Registration Portal (opens in a new window).'; $string['config_profile_picture_enabled'] = 'Show profile pictures'; $string['config_profile_picture_enabled_description'] = 'Should profile pictures of participants be shown in BigBlueButton sessions?'; $string['config_server_url'] = 'BigBlueButton server URL'; -$string['config_server_url_description'] = 'The default credentials are for a free BigBlueButton service for Moodle (opens in new window) provided by Blindside Networks with restrictions as follows: -
    -
  1. The maximum length for each session is 60 minutes
  2. -
  3. The maximum number of concurrent users per session is 25
  4. -
  5. Recordings expire after seven (7) days and are not downloadable
  6. -
  7. Student webcams are only visible to the moderator.
  8. -
'; +$string['config_server_url_description'] = 'The server URL of your BigBlueButton server '; $string['config_shared_secret'] = 'BigBlueButton shared secret'; -$string['config_shared_secret_description'] = 'The security secret of your BigBlueButton server. The default secret is for a free BigBlueButton service provided by Blindside Networks.'; +$string['config_shared_secret_description'] = 'The security secret of your BigBlueButton server.'; $string['config_checksum_algorithm'] = 'BigBlueButton server checksum algorithm'; $string['config_checksum_algorithm_description'] = 'SHA1 is compatible with older servers. SHA256 and SHA512 are more secure. SHA512 is FIPS 140-2 compliant.'; @@ -622,13 +621,6 @@ $string['view_error_meeting_not_running'] = 'Something went wrong; the session is not running.'; $string['view_error_current_state_not_found'] = 'Current state was not found. The recording may have been deleted or the BigBlueButton server is not compatible with the action performed.'; $string['view_error_action_not_completed'] = 'Action could not be completed'; -$string['view_warning_default_server'] = 'This site is using a free BigBlueButton service for Moodle (opens in new window) provided by Blindside Networks with restrictions as follows: -
    -
  1. The maximum length for each session is 60 minutes
  2. -
  3. The maximum number of concurrent users per session is 25
  4. -
  5. Recordings expire after seven (7) days and are not downloadable
  6. -
  7. Student webcams are only visible to the moderator.
  8. -
'; $string['view_room'] = 'View room'; $string['index_error_noinstances'] = 'There are no instances of BigBlueButton rooms'; @@ -660,7 +652,7 @@ $string['completionview_desc'] = 'View the room'; $string['completionattendancegroup_help'] = 'Attending the meeting for (n) minutes is required for completion.'; $string['completionengagementgroup_help'] = 'Active participation during the session is required for completion.'; -// Deprecated since Moodle 4.4 +// Deprecated since Moodle 4.4. $string['acceptdpa'] = 'I understand and accept the data processing agreement'; $string['bigbluebuttondisablednotification_subject'] = 'BigBlueButton activity module disabled.'; $string['bigbluebuttondisablednotification'] = 'The BigBlueButton activity module has been disabled and any existing BigBlueButton course activities are currently not accessible. Prior to re-enabling this plugin, please ensure that you have read and accepted the data processing agreement with Blindside Networks Inc.'; @@ -668,3 +660,10 @@ $string['enablingbigbluebuttondpainfo'] = 'In order to meet your data protection obligations, prior to enabling this plugin, you may need to ensure that you have read and accepted the Blindside Networks data processing agreement. Please consult with your own privacy professionals for advice.'; $string['dpainfonotsigned'] = 'Before enabling this plugin, you must confirm that you have read and accepted the Blindside Networks data processing agreement.'; $string['config_dpa_note'] = 'Note: In order to meet your data protection obligations, before using a service provider for this plugin, you must ensure that you have read and accepted the service provider\'s data processing agreement. For the default free BigBlueButton service, this is the Blindside Networks data processing agreement. Please consult with your own privacy professionals for advice.'; +$string['view_warning_default_server'] = 'This site is using a free BigBlueButton service for Moodle (opens in new window) provided by Blindside Networks with restrictions as follows: +
    +
  1. The maximum length for each session is 60 minutes
  2. +
  3. The maximum number of concurrent users per session is 25
  4. +
  5. Recordings expire after seven (7) days and are not downloadable
  6. +
  7. Student webcams are only visible to the moderator.
  8. +
'; diff --git a/mod/bigbluebuttonbn/lang/en/deprecated.txt b/mod/bigbluebuttonbn/lang/en/deprecated.txt index ae08997abc186..936094c917101 100644 --- a/mod/bigbluebuttonbn/lang/en/deprecated.txt +++ b/mod/bigbluebuttonbn/lang/en/deprecated.txt @@ -9,3 +9,4 @@ enablingbigbluebutton,mod_bigbluebuttonbn enablingbigbluebuttondpainfo,mod_bigbluebuttonbn dpainfonotsigned,mod_bigbluebuttonbn config_dpa_note,mod_bigbluebuttonbn +view_warning_default_server,mod_bigbluebuttonbn diff --git a/mod/bigbluebuttonbn/tests/behat/add_instance_in_empty_state.feature b/mod/bigbluebuttonbn/tests/behat/add_instance_in_empty_state.feature new file mode 100644 index 0000000000000..04b69feab1e42 --- /dev/null +++ b/mod/bigbluebuttonbn/tests/behat/add_instance_in_empty_state.feature @@ -0,0 +1,48 @@ +@mod @mod_bigbluebuttonbn @javascript +Feature: I can create a bigbluebuttonbn instance with default server + In case the BigBlueButton server has not been configured + As a user + I want to see a notification message + + Background: Make sure that a course is created + Given I enable "bigbluebuttonbn" "mod" plugin + And the following "courses" exist: + | fullname | shortname | category | + | Test course | Test course | 0 | + And the following "users" exist: + | username | firstname | lastname | email | + | user1 | User1G1 | 1 | user1@example.com | + | teacher1 | TeacherG1 | 1 | teacher1@example.com | + And the following "course enrolments" exist: + | user | course | role | + | user1 | Test course | student | + | teacher1 | Test course | editingteacher | + And the following "activities" exist: + | activity | course | name | type | + | bigbluebuttonbn | Test course | BBB Instance name | 0 | + | bigbluebuttonbn | Test course | BBB Instance name 2 | 1 | + | bigbluebuttonbn | Test course | BBB Instance name 3 | 2 | + And I am on the "Test course" "course" page logged in as "admin" + + Scenario Outline: Add an activity using default server for the three types of instance types + When I change window size to "large" + And I add a bigbluebuttonbn activity to course "Test course" section "1" + And I select "" from the "Instance type" singleselect + Then I should see "Restrict access" + + Examples: + | type | + | Room with recordings | + | Room only | + | Recordings only | + + Scenario Outline: Users should see a notification message when accessing activities if the default server is used + When I am on the "BBB Instance name" Activity page logged in as + Then "Join session" "link" should exist + And I "" + + Examples: + | user | shouldseemessage | messageexist | + | user1 | The use of default server credentials will soon expire.| should not see | + | teacher1 | The use of default server credentials will soon expire.| should see | + | admin | Default BigBlueButton plugin credentials will soon expire.| should see | diff --git a/mod/bigbluebuttonbn/tests/behat/behat_mod_bigbluebuttonbn.php b/mod/bigbluebuttonbn/tests/behat/behat_mod_bigbluebuttonbn.php index 7b6b4b5432559..6f1190de3949e 100644 --- a/mod/bigbluebuttonbn/tests/behat/behat_mod_bigbluebuttonbn.php +++ b/mod/bigbluebuttonbn/tests/behat/behat_mod_bigbluebuttonbn.php @@ -28,6 +28,7 @@ use Behat\Behat\Hook\Scope\BeforeScenarioScope; use Behat\Gherkin\Node\TableNode; use mod_bigbluebuttonbn\instance; +use mod_bigbluebuttonbn\local\config; use mod_bigbluebuttonbn\test\subplugins_test_helper_trait; use Moodle\BehatExtension\Exception\SkippedException; require_once(__DIR__ . '../../../classes/test/subplugins_test_helper_trait.php'); @@ -57,6 +58,9 @@ public function before_scenario(BeforeScenarioScope $scope) { if (defined('TEST_MOD_BIGBLUEBUTTONBN_MOCK_SERVER')) { $this->send_mock_request('backoffice/reset'); } + // Fields are empty by default which causes tests to fail. + set_config('bigbluebuttonbn_server_url', config::DEFAULT_SERVER_URL); + set_config('bigbluebuttonbn_shared_secret', config::DEFAULT_SHARED_SECRET); } /** diff --git a/mod/bigbluebuttonbn/tests/external/can_join_test.php b/mod/bigbluebuttonbn/tests/external/can_join_test.php index 8fe1d7080eeb3..597bb8e44cb2d 100644 --- a/mod/bigbluebuttonbn/tests/external/can_join_test.php +++ b/mod/bigbluebuttonbn/tests/external/can_join_test.php @@ -61,6 +61,7 @@ protected function can_join(...$params) { * Test execute API CALL with no instance */ public function test_execute_no_instance() { + $this->resetAfterTest(); $canjoin = $this->can_join(1234, 5678); $this->assertIsArray($canjoin); diff --git a/mod/bigbluebuttonbn/tests/external/completion_validate_test.php b/mod/bigbluebuttonbn/tests/external/completion_validate_test.php index df6524095a05c..f3dbf16a2b135 100644 --- a/mod/bigbluebuttonbn/tests/external/completion_validate_test.php +++ b/mod/bigbluebuttonbn/tests/external/completion_validate_test.php @@ -63,6 +63,7 @@ protected function completion_validate(...$params) { * Test execute API CALL with no instance */ public function test_execute_no_instance() { + $this->resetAfterTest(); $result = $this->completion_validate(1234); $this->assertIsArray($result); diff --git a/mod/bigbluebuttonbn/tests/external/end_meeting_test.php b/mod/bigbluebuttonbn/tests/external/end_meeting_test.php index d3fd82e482208..c079cc89df6a8 100644 --- a/mod/bigbluebuttonbn/tests/external/end_meeting_test.php +++ b/mod/bigbluebuttonbn/tests/external/end_meeting_test.php @@ -63,6 +63,7 @@ protected function end_meeting(...$params) { * Test execute API CALL with no instance */ public function test_execute_no_instance() { + $this->resetAfterTest(); $this->expectException(moodle_exception::class); $endmeeting = $this->end_meeting(1234, 5678); } diff --git a/mod/bigbluebuttonbn/tests/external/get_bigbluebuttons_by_courses_test.php b/mod/bigbluebuttonbn/tests/external/get_bigbluebuttons_by_courses_test.php index cd0db47400870..a963ba499bdf3 100644 --- a/mod/bigbluebuttonbn/tests/external/get_bigbluebuttons_by_courses_test.php +++ b/mod/bigbluebuttonbn/tests/external/get_bigbluebuttons_by_courses_test.php @@ -63,6 +63,7 @@ protected function get_bigbluebuttons_by_courses(...$params) { * Test execute API CALL with no instance */ public function test_execute_no_instance() { + $this->resetAfterTest(); $bbbactivities = $this->get_bigbluebuttons_by_courses([1234, 5678]); $this->assertIsArray($bbbactivities); diff --git a/mod/bigbluebuttonbn/tests/external/get_join_url_test.php b/mod/bigbluebuttonbn/tests/external/get_join_url_test.php index 9036916d61e5d..e245b37d18cc9 100644 --- a/mod/bigbluebuttonbn/tests/external/get_join_url_test.php +++ b/mod/bigbluebuttonbn/tests/external/get_join_url_test.php @@ -63,6 +63,7 @@ protected function get_join_url(...$params) { * Test execute API CALL with no instance */ public function test_execute_no_instance() { + $this->resetAfterTest(); $this->expectExceptionMessageMatches('/No such instance.*/'); $joinurl = $this->get_join_url(1234, 5678); diff --git a/mod/bigbluebuttonbn/tests/external/get_recordings_test.php b/mod/bigbluebuttonbn/tests/external/get_recordings_test.php index 96b85bc481615..ca106e9e1d3ce 100644 --- a/mod/bigbluebuttonbn/tests/external/get_recordings_test.php +++ b/mod/bigbluebuttonbn/tests/external/get_recordings_test.php @@ -63,6 +63,7 @@ protected function get_recordings(...$params) { * Test execute API CALL with no instance */ public function test_execute_wrong_instance() { + $this->resetAfterTest(); $getrecordings = $this->get_recordings(1234); $this->assertIsArray($getrecordings); diff --git a/mod/bigbluebuttonbn/tests/external/view_bigbluebuttonbn_test.php b/mod/bigbluebuttonbn/tests/external/view_bigbluebuttonbn_test.php index 31e0ca4574bd9..1b9f698360011 100644 --- a/mod/bigbluebuttonbn/tests/external/view_bigbluebuttonbn_test.php +++ b/mod/bigbluebuttonbn/tests/external/view_bigbluebuttonbn_test.php @@ -63,6 +63,7 @@ protected function view_bigbluebuttonbn(...$params) { * Test execute API CALL with no instance */ public function test_execute_no_instance() { + $this->resetAfterTest(); $bbbactivities = $this->view_bigbluebuttonbn(1234); $this->assertIsArray($bbbactivities); diff --git a/mod/bigbluebuttonbn/view.php b/mod/bigbluebuttonbn/view.php index faa5bc8de433f..74175cddd1c3c 100644 --- a/mod/bigbluebuttonbn/view.php +++ b/mod/bigbluebuttonbn/view.php @@ -26,6 +26,7 @@ */ use mod_bigbluebuttonbn\instance; +use mod_bigbluebuttonbn\local\config; use mod_bigbluebuttonbn\local\exceptions\server_not_available_exception; use mod_bigbluebuttonbn\local\proxy\bigbluebutton_proxy; use mod_bigbluebuttonbn\logger; @@ -87,6 +88,17 @@ echo $OUTPUT->header(); +// Valid credentials have not been setup, then we output a message to teachers and admin. +if (config::server_credentials_invalid()) { + if (has_capability('moodle/site:config', context_system::instance())) { + $settingslink = new moodle_url('/admin/settings.php', ['section' => 'modsettingbigbluebuttonbn']); + echo $OUTPUT->notification(get_string('settings_credential_warning', 'bigbluebuttonbn', + ['settingslink' => $settingslink->out()]), 'notifywarning'); + } else if (has_capability('moodle/course:manageactivities', context_course::instance($course->id))) { + echo $OUTPUT->notification(get_string('settings_credential_warning_no_capability', 'bigbluebuttonbn'), 'notifywarning'); + } +} + // Validate if the user is in a role allowed to join. if (!$instance->can_join() && $instance->get_type() != instance::TYPE_RECORDING_ONLY) { if (isguestuser()) { From 5ebc01f12d46e956f7c25bc2f1c4c4dcbf053a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikel=20Mart=C3=ADn?= Date: Wed, 20 Mar 2024 15:52:30 +0100 Subject: [PATCH 12/62] MDL-79368 theme_classic: Update screenshot image --- theme/classic/pix/screenshot.png | Bin 68821 -> 81877 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/classic/pix/screenshot.png b/theme/classic/pix/screenshot.png index b1d02c5c3eaf289e113d5f62d0d5ad1f48da7198..0073754a294fcef9e50aedb7519302a590690c47 100644 GIT binary patch literal 81877 zcmZ^JbzGERx9>0v(lB%iLrX|WDlvp~NjFG0NJ=w+(lUT_Dc#*I-O?ROH_{Dv{Jrlv z=e_rQ?jN&zuX@&A>-+3y!c>%`u`$Ro0002Ctc;{80PqY703ZjWAtOdcXNh3vU2dF5hzrDV>#mB>$ zUs$B1p&y-EKte*owQGNRdRkfAT3ubm#zH42Bc`FDA|=A3q>cR+86KWp_`R+=&%@JsY`ATHroXT}&&k_8B0leU|j__~$7XAqHk>k%B#{iAxVrYAJi zKPbq{K_qp|)2NxTW=c|I_P1DzlD+GjWn8<#-n4dQP4zD^VH>OC z&MtP>>r*~qpSIVh1fZgkk%4{vEor%#BZEzQrDpaPdQaQMQp$>oigHg+zjmIUDz={v zw mWM;=9qr7hLPvG&M#8>?#KnBlweRrvu=0-z-)ZR42p)bK8-04ZcsT8&goH!~ zyIIlFt4Il*AN?{?lMVN`zgsCC8t#mN&|h3!B!BTL+IjMDcyCd#_Ov?#H!WJrGu}Tt z9?WzYIC}UQCBL<^UukI+-+M$W6HW;+jiE6!rTx^|)^DCXoISggF>&8Kv^sb9bTgLv ztfEx3Qr;GXU{<(mdjp;+VUP2+v3}{_Rp5vJwk93c1f+~EC0@0OBGl_UU3$WT^NO#S1+ z!Sb>E;tf*AhnOJJdpaq!XQx726kwiTaja`WX|*ZfK=FBV=bbp7#rqG_s5ospUvxZl z!e-UvjlI9++L0(GH|X-X7ME=0>|BUB{;DlLzjYn{B0RT!_7LNfEnBmqTYcj9p?l6J zo3VfUaQke4f7Tco@QafCBjRL(KmaK8zpJNhojJSvn##&?zo#h?Fc<(Wq(Y9Pexn=2 zFGV--PFf8a2?>za<9pX}5Nu1Ib=_j~no6rP6+y)5^KM^a`@O3>ALNXSlG?$gKKB&? z_TLFP(XH)6Y3VZPM1J0+J=IlY-}(opY(XFpP_P_5a+946vr>pTi{`8EJG8!e7tuU4 z|5qShyHVR$@E>N0S{jo@*(uc#3sHK55iE?FbnJJjR&jjsMgtePJ;+ZmjT zS1>U%<5^3^LkcrbK|=$yJo&WTmS*XDHLC3=Z3g*_#I#nPJT`ME1RaDV!AkT8KtU1` zVjsp=R%iE5awNF45V{3im3T`CMoMMgpE$MOa+M#yq!KC940RxbyuCSz7~T7=voKe; zl7in?ZmqSkxs-4}^Qm_80#%-Z*DrkJX5=T|p)_(F-ESuW(gYHiWdFW38BUboQ6?2! z{mgz{SNTMjm%qK)yfvG1q#OhA)!UD`y%_r<tGRmZmb_ z>5_{T7d>tY(L{c;O;O9*q#QGw*ET4Um!^+e(s9UQecwu?RnF=X=$_(y%uI!HB&2L_1PnjaQO5A7u)(Fk+#-B4YoTiTWG>;v}n{k%i*&C%y^B< z5l^w&_j`@wAqak2Y>)2TkhoLBAr3=F4~u&Knq%|lkj)oO=NnO>A*A$gCCs1+Z_6)h zMHhRF4qa9iC|`IlJm=fQ>AxTE=nkhVxc2euNu<*B9T0h|L2LRYerXq}vHH8R7_kG# z(hL*rh%?K?2m1w?-jU_{k7y1Ljj}b>+JGFTVBP5A(rnqhw&pM+UBJb6<7XRle4lzUBZUq$wAWMa7Rcd= zMrmmM(#~lLn>}dOc||^r7K&53uWC8UcaUO`_m|oe_-49eyx(x!F1S7LYG=+zYg(j# z5o=dIRBf>-|4AKo5ycv#(qZl?O)w;*M+N6EPOSa$ScTO;RrKLKdT2pVLsH!+RCQ}2 zz9F?>;JcONUe!wc#P5aKy%>2j;Vd?_;z^<{^7%up+fijPADPX~^{>ir^!nkuLV>^fZn&h$52Sr%eM;*gWM_k7 zSOXkM)_M}}9fmD*qZZ|Z3Nr+AYcQS|Y^s~3tS1+;AFtEkVqmnh$HlJ~$zwZNUn0|a z3Tr!|DWgD&i9Q@!v+t!#LEF&(?D#ny=!1_vecexRJ{Idsm-?HWm5z8`dBv$ zDbuTrG~N*8R9Go-W*p5lX$ZtX$RaxHZ;3+$F%lfnbK0gSPAc1?9hMSwCt0dft45(z zr3zRI4V1sd@=f11+dXj3u`4sGt7{xy;4BPcm_*j}0cl;vvi--ZX+)=RWNz>i_-eJm zXJGqhsu=yQToC80oSWaJRsFiiS&mj;6Dq2>hx>r4V;Lj01W1)`{NuQQi|qBr(>Rh* z?{MeB)3w`Eu}a*hrSi%SjSqdBCjz3nCwY9fW< zs!$69RssUn>N2Y-!F}ht*Ob4%e>eUnjCUI4J^jJ4=71uV?(ihwTGw>4By2V@|g*kQ#JR7ly9<*lpq{XTeP<7{Fj#^hDdgWXxUVX;U&N6`zV3t-|Na&#W1+CC;XPgB^`wBrr6gY8 zf;d5reN#3%h~PW2e7Y7EeCmgC!?5A|x=iN^dVYBR^@fFQapW*AuEzHjK6g~E)_e0! z#Tf>+!=SXw=OUcQRp-I4(2=J2-@#e#c7E2eup14C#%A8%QXgCE_)X%ayM1AnlT;8- z3xX;5)5f9KT=jkZxL8rt>;97%UMRZC4Wy+rPCKX^Tk_dHt72fgxn$@G{yK^=eg*aF z&iCBMPxx>d@~*&C4?+}SupTU{L=XBSTF|^_)Knf6&Ea>&x921n+cs{@6N4TcbQsp5 zawkJesJ4`mTyxv+ZUbFuoJw9j%&E{?Z)NplB>-G=*kaVLkW#w@6Tn3LUvP5M0%OD8 zni!lu$?Xgo_&`UmEto#B4#w4=%j;na>^zW# zygu>y-5@}~ZH&w7&SY(F0@aC=8vL;Hn>!P{p5?WZ7uX~e;KSigmv#dVz%K(^j4xjz znGLuhM>T88Qj(&dgndesuog z(km1EHTR!p2fVn95CC9^tsC#O*V5v1>qE42KBXLjz_vJ|_`fj}1Of=4CWJF0UXfsA zXn3DE`wJ%9Pz>43qPE}&SFMXuSGX($uAz(nje^dxv5HJVuKh^&= z`_J0{fNcL}j^GMFNd6zyziHfuv{v6()3vUDj1Nl^g{acY>uIdTB9Hv=UbJF}aH|n~ zE19&&vR6v-jgq!lnCLs-g8pSyjOjpyxTHM3=#cjcw25WXs-H764Q2%R9mDQK9HePYbv|wP-mq3;+NGK)*m`_6pKzz(;#m+B0}n ze35PA-(A#5w!KH9scu6MOVB_-7!&}oguo}(=rm$e%0<($7l4~KM{bA68G#Gm%-;78 zV3aW|R(X}cFufFY9YpS(xKQvL`JN#dkTbw1a>_lgA~G^E{EIG~2o#AeZK(ecPSts# zm0z-MunDg!38Ht`n(1jq^EwgqpR4I69vj}~3{Jfz?Ef}J7>wW?g(o9Z%C?UkZGRVi zcc^oVpQ>L;C7`QP%mjJWz0(d<`sFqvEn|(U>ydc3 zDV8ETuwZF755h;=$F5M_%2l?R|8^0Lc8LEe(_nt0?598?1gwf$Xzrq^KJ?MTC{|>- zVpJxSu`l>~l(nw)OguIm_4a(%YJ$)$SX1NLG<3Lq*2cw!<;99EA8ghJNBG@5%iVzI zAA0~`N*=;|(PsKB6HRO8?2SI8a&_~VKIH7Cg?KB{stl!eP)fxADlAj7U@kE8-jQp7 zf+qtM+gMlVo5NQ50Zj~ReswHZQ$A21V-f{Yb|Zsh?IHY>uVv#9hMwc7PF}|!RQA|n{j0Wqm|!6m~Ho9no$GAJwzw!a&reS z)i13c4oEI~$UF45|<4>f4S0pLc_Ewu0-mQ7B*#N&ea=DA!4)>4%zpjB{0;Ppx zCYqm>l_!Gg$3Nv#kNGX0HMVobzPdHKY*%eKT_}cfHzFlVKGyj6uz)kZrDsXVgmU>e zY)?9?&rO4foy}K(g|0KiXysNZMkDqMX74VCRWhk|^I*O%U!->%Uch%mqVJt;GBwH_ z&kbJlclcTyQe!3ar`Afznfo%!W$BYS5y|A)lxe$reO_rgaTg=n)Bd3dSwN|r8Pj^W zehmQZ(ZkPO>(#zJ?JCym>p^0&%!v$TaX%DpBuT@3ayVX)+~u&LGjNHW^~Lfw3ujtH zAg!aeZE!oNGJsco<&W`MSvO{jjdY)zY}@f4Y3DP84Q$vh+gZoHX0Gh?;m?p^pjWR( z@c9H|*y`+(rGZTDxv)y;I1da%rs`4V6(J^-0DuZL4D0oUHK=nU2_Zq)yTqIFN+-3k zR49G6Ka;q(2^Lopx`JZ~h2m9+x5$S`nw0YwU~=7f;WK`}f;rimlQKvh`Ht8!(9{AA zmuN9~hEhwu*pqF;v6_ccssZEH>W2A)ikYFk3tlJ>qZax`n*rprs6Ht0yDI=11mYHU z3Wd^2eJR4XZbF4cx~9IblOPO&L$mp1(ac!os}v!ZJ|?zhFiLsm4Jn9)4s)uD8*1!S&1Dbpe%jg zA6@`}M+!pcQj1~|mDWcPdXw=WQg>-Kj~_frRqvx|iRE?J8N0xSjdduuUd81)YIG;{ z>H3Wf=m*H;P!40PS8H|W&HRXIrIXg!RP4)`gnfE=%-*Nhcu$MpUcd`>akshPC2m60 z0DuxZyhcr4Jk*XIxU1);{p=)KboJqK9CMckRAqeI#hY2HWQc-Ka}+vvg~7*oEOiZT zN>Y+D-mAcViIrkckZAMgb7~pGkYY}oQ%0|z8W@eU7p06c0WA+Agvv&F=nHW@y)2ZBT%LBYxopw>=I~D;8*#PJf-0t) zwHP=L5wQhpGg=PBk-}C30QiOjeAh|0+y_R>v^9rr5fE)m63F=X^MvY&tmq1)Kxutq zH7;zD*vMl0_q3=z2tzMEMn=Q1=BvYAia0ZtgqKy-xg^D zdN60^@DD_)_ClA$w3t5ofJ+ieyu`LLkfJ<~mKhE;n}j&rbWi(_>eZ-Y(pSn!*Jk6y zeU(WL8z<`f96ZEULZAAHfihLgUN|^+x`CO0h>eY}T1!P0;*$jBaa`TL(&d!FhN122 zAtHP?@Lg4@X&<=1rHS^9J85ogJ{`CUB&4kdSt!55p>g)clZh-(#SJd@PmX)0D3eL5 zUu&Gn|8k}~ckA{w_*Hq8)^wYE^!c9d1Pk+dz^4M+;NoybTq3k$l-5~>E=VmX5akmf z;5CLM=GT!S6kH%#^}}O1mUbsM?$^qJXk}SQH{??akU1GwUb)2YKnxPby?QH&bE6QU zv?D^g%lEnmKYL5A>o}-tNbX!fFYGZUhF`sPQk!RVtX}2M!UM zo@eKgTbSc#RW%56Ew&LV0J;nkcZcv;d}Ps+9|dyZ>^kBX_cE%;)2mkfGE9j)PEUZI z=ZiLOP1^iG4I?l&jM8mB@oWEfY%FuidzRiR7bhX*>enD@T8H~_Ty9ITobBLK{KlQ( zz}cOFPioIou;!9_=X`Kn2lD2VJ-yZ2^o$GA*x=T+fy*DR97gTqvFPK9Ur4T8 zT#l+MvIb~{krtyB+Gm#_NDcU!2TV-0Al#y?obgL^6uE`6cT^&8R%OS2N$9V~^Bdxf(ozxi;;`a28 zWiy*6)LaZ(7eZ;QDIl^JYUoNceCrtWM=Z9Js;%|HcL&hPIzYx1UjPfSbS|0007ak)LsEzIjC)+Hk`0&-52K zxoELi@B(n0-xU=B05>S{v^OR*zVc4%uX>b$vdr?w0Z4!8C_}s_ju41pJDw%|cUkoj zQ1(7Ie<_>^8HgZz)_*xFpE>2@yUs?PzQGRP@!Mz5=tn|eF2G+dhynltA_^c9qCnuT z|Nn#lIzb{k|5teeFd6`$kLIuMAn>myfSuP6g1|-qKm$Nu1o>|x0Qx^A;O}|_enk`r z4Ewj70Kg(~SF_7u$_^Kq_u{W!iT_YkG>9~4gCC~I)r*P7tgCP zYvywu5@%p-(pA!rd=5s8m;KSM2aAOtN(zI z>nBgkx1S|NR&K;^eH||1{A&|H*q%QfFP<`6&phqvnvt)6anarR@8sq`qQ7-H`EE>2 zOjmKibf9?o1C81Tjb^OYZN5u60>Z-Tj?K_)Q^Dt~)jY7(_t^?_F)0bulxlZE zvn!7I@_g*-p^ZPAO!f=WqO-~F+4qact&vnu@|M-;w$-bR5@k_bac0HMTHz@cwNApY zQ#DHa+58K_s3JxsP0I{a&qaCN@bpA0)UAeYH8&L6`K^%``qG6I!itgF7>1rM58jt{ zXUrF&;i?uUeo}j03ASX#x6&4?>*|-gHJ)fk@4CY#9@vf4 zI}dbeQfGhf5wH%#Z{BVtXyS2i`P^xqSg|^%CVwU?STZ`YZz>hOKIeYg1wgqgIPN+x zgG4BuK3@I!jjQZd?{@e58|c&sy&~5{134+e^|tcXkdDHJeVkaP;P=Oe)d`17`j46% zS1&|+GH++|BVK>N!|Vnjv|)MN>U^Fk(6T!7j<4UA-9;^gPEhed@A)V9ns@wECl}kZ z8zLG6zWbXkqw(rNmz1Hd1T;2Q{L3?MAuM!nL}qPT;`rYi9&fFztIsSom?a{&5;@w-c+6OsLYS`7}lDiKR)#s0y!gfBTF&$VQ*|M9D zCCG94hDqXNU&hQCOvO>0bm&WC_a5RkUu8w*W(jM{Swb!qlY+K{h-BJ?+X`w;;>mCu z$@i?QG6`i&Fj`;2IpYXrvSKw@+~X?_N|&q{j-Aq%-`0FrLcCHPH$fF7tA8kTu58u5 z7<*e;sjh$_DKZwKs}vi3ap4R<*6<6@E;a1mWQl=tFBE4bqP;4Vr=R7s_F%$^W13G! zMsF$0#_V@_{FSAV*U97;|Lt9Ll-R=i>F$~$O>34O1?gw8%(>j)S_#s(hf@oak~hqc z`i8YJT=B3u!)eY*->_CogY3x$gH;7H$oo#kLOc&Mh=W(N1Vn9K{L+Fk9+q@+0S@(! zZsmrvMvD%(GUy4vf-~z;3yC0edCS6f6tEfC03TbS?!=Plsp}!AcQe#?6O5_y9CdFY zDX^X7dTK+jO3pS*dgqVEC==XwTk3)?K7zuEr_X?ei4r8R$VL@VMfm?BDTL9gxcB5MIYE-5r(GKr}aZGmEvGI z1!=Tq27~4P)OfU*VV)N!`~|!FxnF>4I-h6LH?ag z5jn4;d0_75&J+vL#6pTXBa!)g?Su76Y*Y}HO#bd~r(*#6AM5`~#~?&r{wGZ%rvH23 zpFH~i4*boq9s8h(^N)mxI}spkDn$tKy;1-MjTB=<0swA9CWsK1YAnczHuQgS>7+xD z|I6zy>A#fzrtbeWKJ=vg% z)Efitzns=QOO5o&yQ(-mA z03j5o4RGG8c8>kM;D=CFGh5u%g(wPB*qrHv)^g=V4<$l>QZCrXGvzd>KK76~U5vc` z%x`i!*qpdl3;&(yUjoj+7(4Nt$`5@l9~%qiLr!35-8N!5c)U_TPirm5haFH=IKo;| zDv)X;qDZLOXNh95()U9(JQB_Ps!*@EK&r{9qQ`e{7bGf7rnmPVc85NY8P zsp{VH=Kw6sS_@=YS{$@xmJkK6?qLN_Yk!1u_bOxQ$^d9l(I!Is5BeKtWTO5C0EGG5{7+`q>WJ zMkI===`#@E^H(Ws7?wIjy#ipYZljI1QG(LB%7GL>(<1>1B0^Jx061$D{?qxy5Q_iX zBmbVN|K*%w5QK+=Wx{hI`HR!L;J>a8g_PsTJVg6fzlnZ4A;Ig98xi!Z#fQM_nc%EO z`s-IDfE7>J(%g-uSlt=wVrBt!5JeGsiPsNB;l9uitDiMu-@BB$;m;@N?^~sN4iMPJ zpSg#Tc0cj}lN*_~v@x_gbIIZ&*h*gx{>B{=Q{mA?2cF$_DUVdfkKJ=LYif^6OsJl> zsDg)wY2a-Q7chr9k)QjQ9@J^v$Ovtmj)3nxgq$Bafj1AXKT=1wG&?n_*W<%ys8Jj* z_NT4SOw29ViTicM%&Te|UZ=mpVGebE41(US$6#NdwXY~J(jydRk3~cszZ2GNTspYm zI@X!mD=cLOd}~QHe3zVbn09SLLVT9ImXrZR&;M!W(BO$%CrQp_lcLnX_vYLZ{a#Ph z2yt$mPC)pxCE0u!1Iu8&ISU|x$`X_18ClT-=1DQtV zs{r+=E9cd@g&ds&PVg(L?OsBt9Lg5f@p}^UvF?^8Ayit>d=Pav60qCn*L0uAIlGUW zl$fLsK9bBsn?p-cr?J{WVKBvkd$5uEoETZN{5>AO*>66Co(6>jF>-qk&8TRCWEc=} ztOuX>>{@W|(v2Cmg(XfwaN|QbKV47}F@P3y~{ft;Zj zIEwj`-s^~m`276CFZ6s{S7n~K{aFB1Ma#x@E;cCAsRt0edcoUrvT~wdmbgS{Aho5d zlSwuIVFMt$+ucCm_{3gv>*|i{*Pq_(b_oduEAt7uaYJq$;Pafdb3AF|92SfMs3t*1 z`pia;(Awmlt`P84RMkrFr^(>YACQ2@sb-%+pfB%>xP`8m0^fe}ureIBKRq&i)^9~3 zs&JiGe=?JQwT60wMg94}qGZLayvvOy3HuHh%6x`Ma(gxbU`%?y&ESfDL9*lcEal#^ zB$oAF)8AF=Aer56A;N3_+k6Ffq1yyCYO`C>_v=Q>MGc&xf$lUVJkY6heK5$)7foJ8 zY-bGcn^Z#y*~}q}i$B5CL-Hs=V`O9c#0zq*#@L4ec6_|I9*tHmeeltgsoVuhZui;a z8&L5s;lQUyeUp^t|zGTZxZU0 zYO1H3?=*Hvx(%|H57euRiyz6j7m>19TdhrSJdt`NVIGNomgbr9L2shrJbQGi3Jl#x2qx$7h(<>lL zYTRyl=ZzP?X-I+;+;zt=`$iBolD`h_sW%Wd#mxz!)44DCU2)g{o4!>W6{^&m>Czry z3kdwS9w2}GJrxK!#)Lx90s&wIY=EEvke~=l2R3Pjp!Bbn0jUvDSPanr18HJ4E)_-e z?-T$Y1l*XMY}9wiLJRl{9*`iYK(r?v167~p3z=)nEcZu)lDLO_Vx)f-BO~SHT*nOA z>(?i}0Bbtb(>z1CG8hy<8b#r^ao{*P_!GB|;ooCBi{+zR`b7A+^j)HyJ>#Wqw9+-h z%JVDaLs5Uq7q^`E__3sS4|P1#r+|M&RuX~sx>#fHm7qxk`uo8RzC32Uf5g^90r$!} zZn4F`vKYm8mI|ZFjd`W?@~?6`8^{}z{!xP0N!snE_;d#fRsk}S#Z$Ht+PQ7T!0_r@ zrU?|rp#NSg2~^hXI(mGM43GUfz@cazaQ{w~Cbuo?Uh-Bl=ITxDUt@f8KJb}haOR7@IAR-JoWP9ZaRrbFOmZRm!3mvu369W4sKa&~Xg5n1V{EGlPO8>D^ zs&~n?dvRM<3r7PWrnv%xj0BRLL;TlJQ@Ma2ncPSSb%D@JQ2+A8Tz5C}sOQd001yG$ zKmot5{&`*y00;s75NrN2{O=B;@lWUP_WyXm|IvZ{qeK@{&XW}0xd0(_koUIyu|f7p zn%BSJLyKj7)6zjr`vQh)bJoH;Y9Hz7-QBGr9)PHKVO5{@)#?+T4YlP~$2w7=TDyGt zZOuXc)va9a*<4PxndgHYAH|&3`~s2%;fyik_iU8@y>pV;^!oecrT$?dDiHJ%C_{SY z?}HSFv}}==b6}6Op|W)i<@2TnaIG7wT`K`53ZwY}GM3eCj{OHNyzVIdbu^Dzzi%)Y zg=%kI5rFlWGnn^@AOvzIu@QZ*>Jxi|4KULR_@j}=kr}>udh8WIN`MwXi3yz`I#){8 zUGzkTj4c9X!Xe{!RTO~v=MbM5&3Zp&ExzBK)J+K^y(~p4XIbiBY5EhZDFe{f`z0yQ zsd`dE4L_^m%Qf$vmgl)K>xICFgGJ3eoTDo(y*=xezM`oXyv`XJ8`>b1FM>a*5#4`J zoQEbZuX9vHrwZX|Xz7uW1t-H7O_LN@c?~W*&I`mdSZNk7WG!G@6H4=X176L$s?}a!LKiS52i6R2F3+c|?CN#7ec8W#ZZfQzxUeqF zlT)tzR+uB8QoP#w_8rP`}xXB*M{w?s`8bEV7Q^-YFf^VOZ|X9 zK8FLGnHX={1q2NMXs+jg59qX1GK?-CJmL1nFgjiNpcJMv%B4a54f%$o-F)TUu1&G| z2}71<*Sy8p9oOC44}O)*96J-6R-F;y9~pa*f6rblUMSj=4mhx2e!F^6ObK?f(dT`V z2>S5O9-H^n-1N(-IbS4ib=;dVMRNjXDNgg9Z$JuDRZ~H~7teCOvtFkFge=S_t(UIK z_nIb@usap{58uVV=-N>72tm#2Bd=<-M|lR(=h4|+@-Ije6jrUa;xU?5`9dKRk{2Tw zFtm_UsO#ebL50}4_&{BmetN+oM7a}@Ow0z|UBUSySm271VuYcI=93D(oDBK5#&vGPZ74-6Tg)xSr zhuPaPq1&8{o>NtyaA>4ye`-;UN!-)Sy`3Klcwz27$ff>$j^ErkXK`=FcGmz~Le5&$ zF*~IU9=yeUGUa8<6` zRjRWP>(o{C?mIojd2BQJ`Tdf^EUE2B;*I(oOwDbQ8UTEb3}t^j%a`3KYFA(nK|+(R zxg|lhmz3|KPcb)fdO3ef!35d$LkpPFG920za%q3h;fdqUrW25);!3a8gbDBeEZr@x ziW~TyO`4Z7qD!-Mt4}?HdvLIdjG()>q=$clZCTeL@9=s17ho^EXiNc#KC(Cy&y6^> zu8d1QF%nk(T6hnCmZf;t9S~|${Jqbyz`a86DHueXOI28H)%5*~y16}_H(*^gH|0oy zkd#xU(eN3s-?9;;Z(`UDko7Z}nYWf8pv7Ixf~1xJdOpCnT!>!gq(Ahf=_S&4> z&XMz=zJElUhmM!*tgr%E*$O)V+6RCWigK^HTLqjKN89}Xnr->LyBxugEl7AgAhyh_ z{#1r6)8><5C+54UYscYf$~i$5%00slJ>ETK`lZtIW z5=D&2q`y5I7OqrH?HE^$I^6>FBFpj~9>6|k;PmYNKqQepGZNhH>p*7QjoL_2?l9uk z0&hUKE0f--oX_?fX;fT#D;Ix2t$OA&7q-9Z<*$v-yQ6tY1lVn(y8Q5hd~#Jvj}nIo zgTL6HjI>D-P8MnG@CAINd4qS;GX!hh*o;*Y0;5mNwzHwe#>fC-XT2AEn6cHULbXKS zzO_w6w$)Cf43!*Dr$p0>Ocw-~H(l`ju{-TVk?}hr^c2ot%PFH$wh{)_OB<|dn+q@F z0IZV;QK(aUcTu+}JnkMZR6qA#RYl~Hr`!?i%4WL@g7XUpVFJBLMKk%Pqn!nAW9x}6 zK{<4>HK2T+;k1t<%aoyI>A@A9^r&m>du4!1^u%3y)T%eobMz{`IPiyS_BiV}WUh$k zT?*pTm~b9Z!?X{=-~kwEfRkCV#;@3jQk`2%7YJ4Y&{Dh!2XRsTl5g7Y`=~`bF>Agl z`*%sAo%VKwQClrT6bCH)<1V4y0>MwF+xI4uP2~b#>;@*)S=GcS(8%u=<_>-OQgoPK_i4LD_dFS7|PdrUF>$4(URl zqo3jgBEwSVQb?7#mApz%fJD?A^t1*{_Fwo~h&J&59zKWSw;lj^gp^3JHXhLlo zpYcrfI4Z&-JanrS(ld=FCsRb6=~s9jqt*^v&caCF=-$CTut&Ckt8*lBZ2MjA$i-Hh z*Q$MTw!RT30`39ANQ-m{%%%K}GUkef7a(9CDXvc!3=fJ#?FpIgc%hHcKZ@_1jyxPm zhpY{B6&$POriozzoZ7obsh^*`uN^4zhGih#W#}76`B#ezyLM^ewpkwG%Or~DqFqlv z?|fGF;_mXAk58D{Co}K$^RPX;Tbx9{}+_gwSA);HEdakaw>`8@v?Q^M?GrP zyC-n}?#hg35=ac_`4u|HHbb(f?uL&h2BwD_;G_9|MMn>YpdtD|8U$E^!J&v!20{-; zz+Zf{zf*xAaUcy0jMz>i0|6nnf&Xp>p$CJ+{~7!{b)X1ib7cDy%_8V?O8BoC^qe>C z_jjm1n~tZ{?;!Q7=9v|5R;B+<$&*y{X-Ik);vxF5bHxT*$}mCNF#Q)h{Qj;6)UE4) zn;=iKn`K3wzSBehG#`TM{?lIDmaJ*i7*M%mi$#+kux=z%h}ZwL+*fUg!q->L?kRY{ z73I>1{vUbJVNM=aj{w;H8K)#X74wW~wJ)BPV2K)&aWd!%w=4aIg2WBR>7tNa=KuNb zy*=62SIU3&pJN3g1qoylAa?45EIWARN%O~W87(l|a1FSptr>G@; zP7F|l)}LP@8ykh2QEi37OmNi7bnWa~Qmr>P>q&KM!JiE3bdX7uP=mA=Kqjy1L7l{z z<+>Ml&-o}0h7u+48adw-gXH5)*c+3E7~%$89fXrv`{RvO*oleW#+c_i6;}ik%QlbB zd>tNQ?V2om*fsl}9GjTXYsolH+6j5I2UaH3-y+xj0zNCuX4x}IrZlb3wtKJF+aTuD zio>`DM2{E$t_q;+Jb$+(^&TkO!59QBx+!YkD}GHMVwQODhf3y}Z_b`?h@WcgM<9eM z6{^twFg%4Hx;seS0k#TB#nif1DW`!YIn*(LH@1TRU^zW>Pp#nhx1PO;?fa3y^|}%H zf@^M~?;9hSsd-@!r^R{_t@@c^?fv@|awsRv7;vKD``Gf+t~#lBWo&r3L^VyrVpFFv z>jlwU!$3~jS!d^JbUB^Dm&+`K=Eij@>T*AQd6yK*Bl$5?Q%Tr!BA6SqiUw7WmQJkH zyKzIx@W5zisP&)AqLX;Pg@6;pk|aonX}o~%X>xF$C3lawC72Dq0Kc@YAPl@zNfwp! zd4-mvze+52;GPO{3^Z^Kkncz%9`ZOI|BcW!5sI}93!qQnXKZ*?lWaH^Scw{FUcArL z04?*6ya^_$QaEd2s!9Y+tj`Lc#6SO9`no7v*xywx#0howbG5KDsWWO5GRI5bokwHL zdqtcOudxuvb7(70->h>kZO|JF3kx7v(yXx|7@wI9;j?l@F)VLse*~x46r6fh<%vA` zO!DfhO=xLpPGFl$D(0{ZHuBsTI=(PE0pO#Md~D}fcYhJgv1mhd(D-F7tjjVp3Aeal25`KoqJrz-yC=a=5j5jN2m+n zV%d_CM`PmB-q*&!N>a|$i$~OQBQ974NY$<5oycp8>bZN*ke-9Wt)Y==xeD$#1cmhY z6|~t3DOHqMnsQN680_;-4c{39Z7%n1MtKdr7+aofR1KdVKVhyH({E(|-}4DQ_$KHrj(!-@OaNzSf_w|PndtAV_5q z?e1%bOT8$s+54rD%XcT2RpMcA4(Vh&+sT+$?|?4?<(ymnWpQzdrUo-AEuKzHyc~T2 zANjl#29GM+(jd})j~sJu(Ql{13Q;ng=<;q`#aX??b?0}5FTyD^sXWr(c%A1uPtTtA zkv;+jm7*ybriumlJa!F2p)Tmy!Pjw0+whOo{K$`r=$s`ML&v$><4PU z1r2b+j6q`l>hCh_p37@ffkS)bHP`~@G+_Gk@=OGe{6N7~1`kq>CXpt$5jS6}I;cO^ z>uCD{YUJCo`0DjBjbew~T-;@9A($}-mc-bXf{Op4+47}X!*UhT!;qCj2@OGE6> zYIL!EFLTLb`{<&~rY&d^Ka^r|m{-r!FdXQlV1|_UPWG&(xhWd8wQz5T|5Pkblwyy! z(eh9FO8Fw%f0u}jb@my*^o!nm8g3csPk*jqQFsdUVBx)kjQX;-U`0rbOfW@`8K;~| z&G!J1*)H9v z6!sNBZTf^OvE60oFc}i%AsQ?y2|Sti^-EojTt)f0=l&Y^;xeV!h92+5Y_3fk9uD88 zq%65}^YUFrJOCH+i6j22zK_GEJ_}_6{=B99$-!RgdUDTnkL_%DaiLw^_eVC7)(nh9 zKi`|ha`o(YrO4djkD;GBC}1jV^u~CSS0OX#ztwb0!6vR43qcg!gVJTe&qnQ}E}@94=Ug2<*qg{b85R0G#Q4MI<>l*Ts+!=9m>fGgj!*IouoLJ z7YkV{-K8&}N646JZswwb?BaX6(Ds-Y#Ay!X(vU1X7szbZL5Iy7-uTMWP&jV5VdA&n zRh#eK?H%rq9^v3y08A!o?avTN-tSAYk2P5dWo6AXlczEXn_ux%LQ`%=^#p4Pp9bnr zO^2!D1Ue}&pNnP)WC-93#da{z(4vVxaLg2$>hxsEgBEvIxSQ`&TdTtqS)13cwPiH* zGGaPRJE)Tu0-hfToGDOIpRm#iakHRB0Wl>hQ4Y*{Ynift+&~jXq=TJulE|_GZQov+ zuzaP7k=iT5FPdU++Nn!_uw!xKO+9L|_GsN+@?P~eF0syW)z>)Ir{uhbZT38D`af=` zMIc>kW6Wv)l;$M01fjDy)8kviiNx@LE??AI-vGDq{VIwRNB$bPnCVp90^f zA?!AZM*$B{96}?QZYa3SK5JNmdu0$G7`B7^u7e>qQCP7P&9<%5Xt|)@@EAV7&N}Ea zaNx(r@Md5L-*cIDXjk3CWgl-GlF9Sk?SAiP0YC>cq{}QCx9j;SUH6~&n3CB@x#AJi zw^{gwY7g(U@Ki%sR=kA4DgbH!W8z;DFyL(8i;pt1>C zn!UmjJ|9DJmIZ|2*zsCs+;|?F#^;qzElt}gkHimR3%cV{Y20B_aR zb`oF%QPYZ{e*Y{+j~if+_2clfdc2k#iG2Eu=E11GUI%5u4@qL0&_!&E(61bJX=(K4 zo<5RJb&HSd>y3?Rv#b)Wnj#$At+g|gBYO$;6|H0obk_iM7zRDw7t>$@TY7PoA^8qG z^JyLR;3+)n>jDQo-J&xT{*n>;*>gZnFMH7s$E}7cW}C-k>W)%4x8c; zncy$edKy$ER5LA7EZWZtn26Yx|Lk$MI69cYuRYayZJ?YvaHw6?r1NshB;Mg!%2*gs z2Sbv`ue+-sHw&7T)GZ!ZFs)sp8DWh#2!4GMoO&qN*GK+Jgf<1TWW__?sPKg#=L`0( zRZew=I3aDx%jbCFj~25R!V(C55!@r@%u_1!S+7sfkJ4=a#K80zktMXaql{JTWti>- z%<;`CWr@(va~GLZ0_Xlmo!eoTsFM>zQ3;-moAbR!o8`hnOf>F=N#oHmuGjsiNgd`| zwWy)E{RWl9u6>=|2PG)FdrHXlbYU_ZGt0Ld8}uDWfHi&hQE&R<+6r?%dCwoA7-l<6 z5_A$0rkzQUaHZnf!(;mB?Jq;?TRx2%c0H;dN5nr{Vzn-}!aiCjW?EboQ&bAJKlMy9 zQU4~5vkKk8t?GJ(Bz}SV0^EU00KpIuPow2q;1AqvKPP_c6^uvu{-xNOl^=8uL-Pxe zKCq{SEu@DWqw+I}w>UAqiaGv$B^$Bt&#`|3#xwY?7_$5*ztCQH$BT&|><=Ma;R!m;rmlmEIZ_W=9v&;oAd%O+p$_zD@6i$tZq4SW zFL21BqypjJD}9r&CI=>#A&_! z;J~TPPPTN`5`|>Ut)We8m{VfPOnAI{cNkZzf(Ca0K=XQv9w79REx`3DNX7X=@n<_L zj*=J|+XQ?zEPEyeZaOU{1!j5Ba0l?YLbpyC^YDvQxKLJlQ|b2wJ+smSzeqaYcH`iO zM`~L5zgA$Y3*UWfq~sw-e%@RLOljGPOJX*BZgy{PjoDoeiow8wAUQ>A2unAa=0y$* z;uM(J$WFsWmfw3^&E;k$Ib(=Bv3z++930;^geAFCt>e5=r=2FI(wfFbie371;PUO8 zKpCjA(;E@8rEi|QiPSq%ZOu!Ixvd9oM~5`dZEkAgZ0R_;TBUbmzpo07RNlczYUhhU za0O_2&jzN5gM5N0X-FHo9FsEF``q3V&P~ zz-)*$t5iYo@WpkZrS|@<*{CH|x`6wfC~0z-W$Xg2LEwcq%(Jc8 z_QlW2F>0S3xcO97mqX=;Cq4dv#XzuddM6o3v%g;T`ym6i=Gaz)mEC}g`=0|d2ajCm zay=aJ>4piOqg}1})ug2|mR}n4-jym6S|c?F%I+`5Ig z4oH-lSQTU4jTy0_mNs0y=B3oHJqSA8MtiGRxd`3pFwujcN+4?y6vVF(NyM0;)`ryz zpeM4bZAGeP0I5M$9{-Uz=}RRt@LUj2@`i69j5Ls0fk2E?Cz^jiFM z;vXXt^K*8s+ds=|vxAXG+>osemkfGsySlCcbgiE=f7SBX+ zz?D!px*g&3?r87}1_ra_ux2xo!VD~CTYj#0$Tb{U1q9NUBswhBqNfKgg1ng8bo7H8 zgt`RNh||?&o8qE$O;`Klx*ieWVL$T8AGtqra0gV#wBkG`U&|csMp>h*YkXm3c`X`e zI{4Lp3g_v{5oE=$()0;$*H49>KgUZ^krd+{>&)^RSH7XolI3mfV$K*sV;Gz8**z0l zA9`p{i>V)u-h6~v;rInz|5QwjcAU}f&0X>$;&c25kG;t{C>~&B8 zzu3b$Vx=W!6hzRy9Tg%Lv=FhI7xbv=sR7ABT<+@jC5>3NWVMfQ6YUQ+{#lDHCQT(j z(LXsN^q=Y(O?KmHJ)!q&b2bf(97^k+{BEl7cnH%8F`7TG|D?B5wPvrK{Z(UN$W}Gi zY0`(TlTx=*J@{kPSZ zMkZ&Ud&!aK>8}h8|AMoJ2Bg;Cv3orf`=J4b4ZL5^!J1k6z;|%8Ztxkp3-UD{OVy|h ziiGm(%WJpRC5n8gP&b<^-&Eh0+)eVx3HA!jIr1f;0Q5k~I5)Zp@0Of*|KU#};;!A8z=$pm&0bhVNMUrgAP zG0RVuvwe$Ro?BSikB+AveTI)?<9+et;OYy`m8YLA2Z0P2(e)#Qmsp;ujKv4_tmylZ zoq+SJR;{5fV>%+`k37%%hr1R;SM&MK#1gr9?A%^L%Sa8YJPksQ3M(YB=Z0HdsgKq( z#YuL|ARXo958d)yksRIt&Rz$`&Rj9^8UQ_4krmXXlH2ozeQY&5VfZ!#nDxKdYnrs<4$7UeN)P zVcb!IxAg7BGhxZ$tTh0*rsk%t@A&I)JPb0k_ymk$6bYkH^-M;HcuYlgYpb1-8UHDL zZ^&Jkwz;~Xh)w;x$%{*~cX}<1%iPlm+#COZsIK{ce}D37>Rp^%U;iDOVBP9|zV53c z6<)0tC4XY!sUrN%^&dQ}eQ{;^$3l_35g}4u1YKSKbFfI?6Df7EAoj>b^IrX6lX*U}t#P(=0jt zTC~cl;F(Y9q_MFW?_y-5mxuoz&H2?HtmVfXzsqz?@j}FjZvJhR&Zv#BxPX}%7dBEYv>yNplx4!pj{cg>5e%>D5U;AMvR}#)mUrp1f zFWqbfO-FRsYTPm$6-q6I)$s!_qx28|If(4B$Ca*pTgim`ueHl68&&hJqMy{uJ!Dt{ zVt@$yCgL@)9aKgk{B=+7J`(7E(7&BG98ip7#u=9*93;SnMqv$u1RF+xq^-B=JuqI+aG)gpQUCk{ zOqU2lb+5@+Uk4Utk@*IH7K2)hT^%^{$?AUC>7BHuQvf4P{D;J#{)j-(y_D(0ec+qa zQ&kLT?pce#Y^$0Y+K}2(G*N<2;3@l~tLzf%eAQQbfvjt~on2g9Y*joBQJF+V)M&y#v}9?|OD@xC zZq7Tj(3y`ulR_lJ$MIrf-1LBfv-SWK_XutzG!)E2Kv+=U!}3gV>%DTLIx~x8Iey%a zVuZRiuk6x_W)|nqvf6rj&27kmHV=(C z0tKA*^2YB5!D`J8k{mMu4~NOk_UZ36a4ve-%}c?MzpZZ&-K#;klm9F=9;KG5gbt+v zGPEx560ECgn$SuX*yeS7N}VrTN_fZZEb{_8Ytrb#!W=W%IFNFYF|;IoGA zsB@AUG9Qs^>CB@m?zCmn@m?vg3voP2hJD*-tVS>I{4(Yu{ai0-AZsccJG|W^?Q0R| zI~upp2fZYgkSV>mYT2yqHueZ@L(C8~_7F%E7JWTlFc{+jck>mB2ZXX*6bt3_br%(} zL$$S#Td5w&4@E-%FTsvN9zLJajY=xIJmOMe87}Wg=X=#jzwyK6FC9Y4SDLkL7FK?l z*{;qaZF~K_*x0Z9q#F<@gpox0#n&wO(;k1OU2o0M@6%S0WXrJ}c zAthZWV+x$j09Je&n^6ygB3+7#Ho{pH*_rYv{^Q0?IhxxJ`-dcKxcyn|K7lcIWvii@ zD}rRnzaW7&eG%_m=PovL=Z?P3V7t(Kc(^W-&g*nq?WcB9aI2AGU{zCu4 zI9+<&xwnwABG!ro?tO+A!jub<>dQV%35Dl~!X?Bw7Ue={0hc zFpLJ?pX!^Zq^&wr>MF=}1L_7JiSK&n$*23jjfX9>|4hlW5Ud>g{jL;P`2D7n67X{L zR3Hu>H6D$>27cJPVmltUL`ZYr@*&c5pU(bkxF(oc8@ok}-$IXsM-df>K9W#Xt zv(4;`aX&hd^lOu9bu$}mO--J(eg-Ut+(W*ZeGkjB>EIwy2(hBY0(;eXsO7B;A$&kf@9M*7o1iqF z0IzN}VVi*mZW7BEo$jC}=b-ya$%3KFgO>Im->u)>(mFMnUA;1%wC$f}A{SAGg9{J4=w!3RV7$X`xPZ;MvGVd#?8k6B*Cl z3mA^VR4BU=vgFbls(b(Hh+mg{GE1X(aeQfZXiZc`o;Tp#b^2sJ?(xw(n~kAIGrE3nlkk`#w2(V zcV#jo^jYISOqeCdkL^WTGG6~UsJvB{NQrr_OVs05{X+pn8#m%xs22T)PW$6D9QHil zUkQ&YKB5bX{DGp_ki$JiV~P38Nt31mw|}m|NA~QM6$djBQtC-}sgM<_6+J`E5LIfO z_5&DLHNa5+`!gBbp6QQcH{@^8=diV-pu^lrXR3Y5p>zWp5d_qdS4Y7jfd*cmiXDQd zXiznLX00z-p^8*`bnF{PWwgrUvzEN zj*EFe=ST3J#2-=gF!~Cu(ov;=eDlcs&WWMb-J$D3Hb=Xu0>Et*pq?NrNi2d$r_+s=lsS>^WMy$-Skc1iiRBa{ z1$xE?>d(b{e{T8X!QEvpguo3rEaUpV%&Ws<^-@V}&&7v=#7Q2kB`2agzU`LzmMjw7 z_I$uXOMNEBhzBj7j)qT_D~mJuy=~p+z=9~VXmz)ZoD*F@b`>)RaAlaI28uiVkZ)cYxlqKuIr4S;^rl` zK(81mVaH@tL4n5cRY>&Hyu8Ke(yZ&>#V|sWXa0IF?#2P1iG`9ts%)?GIVF~_K)VS} zJi33k<^ahX;@{t$U(64n_^|_#F)2CN>TA+HUl5A`b5?W$+KW zZ!$0{*@A2zLkzsj-D@(bIgefRYiQH;7!g7}NjGQ-U*ngKWS-}dS;mR3k z3-+}<)KM^gTy@I5qJ8@}fY&{zW`pRf^f9>-KT}@fVBCPk@uh3Co6F+SC8yuQMPNlq z*R zm$#CDnU|+%*dRaaRF29Nb-b;WSC#Qj^jjz>D2KEHSSa}V z%$a%>9od^3Op=C1j^NXrczBsR4ZaI=cO~@jJg$|uDL~i#-M{r5EqPOBVzRz!sE1oj zkn2E{b*TFgZlRzC>FxIMX4N_@HJD2L; z=A9>sn9R|_&noK}8*}=i!LD0*bd^Y`DwE(VqAq-t>+E!Nocxzn8Ffi0!Hp>RjTXXf zv0>@w*XH!HgeMpcg;s?55xXK#j|hVeDwdtdt5@-(AiT|wN7`dv#2UD@VKMR^_DUK< z3QZ~u6JuBv15SK$)1p|kJt4s<_7`1(tY)n*5kG})&)4zz2QIsaJXLs{*u1^?v5T1n zktTXBBrXodK=955?QQkhW17Vt51F6=y%iGihZbv}NOzd)7JZEH(r!~=vhmN^pT-5t zxg#?@{hv4-Em3R{R=pqKVDL9i28b<6s>|51E9jrJ7}Y;|U;i_a^ha9Gq)n4b4&-ni zshVI^`F`*b zd6lQmx0fPfw&Y}ZR3z|xYPevn6Gcwr7h}BTh22BUm_`_suH=tBABWJfCqHYh^Dq8= zSFb*~s2tcU6($hjoW=F{VJ>lpk@L|D6nCL3h zxCo9N+AW%k&&gQW6+J#=@TC|(DI@4!z(A2&tWWz=s?ds}DN8+0K0v~>$UAQY#rVUz z*2|#6;a1P&fc$rig@}Rbo1)XOp2i_ENfb6(^r89KvT@@{GSJs7@X+$HumH{qQcLD5 zmhe{xb4UT+_sab#keD)Wy4vtup>?Nw%(IKO{+*j%*eB6d-TZ6 zoREI^(ic574Gi}UCB z#}cBXj{(u}=g&zQB)_K07*32Cv>BJbX&8#}I0n5)@7XMV<6;(mnD`Ns5CQi~mGb(B zwp9T|{APY?M_q1t22h8D(LiXROEQjFuakjQMK%?7f{p`?gGbYES)7Kohk$fxsT#OBhGXW@jyffYG0^a&P9#Z&ZTm?6ORA@eXlG?Xc;rQfPVHvtubvdkw`UIR_em6b0rza! zDAz!vR65;AafLooiQ1H3sjox)-*U~$?E4#eLp`esE#LhhXl|oVJVCb^zx8P%eqRFJ zwJ>h+J@ypw`4uyO^%v@Kff>e1VS*+plPnMH58J|$QoWgoWj(-}-i+4XUHv`UFgtxG z>Kg=J&Yf20Oub53I`t@mZP4vCxhXEkT-ObzGu^{u+&}kV|EXG9grnp;Koidv#-Kb6 z12*{ujxs88-!XJMO5Y=j;3^^Qb?7fYJ0dGR1wnVcz04KfC&c~yP*F+t;D_~l&Gsr9 z0g)byq(5B`S%=X@fZO1kjJ>CJzKb>Q8<;(=D6DGwZ2v@$nvU9mF|gia54>tz!X^dW z0Gj|KuCv`a6RpkPRe#_2I5*;Esl)AirmPSm3gJ>i>$&9+B!qM3;YIeX)Mc603{eDb z8=GWL)@~6}E=DwPR(>+7zDkDJQAWe}K~4kq0@($UDoZ>vQ0=rPugAv$chk=q6910a zWS;2EeICM5n>b$YUNCgNn?V^)6r1_!WhUWs63LTjxy|XWLSB4FgQlqd?;zS}3JNJ^ zX?ua-j5e$o?0QHMQKXUDQ$2J#f@bkN8Uy8Qx~2<(TSXl`wQ>xwQTxO&Vr_!#u{piI zn%CYrXbe>bH>@H*r{-ja2j}vZT?4B!(-TS~911CiRa)U#Ox{df6wT;TddcsSFH*Bf zh4oJ9Lpc~WT~0!OQp=QZn`sW< z3DZAtwuu-Net~QBhEg@*3fHTLxu&Yr`=o=``iBuiR~y508}D>DL3{!{#q-GbH61$P zABMF*8~FEN+UMW_*|c?-h7)Y5XhsQ1CC^zCUnV+nU3Hok`bPvtuUv}l zw*TQ)@tpgV%{up6d^3KCm#p<%b<$@dze|jjj*|;UH!=R%>+6@}+^$DkOW*x{w%5>o zv47jnvO%*Y?k+0p#f29U(F8g)DWtR>K2i?=xeT~xxfW(W0@4|Dgx_%Y$w|gW-DOe?|4xE6LIkw-;qLArIt$TQ7dyH>>k(-d72? zhbUUV_f5ol?(2#5f zHjQ&4dn=}fOY*vRw_qgIBicm~@Byj|ymxCJBR~}dUZ&+>i z&HVj4n)EoiwEx`g@hq?j@9`3&JCEc03st!2;3UwS4Nha0)qbM>Q>bK>nr1B@efy2ue8UmF{|i~}YmcT^WPVoLw0_?ZGaLPYE%5EMVWXeUXn38Pu{Ubu{#ZMDLWL}^gL-;Lybr?Tp z^am5|ofzu8jgu{KaTulBOa1(+pOnvTq@oCC7Es4h*qp~q(Kn>gS#N-$MjE0sf~re1 z`CQb&8QiB=Xl|nf)yO*UhmPyOKEUnS9XTVS^X@yM_TM64E9$GbuYhzw!4*wCaZjj!R zi@v~AAc}5HqsEf;{wEAPRxpJpl1ZP0L7&BajO8`-`C0Gy+*!2#v+-Lmf-DU7UZ(KR zi$|PyQi)Uvca3i*=~hiv2aE=4;*t|CqBwpk+GK;>K{38f#lTDHWHBHFUVgiy@<*w&K}d~Bt( zw&?9&!i-LWVZ$xb4~VLs2R--j_j!aNcyP9c09*VE>+a+4!U{ePQAf05Q*RRWCgDou zx)<~j^phYy6n~(m#m2`7bDe?dz>(OX8HV1=*0-)QvmypQ&r$vCf`q@raER6qNk@`S zAk)v->v}v={9o>q#4a~W6UHS#5e*AJBpq4-vZ2<$d8-9J)zi^AD(3~A98QmJI^wS9 zdKiWy41}YrQ9aiecEdJ~#{@U2mqD_VY?5JNmEq=a*? z|K1XCW>Z!$s&_g5?9^9`^XB6|Ho`3nPA9=QIQUYHA&W5Y_ppz%QsvZrlw_<{Eco*f zdZ9N72?T>kQ80weJu?`??Ls;n%E$2OAPoJikBX9#GVw)_xEZ1M*=@U}=#^{oMHDr4 zV(IgSft{Wn`B4y^fBFAZOUP z^wG1>aMa?D9}F*`GjKJyf;krZhheDwg`9B|GvvXO%RfD*uh#%&T8GE*vZQ)OFC5B_ zH4^-6N3btE<_k9J&)=QRD8N=qJ`P)+e?Q>%H??~BU_&X3<&mSP9?Bz_U$?tK$YbX>w%a((Y4B&LjA((=pxORPs_w-39A zKTW}Nt1e#s{J`cX&`+6d2+TXc_jt{7H*>0z3h1S>N+Xmy=?2fh!Ut% z;N}E`OhiW!b7rqAn7Jl%-k@b0p)&%j!a=YAw2dK`m675+azIbIQQibj_?iTIcmdCM zCobu95}`PZr(_p!RBn*j8MbiDwzPD>WjHnxen1jCH1uVs5GfRcK??LhT_8V@kEy|} z(S}23VDDTd#RxU_vGRqc0sB*hoK(53m~#2voL_JDltL8f9LzL*=;1N{rRQG2);|Ee zlP4yg01d+xIH#?tNd@;C|Df#{;Qg_+VWcN1zDzuW0TAvcBNksLx4vun+e09FF08_P z6o^pMCRGYAZOw6nUO*xlst_$s;xIEyCJ&t~rCB~=b$Q@Se7%DEf2gNw@UPdU5 zE|bM7-LSfd_r2$5OuGrbY2pS?k6Ip%`%)cGSbMvv0;A*o&C>BNV;y*v2%{g#*zWHQPPSD6?tiH8{qUq`|l4V^uhy4uL^$I-6i%D8fSQUxF^CH;Ovt>(?`y-z9 zZq^?H>5FYeC!*1_3ijKD!^D2PQr6AM>y)Ol3h?@3vOh@EHvd+8clV!*7Zw$$&`sBx zlo25yh5_`){W1Ade2%ArwPDt*Dw57oqoUk?B~t8MWfUU>;N$%-4G>P~c8jUL<(4j= zZ7Ui8cz&L3-W!fKEDDT{XHl0Xn!L$%-*&GWl?3QtzGgBH^ai|83{+C8QF$`^!qke# zV+oB0Qc{11k8Drm;(UxOo_66QO^}lf59g_4Wae-0mI>Z9!q8d*3oK$1aeg#CRgpUB z(+*S&zLb~RIsfXUk?>nqpDSFxKBK88iO>+@7Jb@QeKq!2T%4ek3u)+Vc)ZasjfuJs z!-<7s)4y+*V#kvCL=8lZj7Vz0cF|DZ!&BGL@xe>fe-rysMis+gx+<)swKck)lyv4a zwU`j%t>4?nc2WnCPIx>3p8uGbi?%iH&{jx6kBbtQ8m8;u91ef4O{dg=?acxXKP}`_ zkn1jFs}08eh-n&3^V4Om|5_`dwp)g0$LPd@m12NGmW|Y!xWAV>B?BH(W~Ps}4z7q( z>jEnDHdR5$iVf3bKZFPq)dTypAkSnc^|l8#4G;g?qX7s*AyUm`@6POF@FA$*#y|*>P8c21qxtveS!`mT0fy`A zDqYsQxL>&@+UIpvd2N`{21 z)&X@8SrStxPfG#(QL%=6@{Dg>YjaQLa(EvOXIhU`3@WKV7f=MfP>wH=gc?#|PZNZ) z>v;XMbu8BG@$iaG^=+P_p)sN`(kthd3a8o#`jDPR@nXA_l~xV@ZW^|$jqT9+5`kFF za4ui`)cT3sOTUg=04}8$smvPq_QboYxH>=`E?T~2?RNO94zT+uOqK2g@m(EA;El@z2$RB)eV^hOkRGQ5MbW_-T8 zv?Fd^ON=GOdv1|{E%zU}-<$Jnxxm5GCidNKK=1pW<)%-`W7+DCIMJ@{y?QzkP0cgf zg+%kv&3;8dw*4umRJJ}m8AGA1VP3$BW9yfi`NAt}+4@g%e~GWAr00SPqE>tlD!xc? zb@|XQOB2Mn4U2^hje9d}_ecFX4={1TC2QSCdjW-)^RXA%rn7n3v81K!oP{b#E=Y3W zHhJYfHK7Zq<6qiCYaL_(pgBlyF%V)`lGPCy;JskKOSD^7mcQ!fwumG1>m14`rNk71 zOF{=7A<7Mgu_K`{k8)$Wd&+m8{310qDX95wjK5jR4!$&=4OKCe{K{_-hd(u6B1ZOp zD;PbfxC5V|#OL0#?_r#~50@DJ2Lk^*C4ETpkZIm|{5}7f7%ma0Y4EjlRqcVcZcb;` zTyCPiwtqQQI9da?X{Ic`gIF_}&6|A8u(Of?HzKr`@?_B%j0(drY;!bH2c)oh&Vv3JU`TE(y@GZl@HJua1bRxWd@8n}{>G1swc zjfk$x_!m4mt=22zgVRo_PIwBpE1pK5pVMzjG#GwZ@5n1ek}vKRARj9 z+O$W}K)0e+x8M16xGCttwFZ_dzqBRT7Ox)SMWyTd(EJIk)l0gGhCK;}_4g^d69UAi z4!a*xMU$rhy${0$LXuI8SYJ2MoL(cSacu)cVxb1fD&pak^^M$Ecb1Szk2Zb{xb_F? zXcYb#dU{JEyE@v;{^}y1rk+iPIq)fa%fyux*^t-Mllei0Hg#*atI6hS+sW>sGo6wp0p>?bsajjWLAPYn_yLmZ^w#uo8Md_V{F;3 z_I2{)t|k_c%Icr!$DylrbE`lJL0AlpT-_u3aB{axCBL4}pgb1F@ZiWGdMNl9xoR0% zM`}TlKm7L=3ZsN0XAJP;w&a^lYJ1dcv8hY?F!VnHdHxrV`0%3XoLG-8K2Cq47PSSQ z70(IU?E?#?lWWwytZ(X6&%9#D1)K=Q}iO!G)eXEOM| zyGkt7KVtYIrh&SCQ;o_O4Ijy`o6JWi+dZ_Gx%Y{BB}XXWe7kVndb@ zKs%Io4o7lJWbzFe3)IeLa1(0+BTO=@{Ki2N8VUhXQL8LbAU?`uVY7L!OIB{>FbC^- zL&?^piEg@qCM0x>;yOCOZ5xU*t5zep6czY(c=!?sapND~Zm#X)s^yBBhZd2X$4(9r z$-qJLXcsPRNkmxrs_MwB7Gebg*^u-idnm)Oggq+CHrF{vOo;e!O*Yc6O42}4x*#ZV zpUuBqr?L! z#lg`>fA}67dVHehHEqIPV_L<eEIF3Hv3&JX}Xxm%F$?n%b?7f)Ar z2m-#;^AR5Hb`yjTDa|*9#@SPYn-B=6JM<7GKB$YVX72c{C(|7etATbTp*Quw-jTwt zDb5KvO!%gLM;5iB=C7mr9kx8U0I+baO)M7 zSHyQE01+Sw=NrsaFdF)4eoio8PpO}~#!7K}i0T~O77aq52p6!3ST>%8dr&){I?4@e zOgZJFU9M*~`Y|)njo>iI2yNhh>T1x-c~2j)?V=0`o`=B6mKsR1K33CPB|!YS5f9~} z!F0C19ur@m-y+101vRFXKK)YArYdgVsOc)_tF2u;`eoNi8Mt6;(rnNwQ@?j^$I$n@{_p6Lt|3DkcQ+OW$JZ^b zoN~2X>{K6rbH?MfFJ_f)k>1fluw^jTftF=1FrW%XcXVVb$As-b`&EHelLf;VRd`?G zcV!zF?%J##i(oz6zhE2^^|#ip^Xw|mYeF`5Bo%Rci<+9)S!J+b69eF2D424kp*@mn2tF#l>kF{+=bopjW=@w7|`U#`AOcNX&Za z_BYSMg*H6pTS(Ya6yJkdpD`*Jw7?-K;-*u}f(ClyNe0Ob`u?Q1;&=dhjF($??4C2C z{{vkO=vP4HtHC$!vTOgH*QfQ~{T@uHMfm*#gqG?E^6%zA+u)#h{@d)$8_%*xf)T|lu*%wH4n zD0>e%-6Nw@*Xw~e8|M#T(lh&uYZ)g#Hw5twORN@I2RJat9kSo!Oe!EnQm8`Ylvj~4 zsYX4#KsyIoSgJgVu`sDQ#>g>SjBQc>X3+-TBM|w{Bn(%EVk*pO!EG{51<*o4x8GEs ztjLm_qYs=VZjZ{Ske!d4Z(#nXbinX=%@<61tryXY_j<{;<**f@;VsuBEx7 zBwBv#?Cgl;<@>f1_ndDV+0kNC#^JyRE%Nku^4~c63{-aT>;ae7jS;t<~Lj z)~-&8XWHbxTc*ToWOq?(V!SO^y|JGXc@|GHoD{op&a@_^!58>zAZ$wV4@feD4n!jB zy6gRn-WJ6O2!DI=*&y=WAjsVRu=GG7&i*nZk<}F)0_>_RSF)#)QV7->&?5eh&eIqF z1qcn`xN`tV?$VSDlIK4_yl?@xf4%-#V>@^%rjQqX@2i_JYT39`S;Pp#D5Czoc(csG zFr0-A{Va+d(R&=AMz_HJAQq~s`s-u;>B;(FE^G!f{E%IFVxhF)yB$X0@Y(0N7d|K9 z=r!#477(=l9*j1*;Q3)jddg64R~4>J7Ir0*Q$FuAq!Ak@O%F9BoJXf9Rjc&M^lD=F z`8WQ!&GZaUwDv)--dECJ;fc`=C`hq|6eByIZtsRg3K?n8=Yk?ea=#cXqsl?ZNQrW- zS4qDzxx1yDyyxI_hMnGul|bWh7x%v*u~qlHU5qj>o;YUCyS#J;wM{8v3IEA!;oWSJ zidqtcSP&h@ajH|zVp-Gn?0S!^2a8Lim+#VFy;rm~e^xc&q3|js)wj7eZT;8hXvE<+ z^-3Vc(z>4|LH?e0h1}$0nILoD*=;L&{G(1Y->SDf6wRJi^>9L>t$`XJ{8wm!4(?fhHPs>FQ=l@9IG_xDv;&^0O0%57uBP&WLy)vb|@zmLjFkJAj+ zStcpMIqXcaC*=q6hl+Iz!ibi4IfreMU|xb&XtlUgZQ zo?z%le!;F+hce+pMbTyOsb`Mas3U%9V5hRnkXZa9wUPK$UhjgmO>SHs3yHjEp?Xv! zVFD>0?R;*QKq1~X<8kas*slgYd_cUt$%Ox_*?JsRi30cHu@lqX-`^`k-K0+t z=F|{yyYIY;gTVlq!GKD9zn?>6OqSuEUi$K}iRV|-&ru^~2I!e*2G7eG$FN`be<@qf zu|b)MNPhs`_REFAOcg>THlx(&Hityo<<`EfYVCKyPt=j*ukfV{QB(B3D8WXY{nPTv z?nR>PW%CnvU^MM3@3}(b?9bAC4H9*|Kc63q<;V~BG#}GvJwTp;82#@)CL`F$il@8v zE^eo{Z@e#d&Whd}h#S@9%3wGy!Vt*-`c;~*eUpw=AK(hb3vZ(HKZUP=2ARH!;+7>$;eaM7)~u1I;}_1Rp06`7c&e@RzyTNhSwXtvPbnp z$TEZiRE)puhxra4U}5O7fXYD@^{h&9d?A5W3bA!%oh>a*@0qNS z8dI2Fb@a4DbS5c`CDNW`*4L~Y-)A-n*$Dcdh{BHid+R`skPwZyyg9Wz%}_BH|Err0 zF2cm?;tXyl%y+_&VF;x!7%*J3w2-T_bR#nLZskOi*+wqI(AR01jrqUPRw;;ah!gPD zzWfLhcK()vMU>4(@}nh=3Hmm5IB>*{RnB;I7gn#*Zw zJh4N787S;+ywGKd(XikcUZN2S-zwS7_JLbz@1aEZBUoM;FEH4^D)Xx^GLQ;2dF%DP zx5m9gMK`1+b`E|IjT-ihV~|1wS+d;rF9SgR=qIi}A$pa{e^rBX4v+J4Cb??VW}C+s zBI7ekvdl;s2=o*DZ6a|s8*WqlZh{}tcKvaxE6FBmnq4&U*wIpOW$)Ang&+(oh{o+z z@gvuZJGjsxl?PaO6$<_89pm{zL`+C)AdcAhgOUPqSU*WqC+H0Q0pioX7ZL+2IFPi8 z#ifr#-Oy`CzhQCl_4M;=TJa}#JD5OT^eyZ=b!OVH&enwfY)Ws8O6WQkq*?};`y)dX zLLuSE7(Rt)zVQ|9lv~&Gs&`b_JuxP>D2iv{HYtwDUT9ZALuVm+ z?cTZ;i#jkDDtyI=fWR3|>+Z0LWQHlE<^R~T$Sn4W_e+5y6>iFxJB^+5!o}r0xWDQy zQd6jIJ8P1Mxa>8gnFa=c(L!Ix{{8kks7yl!6)POHVk}0(Klw z#0V_;PE7=hQDDGgRhIq8fOVH1ox!d+v^qlCUJ!Ldc1NxM^m|!x-~<>b;gvn|%KXVL zfQ$ejx#GnsCez9Qgx;-~Gq+yDJhbmj;~eHj&u~D{lT`f@-z3>5kTZb*6;>J1<}2Q; z!*ZfuNcNr&zK3=`?hHbQi?HXg6mcsU7W7{brSQ*kNxOA0FA9X0qh)rS=8 z4sB#b{V3o45Rf;r75!}Yj70~Hvc6vVK(8F-);hEe`Aq}HuktUa}j2Fi4EZCW=s+tXM(?E{E*-T3Zvuj4{$elUA-DU z`CCqn<$dLW0jt3$x`)l(haPhiY}D(gx7c@)H}t-GfBj20bkmJO<`M|`t4sB}XBSPnHih?Z696!)B!4@(OH*j*TU>tTo5#4o&h^cL5$WTe7;9KYt!vTv>=LPe{Xo@H{nJ zXdj)WrKMi?*r$jhy!9yWz2~-BN;uQv)0Mw{n>utmSj@kk__orr8fypo_?+C~<3swS zo%+sJYumdD zXiDD+AN62xnlTod6sX6wvUibcVH-EOh6o@j_TBFXd}#Yue)3Mw*gOW5FVo&h*A*U+ z)k0M1+h3oD)TD>V}w>{Yc- zr|KnJtC;I%S`q5cA$Dipp@KTE-4PBpauGk>EFDVRA7Zz`E`Y$#Q{}PKu9Xze7@0|9 zywwY=v2K-L{lO7tP^dg~n_7eKd~1?$J&;BpbJD&4bsx_|#TgEAT{ueO%dh>pO-CcKqBPm>?%41|T53|C=h#qkFh(r_W9C}dqUp?Gs^AaMLkK?`<35 zMbV5=x2r}$^((yp3)Lsz?AS9%BoG*ns4fHSb+6{HY9fXsriBe~!tv0`6w02*6J zdPtG{kdb`SJQ`1(1T6iT*tc(!@r`gh|-{~wyp!mr7=`}^A%FnV;uC`pm-*yu*OJ0+yM zyJ3Kol$1z=l$5l9G>U+PbV-*q5BGilp6j)L;5s|k`NsL2_XjZifpVXL>cf2s0uVX$ z^AHew*nN<1^eh7+AM*M2eVmFaV58SPi7aoPtZJlMbqt{lXH;tOr8AhpN`p2I!l@Fq z`BgPOn1mINJLoX$2r06=Bbqenw?0-%>DpI4K#VU$FN%G-9J=9up3XaueLEMFbfSpf zTuShw(JCN=xFsJa_g+Dz%Dg0xrrw+Vh`l@Q{7ZV~C;7*XSw{&2<81D60Y^uM8GmL1 z*?eXEivS;NRLAqM`A*HdRXI#n2h^Ci#4R3K!R@DF3%5PyvmG;IuEVF8_^Spu7FS_%rs_W1+x%Q#fW+2h& zmy&g3i;ZkTo74imk7pKuo(R%}uFD*Me53tcenh%fUj}uM;KSMji))#b>8K!=^*WhZ z$XcmpP~D%-w)3su5|j8m0-#{3m#jc33hSgr{{4jz+vVnuF(~ zxkrhgsSpnj4}MFLGp%Gf{sm@T4*^eU5hVN!PqTMjZ*K>#r?U$WMOc~wjBKYr%m||J?P}1-BzKpp<6fhA_`_NAIP?i2LBSX)0 zNz^var|pKHFy1kw930wm{N>*4y6D!oLAC)H+L`v3@XfVY=#ZsCC_FqAmcWu`{U}rB zS_C(i2FY+P*ev4xI0@qL6SQ1#>SM8^t}?!wvP3O}I*qt%5j-m~h%t@k|0H*kUK(K} ze;5@!N>i=f9Ku+^I2Iv8+=!$UvTwy`{rO|ISeWa0*oV0xEzwAl7}W$;fbt~CR{4gd zh_-sW#HG9I8_Q%lcY{y_#qjeK1g@LCWBwW z3YF$HPm)eulL}ZG(pC+iPjdR3i1hnz+2ub7MxZ%rY}aNQgn$7R(`LIJ;dBYJ)1NV> z!^oj?8uzc`szVtXKG_wrWD-t)&Qcw~7Ih#+)ioYV*FDZbG)9_vbDY zuqOt1sm(ODkylJ78;wgNngPCVjxN0noy;E8Pa%+nK?(y}8PVC!y4O%I)ZY0eVF57} zaSaURh$7P@eD%v8P0>fcg$Y2P)uxPZ`Tgt4JjRdn#B4jGHsfiY8YjNL3)!N4*o7@E zJEBDJmgp$(NS2bD9)Ph>y(m~p)|dC!nJ)9QhLoMpgxNIyuHVk~iRWwg&1L~?cx`Rs z`KLB%#LXm+C5;M=!=oC&bC@VbGwpQ#ZqWd0s}RWf0O1iJVL@hIoBJ{v*dft+#lJ?B z$I%9Kbp`w<8>wEwzux(n$Eq#*`aN-89X^6dj%aw{1H{#*1|EQi9u1Ttj08i+!ZDHB{9)3IO~PN6REG8fRQ7F5pT%X%v`@iq-YN6$SNO)0mPRVU0I}Cu z5|y0m|85>nu9jVnL9kqPf$d+yFd1usgs)2(xUE{sS}y}Huskmkq~oFf7P_vdB-V3Y zOC<6rc1D^o_KO_cWR=J>J@@Y)M;hN_Cs@Bef4#t7BtFnq4lI@)YM6ufJr4{7UY^zg zk^h44QqdHQz>hv;;^l2Ae%lZ%ydtU1-gFm1mL$~1-%4ds$QDdUS^NHZ)k!>s;#)6o zFRy%wAdzLi4&CJ%%iBD(DRR0{g`U*1k}-Wopo=Gasw;M7d`F9FQWu|t+KGaBmL*?q zqosEqU_GNo##jN`-{35o`_j+BPBZ}urZGQ}RzMQ9QGsCdb%X4hQ{VoUQ}J1?Wwn=m zt?Ql-=JTq53iLQ%`9@_)4bwoyA4zHc(5}Y2d!csg$Tl^m4A`q{02Jv*0#Xx}^q!ku zV+kql#X$;<3ygt$#4;1jG>jQ_b1Mst>z%Hq&r3`42AV=P3=Bo!frgLy;m>0gYUsS+ zn4k33(wjys*#0$G1ey@!eW9sb_j96VB&XcJg?xjOCFe*UvU zYtLA{#@n%t&02wf*aBbD!LBl=tf2p23WWv(b&_25C#HVl`%qKY%~}Fr3JZ^pTtjxF zD)Zm1_4P+yhMl5IQru{_Fg*2z;ERffxG!2~CTTF%LXv?hcg)2~tk8GEn^1PDd`&af z$L-N9J-n-b8juC^=&>3P1moCq*NOFd$2z%$QV+f`Ih zsEYBolhuqccD`jdwU^cJ5>j%zR59?|8=g*9qOeG>IP&Tx!enI$GLhub=Uf=Dm zxSB;!C2$58n(V(CBu|yPS3Rj?^T5r2bJnur=Ply0^xPaPvt;+z8v z`kS5yxF#apNpup0f+ccC1IjdCD=_~%muSx--8|Wn^Np#eV?TFC4~${OqW`$Iy}|mF zdl|7q521_2=}nFKi_oA3(`9+4!Q8PUp)+QbcHv`ZERf&trh}QF?m_RiA!Q&9ud;^k z5MZRhPGblu`BeI`Qu@TI#j4`uH59ZN_SJTG%54dNu~4jqZvw$%D}HLaIelVBHuyh8 zi@Zf*ZUfEa9A;%C~_KfHXk=`}B1YL4mu+EjTr&2B@z7BiS7 z(&?=Pz8l_NbxdXD*B`%DDX|IIf?wk%070UjEYK_SQUVA{q$@U%hpe<7ZCF?T6Lw#; z`{BNLIlQmICv%N%Jq;}um->00nMAUbS;VHnlk(UIP%&qu$N`Fb2QweW1cc-FYgB)i z!rS)}knUsQ&jYtDR`yL(3NKL06tkxtk#OzCdzK9@EK>{5E-aAmNi-u3R=T3drRd-! zF3J3(;ed1uM2aUPH9m-uf>jPet&xSlE}~21K_ie`q$M?~VEuAZU_4Lnii1Ie<=r8N zNK2>tL=S5k5N^5W;sg^(A2+eG~n3 z<36JBuVeJ{%VQ&&T{*{G`rc@h?FXGSkMVy#yoT6M?u1Rggnqe12YHj2r)ISl8 zT)d3osC@cR1wmRxk{43LG8nLdi)a~awMau>V7$z+e}sJe=QyG_+b$RPS;EI{t=Q8F zKN@2PKB_HzyoSbge!)BTn;dc}t@r(ht{V!E=GY)nth^kmhAV_rhtnu;xu>@JpDQuG zBT!YZaNbr)o!$~v{aibdY|7-2GG#}2ZU#_VK9aw=%3 z^J)lJ56Ut&kibY^8;s+%&A$WnaW z31}Nmfxw;x01g4~Na%iJ0!gNrOe&DS^z&iHQcIpu=35xx=kO0_wn5Lneh%;&}0&{?C zoN)}9O=LUfnLe{yZvu*S9N?Hd=Sa9oJ#xQ8o2;sr01lha>HW@Q_9pw~H`U5xmt8Y& z!A#TD{SSYyDjjd=ZfGpome(5cj6|? z12Gl(7@4jQ7)CR(2d|Z}lX0Vw$^gM=!z&>p$a24N zKl%v9IvSOy(J8}_?l5h>uh0F_^0i4J7m%r^+;iyBFJj773J$jxN|?1N)v?L4QCyFMnox;_ zG}LEC0u;Z!IO5O!eJ8XcIsIH&4?Nfsm}1c&jF7juIWV@ADAxFRn0R~gKYV2*xi~-t zXl>a1iv_hWfcCfv5YAR*^qvZ32V3Je%KCFJ%^47y+1pjXKiy6 zf)&|I8?x!{WHN$uW*28*HQ9wB(k?hV4cFt-S`D!<4n&^_$1LQtP_?^$#&@|W;^DL1 zsa@!#@Us_uEf!{EqppI{Ue@ct#Gkj0%@CM|3_fs^2YB|SnEXtUna=&%QeV7XAD!Um>H$BAAdPsy8F2R3v1kz{;n;EqC&k$Zf()c;~GmXO4%{VpzWg z8~L-rQkVij#-9LCRATnUlic&7qZx)KTr5z;OyC^-7hf{5X{{{?Vf#EN1zMxhMZ2$; z@`T;Grs|LETF)&kn8(omRcJYUEGE=dfhIqDnQo7u7h`GT8tA~GBlkD< zeBxlGi~A#W_w$O{6`gwsYq%JR_Hf*H%e`MxuGVWHM1gqw2L!Tzy&2{}K?2|*1j&rA zI|HrlJ^8?p6t1+mZ_e|eGbyqE`Ks~ZoLDf0kDaTHPSgyN{L|=5PCAr8-$vKE`~v+|7!5ZGD<6xw6P_+dXS_j`@Pd;;OXOLf%h05kr3`` z9&J^gZezunK6*QowM9S}qFB@L{1_LnumN1u8a4Of@1W+A$?)IOOk2OQca{B`OTizA zhN%^x8GY^k{0G8ww!=N4XtBDq@if>e{voCC|Nh}yz|ETrkL5XC4N~U1c?mP>0$A`^ zWE0Ham}V}=~sg|K~bRz0851wHGkF*)F4nCUAX4BN^IJ~wC-}XF8AB4oHfip*gbgn zAK1}emDYdYs+^TWQ7W5ev4IFk8GcrV+G2)&H}E>7jiSX8H{(0b=fm^-E6*O`g-uBP zITC|FfTWoJS!Uk#pk3Y+OUJ$;j%Sy-tx)`i81%eNekZ)p8rc;|2Nz(=aP2v5RSLHc z{p6;15as&x0d4ruBlz`&wC9@51ylxJ9AdDZ2>s4=ffgHLp~Q<6s3y zYy6xKvMEwU+73=fU(3jT=W`&c^^Lb?`GvAiuYIaj3E*gdI%8(0W+Ebo!jw$;a}7fS z?H_aBrl=dfq+7%ybMFg}q;nS%q^b14H?deuDio?d1a;d(^89U&hyKl0W_8rQnGS2Oa2o+q6W3$yZM^V)b!y>LE8-~DS3HC)rf>aoC9AHIuq7gpqpf8T@+(Aw*Ai|rNcX9q-Y+D?M| zbYqO*M1zBY2(I7;Ma0q?i6*ApueFw}zLqRRM33yxNgYzmV}AG&d>CpOmf zUZF?CH`C@{pLG8Ah2mn|Xt3qis6Jk-UQX7T)g5Q@gr9^Bw#?)3uq;VcwExo2U0O0W z^1nOXG?PiOlEr%_*TE`DOEEAAyvx|x!2pet&!*zb207P`U5zdOGG!q9`9nIYx;ZigiHeeNe<@y{WJr`Q$=2(8z){Qo6}AK(Tr!0zN(cRzS) zv+PA2%=?pLb>6h2QGl1xsfmOxV%)YVwefq4DV=uU2TDy8@7CcJ{lWPLs3S^c{cN>W zG4z+zSnn?do0mEj^vnbfTgu0&zqL{V_PSahQG$*iY_V5N{S`9$Cn(9VTZCP+@&5c< z`6o`3va5FTrI6lVW-5dxZtRu3FD6!_?lX%O0-|6{AoELe^RHkgbv>c6;&ex`q@C^Q z0n}Tw5mCs;^2PTcQ{5cCf`C9N>wyi7w)%sxRlmtaGaL)!u;CBz!0bWIHW0qGY$^~o zMl5yS_Jh5`;k6qUZj2H8!@Qczg5+b1)lHk{ZWT_ zZ%bVz4AkB_z_b9cEyE9s3wi&ZE@x6lK=I1x!)Uw-UiaCp78cx_yLmA5Ou(Ae=g$cq zI4SZNzY65O*u7XYyYvX43(6d&f|-fk5@*47RP|}o@iuYG>g|`&`OT+y(@a5s3HgGv zGtTiKw1hn=+;{ISVyFe+D^NJe6^ZNA(b3DmFWpEbpaco&&YKHj1M(Mcu^2$cQB@D( zz_D;Z1qbyfT;|mXyAm?~wJWMElwCA1A{^CKM#0KyAYw3OYIq=fJ(W1lzu^<0Z-W{4 zxD5mhb`1?ZIcW+g{kL z$9HRLUEIu=y!n3RnXv7|SX2DB074-Pi}}a4H5WI_L1=NIP@*``6Pt$vLlN@X99# z>7U~a8YUI|B?(3+{3I>7OJD$LijDl5a;wWz$BKZX2!ok~H_!Y{+E+sD*qZH78-P_d zWX(Lfi)$g-bKF7Hxq)?BgvYiux+^M2)vv@8NLN}8!ak0aIOq!b@^7A|+TgUYNL0UA zQS!Dq%erIG(qeFMXHFZO8#dKv%R#&JeDAIgKzf8PHIj(ZOZeg}8 z^lYy%t;Wz|sl7X&Z}9&f3&0=cLrWQS&F9`QhF`hMK;ExtHeImhZ>ve|ua2i#;C|Js z^Tex+j)*`(09VN<{9|5S{>55{%<@Yql_V*rzWspRn1pJ z0|v!T*08*lzLn z%W8$Uo=pMN#gye{MWXo>e(LIiW)yK7zG0JZ>Nqa@tYdS={4I4QzMS>k%J({~4vut= z%W!H@b&E@AIC;$&3{gy2OV ztw^LzBqlmU9^!TG%^qda`{pG?#=p}el_p4CF#$j&wVyPaWoLV}h!YtD%JRnmhBp(| z!m0SQQ=KB|4CPq91ckF4Hh3> zsZd@|!-2(Fqg#g6I?Ca4YwQ^E%POAcgd`G_`5)hO_FL3YjnELczku)dq>n%e8PFfR z@TxTXP$9LfP67`VlL=a_#bUi6@)df~mO;F<{yg!nL(8gWh#yNRt8!>jkiWiL-*WRAoAi)KQGzij9oa8Bbpshvr5G^ppvl&bvY`E|_pLkAW z+^|%V-o(pT`IZlMa_6?`rA#!{5x<%wnvMQi=XhgXLlR{rH(8a&_tJF1DL*g&*1qVa zbM2+7`rXoxqrVo17fyAa@G6HeRComDL!>M%m2VV`o0=B&+*VgnA>^4WE})ln+&mN_ zvehP+)X7BP6bNRT@E^K)fq(t__=lEz8dyNCY>v$fdM2WANXEujGX zpnl_W4t|UA`Shd}o+!C;un4h4DJ_T31Q(bWO>b<^ZTj(qt&(87s&vY*^Bz%QG*(*A26MHe_ z7^DA7pt^H_1B*2q@3u$-e*PjddC!H9=qsw=M+xUOLc9+=7SswL(-*?ukEJVPjbSr>vR^KF8_ouCS)}K^}lN zMSq%thVJ4X>V%Dp`xrjalbKZ5O%eE|1tEFfeN)5&jg8DH`KNA)mazD7`nn}sN*%(A z)LCkiZ(P!QudHlT+>iAcQHsUgO~N26!xVS9ululhuz7QyKEdg)IMb%aeo*S%fEz0P zi7ZLhs^OtnB>Weg!%+(@!N5dc2q)o{EV?uO@K~8Is?$fz=oq*442m)4(XCSYYh#sP z5t^o2Hi&m=&D0EaY@aKc9X&*!=fMBwsJ#3g-)!rs=xai*T8q9ux&CT>cKI`7LDns` zUi2I1Tc7gsnntB_oqsxwSfGTrstF9FP)(L>XG0xc4&aQK*nt4E%Xo8w6-|ofPrLoa zfb3i4QiirANqXZA*-X;z8`a79$E~_Hz|GGz+CLjZNfN;Dm-@E1@z-GxjYFbLYVUhV zKsdNT``aAe%I`1aPxd&$J>ifk6kvGY=II{+np%wvYpau9BQu}?O-P6B<2W7~tf?l` z=|?)>Ta=?Ozj8ZP>breX|5@T&5rn}T>2bL0ur6ZKLy~-;64B9o{apK(H8mW%OD^%PE&imTby%w6YS za%G{E({GFiqbWer zlu7l0vAVanXWP0Ecu9LTu_~B!ki0?nOOCDoi!F+p!jd`m2FLf> z4Mr7L(RkUbsrpMf3wl*cCob;;g9toiJ*1IKe|n6CEz*S2vPg4;*pQ*pxOxD;a!Ggy-R|!jF`c6mGb{FU?4dl(3O+7F1;kn0Kw#ZHtBao>@!96s)r)dag(0#%PRWXz4I zU&8U6no6?v-lu?JtJxcIJF$4R!)Ts_;T|+VtRR$!PW)he1B0Rm7yy|(igASQWQu=? z!1hdUH9)L}4pfl5W9cY4`a;?1S0IVIUsF4Gk6}2*Gd4lRCIej}S&t-Rua^1*YPiGz zwOjv=D0nI4G#9@Ti|P+i@M5Hl&*FA7Ezk1!T&;zK+i`f&q%Zn$8fJ*P{ZlHcVAIru zYKv!LjTrqM4XA5fSB^Kgp#z{w9+X_MqN^}9?Igs~gw(0dRHkCha@s&(o2*}B*=G{m z%SmpJj@~*`cfGOpUQ1{sB($;;EmP8QY_3F)51H4G_{3jEAw z-k-9e>Sy!9KH*m^)@QS9{=0_k#P62h1_oyM{B_m7*3YuB*d*k2v~&`EB!8;O$sv$} z4Ec#oy!mV<+tSdZQGRc3jyKx(KDvfx4DVdw^Rxmc&dESq>2EFrFr(VR3pB;F=(!^2 zHm`=+G@AxC4CM+Xrax>%Y?Z&OV?WgM{LLw!{AWD+Qnkj2S_?wE`(!!MjjN~-n<%6? z=u1lzAq$(S7*t@E;Fpbs(J_>nlPI94qoI|-B4hs?sC>BPW3TYi(a`B;E6X_Q#ZQB= zuO%%6iQzJ)_NeX#-9_+0ajmlg7iei@KZabKx~+j}o=L7e`*wg~m?V_w@swaMf%fSp zSL;uAr@HC|0yW%}8UQglZ(xBg$;avpC-93oc?E|wh;_8LWA^GhQu-3GGb#K$Ea2ef zuqr?6|H&&EP3fT!5_X32gAa2XEQr zBXTQG4NqoI6QgfcwVDRu)Uvks<{RIbz7SqSu#SDt0ba@vbPc_{RV(3%A?_FDjEYH) zHC{C{x?Pt_Ca0$9rM6$Rek|oH3Ulb*{0@6Ti-t8>=(E`+R;T+nd6XmJ+M*%4rO?Gk zH@tU(M{3ae9!%vHm=7zBhRz^JGwEX~ru@}_AaPj!ZxQmuBk%L|5=yI9=C>&il|?Kg z8p+a1qkHMU(M68887$+^P+o*xTn-6Sm`mLtFr*6X!|B!32JU+x!YT7^kDdR?M*m}g zUjl7;eH#$H;b{6Jltb)mz#@t;@Hu{`05Fg8YT(|%x)@DUdz^eY2Pa7C7nf9z(hvos zDJnyrMZ+W4^fEhdmh;&7I6pq(Z1&xY_;#v9G#eVDVc{<7r0`J?jr&^@57<$?t=+oC z>My;=98C;JIFM&-{?l_ucH0XL(D$3;hd_D~;%9x%c-~w>$H*b_4AtS}}e(or51`4v8G|C-}?(uEVLs2K!28|=76IX1eyMRyBd4}F@RqYXu>Cj~@5m%e8{ z`j(5!>Bi4=-VXwyWDo+^E6mZ9GY@ta}J6aOUZ_cln>mS79}r0LY}g zE$s?fE*!8{Dkv|1xH&ajrT5C@!WWMlvH3Hwkhb;q-j~_GRX*h)!}Q57QDT2nXQeb^#}?(Ur6icq&m^np zqoY$(uEV5NC)k0$d=$1)0cKmA~66S35i1#h6f>vp8(iXI&IJ%VhN8RRQB`Y-J;$D z58!`MaLu;l>J&)u(S7!LBcU1l$3Oi0_qPPs0VZi?G|xh-;GVUrI%m=tvAkDC?ZQ4k zX!R?f7dm%#CWlYf++|Vy)39I^>h-32CIV?NtCH)E9oCw=L@31l?a)T=PQ3z6CI&iR zTCCYp{aRD5I1SzyOr;OvNyq9cfw%Xfb-ke_ZS#3((1JKkQ$*O|AARq%bET4@1};-J zWm%_YS<^cSZ6M=ifBSFoY9G|0cN@Q}O1kEJj?da&evd2M&8jkUn-jIBV=JAjQERPL zh*~Futu46JhQ>*(;kW;8_;qPYMf37#JzESC#M+9M>7E!uHX^_P6bA`%ucCoNr;Y zOKJiE8&m{zVP#S@DTB^$ZpuX34H)RAX0$%%PHwt)G#Il)N>rEN9S-hb3b8n`5j+Y8 z?417)c>m4K0H0J8S21?M);?G2z}EO9t2H&;oid{`WB0YrDP5DT6kVBOXha2v41PAI za2sQiE}TJ|)>b<%-fAnHt>_UyU|s*v`R$$j!Fi_(L7z8nfPvfTTyi=Zz9H`8#oc|i zU_FK7zZgachTU`R)lD(8L@9y}g#iup@L|zZgf|dEBJCeYods8w1#fqEO@0-f75>b*by8tCWKl$5zK$$37l3I*X)Ovk5Z zllUU*?##vC-%$rUt;ThrqhkS(TZMJz)W^fMLH@ep+<{-38zvhhf1?*pPQ-ithAZgJ zdwTsQUB%>My?&9Y)~$nZFwwg#31TrTMU*p~%ydllCmR??RH3?!n0s zw2W-=@jraZvT0XhveK%Rk}W6zP+6qTEHYhaf3xRv?2eFt4IswvfF2H|@*$OBF)8=@2@nY~{ z60AO+hT)svd00iHp`jzQ;QOjF)&20!sQ%sQ;FyEUqqym?MBpBD%JzVLm zeh?DsCp5RUwpQxI-)j4_iINtwuM1TAEm?dyZ^?Pd0DnL3lL6cAzVs}T5Es|=uvQ7j zZd%v%lo4Csz)-ujk^4d;5DHoI<*fK#36h+gtf$jaEX8KB{`B#Ufm%1LTyx*PZ+NRG zVl$L76dBA#`!QFuyMvle>eht+9X6A;Xh%as!}*=v!wXfPrg8bAGFp5dGWlqv3Zx7@ zXI2f*0S<-zqcP4OP=j8sp6|smEr0p37$DD6og&Yi(dg;*@n^hQJ~KS_$$k-UU-B(Z z>&^6`MDTvRS=8Oh`ZBMW-xC*wj`x3TDL zS@lV@77?5BuvJP#__dte?#ekHKNsD`k0er~%h}oX%*pia%L_SglpJo!KT8JLud;G& zmRQf)S?ck)L4tS=bNYc7k0|$Y+TsEx{2A_A<{~Ga-BlHj1NQX9#8T6d0h;3CqN1ZT zL|JGZ5Y1it`Ymb9h-I$5+V4{C9Ba?V?+BLj8)E0}mF2G^f2Grq<_OAg8L-q4985(J?5Ryp0tOcE zxDAgOw`b2`yWfE=F(8Q4*qfhXVt`KExOoJb-&<7TB;DBRK=UTZ{yHA z2i)-f9Lwgq)ZCsk-?Y)qYRDfp^Zt_pNN*UrE*Xv}I8ES~fLlZ)Rt|q2I0;rGiU_R! z!$rgjBaeRHI0l}SWqL!D6yr<|K<0f<*)}E)RAfUXHPF%eqRmE>6fFg!rB3>ooPvw1 zRyC{c?MKTx?JRi~)vY}mmFGi6FS4$D%JjL#8wxD0v!7J%j%2PgE2P@jI&?u7uE?^7 zYacQIe*N6%)eOCCJpvCe>z>Y@MAZJgshQaC#kxW{1ztd6?oQVJFG=12rke+MMw_ZH zRyG2hLpyWwg{uie%`;2&$BrqfFkZ>7wxryogn0zMj$D0jX!8)EH9C7BXXWhe!EaXm zF^rb7G_X~1@tDN>yUlyZ!bmbt>W5~@thf4Y6wrY`6mnu1(v+`uXxcj`HcB(~_>qzBzd9Sm*{7**kVh@`3b=|euBx*11CLwG&H!r3V9 zJ?_GsG3gi*csP&6M`_ORI!bI!;qEt`LZ?;^>U-8X+^*>#*|Jq*|jIlDHxs0V{B5Sip1ZHEB$0^!NQ}vV6x^#Z3 z(@pR=^#~nvVUQ;AwpRj|=xbbmsYuFwuOp2`-z=>U$K#?q(AtNSajxP)zT812jFK_J z2`q&UZF@7L-5pKShLS8)_eJr5o>oo z3f1AGFlsa3R&62mV1UF53jfMRhL{Gv){GQ{#A;Acids2fL)pZ@6EM6!DnIw4;VF=Z zgR*kH1kmQuC=3wW;P0dg!20@VRY<;F2g&yPSXrrp~Cpn%-?P;zu&g6 zsi>$NI|t8{$L}R2@z8)NtcR|R(lld6`?S>~xm#S0>J0gZxUn1ZQ7l9;(E`Yl!|o)9 z&`@PTSOiuwl31@TdgNAh2O#|;=0XQu>+lrvKaBNVHE$kh$RCEX*w zD;3$>dnSft#iX(J2slc4&^>uRUYp31inNovHwY!bBB!k4uJAu%wO={7-AtcMd94n; z)=$kKyZ1+%3R@z-Wkm<%C_B2)NVG3!NOs@-qy4cn7hF%frgqBND_{NJI6UEn-rp6{ z{HIxE%@e~rTI!z#-K0yh5D#b*yUPPGHfHx4ytpxBgj$?F9hCwcKd^^M$P}*DzyT|X zal2C9K0^*|q>e^ahF+NRY~k<6gFIo2i=LocZ;$e}^V&Z7`CGM{znL3>u6ZVTrUY~g z=j8)o_Y{l(I&AZBI#-yh^tM#+eXe;BN$24|bfIpi%3la+N3JJ}{;nWZ4%G+E!D?2I~$7g9xDto32UFUVQ$PJi6Vb(6YQ!(R0| z6INa!@3Z1Zv$0lD@DB^-)7`E@jI7OV<|9EFo*G=~HLkU)_klvwM*U1U(V6N)X}jOZ z0SUO3Xl?oW`x7K(@afLg3mpcX=$`K*kI1RE3&_V04Ejh+1<-;B1fv16w(MU+H&jDH zSgU#|-x1v>0T!Qn$(cN%VqqT=FiEHFm7tODa`D!MYTHHJ>3zUgcCVwYbw^d8ZO>h!2aUEmmS#C@`Dg7@s&=7s@qXHmLwL;>iFt&*{J;fQ;iYC zZ~f~lz)d_jcDmbf#G(kihw2G~qoI@Ta5rvRq9XsGav5Zp%OWFFd+3At0=QE$6lA~_ zLl1Xm4*tSb+g?aIJ6qccj!$}A>CH0Do&xdp-60MDk|2!t)=H=t#rAKD-=Pr4PWog* zG|>sbFQLN8K(O5xLQ@%oAAH;-Nf=U}H8f<}s?|Ojf1;kp-(jjSS>JG@Lv_LC3O<3S z2m!!+5O3&Q5nq@Y^Wp}3iLyOADxm1Qa5_zbaJ(!PIGV#?j62j=5JM_J=C+HF@yK_=K;=~oRn*bd)$?NA_l(wZ_qzEuCS~^{taUgg=q~ZG>e*_yM1WW_ zmc~MC{vWpJ!TdHwYz8b>w)Y7QSr``c4-PlLiq5vpx&GoJ z6pK(_f^!u$SOyVc{__GI>lHm%ZOD6U^3Pnnf17djCx4CEOZTE;H8L=rVA7-JljfVV z6~?9Uayw0Jwf$J-&N0nZS3&sW z2Wdw(`Yq6%w)(WB9RO-a_zel4E~>W5KQCB5E^@~6PD&r`x@TFmUmLfKfoAoT+7Dz-6CYU!a$ zv!354-RQA&5^C?0IV3zmWVCQz=+t*Val!hMS4i@U)(-=f=9@6I(o66)IjKHj|&aV}O-JVOJ5mjW+D9 z;3R;+4&bK2>z*Ic-Y8XcKc=}owi%->VTT#{VH$y_6|?EC8~W3`{KvBw35^wUy?M|= z+ZiwU_z!*@Kr+w%#gzNUW?hJU5}Q;#vg67D%8mR?2LZOgFi zf$@rBG!5Ve?u@f%q=j@`vTKaY z`$LGX#AMfdHk`5L*?K~(4qxhK5CjD>Bs_)AJv?N2WIsg=4vsB`4QSR^R}&NC%IC3s z&YnAul2d0-KDumrk+FY}z^f;z(i3u&Ob-v`7_kU-TXs@8B?`;rbqW%k~!-) zi^pO4plf%hcIed+9B#-}TS{@J$xoEFs- zu@4K@cKJ0nHSzh4A1uoTnge5RxD6qefB`Z;OV6|Sgs-8r!znAHcQ z9s_^sJxpt#A6<6G*-BFmZy%p3d%EuWPR(5h5#M#^!;V~$dT;%|v=+QU#5nTwSK|G+ zqAirLzxSWY>WB9`AE)yhtbRW8js8g60a!1V?Pq^OkRddO3)s+S(46_fKR_}LR&Rn8 ze<;9k^*#WAbOS)zD5;;Z@t8>T4+u0-@b&$1vHGD(=xcz)5=JW}wC_4ho&Yx9#Psa? zCV2fSp>io(yKzej@k+_Pte(pw76=9p*I=_C!)CgcackWi92~qcnBOZ{gDmawcmp>r zw$^g!+8HPaBM^Ej_{M>JFP{AruZ1)Sn!ROpt$$zjR_PrjrvzVF9@miy*BMRmRgi7^ z(QKkgh;we24?{qJ3SC|KEIDSM+(k*18z9Jd&4^3hmxw2ptKk>IL)lOu(ds~XVS@3Gnrj(4NU3+SL@yGih%l-P|;U?go zDei+J^JiAEgcc80DXvB$EDR;73z(vIQORIGYxp1~!Hmwt0j3hZB0fkFK&QZHS2S># zKoat-!wAt}@$t?!abDlETby)Lj&WniZ6K&N3A*j5=S*LA61dop1w}+8!j7Xn7ouD0 z;9rfv{H|J$7#Ek%4~w`R5)Vto>aBd0Zwl}v)umMT=HIo419DBrvznt?CRp3sQ0ucW z6^e{>4D`bdF1G}i@hlFx?l=B7DdmJ3n+Y`#Juekl=l0GQd9)bxFy_%apE((4i`m9ZX?#~~2xL-WcsL@JC+8hGA=6&ZV57BRK zZiEi77z=zWU)XRPh0lHRkgvR9@V>_ve!a^#%W)!NjnWkLao4+()_)J83>|-0fRHYY zYK&vH_TF0i9_kZ2iJ5FVl)K?<-LcJ(+1}oJK-}<^AhV>rQ}_%ITL&=+aOD|n_zn{i zy+g0mwUPxxq6VX5Kv?7zEsB)ZLsfbr}^10EuW_pAY>no#wFwIpQmg|l)@{0)CP z?p%_~-wWqnIy!_S&M+}0NEQK|RED3F`LmEt>Lss)da9v(2BF&9weYgBCd*%@S*gAG zEaY+8LDfd$w1dZA6$GXMa8fPi?1b_5k14k;Ux~i@+~Q*M*X#@%;{%(*)(#+WoJcSW zf?G7H#Z5butwkUPa$UFt4Na*8Bc}g(erP36H~1~kZ=;zc@XHI6RC~gD?3;J9CR5*e za!UFLu5K7&A*EtNs{4vTprP=+D5PZmdqh}Kf1f$7v(fnrrb_s&(AH4r9emyXdB0zmifU zy5g^Y+OrcETyy^V;9u(u6e9dk!ie9Yjj$^ki!{{A%qwyk!a1zp|7R$vey$UMarv zI(6`1}iOx<9HmM3Ba3-uT%4c2!7~B`GhzKl4|1O1>O*hvVU*SxK%Fvj0 zWX)oe>A?zz&`5djbOI1;0ud(?;}|^@#%n29_N#eU@_5APxByuI-oTR|E-iW*i$@La zx4(~oX&?KlRYsQnl5Il+q|nXK8d(}0)@YpSc?tS8lKf=9a3O5dMb)*7R%oFG9t;Yd zKh7MHqP3jv3m-&PvR=OFiylZ881VkXF=fUMl*}srCNZ*={_+D~hWha9I%P)w+>2V$ zA0F0*(HCz-TYBe>Ow6$*{SZ1ye@tIZp(o|s$1Y8lPb$DJ63Q(TXE90#qOzq3eMzOK z!hy$-q7eu3!}4+h0@(;pzHmfbd~-b^A@wJO8E70C3FyrJ=+vg7Ih-u8bg`VqyDgUb~UoizW*PTBh`uy%3VZMd%a( zmW!6DMUWE=`(Kc>vos>!_*xHnVO$ovSL}7N`ott8E~~5`%$ZU{@zr# z`t23x!BRL=LPZr)35Scss=u3Wj@mW_yXMQnhF*OJPV%p#*eToqv@KNjQ8EWT^DWbX zs3=J7=BWc2H30E2FB#%0m^ryegPRr7<>B(SaKQo_HTEMQ6w8#4yv6ER5mgJR1}+B# zUn{J?bQt= zDZKg9%UyK7wV!^4C}T={O5!p7<&!?*hC2w90_7fnQl6ShpYCz2noc|dGVDgof&vX+{Xl2~A7-&pche0rz`1&B4BXKzhU0U-~o_KWf3_ zX0x&{Fxv7@l2m-9O*1R1cG;GA;f`O|A8#HR_x#!Q!bHW2hV=)gIlR*g2;~$ve|e)@ zCzp;@v2^$k@E#17z>WJkW;`+&E%GV0xX6QfZO0bJ&6cS6b$j2ZYhtdZwu1IkOS6wa)LY=H})VtSWZ(s*c13s4EcN zW1$k22OB?ezO?G|<^4z8v}8Zn*n$ZLe-JcLjTSNI$|i(Sj;9Pb=J`fZGsrR z4MvlRUX#f;kGX}y92!bQ!327S58p#deto!C?!I48NeL>xF9bHZ zeW0R|i{F<5o(x61`bu601qbCvSG}qt96$p32O5eH0)h>Eg@{7;u9sGQctWp@fS#gz z(ye_L0~ z)G<+l1)JURSZbxCB`H_U7UdPUB9zNJ*%_XB%(=JW+4vT>nQStYLqfj7PCxW}%%08l z0M`iO0EDt0%i4AV3Flpt!n`WOBD;VY1$odlDbU)%VMt)&n}y*kwpE(7G#UZP64=!m zl+#R1{I7Mu(?j52w|9TezONiWQ#8ItGW^u>J>SY$rPO0KHG zd#h@cSv4A>{+!A^#pEhv%{jps@=?<~Z9sGsNxybT`2W7!+K(H1qpQCqn@q3kQ;yTmX+I@!>%W}gB+E7P^`7>Tp z#l=ZQKkM7%U!Ph^>tF9fq}Ck=uXdcSGO}yn@4HN+?;hktDv11(&RK)AX+F$xv}8=8 zTJ*=!G$O=R$S;eQmkA}lRmMGY# z&B_kFpIvA|2^~I2(=PfJG@E6`kvXkFQ6K zCKGn#|w6>s+r}JofK*%(F$>2eadPHmw9Xtnr}q?;jrTIv=v5P#QL~9`u;@ z{=-WQG8PpLzASGizhAtdpw1AI1tNp`;oAOxD$tR3_a?1m;O_<)m76Li3haU`eZ?XZ zs>R>uCz|4}%x)ggp~TYcY=lX=Us*hrn`>m3N-KD^~wBkuex^yK+ z_9(uXx_@Y&Al*S1^KM>(Z~pRP#(cP^3-@J?WJ`qfX3myEB_QhDnz{nxE zUlp@9kRy}lHNkixrQJ|Cx3nyRXGlkxNrvb>Ly6UuEc&)hgD{~AUN3XX;T0F*VD`K5 z915ICwW9dkqsn@mXPQ$?d7<2-A(3O*Qniwi)vHX4RrMiYFaQei(J$d^b+p(z?>=!FJT7rM*> zUe)&s10E!S?#RP^sTY%zS)z~GzVWVp5Bna_8xXd`p_s_NP}CqvzZggPTut{3D0D`s|3y1U9i2RiHL!d?j6rsQYPJ(td47@&Nm!Zp@+B6udzRdTNByc zToubgvRT1(RH?9?+n1!$Bko==5p@2>Q$vxGX&t|v82s>Y-_oA0&^IaNqLGwFkyO0Y zfZv3bxFhq&I+e5{>Ll)0+#B^9?*mSa#a8*#^4Qc`{@7p}(unuO<5u*}Z|i>k+eSKT zmj>mMiHb+Zpjc>ffqZ;@I5K9}W|`fTDMYVJY2wWHNt28wB7Tncxo68X%<-vs05}0K zTLKiWp~ni0K{ljE?DR8)hwaOi`VGohbqFrj?4=RjWbMwDOE5}BOSu$r^S_(?RL)a9 zx=jv#-@p)H6Ka^v80@ScB66|Qf2G#1utg17vI?*N8^r~PqFDRg2qyWu5H0jq0t$hSe5r^nRF=^q#LP6s~9n4E&NrU#;EqC84r=* zAt%r6HpVw?He$~%cB*rKM<^pOygqpM5V*HxWIFzug?Z z9nc~TC3I(p*FcEDaJuT0?26?r?cvl=LupH1q854V^6z)S4k185+T;m6esc~i|3$IG zGKKl;*MA}ZD@_Ts5qSyuL*Z1|<|bZbu4neB24*1_y@}!Zf$5RG)}ekNpZR?yT-Oz5moQ__Dh}54eO?o=(bdw^DBb& zmZh*>QW_9x+PL)>=QUAD0d6Ha@GG&_BkqEWJ{>Az^$W8Yb0tnxj9Dh<*<`6Ah<21F zu@?5=guS7~O#n-bH5U8#Z@F8M1THV~o-zakNsNstHkHJK(N32kqIvorgp0a6NiWv;&i31qVzb*>bk7Y%%iOxuZ*sjbVW9{}D%98SY^>||i>p`TKo$$H z45F>Ew45IkX$5o@(uDCsKBjmr2m8?$`8TdlX04suO=;TqsiaZj%vJuxnZTlA}L6dZ168P5_AY*ClFZ9 z01ZCquh1P6Rf+=rAtIC^Z3-v1BrA3vf}|Q20ZHlqP-tSZ_+}mp0&+r}n0|Ua>@58& z+%so)H6t}aNy4ij$$108!OO;#7&E8m-k!s0t$)=iC>Rk+V&7d(B8a0;hhzGHqP{OY zedheOddBEQsmZabF#2e#av0W!%Rk}8211VhvC(5zep9mge$3VrlQavzk#@*)?%;9S z#k7GLah>%{nrkli7PbwM6;(lSz);lJ9BKEbj`3w!r{nbV1CbVtQvqm6-=c2A_`@C$2?434obK(Rwj=sF zTcC1oX#`(|8Ga!3cPZ1IBm?9F^Y*pG<7GkMErnRy=1rL|t>FeLGMi@AJ&KLLhpX1B z`m`jVACzjI^;b$b{M$_}6H+{8ulgCNvgfvs%pSIXy^MPg?fF&S60Cid4_C<^*Jf+&pmkyMO7j6NrY zo;wl{`vYC{zlbalf=4^d0?F$CU5Wqf{Y)6^<%Ae`e1-hp0cm&n?!}He6nY5)QKhXp z(i7wVk@3Fgj6#cLDoF5B>O8K2MXfdg z@P~GAZpuU--{K!o*D%+e{i+?1_o7GcV9YgU)9w1?`AxA>B^n4|woi!HibpIw zhWu9qA31vSS{B=xLHclb=$2pF(7HhWVnc89gONX$4D|+*{M`xDMDLlR4HriP}kuHI>69` zIUdS3CNf+zlqs*w-j)-b`&H!q_18IR z&nDW&yYp4n45xGCoO0m5 zRt_PLY2E1MN}?Lea9TdOe07oqe)!0>VIP!GF8_S{D(}+$+l!u8QggzMb#8rAJ-@JT%0&%`H8+B>0b_9~A#{p# z+t*fT6cA9qMJ>}1JP->&gNxZLYfMqbq+GJLzHxfuLc}uvjsXKr1b>K&Ny`1)Cgjg1 z^r7P0r)LB5Lb?X7&E8>*E*9qaG2XX^rm69n3pEroaF+34pQT=5$xf)bjsDSLlyv>~ zV<%nxG9?#@{UpD1LxAEt&U`fsBGO+e6o(M-4zF~Smg&vnqi*ocmLC#ql``%9Kcwms z==1i%yuJ2tKu~CkLb?8EESVoq2l=m^Sj&Ij3){?ZGvZ!?o(q2R370>>sf zW2nH7)SSH(fXF2}gB}O*-~oW4altAa4+-FBE2CJZ|8$p9HWo+2%eKAO2u`@5-DWUk zvyQDu!#-NlJ1mvLa~HGzK~pus8U_#OotxdbWs)j`BXlP~ip^)lmTKpY;*}rE$jFKZ zr75FvjjM{to4ydl3&b7=u#*$3bLjI!+n z{aN&=U&jT8*H2T6)r@M8z*sl%!-w#o-r=4H9Wn{-;U{Q%U8~jKcVVneiKSvg86N-k(-ts3pDShGb`u3KFuWq89)&8YTz~{b# zR}~Fc>koG8OqmcEm;-rL3IVhKe3_4CHjGDzt_(?_B^G~t=~%A=#WtHae1nhv{#nI^ zigg8@{`!i5rkn^ww|^prCZEMNx$XWlk7fU#U>p|!`^J=>f-*L-O_K?WU?SX(a=y=p zGBU=Z(15d0@Nevb1>Fae*uMvdkUDt^&Qee%0V2VWa2!YVY~N0D>B+`$yH+%$=F{!J zzur|Nfl7xBmNXPsxvMxfb|MagImV+Hbln19aZaiti5O(<(ehX&8v!i_+@#d>7l@+M zg>VA)=nV}vHjWNqk^&cROXJR__RFsu#Rr!LIY!c?R^WMFpJzRma@T7FJOA+icoh_& z>3>5`$E0u}o@cIXo*j#uZflsCK);;W-=KO9;%sG({@s|_#U#kkuJnVt9}XW-B{poEbmuwP z5(bvgOk&l4?D{&XGEDOzoB+vvhbsPLz|-;eeUc+q%sBb`LIeO5u;p>2duCpj5^Hyp zB0D&QCZ&jcT=;Z(dXVjG(&fK(ZMNR-wKwwh*5dJ<#ZYaDUb{eIK`RrFuVv4Z!A9v( z85CvI@cP^TujU3u0SskA`7Ra=k_o~IJ9>)>kb*TsXE+crAXq)cD+bxsBc?HJvA*91 z1jd)`A22PGwo~E^1bv`26*1Ncl7Ld3yb;N|9{6-dPkA)-l>_>i!h3WABjd(ri_1>dh6}hs(EOEQ!N5bvT1HK;LGPI z2Qa68II*T7ZmLcMtB}ikwp%ooX-J@pu=zXA#RwUPZIC(tZQ~9)-_CT%=2Tz(Erk=* zc}Mznn2ZI_-6hkJ(lG2VwT*isHRk`W(Kv8*Oc?7&xZ(zkD&7@W5JBxBxrwTm$_osS z@tCm!f|3vbkt4fsI|c#5sR)0N3zHHm9$h-|C0{E9Ul%!EWEC~M#`(y<-l$Mx|JsjE zkz?^I<*%B8Y6av^gEUYK=C4boU2QlZkJkl+=(03YsP~n~av-TxU#U&uXc8eqJ-u%+ zR_L=X%%6?KDS%jygZV?Kh?62M>E(2ed@J3CY&^3CtH~t1Puz(%eWQ1Go0zMd!S|c{ zwV3x6mFFhizE=K1UDjL3v?|dcKfcfylPoMl_rG>U$FpeW+e{F~0#|^$5#YgpSk?V4e}!B{#l&|2G5O6 zbM?^Dy z@CRBVrE6Q-u~cKW%=qmoQgr;_uiVn%IF$G;!?9+3w*60Zxf}wkXr1KuDUW}QhBbGh zQGHmNS7VvUr=vrJPw%v)%)Gx8D}^qW6TrK|T-ityP;K1uXi||<-*cf?as^ZkS^7E3 zbI!SRbT^vuV9jfynWGLy$SKrU+jocIFn_1bn1NaZP37d2fqVDSY{7rblP)grUKnpG zWnn%>!ED7^uM)n9k8kIejR_mOy>A`y&86#PDEYt7htHYuu38LB8qye~cDRu#Ij(5_ zp!KP!uC$WNocj5h6qcGXLosFwggwWD(79#AK$L8+RtrG05BY3axIVaoclS>%;bgB_ zS3=eNYC8)44mY@0Wbs=ig4!xhdhx>J#!4IR(y9JG& zd%Q7fFpu~j?!sRD9Pq3YbtJt`dg8*;2kT&ciAi*X-{?ij+EL31(h#nf+v zryu!0U6?x@+x42ozy9%JOh1FqFQ&WuZARqO2hDxqT*G(zc6X0ZUpI@VKXLokz1A!% zNCF8gK=QaLm%@J5CoWZZ=W>wojoxt^P>WZ0Gs94X_S z9^SS|oB6-5FuZ>ET>+CcmZ1sd)Q6CEnujB4#wYrEnXp|D6%CtMRwPJ@mGjpxQe}+_ z`PAml#u4gJ9ycS|7=4;7=d4y-XBWrQdmju!B0J>A$6ijQ%IPBU(_`D!^YO=B5976N zQ=!v|@jyh9nn5_oT38j3dTE-I)ONT?H#dBz6~&i)X$SvjCZK*~j?g5Z>6U?L@lSo! z=T@~7s4_`1Soq@}RBzL{S{&D%NsNx@gqjiKoLzTGOnj33_AzCqYjf0&yws^GfmS6| zCHaqzfTVQb&f7V5;}@6A9ee!yysc<*4mbxGmgju<+f)LdM$F*%aeeSvAu9O098M^k zFdz*HPP4NqP+P$Di?Tn0^<<6=WMVk$*}mos{TbYW;;@c{g%hymJYR+KtUSn4_;D>w zGXM4g=|B-umbaaW4w)?p*MfwqrxNMTYTab}d2^Gh?9QE%1qnT6K}0+$?W>C_ZVh5R z`H^Dleut5rX4`yHu4ti5YK>nnx^ULbGviiV&~ttHl{rnu*wI~MQP z;^8i$StUNGg&ePE$&;K1-<5!n5=?399E?W!s*|=_ZErgTdyQP;-Z-$yIb#IRSe))m zZa~{xo2h(1JxoyDHnhs7-atCvrH|(r>;lu+V)Y_1k%-d8TXBNf)LQ^MuiF7`gV-dn ze^3CoknTmf_JdYbxo%rJ_EyvWrPa!t#IUj4kD9RY*u$RqhQFbW<7u$5?~(M<5e?w+ zBnZ!yA)!9wiDw#%Ky zROb0d6fby3i37ci@{3iOu6NZN<%RQUw8lej9y{)19&y3r2uro~jtMb-(~7rU6W#Z_=LIg%|5VVQ z=c8U6B5-L~oH5g$)`(EspBuJpTo3$NQ+dsePx~O2N~fZ|+wHhO%R=K_mQYPsFm<(L z@apxtQz#po`eW#tLo~T_mv-p$t=Uf0A+6xL;JUAQJxL&!&xrYbzD5ZBdLM2Gui$;a zx~|Y<tuKtWL2d21BCsPA|R!0fvU~_Jc2ZyUtF@Ro? zNVm26{Fpli#c~OcU-Xlhu(T?LQ&5~aubtPM_L^!fXD(K$H2r}7de>W}yj-(EJN?a~ zvAT@Z6)T!oLJmO3rsP-)PLQL|DB=8bB%lu+sBg6J7LCNkh>nEE&co!s&x_>9jj~x( zE}mlXX*0EgVGe-=_}03zjLc2Jr=!M5e(2E2Ig_@@J82`0@ZVZ40+Wo4j8i)_pCJ26 zB5?)!MXmW(f67x@=dTh_)}z!F0+6?ars*-RzVNsKx1-7Hjl-TF&T+z*0Dgm}5R`lG zw~jpA=0E?o8*sJw-J;$_Y#!C{0%-zAkWF6;J$+pS!;D2a=apOEwKlMKX;NdCK|x$~ z4KPvQKtv=CdXqJB1Tzl`A|nU8v9r-#?4yLT{jx;Dm)H7ShAyr7Hb`R}6l%%dEU3z> z3t#A%@#ap>Kklb-Ze9NVunYi75gOvi^X5Fk@i76^^O3~0nz4U9L;hMw6pBQm*Iz0U z^qhXh6>w$z6Beoenl~{_Yt}0$^tL0qu9T(h{;Z~!HfR`R2t7^_XF}ZKhTPqCzO9g? zXoe=2BXElhB!U8eIu?Cib&^)>9ux92))=(^VDOb!#*#ZMoemjAbT@U6ros0x${b^c zdmzr_mD_*!tIK!?n0om0+fYZ-mXrBNAt#+0>@BA#&eZ7X-RYA12g2aHtK515c-!T> z+x1`F0iGY{m7}z&d&qE>G3ZXjks&{3{P3;qv64S1p=IT$Y9#3?o?*e=)0c0s$ozQY zJR41-InVn?f_puB>IMs~0@85qavK%a>z zavSmftcEoEc#As~7*Z^bg=4M?$?(i*ekbks?LKqv`w~6x{8KjmM-rCBr=Tqs?Bf+0 zevVpiI59@D6PttByN)nyO~b6VSZjMjj~FI9y`jC}s9T%Q#WvHz)7OcNU9w4$gw#{7 zdK0Zk47+oR{~hdwfZ&^3!Vx1V#a+_gXM9gUv>`#I5=D-`PI6>D6^m@|LVRyClf(*c1wqxY-;II_e4q4qN3R1RWx`0<}E>)VMk1Df>L zhCsxeeq9xLqwf6q_PYskx|T=W9%-;V^_;?Imm+f%>(qmWAv)@ZJk_-a&kS7LZv{nv zNRJlak$^A<2}OuDWz-;{*o7l;0S3ZchcBJB5djuiWRg}S9-Jy^w2X?K^aqft_?yv;gUbqqj6bp;RG8#{k^NggLiW<8h4`$V;4mBqV zN@7M#P3?JA$bPg-mNPPPZaz}a5$J5CV+r_BJ74G55e!)h7yA9Hc?^FH`7#^ z$BrcaR<#pjjmg8;i}}YiQF>E#Hav00B1x<+JHm6S3m&$W>Ap(4LQ%L)TF9o-jEH;BDlY6X+#nw4{xEvF+P zRw{6K(fN%9;2qCxieX*IFf%STgnWP3;##Yu664KV(7Q9!H8ODM&NxlNyq>Xw&^^7s z-B*@AT=axP$6~^7dIc&FAOdzgOcN?g(>0%s8x3m5DxDn|Tg4wr`J)&WKWoH%diElw zi>xi(q3PD|W{D+vH8(peU#P5lNZ~l-Ax9DnvEOg{LH~Vs{=NF9@khD#_rx6-7+~Fv ziBJO~SXP+p=;KQyw&|pQ-Tt}y+qkU6x%{jmB;oq$bX#22{L&A%@h)(`<_-cVb)2&U zyoG-xP-)mfDB)SY5F?jLn{|fJ!ek0c__mg7KPtLjs+M|8xemmspvERa&ygYi1nV?P zZeRGEfuO$&9;gUR2|q8u)$aJV7_RkLI%o0U%ax0~M($@9_!S?Vy9Pg;^Kq6&YW&Bjf(ixyT<0O3c~xvjfsqPegHR*CT~%ap^zHsnXz zsFoBAac!K*0rXh>qK)sE?*&1ku~1L0P*{hdQ3BVt@qZK4?m)|f(E#9Xp;H$z{DM8W z_sXdYkc@)&qvi32VZ}xj*8`;tEcjB_b`oJSNHFTuPMYKf5-BaQgR33v%2D!3i^g77 zsS!6Be;8rXhrC*EVfzKCP&%@X3JV;SteE%~^-HhGN+b=jGap^4pL9~Fk6cdlZoy-! zp^@H+nNGY^mH03IL2`kb1f0V>kN*~eS@si-l;%KzCv|*Dwo5dL9Y+yJwJ4@f~* zew+cv(0^qI*OGowFE#_*!{G4p#X;JMuP2{ziy$zn*=Twt>2Rz|3+Z*|d9T-?m#`ne zoXk_Kot5|vE$5T_#(N--4DG(Li)+rV)<5lOK-kmTWi;6 zepV?Q=4ov>@PM14W(tTUu6E4uPXFzbzyx0!sk(jV9pv$95o+6C%V>EtQ< z+5vH9v&y|MSI9AdyCmFb;1pY3ZCfydqc7y#FY1v{zi(J!+F*p z?Y5&NFaSVn09o0sD&uu?bmtV&a8OvKDCV;_6}nxxCHg<9Eab30eaE-upLGRy>?zz8 zu7_BX@_BXS*oqdfl2jXoZ1ZgWuc|++X#+)5mi6NkCuSi+?!}7l zWnd)|8S&!Z&CHsE`ik;ZE;Y)w7F|l_faOS3ILtUAHV&5tOhP7vJrE5(Ek88TNT*(9 zqke-`_NY^8E~^lVu+ftsT>1W7MWV^&`dx`IM4+omvgSA4!*i`RJ~C=WIfqZAA zxCPOke_y|aZsaxmB$(cdF$rliZryTS!$G^57{`-+QUxgH`63v&*-VglK*@yi{g&vg zsfXMFLW?i$I#<9|7@!Gbx6LPo!1|rV-}M#?8(Uj{vX~zr_|NA`iTpOeTA!Ml!6K1{ zWuw2JY>A9eJ1?ziKIH-=EaDX9)kQRS9MK$FxqmeX@QR#Vg-=_E4-Nml!kMt6ApWF^ z#C$zV(KS6-8CM zaEF4?!vUq1x4t2*#Xd7g;G_!Pz*ukAnt+QB+S{N^v@?v)(zj^d+NC*e+Et4?hr3Hl zGnPg$3hx(3sl;+wiSri@rh>=@BtZ#62d-%NxirC=u2&yPK#-z8!Fg;p%s3in%Dev~Uw#x5!-8g5A}&_H2VKh|HfU_STNJuMyaPQ*#!1$E zns3PLaTf;!{_F@g3D6f_maKzXoj`pCM>6hG)zSf~d9}7Aj2ZJZTH2)#Ue&OB*Qqda4il5@eo z5*Cp=%W-i#dOct;8~v;zcdz~+dt@6pNLa__d5m!JT`lRnE9&eK7xB^P9!+Yn^=_YL z2;GvbdzU{s4EHxc@pZ7O!D;7@P8IS}StQKe(OPoBx{aUTlfD4PgfBzxzMWT2UB~Zu zYn2R+aGQ&kY9WVx3TdsG45sV^@41Ba^LM|3&L4443a-V@kFy~ayEL>bJzP^0IUJd@^toLn`b4jcm$c(PMd*SE-0x(b&b*5S=_a2BaLbUXQUoPw&ZCtl*=k^w+$A4)ODcxp#9wM(agI!Y9IFLC%Ytk63~`Q}3hGKB*~_ zWyFp}JK?VUPN_~FHhSieT-*6$t&#K5B_~+Y5D27@o$l5@fjlP*Vt$#sCJkua}1cm9| zT^Qc{l=r3{-&vkJ+Wjg&>M7<-s`U*Y_aGrkO@oVUAFSQ3At^@>;g8h(*H~ok>g38I z8gRB(s7Q!NIW(Nb*bVpwkj6e2! zX|z(K_kO(-+HLY?;r*Nf>&A0GA^4$O@3D674J`^J9=cV|ztvjlasS2pOKUAkb4KUgQ>5_?Rorq z{nyk4br$`-T-A~EkfT#s-c=Kvrtw{-*k`G)&r%zoZmIF;e#N2g5D*Zwe>xd}yH(_< z2J`YZLyAqe?1Q>&=iP%o3qknH`b+B5K-+8gFmN%0b;L&#t4#TZ@&r&*RYwjY$M-um zl$_0aVe#Dd>S6e+W(R3?or9ZRD%|+0Qr?nX84--9IQlEd19t(;2w5Ky{f5RlT_na6 z#Ai*;<{scLx$7!l=0RIk6zgq z(8lCW+J9kej`Znw0(&Udzs#E;-0!VOVXhH6j7#TcxgKmAy{_4-ErFsv&wZ*uewFQRRUltS__IP-S|mGgN_r*M8v91qSd*8nO z5SZ)|4vxqCuZD$Tyx*QO#aCQ3cx|?Ez^X=rh-^&^u<$FSB95IqZsG5puSD0=GRCAM z)~Wgw?U-tHO}?g+cm8)Rz1e(PEX2G zr%5Qns13hv|4TB8D3L9EwhzrWYC-Wet`*8w2pYP*`4|`1(n96$u6tg?__$D2I9a*6 z6o%5r3K^T0l!kFqBQQok)HU|>^lWXd6BUb~rfDL5@_ewyo^)`b9MFSQv(#0?xYc2e z=7}eGnA)aXiSOd5-t8&x;qKwey?UZ0JINVicXD)8vw171AgNfD8}`sXbyEMfci(T* z4R^V$sg7M8=~J@2xIh?Pk>QZ`UjzXxGp68jr`PGc4BFAhYQW1oEN!X`E07@8$+fI% z$Qj2X!kjm~^(D-b5DG!SMI;W{ZwET+X~7zQKY+hW{=HOaQ43%Q;dB3u*cPrg<^O-` zdh56-qPK5&U07T~SXu#LNNJ=&YCvM8TT-M;K#-7T329hB=};s@=}u7ur8}gRkOm2* z;TinhPki3{e)o_0%+BfST<3emxn@qNdY1N=o(=y$+jg2~=Jg#p3*?h0bnd51g<}3> zmPgaxc1{L(lb*$YBwv0B30|+Z`z!qUgc4RJ2gCw9IV>t`qjF1Jy~5@d32D0j%zm^$D3;coa@a&}Adk?tc^zna*?*qjXI)7sj(UFgsn**4 z6j_#F_K*nB!N{rVx^V`L-U?Sf$yU7LQ21)c;;&S5O_=MD;0GooLpFXA`2G7F?z0O& zxw`Lr2`cq&ZTE@pW%I%}^D(n4Hk^Qo))Nare$OL_%g>VyempzE4hQ1C*2+ zf%QVe(+qPw*!${-&X`rC#UqCr+0h6rEI%Ik5G2|nNs%WB+r!iF8oOYRR$Me&aoeJu zoGhAjlypxfL*@qyd3CppN=VOql;is>bw<0f`l=~oAm(}Vs{!f=&j{%k^rjNh^B-Ss zJz7oXWm}5ZRXkxY<*Sszd$Biz`XJ_IN*|XQ`|`Zwgz-UHCz^Zj?(vH0*36i6vAPI) zrIY&3e3`uyCj!Nhv(eIYqo$jIky7U9gWpG=x$52lLYayJ*&|$=@9$)|_g))VhfBKE z7pda?R9=%$Zl0Q2a4%alIi#)Eh5df-0CGIf8QPn6vi^SYSLF2avM7Lp!Gb_#yoSfdUvdv040q(6JY8ubkCV70tC`_${gHaM zPJj!C!B7!xB2=b|DP~xOVhx#QCPzcbd42yMzK#9F7^o3ki^J;^GTVN>da;Upm*Jhk zHLe-bAt00jpPXlidr;~v=4@8Z$rLwzhXo7#E3wCL_VZ{j$xQ&4T1L$89Su@tafF2N zUw1aI<6nO4VW1Fezb)F+(-U~9^r&$`zRkDt0{=fnwO4f* zFNHCx$shJZQAijJ1Yq!MFc=c||L+QAfN1moz5c#r!R7>pf>&h0-P?nEA4ChIW`AOT zuFQlT_Is@}|D#1`fZt_OAKI19%G0}`55Rd2B45o*D=k=OB*PzW(qw3lkAokxs;15(fz})ia7k#N5|QdxZCi} zgpQbM`4c{)QxIVxXB4h|NpYwZ3b!QLLZLv6R>UF+s~7spl!v`tFN2LX_h3+Q%4F)} z&SBqoOUr=J=Twpb^cC61HXqXxDsAZJ4$o~qeyv_*3lq*NS2!Sr1`-6SuaIzIyO!>f zA|W#2?(tug!%2Ms6qJTsvOF6?n=@iX;A=RrRUlnRrh>ZG1OX;!)_j_NGx+Gy6I zXNEFBS;cuQZv?JNJTK9;@bsDPvVuboLsgcCmj~;0k0%k8b=Zni6ms4PhMZ=||E>P$ zUO&KyZzXLD$1A86k_HRBjFvc8#=&ac96S zLj%A9X+}<7b&a>@=Uy%I;#~{MrjDqW8|6^B9P`=xr3R{o6Mhc_{*@!#{e4-s%!3kNnSW)Ri+X_ep9_OgySXJDQgTN)2WF@L=c zQC%I?**+!gOHyx6W+?jbqG`d|f32~uWz1L+KunlkJk~Z{&tRo1AVkB8!*nY^i+XW9}t}>rmuC7(w13VT6I~! z0D)Or%sU_opD!$*QEB{pZVMa3zY(^h zZGJq*wC4mikt+ez#z220X+#0^a(p*Le)D17QR?e=G*D>2dp3kjwY&n;ZmCX=IVT9% z>uChpgsPt3f>K6Xw+OzKw;#I7(CEDLtBW*$Vo_mv>HHH)^z;ZbmNy#QJfBaJlY$#c z^23LCehogxt^7`hNtJ#TFmnw>i9z2r?_LQYl>$(G#(nmrvyz-l#D?~~xy;yYicU|b zz`)BNuc_vf^Q$v(EzW_uvs_&Ks;*))oFTW)CwpiNQFG@0jNl+cG3F?|2yB*Cc;S3a zUJ+1w6O3FuKrS^S$mYw|X(=6RjA}Tqp_9Pzm7OVh{o2&ky2bbIVtV?A?FAQ1hZld2 zIm)8r+8jo~_4^<2mc&YuL~q+T0=4nPKXN08A&^1WwWzw*H98fId!7qFvWDOb7nAwR zW1Ie7$;&rBpP=>N4!uf|^-&O>mKNVp;D%g!ix>_{%Y(hXgDY^njWIPmKR;}|spH+- z3<5b2P6cJ7I)~@8Z-o?JtW1&{E)~9bU=GP95GZ_L9w|{oLl8uoz<6J^N&2p77a}ZXex5Rq<@`N=Q}vzLW*r`xAFI0e zC8?`=vu~|9R0gUzh*%qc1jtu;6-OvfwAW*194Lgl&}Wq4C=cBFrf%Z+Hzrn|cOndg z{b0JeYHwzn7$5~$dM53_I)xj5Cs{i&kXKGGJJH4x|?+_h!+DzQIj*5m7O zqe}IUr~<%N^{W344NkzbD-f(h3mKYG<)N)!Or?IGL?^sXkU}O~1BJMg+fo zTIX)~O9c&;dB0!V;vB% zq2kCNv9@+pQQ0$2;z@c{k1qOHO{))+^;2Y+R4JcSqfQ#M#$!)!Uo~Sr*3*`aR2|Ch z6+RR{(vTY#p?~py_WrQ#?J&K@Kvshj?DtP6pO-R=@r3eOZ<624$=gWdt>ZDk-%Q}OwLH|#s4HmwvT=s5fQDCe3BwER&CBeG&2|vk=u;VHRaOKx&0Q` z_BbD$gluZpc$~yX#0>alWnY*DHg6okT?tks2?$}YMT3(xs5?0pUlRV27l*iel?r3` z_ewJbu<`jnl5hW0e4f+tK}paO_2 zj$<(sbGZbfAKofsooRaCOds$*`kVTCmXPxmY-FpO&wAnDH5`6-)sD}s3%pHSO;qHi zv9!O&wI$)IG(I1|V2%;%y$*-;0f;ua7rihe)l8eFL%m6B|Mi1!E_@+gH4m+cS$@53+;opZ`NoX}& zIBCTEJxPKMSzTtVNHQKQ0_LxdgnOS%KR$CEkOu9dR?EU$^c_jh$8xrhNNy^s0W9SZ zy3n2Mr+S%t7yuIfem&6t>hU2`bT~pg7Y?oXd~G@44M8Xo73@Hnm=NP`1T?qe!kD5$ zz&1)GD+(6D?f*Xj+@>=WOK zUJ>+X)`|-n)oJrg)PzD94BUhtpSZ-gx2^9R{uLZ|^1)4Hl%V56)vC}yY#4Nxz-q5Q zUa}kQcX-TB!loMi=VHo)p_@1s(I!K?wLqW+pu!z-O3NZKwMK`MAAM#DMB?3vj(tVP zA|E|*YTa)nhitF>a(*o&CVdqpG)6gI#d`6LrFQADhk!RO}~j!^3Q-hQ&m8` zMw{FASYSHcn#b^E<*4KE#U@f?t2%WDw{pyc&vN>&#c^SDA$8|C$=j*AUE8juSMw}M zT@zd-_<4Vqn$MQ&8RRSYV7{MYNHO z947dy=8S_cGH9$e7jI0K5xeweGKL`_9Pwj9S=#CIP+F6=JOXaLw;kuU>W=hwd~CEzRBP+kL8)?n!2LZaFy-0dM}U*?YlZ zKYTG_?|i~|QCc{p#@T;;m{D5tE-YBl*(ZkN=$h#XVXQvXXV=PhYyN=pHbkY0zo)lX z$YE_S&vM$vA9&YDa(2zNGLU!n34ks8jvv!oWeI(;Bj1#APo8XGr(#ZAc7E-34M(kt zp76}nuK097tS)Azy;`REPY$cEU4Ff#oSaz77qF zh}DTY;$ah@sWLv3yCl9RyFogPwNL92t&=QZfpc3m{R^*uXGmh~_eQtAVVYmisegIR z$x%yYkMy<*c<3@i*TtmUfMagx7BS-2rQ(ak`ecKGpgA-XuM9m2F)o9 zQ?01(+cTR3w_hMxr&oGsrluOnbdbs|UnQM%5pd{UpbdPHxg%bt4IQ^kF*I41eK8+p zqoWMu%6w$QC7Vp2 p${vbbbpy0uh81uJ`==`*2X;wvaBYJf~cgdzu;Q~CmU-A?F z^UfW62JDVO8QHlS`HM7q%`mDvl zN!e6j%Z5;1!@Pk{H#MGCM;UTOTWaf*6Qz#Jxm`xNNnhPJJp9;YZc}(rtQ0qPW*L_H5wd5^6m3~pmiz%4p;C8KUJONkAq~_Gu z3wMuEDyQirU52(;WSYmWHQ|_}5=Tm{`Q*)QjQopB6+A2 zgTiB(v3tFxhMTp_HaCOeBAB=N*hx&{YU9H-S17WCMn*W(e8F7uN5dE@&Z zWXOpZ%&51W`+5b&K=Eu-rp&q!yy!;?5e%iunMED3P4XpDFkJ~yrI|y9!c(-nHQ+_M zyDE4vB-@yBA!1=KJULCkZ5tW-@dFLgY3oC!M}>18=*Dqhku!ia0>cE^d9U4XWl^Dv z&Gy5|Iv8Jx-TnB|vazrbOkEFpg$#f0U5 zyGhyF8u{oUWUvQ4);x?EJY_@8lUS2Q>S`e%-5GoG@djMn3j;(VV95J$!Y>V8asVC? zWZh@W5FwX%kfFD5A!7wB-+LJQDDee&4?s0eAmw1NYXmYh7kWe(cX@ytx%OWF?t6UL zr^B1C947cJg=B#&QZ!5DqqK|-2;g!AMZGo|zM#;Bkk$^9B~ib}h~>PxMWIhE&867W zG(;BvGud0eJLbrfs-FKB`?wuw3EeJOQl>!-2{KuH7|FeVHCJSq!PiY94g#_?rW1ly zz<(-ogzpl0UCDJA?n3ZDQ0c0%D{kAJaI!S+#9lUXpw2zL_NI0 z@_r075F(qlq<=H{)^6^zoYAG#F23QfD{DHaoIswUmv#CDIi@qRuf~4JG^e4s5Pjt; z1v_!~6kJU4+p$sD1SD$}n(l95jJ|*k<5lyZ9l~Pi3C0RK(!PV6MBb|X^FnOqS+D$7 zw-3kvPEQ$fDY>~#K~?sadiXx8)~RL=bpFC_o*zFyx2f)euQfJOTExUd@ZoWx%^P*zZ0^DgtODo}ca?PX;f z2P(!4Vm5X~TZDGZy=cRU=gqbmu6GZ!V;aAk0PGM&;VKixp@4rkYd=yrPFQMmJ8`&={z2R_U=)uqVS_x4#>&EXB zQrIWwef4}UT0>ZYJbdt|#W2Lv$LpB;iX|}aszAl|wZ(TrKX94%O*qopbo+z>$7Gi$P_0Qf;BwDKC3PeC48mUrarC z_i$v?g!nMq*M&lcA$w)Um!vhW!j2v10n!m^W_5;n`n0ph<(Y@`Yr`9sd5N}HG$4A6 zm&neYD3b~QspUa>IYzf;MU<9h_05ez^Zie2%%H>PH#c39ktRvU(Nn#z=v10#>?E%^ z?A*9*;q1#YyLs2cHw1=pQM7PU{T~|vI6w1Jq78Mcwc+Bdf0%0@xHX|DDC@6m<(tqa zY;8&&n%grkW@472%)>!>JZ++l4>tD|7c9EKgKM73X8)D%JC(gq2=C(9H?S8@+7lTm zChQ2;66R#ZJ{^|Cll#yflY8^33n9$dpbK{LR##^8?ldga zGKtN#Y$zWI(xgQ;XYW7bw9VgcL#q-5%FqDyi>O37SJ!}@u~-$q>%X+v{;|X4ovKnp z*%Yq(5Hr9qn}kR!8Px(F7v{COrijcni~Mf=>aqJdMJ{t&ZY%a@G;kQ^u{1V>y3?N7 zCgtPp$udx%He+aL)Oyr3L=pz|akw?p=LDpR`G?XrG#RpSLn zZ^wK$!QGI1&a_t8hsr8lkE7Iw=G)u19p)>yQuWK8#Ey>*4Xf{ISGW@weJFGo8tO35 z`Tjg5{K$I&Dd|lW zCxZ0M_LslnBoK+Y8h%{0Wq*^HhErV1G7tiKj#@4$1A`dst9O?3B`Q2L0!5CuB2W=z zIvLmHl)IVf2WIa5VH|(p6sU@`l|fN+Cwk!rCqqC1KFgmFyOP1_&iPUT@9MP})mZzI zdN#>n1M%IU8PEZ~W?9~k{rz3!)dqB9(&YM4>{n}fT8Pl%B zznuPLHESF2e9-<_6`H@`Y;Qd^s&iT@d@~8uo)+jF`So6xP2}NQX1Y`-c!Hze{kUO+ z^$hD4X2bNVc#JAdxVKOHaB6ZFDfL$-ZOhvOV97;T0a*>X3SMO~^@`*e>3`aPNhNo* zb^Uc2!p*$vF+JRsW~oH#OtvDqHCyLt{^Qb)N9It}0BM?@WWP+DfCPU_PH*=Mr`KU^ z#lL`x2A8u5x3^rw)SUfO2sshQ^u6GjR+-3b8BcB6)PCweXKgIl;gET=xPY|rZ#@k< zD$EapM7j06QrqhGN6&(95QKv9$!Gg#lu!vXE;C4h-Gf%SjB z?XoTDYI@H7u$(+SQj{Zg%&1$4RiN}zW98<#r;V>-&8(-_R%s@iG4ByD`HL*Mc;o4B zsoU#{K(<4~b!uHh?vj-9n4@C4hvUR@)`WV{E>NtTvX6Oj^XNOZ~ELbV@Md>TU3$T$Q!qzRpQY6szq275Imd5+9&}C9VstUiWg9)vCopg0K z#a;=VqiZ*6xX!k>?v@cQStUM8tN!1Oj=LOL@AE_R6_)=haX#;1FJ;IOucnPH+Pb6- zt9WI9dQ0p?Qz9}VOLh0DSWbv3)Os&`avWFt>uts>DzmLml$ptFI7j9T5GVtZX$?I! zQABx;7@;~b6E_jf<(}ssMp18?bB|ZaIj;>V6iGkIgBj1|(bCb|#cvLh-5^LCKLz(B zHr!c%e}ldiBeSzKRILDVCOB-c6&~r@5oIg$ z5YC@npMmt-A}%t_w_D#2JhnVrUmjIZmq%irMroEfl4{9+DIp)qx^Zz~N*e98a;)w? zKO+HQynoD(of@wtHHt2Gw)m)MQW}g@0If#rCcP%+~{l32Nvir#fE;pvACt7 zhI8HT%bSwWd}no`ZY3Qq3x@q#ew44}qKMM@*KxkLdFbg|VyD0Rvf@ATd1)`aUDa(9 z@UWEr^i{Q5FI^&V;auWN1Pn%3K-AtTXuv&LFCFgW$)s4;cDI2GD0s)ppp|!}{iIYz8>7d`@r7x@5vBsre#W!u;a;aw)?zT7~KVU?~V1HvPJ5@B| zPgTCKZIL>qh4*j0hZm`6v6=0%(GL>x9wq!QAwBTNDgjF>57ogXRmv_qNi&F3T{HE;8+2tv^IVBkF(~-mds`a_x6qEcjO|^D!OJ~)_VdP%?bl-Ak_-V-KPT!T5K@b z-S6<9nljTe*BGEF4!X~blI*{IIKa@qz`zPIRDy1Q z8c|!-1J*T;?GxM@B_n_&XDW8@fbl{L4v~-;?}6%IJA*=*{dU}2ZtPBu#IwYecNW+F zB{3RXoym~zp&HO%H3XF_z)I&jO%-i*{#OS!9Zf8JJsx=afQAGM>*4P*GKdXt;vIjh zHtLPr#r?%!0Va(T_+r6ICe{aE$2P0mm>JDq@+&_ay%)lX^<}~CY_FYl&K)<`CRn+e z$lpJ_ko8^rxXP9l3ckIR8q06`#l`Hhz?Zqt452_@7{kGQD7_RYoY4g4X5~LAocDmm5yA>|5U0vC^Hv+ zslE1bC^Vbs%?dZRAIp_L;gR9XgS`dCgpj}|shYL!$&~G}^F!cL78y_y^cLFVX-bE4u!M_h~WL>lm2>T22SSSH%lo&hAs>tCVUmzMG zuvzy!Lo9`|Q*@9gk@;(@*;}w5iGWxL-cT)bME@wql)t#sJ)8_Z+0&0vMdVI*vUIhm znX9!T-26n~x5lkT=Pu*mx47(Op|~kyNcrYjj5ircNF#PrC>4B~S*x0xBK3_!Z<)7P zxt}V+`B@pF@{hRnMhtjzY5puGoWXvCXSh`%`U`2H)UE=ZWahp|+OJHDlfq2g=9KB{ zZoNhMvH8&vQwNFmDYFaxLA3}zG&9Qig7&;_R98{OG^BA+UDrZQQjk8zy-nrOuyx{a zsD`#bp8K`+9Nv7sgpa-uagn%==Ce--K3qlyF2=)e1>>Ztq3v$clL){ci~>9tuhkYJ zKz3q|<`m1tc@InaT6^Y2xSn^Y<-!7Gj!%^k^MYRPdpp`1OlgR9w0*X!i3O#E5x zZuWKB7%|~2B-fvMW@)Jhd_U8@NT78FEv5-7OePeutV@JYL;X(}f{NZQX z{96TR{PvuJ%oq@FCgQs5L``F!OesGWtJ!JZNG8bgF%iDDRQ@i>0G~@X(`qJ<37ZYM zK`EJw{C|=CnuY8y2$nlV3EO)yPqO$>G~Qz1Qx*3$nI|+13cj6R_z?se=94}7P z$#PBxYv!|bPeTc26#dC7Z~;i@7tj2mOKIApT0Ur)cg-XHZ*L4Z^G<{r>$JZOAu@#c zOD}W`ctq#8;)b7M7Kn~-OLr>*HEW;vlBh7*M)7|lvP47#=_sNMOtUE?>>elq4)0d# za+y(_`(oZ#nn)$dW?Sg~IpA>fvWOYA&6#DSu{4HWpmahtbbar|l&3B^nR25}XL0^@ zkp{8HT18WKW;F1&yumt3$@b&GdWqG$OmIGoQX=lS0J-(&4_!o~Jy&u@p01_(eZ=}l z?xvn%@Oj~kON|WHw`s{kZM$Ou87lRrlyzIvOhUKr;qJ5%fcif~m`1ogac#U8y~}&? z^;?bKohuIp2t1_d9A8^o3qKgx9q&G~;~xKV)}aXrKxDlu#_!_9Tp>`13SZve_&>Sp zn6Zmx27c84il$I?u9OwNlR-jD62Nke%7;rs1v~u~4*w%$Q*8Ztcp~~8_kCDvL|nd? z+NKvJ0_%gD-A!87jW5eI#Yf~HzHPLdJuKIiIwZOR41FQEca8lmc>+x5YD+bE52w(= z_mj*3zpck(53z4guBt7r1qJXJF-OWc9ju7qTcc7}<>>RkCm}Ce?$g)x%n>_{H>7JZ z-x zW$6O+H91K7f8aw2bY|EV5w(-#=QNl&0fOWvwY8fyP8gvF2sJ@l*K9D8pr$CL1$?M| z=l09@Cbv?Em8$niBEdXe-aj>uUOMVG_-{K2JG{&K*X4S!xN=o!W~5U+UdS(+v9~rU z5@d?xT~7x43(9pLvf#<6p|v#pDqXY<+)PYWi3`-Vm&dP^2?HmL!3FA;{IwgKP(cEB zbRs3%kP`i#n=GP{L#6mJJ`43OZ*n-9BE|RX&pdfN|IyF@sFjh&%lgTJW+sUew3_bC zD`zsk1-s^|7Nr;VgiqP?}3?@WrLJTyy>{xaL=MgD6E+V~RgVNk}CbgU@dX-k9-n;)5b zv-`+_zNo2OE-AfH6BrKBNz1~~I`mZd#apcCqdBD#+^cj&Vy>LFHX|rx36kVjRY8mN zf4D-34FW$Jara1Iu=*+xhybAiq>zqx#h?GK=of=#o-2M-`qWoD>i<{m`%kzA6gisj z++gVQaJ>75_Zt&l{tdD44xh-HvQpg<~PJd|y>w2Bq=rHBELSJR8?l*7<%ZEZ$^>TyS|UIWc||Eg^W zTGtxWYkL<3&a~VecNno; zO2A^k#=_-PXX`?|iOKq!?d(G5$A#BY_saZpr};cSv!eqax6nw>!Tj!EB)!#wpSv~3 zfFg5}&vHiQDXBY8rUC=29`b2X=3Biw6nEr&kv69GJYjB)*c9u~PZncxDKgBnQJnC3*m+YNZg=lR3{U&Dni?!>9zggj;W8|VE`o)_F+b{ zZ1-Gw?u!#^1lhRW)`0^2XE5{Mzv=|-Egl%FFA!q~Nw8q=lremWUwgq)neBX0lNPwY z<987jaH5L4Cz%s?{6O1uGlOA&ZCB{tTIqQ%gR>cMD+CMr32RcPP(+l(TH{te_db%i ziJ_1r_+p1zdH+f1s|KA6N6RZyq1ueXnN`i$OGxGTgx{!8IGei?r?b9W^ifhnVzq&P zt2k0{)v$>bzu4Y#{rST0-wdvT8^P~W(t7GoShn8_;kPr6Rw3IDh+#+c@Z*HdiiFFx zwO@;KiWi9Mv>KvjwX4e__)Fq4Nm+E|gE|fua>8nNH0DaqtD4l!u@TvJr<%$v2^y!t`A!Hu(}bkN>K zkBR%`kebJ`L19n#ClkVIngi8;P-z~Fk>DRII;@ef1MS`O`&P$3; zJXmcPNxQn|W^GaL9%wY@%bI#%nYT>&?e`+{m;%_(O^P#^Y9*h2)^p^+JT;xR|Ni+#0i3_jyQ#kg5)`_j%&4Nr z-(ApQ_bF9g-q9;cQla{>P|x_#VnQ*3#_!fMpB0U+mth~J)1-!lCh9pbOUW_d694WW z7@r|q7yA&vLmDCZO3{=Y^KNJ+W_nbU?laHg<0*`LAN*EKF-0=V>s83ptgK_O9=s;m z=zq(9xHhkt$rvCDb&p2M!s^X2sgbeK2&P016K>SuEJM?F6w+#A;%w1@6D8a>QqKM2 z?zB1^{3o~0p%kZ^okzTn9w6HI^wLa{MiXnbUVEzR7%b%Yq~Cl?c-h+W z%+`q+3IA>0W_poW`d~#@@yDuJ1VuRd4QF12dfyl(kILW$b{H4FOLPyN@PJI!Ite^rBtwgA zuXXdLm5dTBq05rO2|GPlbB{TTK4g0Z^BAY@s=>Ry7M-&+#^xM6Yz5VT#NEW5KAC4h zo`}Q;1)RQz(*@-|!Wh4RQ{_rS?ekS@hJg20as(Ua;d+?jmNR`^vg7+HN4uZ6*KB@{ z-~1fL;(@U%uD2BDS3qVBQ=uyx$h6)6`w=NBUoCx^TDrEGY+Yz zUB4Ec7q%<5XgceP;;Eb?z_|S0A99gnuV$41ZzP>xC=|QuU-d8=?3JQLEQntFmhF}# z1W$O!5(hrL{z(znA{d`1=ak8Z1=VHjpUBjBq1!IDq|y>`VSpez{ldFDVitn(P8GY) z5N$-@IAf{<#xtV?n>$lF_9-zpmFml7@yg@@XN?r{*c@K{3Q}~c1`RfnH+ zYon2s?XRI`=XV$@UOT%OP?rK5E4 zEU)4NCc>h(ZdVuu`yK?QqhzaHkH-<(`{BI1+MmiD>q;Qwr_WD2xFIVOAJD3 zA{a_#PV7)BsE4oolFLOg^aK;SO&SmHS=A|>_1@D~T4)2j@|X#j1D5-d%X7JuVKKxF&P zju4n)U}@sUu0WBHJjN6e1x4A3GLRA(gtQ9b;3n3=g@_dd5fAgg1h_0-JRfmalN<`++Bi0aCdhP?hxF$xVwJjneUyNsT%ol zs;k!N?%KP%&+ffe_X$&!mq13uLxg~UK$emeRfd3oy!m*3z{7qZs(S4s9|eNFq^2VT z1WNzE2QrBk1^)wtbCQx1gWHEDLZM?co_6f~4-(UG5^{Djw*x_xRE|l0KuAs>kSNg6 z7-V7ZWMO9ufsQ~z`hg?Nh6~8z2PxJu1S- z8-#yvRP0QhTn#}$2wnU4f7L(#*SUqGv9;kxRS-l|cYVwU4F3=QVFIr7ePFQvs;OEySpy-8&t@ngAihCJi3+K@ zWt^?R+_Wq_zNR(7kuco(JZ2Ns$r?<4P zNqp*ox+wE$R7)ngpDa93(*GMI8uyq1XriX5$Z>-4$9%{0kmuSg>nyYZEHs+=3qSbg z@Lq4Nv{kK2;oY{>Gl_ZdTOgWnC@MP`I}?&55N$gzr^?swl@eu|m01|r9-KU*yilnt z2T%fOM_1(sGkjETcZGfScv#T)P2lrH-45zYq%Stss}Gm86Bzc2ru4aW1l$cvC`ose zT}vYC!jlBL#Sn10#a6w8t5~_1Mvi#TO$UAlQPIjutdK7=HnFm#?lN}WqvjihQ=To! zQ(<)OIchdBZwSMa?8kna+T}a%;BI_hC%6wVaWe@Ncw9imtt~aza;J1IP%=`_LOxEZ zd91|L*p-aFQQW4{-Zwiwhab*1`! zj*X*r_($+VJe%*0QDe{f#ms2>dk07G3n%4nE@vDS9Jh@q^GNe_cwGH=D%R%MCn$-s zUQGyfD{e}qEl-779hFAtW7UAEjhiWsJN@-=Hf zBw?bzBCtu~|Tda9~gPMmKj z>~95$Iw$kBX)8VPV=#H_qX~HLKd{Erxn<= z{y`q|x}%`Tscro!m;p&exh7O>e4Yy0hhIis1c3MR+mPFG9$TD2aa)2{d0g@lj5J)N zTw!R4_wq`sy^$f!R|Mc29>P268!9JMyBIv2af zy3;P!7#%`sebv+haQm4KpNtO(C9m0?e4DEe^KEy~Br(8^VfEDCyXCyQNF8cK&jLz} zI#AQo7Kl>fvhx?+oo&jt#-F+}kbosDN|6o-#?a(M5va2hc=h&;N)v|!UYQDN04X5q zv%emcC3ibWPX&Piws%D5z`ZLmCw(5-XT=P-rj7AE88fa$j{aa9?Td-}Y1_dPYn^du zSB9;tJr}LX%x^>t;kjF1;<>ZUHMwt)RMd1OM%yVS8gzSmH?^>^8J)*K#2}~J z=&8B6Zbb#`+;CXwyhXT3e9o3bGcF#U0J<<)~W*g&)!RcUB zK12IpmI&pN`vvYq4DQ%ppdP?6u9KAaq3!$>xHU6y^0FRbLdw2cV}vP#&55C4c|dFd zybf3ir;Mz=ouH4*Jl<|Nf9IowBeb-X2~|&2GJJA-eFU5HItB{7PupTuns4p#RoP$m zYQAj^wP`fC@aalq8^0G2$+y3-dGbP!_;YkVkDE0s!Tmgaa?lKE$y((0){^4x zFvI{GEV1-FnBLw3Mqc-NB=g}4wwk#?MzRN;YiAbITm&nJ{vONXD~&bOa)ljK&^hQOhY!kdLcCvrwt+QgBzIqh6^W zk&R1AIM$htzWWhnDZNvX%Kj>;Rve zHBtfS7R5H@eF~_oY;mSrgs#d*sMCBzz@B#L!KW4TQ@L6vA5uMzpoKlJmz|i{11U%* zo~cQD3C0%H_#~?zIgzH9FP~m!82Mmu&RXnft<)L58-;X(90*gYlSorC+TZ-?4*0x7 z&wZZH-7crN+wtqEH@G5kM8t>wdJJhTyLR z;PQu{SCZi=X>3X6;`Srd?Dae^H6N1fm)U|!BiSR=Ye4f!erJ2S6e>TS%#?k;O82R9 z&Rt%ZofpmE?fe;@jVzoYo5!)JN~~}d_!CTzIz>SC*Y|;7j<0{=*6MM$NSYS^M={zksu4UUV$Z* zLP@i4dxWN47M50Tl9brj&zwW;0Q!oL;y1%#oX&Yt1_YpJe@%eoMUjoj&xWi$V zyIU0O5Uq1Ug6D6-B1L2W2U*ty+_SLc3a?geDTP@DcxMqYMvp{7nR2<~Qh2=}Kf)sy92t!c`@YDDn(0q@nt2N$@ExH2fOPQjh zd}8vK2+?XqHU=GkrSOG?>v}?7!SlGCn=D?bmBT(5cVda9$Ve2XIBCO|)m!ZRUTd!d zp=3IZKLMx!K{cnlDeoN$*E}h?=&5Eez7)L|iZ8R zJ0v4j!E__U=TVnXaj0lwKKW*A=$d$C>rW&rpP&Q3&qd!DHq5CkYDgLUR#ju)EO@S= z7wZT8F=bDKEqsGro1r-CC^`(*|A{8-rAXg=yz`PE`N@ueOH68w#b3oneuWf1K3AEM9EtxWg_MVNL` z@lGT^H;s4N&AXokw_)jU;3CCc4g%}weD?b8EVPv!S1 zG3+{DSc6IFSG|Wku#$%CM~}>{OM6H6EVNo+7mv24Qpv2&f5jjtE^w-Vqc2}rCmjo| zV&MetfyoPrvEwEs8qJA=g&u?sN3yPBSprpfA&*H2kH zkukT*ZRYr>t6kxh-V<@C?2rw%6SO|VFKL_ujd$*RLoixV*XBxhgw8?b&z+oEc zHGr)qJ+Nl!eTdw0zsB~JN1Z-PV!dwZ?x5)LAz(#g0kq?DU$ZU`vZY@UxN|sLbHb+m z!I0P4dP09~qsbAiW__oMby=(YwFVv{&Kv92aOIze=jqLNba)KV^0QW9pMF5GC1qO@UKkiRG$7SNn!C}@ITIKdn30ZC3C z^sveeD-P>+JFcs9aMI_XBc6~D86EjmCh>aG=XtTXg2s3BxGfI#QPu$$rSlCD8!kRF zik9egY=^Eilh3cq(Ov#*xorn#m+@){Z%#TOWg;uzx3}8u)Wg!4MOFu%q#tgmqz?P>$@f8|b$R*V;5fIS zG5(pO9BA(4p9kip4x`}myaZ?yyGIH75lEUpW(Kf?>?EjI< zKKZAqpbz53E%-krRO^4$B?Ynn5j{Wr%0>#^}d}n*UI_epqCL68oQXV~}6}w_*QxgCG$88!&=z|JyyXoX;Pv z9Q|ON{%iao2`ECs?1JHFj4VwIWx6Wm9Z z_-wkgeSO{aimxXyea#_p;r6EOJ6a~uYNrtVUw`&%S0znDHsQ6>;~ELx@x(6=+(ggY z;F6j`jSKT%9Bo^ID-+X{JhOl+?AgdJItCkW z(Dk>Gx+&|s6x@y{OcK%Ad&}Zpz$KVRDfzSY@!Z*>zqk87(OEVUD)4u4=7Fmkj3M{u zU?cF4njmq__4#$=8` zj`H%6vFtgsA_RPHek#~zRV^_t`rDhGuk|j-(6I!BJ8Q1$|7N?`9h==rjEW$2@0zO8 zMpx6HUGn;x*efNXFRiu~-tO(?Q`)&tqQ1K|I^qG(6QY36Ay+{|!>)wK!=p^wLs8c~ z=F#_{+TdEc$n1hw9a>V6zWOX~k=T$1+BAd^0{K&~_T1T*J^hyqM(*4NJpzS;eSlhaObld|JGgJNiuY+G7(sTa7)AJS1R&?c+mll zbj<<-!PC@77!at<*m!?6LPHvSysmoZZc}I2Sr^+AEdyWor*O{bXd`xi_Pu?-I*}QA z93NVQ&glTimj@4ZULETo`t%AsRxqllaH02kn{n~9XT87b`v!%F`m6H3gNeOU64E(p z_uo7pABhEI^v_6+`hrxlm+Mll3eqhi*LC^=nbTxZ)YqGmh<$cG1-GB7wguUQxDw`g zJW~9|deCB3i&p9*B;ZThzJ7m0Ev82OnYF3#w(~wPF^lhJuZB6K|G;~9Bl?K#d!e1k z13|?2cK`mTj&F1Qrs+So&pzC)0A=trM_i2o0luZUT3awu3^($dfxX>So_86*9?G-b z{xEcNW5q_1xm_-=O{jZ@Wl6Q!ucZ+6lLu1}wq!!42Kn}E14&8_sbnoW(Ys`Gi4ZVc zmxw-x(+U{=+=5>G>FGN+dIx6X3wv}GZwPSFE>{oxrWZq8UM``YKCg?mPg+UJIE)cA zy2Ih&*|qm>sM*U?@as$xE6RX}mE&-Pio+8RdG34fBi0}T6oaH%!yPDj=i7_TQmQJ# zgT3jECrjpE(9M90zh>s|XJ4+`%>wX0zSc3NCIU{c%*{IeH#z<;n{{?=wmZ(Z(Do`4 z-jS9#f~OZ|ahoU(2w0W6$WzyGxNq~GSw-3INz?TTYCvj2N(QA?k3HT$KPkRe=F<5% zdvlz?nsg9AXg5`E4f8!4IuAwmB1Q zqMYLzpz|e(QISJtWYwP5_jQf9W@~!#GVIYgF*${$`dlna!28vpvG=l)DAX*HVroCl z>^RB4Hjt`XT5Fmq3x~PhXMPh;ooxc&UDlVXa$}T2$xtI*XO-Jk z>e*DVY7kNuYMe*$f>#*qYw}Vgfvl)rCqkC!B&x-M$gY)z=e-tM&y<=qhP)qX?9cF0 z#8NBi8j&v}$4A8#^w8Go6`Hc=kLa??YIOQsF&f|Hbyc9rr)l@%73qYW9a(_|L0(IS zT&~-Id!M6}62_KGcCj4<3W^Y1*@9ocaLnACFq(e#~=rAurnT=N2f;_bO)sn|G$r8nZt9^ZZf z-e=DNmpyu6um{V}=xBxowSPIydVfDmjRw+?!@kSv#jFV6c2#J>t_=+B znu`ui=yZc5-$iW6*URZ!8{v#<``W$tuMylvhWF~@RcFe>@p(sge28z-DJQr6>1%B2 z!4_DdLwte0r{-JsaLwgx*YJ?~{LAk;iVrhI%qKWn!12bBKqO~&%&vp-RgKt!CRa55 zmO1tX^j2Nk@qat0+{3|`nU5JqJ5O|4MEofK&m`#o zJA?AmMm#_lLH7bT$iEp$pr#C(X&%)V*O_JHS{v5Hf6Ls{QMkgIFg-XUFs5@o9kC5w zpRgUQ{V^dh!#PnYlQ6pylZpSEufqKu*Mqn1*B@QY4067>ZIEkg?Bq|fzQZM~Mo6db zph^ejf$`yKj0%xDdKK+hIUtv6dlhK_x>JnX4~{4I4|!vA%|QR3)kE~3Zr1M%BMFU+ zbwjc5T$o}}aMH$@m5^GEO8^1X*$XlO87K0rX|umngQ3A8hlHc7nEn!!AS56nq$^u$ zHVSAN6-NfnFV_w*Ub6!oX;O(YCS;lDXroJYJVKuT6!Q3ty(0A~r%o!%{gUN%=ZC+c z>0r1;s#8|yu&}&11!(pYYFBfo$5xZ!_tQ93w!R&1W&R+59EM0f_7rc{P*-937qSrU zmGvlgQo9`y;|x1SnDwwM=BHUm^YKlSEsbHPte^R)JezLE@I~6s?jTnIZ|EI@PPRdV z5lW_Doy)I)s*CUJ96i_AgAk#E!lHzQ>eb{`n3x45)=0yNL*@=<0^nIr2k*|0l-bK| zZKj_k*`}_Xzh`Z9``56P$5R493)i1qsR&;e3IC#)$XXv=QiMGBS9?wghQWuKOdt|6 z#pClLZ;7tzgswwhUD415LHgvz%AesKKEPTsINPoYJU4A?B-hYCD-|sdTl^(^KDTU? z$h%`LagH&^dJrrxl^Fa0GxWqlcSTWL7RKsQ6O%IAP~J$fl!A1*)Q}{L6m$KW`5Ks3 zx}>^&xf79)$U{P;hqovAwX>P2cSB#2MIe3OfTw?1Bf8_2A$vn_bl9<@-HBlo+G-T% zTxL7z$Hj5FmDE^s!Wv(P>Kr}UmR{-;Jys33heII8hB1h0vp>29@PBa9C;6*m;mVMUJXq)0)GxYxTu8_X zUr%`XCeHajaz_2FT037<=Sv*=EkAT@iLf6jmtg}+6y7QCrzu!02LsY_`3+xOR6O0BhqsyIc8IM7C2^>0QeKpZ9HQI6cp zL504oR2PfRYSZ;xHg+EC*2L}g2Iq6yh@zlDw*%sa7(*(l9xf#tV8jYrrG9QuGTQ9d zm<7>Fr(euGz6-&j;dC$0+q_xYWe05q`3E25U$v#q;pJ$zZ%z^LsmU~v!gX#O>Rd;< zHsVNf2>EG}m1!p7@SOo+sFy3ex(tU92i(;)CsmpoK0j{w)&*XESdrnsO~MLUPDmWk z8PSapoul}QklRk@d#S}s84TTfmQ)^ncW?9^bB?0Y)?i9pMM+oQd9F8x+&q+8x@@Fa zl_!oX96foY6Y_OMipL+A@Fu-my^kFwWPqN2 z;_I&W#BISa7YaU}vEP=k-PaZ5X`G;iEr4@_!w7wpi}eL`H66<^UBylsCOv}Dh<&B; zSHhU;S{l(I!T7{RP1P^oyU{-Lx}^drLu%QQ#7Y<(lm;5Na@Xbk-H@FN;>xb}Jo(*$ z=yeb>ag$FEIC?(UEWn8Lznx4xnTIYDwD*=dP0;9eIhAuAPg#$U?!>>p;4^}E z0tv4U35K3QV^tA`&qRDuSZ)p?;tp~YDjPWpnv4T8z4^13G-Mt`h+0_k5&(`I!;~BxvCZ>9=PP#~x11QEjjbOXb1Ip! zl-NNZipUWu=6}jVi>0Yx%26tsFm>B8?f={b_z+V5w=(koBSieq9=!O;$rx!7koHgG z2u00Fy?knY9PHggKZ_#vYsOZJIEdN9CZ8vpO73}O922P{B_jvEQ#tQ|R2fo8 z;r;KKjsJb6kpx2|7>IIRUABpB%)xQf%#qBqlZ|03r0`I51;xcw**;?ih1MK09O)fL zpTB;zFijZU3l+~3{r<%u8P5X^=HOdS`|oY<$yJG-zkjK;jx|5ad{8tuK=g5ImtV$5 zTJpf;pqnB~;ke_65>#lI3Uzm&4VJ-L1bJE%70|)|3&EWOGi0qv@w>u z@1eB@+Ys5PU`@yY-b^()8tM0tKJtcb(^b`7)e$Cs?yw?yx&o$UVFyTIMn~S(+AkaMh`}e66(n=Ew}Z+69xuw;eL(YiN+32~oa`K*o)-7n ziHK3cW+Y$o_9y1Qh060`?Rp7PP}*{?d+zp`sE@w5FxEF2zd{5k6iQ-#Sh8sf-O3C@ zGtwG0t^?>u`1^$1+}KMMf$3^(Xaa(xl-DkO( z$meeMvq@8x*?eIXQpX+7&_rX+mT^oSz&xj4ddAb?SB)3%J>U{^gp!xbu`@|&O{@^a z-sKZAI?YO{QN0WKn?`VS^}oT8gLM*wBXU^+oM~}PoRNlv@yL-q^KjL_)A3+@xBE_K zEpK1A1DKYKbf<5qzbk+bt@UvxC85!rjtBleM}kvm2*_iK{*ED~h*?5mx`!i=f@Y!9 zJ>#yX5qQYI9-LXSA6-COEG4VVANM^aqC%hpEg+36ig1vU6u+n&NGRUn2zYGC-F3Wu9fI}1#l;rj}!bhTL&y?uEv0A{$)R9tZ1t(_cVKl-IfJNcYSn_ zF1**3_!kFySVqE~M^4o3J?SN{_t$a_^%&i9Ae@k(0S3uXuX$7zJ6?LVisYs{(IBYW z$iNRzy|t zyz$6$QLt12rP=AkL5Gw7XYYh_cOuC8Y|_l}E^pzFrhar2S8=RfE}V>Ke+6Foml{*L zJg>mAK{vVA5^kq^0d=r9xs}&WNJZ-@Uh*zPd-Kqg8!3(mB>g(yAYVloW*x)3-Xgkk z1$bW(1YVB2HplB;>k6xMS+e8agk}ap*X9?8AN#?T4F2qt+sl~*olg6#EfYf>F<%KnB3Cix==9X75?V1ZQBljR)_nSH|~vfd}G{$7^DK4l9o~Ye=~oZyll=O*MR` zkF8Qyd&~M5Cp{&F>2DHn4?XH8lfSo9B}GZvaeEBNfA_bKOFEYhej)`wVeyeyx^z_R7t-3Rc3 z>e=S*o>JrpXkQr>m9G|gNV&L4b4ubP2-8wrxVyW;4S}%6_muXN=Dxbj&xzj=;gJzJ zN-mK!R#AJVm|g#EYf5oT65+w#dP7!ga>773AO-;=&bHbF-ca=4X1aDeJ+sMDP%op- zUqUBsY+HJWP@L!Ik@U_;L-&8tl^AO~LrXj_Bczq4QdQ(GM_#OkF%Uh?Q%XW$-*Nl^liMc<+r+@rOqNPg1M)&cIvX9kszLZ?vTI?^0hP!zG)vbm+=-&5?q0! z@7+w~r^7K?=lR5q+3aN@LLSWmGiZQr|1t-`8{+M=cgCa4Cvwu7(N`*eu|)pG$ICu+ zCRv(aso-IH+7J({2AbT4X`IR6;rmnB<2Q6p)2EJRE{C@I1ijRB+f{h1p@H}!=$Kby zCryoE>Ve`is(hgREB1AH$18EZ`~hdnaE?xfA##7GY)H^;b+g zUCzP+-FcM$Wz@`~14r>k8${A!Vc9tZ<9q)m8q*S=@J=Z?%sk47G_xC?x|iu&5_Dm( zr1%aSJ7qX=Go9@)3t&NlZe7^jIQQmAeNnj%fa146VubJu=b*6R7%=%p7Q`HUSt*pv zcs(T~xbDe#_Hh=~-r+Ptsi(*C`nHp?aUBIMq){V)VP+V*duDIV&(6C6v z{H~O|xthiLOBtIt3&;Y79zd`cX8z+I1VO;y#m0?G@ilrUomPPZr-E6?uoBXkO&q`6 zKGOZOdY#QPPa^YQ+d$Fp;;~x23%n+wA!ys!c^?b})#XM-E8Ys#O$x?hwl6bE=^Uv&OnN=;8mo(xhXw3wr_1*t=v3X#a6_{JpKn` z(O-Z-^u&|NPkVU4`@-{--H|{c@{o1in6?4&i&IT`!T8T~KHR%}oK##V_p#@}c)H#=H_MGAsi z2kVH~V-|b&*i0KK;mViU>^u1g7aE~n%N>pM$#&(>)y2ge832y%7wOLYqvQ3mhw_jY7E4EJK46!C!o%=KLiM*a|u*K7x@P2V1hpZ=a^lRWXf7@hq?*4pj z>xj{}tqKU-ISxFw>m z{*&QCi^Y0-EJZOR*wJd|#4Ru3%|0+lP7P2`Hl}cYKb`6Lu+8WE;Jx#5q8wb|s;bD$ zGMx40Z}Yd2PL-XTC`!%F(y$@*r1mnUpXWuWGjmnOnb?PNL7FYUas6H-d9j~n%k^5m zLhMj-H#@=+25_gPRG7S`?UBJT#3N1S70?WJ59AR5T!_l*!z%7=^5}kB@U^GRV)KM8 zjG4{y;Zq~8++>)OvsJ|%jZh6w6HO>^t+IDvCCE6mRD|qf%8>B=$s+W`m8QX})Er6! zj^}$Sfa(|R-`A`*u020C-KLQJ@io5k^%>EDXT`;Km!rk>w9&O1%r!8|#Zh#h8&A&@ z9P!o!@D}G&$s5Vrm2k4JqwnW{idOVLXGzJ=&i;tGkz*RdO)<}0}kn!&K$m1(ww8%KdPA$$m^T&itI3oIuWbvVpY}X zvpe!s|52q-RG5uL4HrYelIN-J_}&@ZRIMd#%wMI!6AyL963UeOY6ARG9N0ln#xdUB z)T7iDO%cy$A}>_oE{$Od_XdwJX-tZzSPo-J@TegBb1D$f4peJ=2`cr{^qebw;ujQc+=I-t+N$6>9TlbdQHcGPo@QC}gCx*e8j z8I3-yu8?eeGCHc(U5zh2-FoKPs}=O+%ofqHRAAuuzI5XpFPD61b)H5$mH3`{8Xskd zW@~wx0(%Rfp(!TCDU{JVRibae@b1>lT0>p9~jsH5Su5x? zo#)+=>KJb=D}9_Je3KW~QAcY{y`?`igXud4=+@o_sCBoFC`#1m>N0_$u>9>hncqlR zguh*-{DX_dX4{pciPOGWl~Uc-W*kh|mm9}V#14q_4&BF~pwm-txeBZc2U*2kduJ`r z3%LWS70M$oZ>CA&v$hkCOK~~@e|kv7s7&mc=GaxMb7#PwZQ{eK0}n?c^s3xUKfoE& zS|8d$`@OVPht7A%|>Q&VGoVB>$;MB$iYx%B4d+r&D)CbBSjgz_RkZZFrH1@%V5Cu0oUAUoCRBJ zA*d2jx4cB2oN+-Ml5Ry<3I2+Q$=s$8+8qsMR>+H{coOMg5Co>N$|I4*U?lM_6*0-;pejw3w-Ae|1JBg9=qF!>_4i*H(@AE9sqdpV!ul znXZ$A2tLMaz0DTHIr6)d#sA!jqq1Z_hNIhxvNbd@IIIPWXX-|WXm{Qvo9NSgr4Tn3 zu+b1aEXBb&M2#Qvho7Zf}u`iBIs_TyKKs3Z~>!n4iT3XK)jaMmo6UBT~L+c-hQ z2o81;{Wqjp<+xS0gs7R0gtNo3t9@C=X&XbI3j2dZhBdQR!%jCdYVC-e5?kt zpU#DU8qie3xpgXRKF=ECSg%maqByBJ^31x|&_yUx1+wZMtDEOt@)2*`hc`S3Pr2&C z8M7q=^TN@;P_wTs@O5;Fk$j`wgAQszM9kLuRq`#eS3}*w+fd8^zyvvRU_{bw5UHPG zw=_r<)FY0KM;3m2L;ESW1fWr>m?wc-E{qUrG+d<7##pXbq-Z`$zn-!^sp|L^i4{vL z5Cl+o%{sdOrD@)J*M=<|n!5ff>wu`5b4WCvE6ku~C~~hMXe0JZwDc``&4C)$aurjJB@UM##LgBZ|cT*$R?KWthDF#tg*+qJOFxX@cw`6>tJZG6EO zhgxfK+H2pES%39FayGZ5HLah zAuKsw^|dt)LQ6qfttg5n2vbx97$i`uhgM7&h={X4SCxZo2K;<%rWjzHj$74ZOR!Ka zK-j|VW%CQRvR_;(dT%Q5bR@Ps(`XecN_t!Ov=UIem2;2-34*}*I*U*?e=xCKnrwnl zkV?5gYtD%5f7*Zi5?03#D8N`A<5ItTXhF3HKb|5Ts5D%aYy>Uh*{~Hp_!Jt5pDv?9 zL2Y!+bXAzn{eg};hchSKgcG`?hAEp;IeFf_emu-B@jDA%H9~JuD<8Ll+JHvNwZ~a> zsHVqPp`&uk3h6OeK_OWjKyC?KMQhwKz`SOgzpskw!HF`awFXv44%T`lUJAi!V4Mjl zw)@Zp^oko|XJskk0dcMOa*~1uh0=TNj5CDm00RrqpMI4*bIeC=vXr>X?Irc6rsg7ylZ+wtv2?3Z!mFxab}Qq*D|Z)J za<7(u+RqLK^YE089 zTu{=NA}gIqLh%v1pev?%noCoA@4c^D2$szYpFs3wXvGc0o6r0{o9GWhHPov*hm#GT z8}4-&Bq$O{(#PW`!4N_CD025U$AakQBAd_V6%IP-?O@Eb=%|*NNNZ|>m7d1TGPnxd zfWb}hEA3)PPYrz{tPOz9%lb;g+*%F(K}gXWstLpVa{(gX-=8&1T;``3mfM4aI7Li@ zx+UAj36LX*ipfIM0hcz4Dgpa~xTq0y$!Dwwkp2tGA8rB?f@qR-f+Tv(gIc?TcI1mP<@HIZ(4>@)1mMT_1u;#JQTSjKpMixLOdJ!I)q z#TqCk_cmZkYnuZSC(s+OgCIRi(O{r=egYB4H~zvblQ5A$87EM7o3~97R3VC-5bW$w zN7W!zf@-F5{Cg#R=<1=gKT%=&T~^n}7(+n;r_^y&>!%hgy-&Ug9+C+${hSH%J1GGS z(y-maHHIj2L}OA%B2=@z3wWMmnt-tvJF%9tIbEn^Y`n8A^J-J`Y-mua(>ni~=b(%;S}VEqm0EcWQgnjbb%N4o z3)%BB)7Fozq!yr9J@;P9;N+am&-L)M>A4Pee-qjU=D8qAQ)k3;B~^S*M%4hdIHr6n zAT?gl&OjF!n6t(#(-^42KeG_(BBgk`;M|q|s$EJiE#tizRCn37fM-lMv2K`&Z!DRRNG5k6`X$OVK)p_^i1#W**?G@S z7dQD&sG}BsV!T_Y1lq%{<0k;)B6;WIE~v%2oq@GcguvPS@C1$Ihom7?35+mzY_uT7 z1C3ZNlRWSp)~W{~@XDgiPc-7}Y{jtr>})~h+v!Z_njshDmsEid zFgE6uK0O^JX~wir5lQePJQ0od%waTh^a{Kr57P7>&=p7*tMS=%_YrfD2wu>YTh-06 zx#w}_bv91EKT3UD{ab#JzSrUDy4p!cJUeLe?2r(3nn(3}jj+!3cc^?(=4p4EZNlI zfWQmEQftKDI)kO>opyYaqZ${=rdHv$Kh`rltBn3Fr=jh2jV@`_#pZFVD&4)FqU^#W zi*rYXj-FT92F)V9^C7ngl&f|vxPG85edK8!om_lUE1Ubtd=V{PK(7TqeJro$)xJy+DBdX zOXQTkuY`Su3^Mu&Dv)haB|&n0=nSce<_ZtZGvC|%2)kL?n2c70SscGAH@XfQUxmXy zPSh|eG)#Cs@>tz}#lpu$bykd`zzADtN%_l>V!+ZHC4O7!iNGKs{2Hc|QvkQi{9yiN zQzpdhYqhi27sznYy`EqVMpS9xG*cMigzXs^!@LuSGG57af4OQ=7#dLz{y`PopA-8I zG(tNhYA7Wj*Hv)qm@74%WNL*>ps2<(B=sho?|s)Y+=5!U5#~GBYJ&e+ISCaia|ZAD z=MA5qjBj<5Dp#yf=?&3YKZ$k=BIZ1Z3Q8%Pxj2T~?AnKIs(NgF{lWSV_#p4>&FuH4 zNhK8ID&JT9_BJ-LhU%b=_l}~+>87_(^kJ&^z7&C;h>d54MbaVYHX_rFLBFoYYw#CT zK%b*F(uT(&*W}y64|kJewEeu^1z&O!b@7vlHD;e0apU3e4)WtU-6n0$WfK^#S@J#$ zIf9{n2idl@-)j~#8d|^C!YrCr>Q@Mtl|(a~suyxhLeFV?bp&f-hwi&#-U4MIeGN|-Ije=?)$i)5Yn36U}Xz6;x(Kkw5J>KqX#uq{>4mE@%gLg`S$)9ob z8Se>I)1{RKXiesYio*qC@}e(i?>BH{A#Z6vP_o6!mt@@zyx;DUPy%ASf_xiF#$A0c z7&e-j5cr@m6}(PXM)sABrI@kN1LOo#`8~N zlO4;X)FDZvi9#|9(__oPJ`+*jj4|=`a15(uNkcT6Z%LSFNbw?7v}u=UL5*YZG1^;_@izNqI}Jn!;XAiu25YLC4TPDvs)a6W zLPMrhrJN(*LYP3piHA^YoI>VSY#(fvnw_18{=WdGKv})g#lsU zQ7l%-7mDOOKVxHinG9`tmv6PnUPsqqu4C>P*?680i0rkxjGRW&HOZ0L?%dgIEUr8+ zBl78GvYtRmjS`~EgqNAI%?im59E#LAa2jC^7>((Y{e+chk>GHI5~QqSZ4*xztjZHp z#OXXjgn;Sl(zC+@IHZ$U2Z@Oxig7vu+eGO$%ElxjMj}x*j};ncBP?aCu$e?oNVN7d zV;lhnhf@mS1kwmH(^=cKtG7s&Wo;5`mu+EJm+7lm#VYeNx8+JTO@ZRCfmD^=q6sAVG(#!6BkFsM;fwgZP z!}dPL{r7F;`spLQdH5{3z7anAxg8YVco!i+Ys-!&KEsYZ4_DOPrH zq6q{fUI8OYq_TuCL3Vl5gcV&`&S8Z`8rS91i|krD1J29*|2XSh6@aa!v z-(MW(?4gr1!+xH6dMDB=@bssj1wdPPdfjHMxkJeNRY%4h%LM1kUApUP$gNd4<1i-4 z%wDX(nvBq9v?ERuI!R0v$0SLDbB@5zQ7J15xiZp|g!vqWLV?f^K*(kk98S779oyN#cZs#YL7Hb(|Gc z%4MpxDurADB?XOUlj*5xTJ0vD?~%*p2*QxS_d!75W#fM5ELz7{onUMND5P+BN|RF- zDPt@KBMTU>Mye_ZRRxjD#`q@5I0#)xG6y_u4Z?yl0&T%rh0s1)TcnE-){^=TC&3}m zp2TX9M&S9G3{Tu8G0dJzlFjZjf~j;MtwhM|f-VyvSIhV+R-pG zr?~E?arcwuKLL#P+4rSKuxU!1bRU;=Z>)s@zVL7UZRRlMOy)v%7k+)b;@rx|a{iYf zb3bz~v&5ylL3YS)5jPlXFghhoGg6lX!XQ9NNf_oZCEIl<6M8Dw9U9yW0p3_|AdRSxF@ zV%N@0XSykFbR~oc+l^Mu^zLvcctwgkAu@u@2nQ=!PF=Ulvb7KV?pLvjRjlHJ_@#7; z(~17r?`59i>96rEEweSHd%M4r)+G_b!->pd)nU3txl7`52BR%nJB-Vm!FjjX*TDm_V!Y%)i6cyt67(>G2KIhMM+CerbyAia|VGTRUS?a;>!Vy7a(Pb#LJlG zbe-8GD+z1e+&8QsGmpXHY_byEVnqtVA*@5nWpf;`(q=?E33x8^f)}>?XZ9G?^>BC6 z!%44e87nZ$1V9{a*$aNfJNMUOp|pxstm3D{&#j<;tc%=F3+H}~i}^ncD>~(6qnWPu zds%WNj6++Cahc6)dRLTbmZp}C#=|gN`B`OM)?5bRN@uB4jZPDa#Uf!LW4tGwn7O%G zTFoYfLV>=4esYBZ3$t@fPEHU<5rJ1_r1BuMdKzsf8C_{XK609m|vV@`?me;-1G#Ki`R+MHYHWgy5LmcOt!NRJWb%5 z1g?$eIyj|>tR)nC*faVY%uNSejgF!6FVlHc&|%B>E`aBle^vI?v6#(-R?Ry zhSX|Inyfg*X>CY#LK4T>c)a^`X_{hE-3@F3sT5%@%mP;Y0O>5Pr8+a64oQ-d5A*c) z^%LYmI?XnhE?lP7Zc@k>$rlTFF5u~n&oWS0M}6&0W|~t>jGy7+nUlQy%A1@%ca|s> zC@-Lsr2NnS=6~UHUw)QX{_Y*#di8yt|M7SD@^in(|M>g=ns>gxz}rV(Vv(a5+afW7 zP&jhZ;dxC$(?r=6i=eG7ebt9Ku>1EY6)F@PFgw{~Y3MM%P)OlrZ1nCm7TuMCS&2Ot zD}gdrSj4hCQyQFfU6P{d?!paHYNSk%GQmk7gqL;fBAc*eGx!fxxs|<@xa&{RmAQTN zyJ7Ce1+LHeJoMlYfBBbx$1`933eky|Sm=F#y_<#rn7VX`8+I*^>{$Dcc)#6&jq4=B zCl`D~+M7$6iC`d^;LWQ|p4h(&fZNwj;t$-4i7!3<(0|oy`Y4=u z_gyL*_p)jD7n4e-7q4^d@(mt7@bpi7-1Ozc++u`XBfaDU$Khi~+41n>lt1!GTbw-h zCe<}N&?3i{wZq+Sq+CCKm9-DsrRsqxE9Omq-A2wX~B+`zs~{fu9Hk8v@|fo;Pd@l&h#MMC}IAO7JV-u(q@Em0H` z=5jyfQwjXQ&z#GoBJe$ov3Q1r0(a~)Eilmxy)I)_k_z>^5;eyP~*LI~nGCZEfF z??V7ks}nPhF*K*n}6&do}WxiQ%uJsYpb zv|23|78j`3m#8l-u~1*4(aId+(o`dm-Mn&{S(B16=A%wTJBq+UrCOub+lwz1jYfm% znHk2%r_jbxt@V)47m&VBUv-qNBYPPd^%&jS$Kv%WfBYZ+6aV=K|ADcw85Cj0W*0GW z+~MJ!pI~?Y0fNix*p_>QFMZ|99N51H*Ot`EIr{A$qOo2Ug7f6Vh>}VPMT5Y$P*|)C zh(wW8u4T0LJGk*)+7k_eQb^}UiTUsygwrFD7ojYBw%v>IV!1Wlxk zP_~J18fS-)wt(k#P@X_ZKg;=*9!lnsvb!%=Ii$++(q%V=Ei>Pdc%DK?4nYzR)Zyu$0{YLWb1}9IQW~Q!L7(dJHcAnv$0#nzHbM{u9HA8&};N*+{p8xp6 z^E|Zwey$yUi<37O7+t>x0nA-`lmG2&KVbWs8rQC0WO90uV&7VN3KCz!sY9b6n+{%~NPA+6mi5U8n$yQ#qqgy3inhUA5>hJ&CdcO} zma0I)m9rnnn^3KhUb)8jykOIPLlnK3_YPeoZA=h^1L(<1ym$5{ z>o=^Uxv+p2`Yg;X&`D#WMuX|aMQq$)i5@l&SDCtUlrgu4&6PIq9XUm5a6NkBD(@XX zPkDF*Z)u)07cLWZ9OG9{b7j86U{9W#^DTOcKW$xE#ZQ8@7Ogd5m>uQrO+gm|;bN|x zzd&wa4S8MX%!OOj`g;jHL1S`(AHF-yhF!x9#C5*;_BiEQk>Qe0C>*zDBZ@_zKv|Al zn&bG4W^0enp|i7`TCj}P6bnllUkMhQ8k3})zuBOj8jf7A(-De3VR-%05`}VzYqa_C zg$BiPNGXsX+u8iQaZd`mwcYW!=@xX0u{tZ{D@HQQk~O=+g2!=Ja0^`nn#`Fk(~FZy zqj2C!3i$#()he}WjY_FRK9|qD*1LJ!z8{j$u1ZjO)9iX*~& zf!={W28V~}@9U?3V1VJ_Vfv~S2CD0+&j^B0(z7PSr4` zySCkj@-!m#X}3Vngfv{6sCpacylmtyx_j#G;f3WGS-9&!laUjwuwW8A5#uXKRsSB< z`tOp`!7E%wi5z$yQe~O&LUoI?R7mN86n8Swh3pn?1v2AANTITj8Yz)Vp`=79^?`!! z6$@NFa*f5#0=-+dlHRz18Z3}rzs@(l^9>HYHOb)me)5GJSI!(^fjonL#9MEj1218E za-OlHCpmm+j7yhJGuMfk9lJ_iH2KzF{0T3v-!nFn8=-ZcdEx&i4*6bl`s0)JmNA+ZS213%vN@-Q z!VkX7w_bdmg>jg^`FDKduU}_&DWb2xz{S^3AlT%@8);77V7?hMH*<}-Ytx_tt{gjrT(h12vSs}CElhi! z>(?)G>vDry$#CS@NtRC68Cy8U(G!!*)Msce&U5_SIsCNF53kN~;J&SdD?@1ty90~c@3Qw#(qKfuPp3QIG$ zsHcweFMplCc>XQs<2?5bz}No$*I3X)$m=IKHLK~IJHgRgvrLbTWBq`!mS*cv@4ddQ z{#xT_R?vMVxOL_&-aUGbv!~9`2>RHuaTqC}zSySKHnfz&Yj(Jl3QD16b~?eN9S+?} z*t@C9jf)e!ay4Rbpve4gho z?e*Jy^UMNE3o%!3&2v4?^YF$Zq5Aky<J&Se$SKxsXDbC)Z`ar%^(u-6l@69lKJBFbv7(b9jDcCX;kJ+`4&-E7z~m zi8|D3RfdO#=_%JJ=0b}39Mx)#TD3}=B>eCP-^J54wyfRH*o7ATTLT_@auZuOJjBay zzd|!!B%dqcc|Jkj=g~cn^XwN7(zCxTC6=mQ5)o+DQlgcXTjRG@$xa+1ABPtlMhnOHJO~M6U_;} z@|E8ps9)itS=%Yq-@e@Z4vfV#}t@REr^A zI#17w?a*ahx?`Zw9XYa5LVM|tD;HNNyOze-Quqcwhp z_vecI^DjR|=jsJIeS7%LPw(NvODAcUV`{_OSUi7}ptc6lH^S$hK0wLF%#O{mIDdnr zw1elKdYH|l>nR1AGw)yF>9712!)BVZ7sgn-dmCSP_AzeVJj&?yy@>i4(^~T7-+GSv zjUV#HdCRr~kMqfgb`V`WL7``exE)i;OLBc{8K~6Qv3&~*twlcj8^1$m_6EaS@8i~~ zcVT1~2ksj|m=4!2pT+Y7ay=vThE2Zv-YpJ3{vhe)NjiOb9(eRK>>e>3|M59CKlmx0 z{q!b|T%O~h&1*q=RC`L#=MD~Y;`BM%7&f0D7z)j zU!Nhb3~|uU=HV&}NA%gO847#toYjp|h75c@v;D7nc6c6|0Z zw$z(6MTPtBTT3|?+^gH^D)?CybikrmxAlIav2#SVoqX|=d$FlTD9P+Z9g{DxFf+^f zX2_;KiR!Jg#dUb+X2iq$h8ZeIj-Q;Oym6S%Z7*@=%nbJ*SjT~U!l}z`(vruTvf}z= zLMf1J-aN=dn@iY6i;1Z=7v~ZVKDdT`8_H}QDX}nBM;CI8*1TVuUHv^N=+2Vr1g!3U zB;7dAOm1bD16W*U^J=t3o0Qa~Xq}Q;O=5I6V8x)V0qJMvFG^*}U)mBUF>xB>To&i) zg^FT6PZ)$aVQIITv|3H-^(CferfD{tl&V!WY}mk1Zy)Wb&0=!_-w){P?W0&Jv9vJD z8?V01%WuAfa0Q;&@kQpw4YvR>4Wq+s->{DZyB;K( zah&;%!G;}b!ydVsLH0QEIYqADQRoe!xs8QnoK6^{{dH(Lfby#B+x`%})gDahKqy=U zxuPH}!Q5oRthxy@1}`&jaZUjSB|#FfgO3urIgX#d#n9eI z**PRQaqKER!`m3yIKsZ2>pAkyDf+hVptsgT{lZ)P+20;#_v8Dye)xI5^P@BD|H>EG z?x%e7>n~s{1MJ?t9V>hWii$Vhe3huWnS+n)W9;Gyn%ZOEp8E)-N6t;~=U@9K=VdRy z@zevPsiRiup&2KPtlfmv9qN&$x7JT1g}s}$aOU(0%0ru27h2vse}#dOEo@ugN4{?@ z#pY$+KYNXVbsHJz?WNUNVB7usId|xFdiQ;b4b=dwp~8U`dHIbq|=x~4Qyq7HQ~gm+l*}7iNA1( zOA{&EcWz~;-P7x|-iXDAN$ z@`Vxj_ThOt`H+A0*Z}8GP4V_v%>J!?>?lb@Z;_s)&DRgl^U$_F3Q3FC#tl#H>LHBV z{LQ&0zx~(<4-V%@^e;z?E`*?7U!td0`^chQuh+@v^MCJVeK)k$bUH0;8e`HJV-s|m zg{-7HBe>~Qlj``+=f7UgJ*c9tDY1)lPO;XyO2%D1ae!Xlwobz*d zN|5S=G)X}U43?zRL8pdNsYtFuI3#|3kv% zBN2Q5JI$V_OC)2O;`iRN%WJ!(t1)(MB*K0~N{QnBe@m zl$m+W;G@sex9t+T)dw~r=Pw`wh0Ob;?F}eZ#4ooovJ9S&6duY8kSaiW9=;b4s1W6Q zC|?oy0iN%pq>t}=1VKQdQ22+v>8v5K5O{aqZ#qp-et-bdRO9CZV z{+>Vohnea(PX3s8PDFhE_diedr?&D>IQGulTse1@{%w!($tQRIq*q32!0%o&W6V!% zD(;3(62$VgHx{KWzQwv7BYb*G zktDTvN@l*_8cI1uni#YY6nu#_mPkVwbVumUp^Za3$cGBgIht{1MT*Nva(SKGO~ zkffR6iRWi=w!$fluvyNT%u>O$)=1$IhCW&+EG;ZjZ*?fws;nPbPpMd-vADp+i|09a z{wy zbfye9PC~9^scwhrX2tYT&7qg)DXmp(+*qJ@LqJcz#CAg7I)91l?bnG+%C0qgcyjOK zq!w&yklNvCg(x@@?MUVf#Xh+DPRglQ8< zlM24j6lwxrST0_OIsScxC@#|X*mZx79KsL43-DBcQa;iP@H`*i^YMMZ>&EBfc|T=AU&TLJTsd$J(+ zZ@x)G_VVec9>M?BQ$wzTzhBUQF0!>{36-@D4M+){NV9eGAP;UTkfzy8TpNoJf}9Vj z&Qi8~vXeA`?`16SF!WR_Y}mY+LcTy_VU{z;&vEnSH5Ti02w$*u$5v{kK7=2VnwUD* zXy{U2J5&leKKscpa`p#x&VAET+pXB}DMz^&kQjrtDSz@OSD8B{ z*u7^XDuu;a$>c@D6JL(mzjGtEkDSBln{4dggz!N`AYGL$sm{>W7~3<6lmUz5PcnXPCz~I68Ol>6wutAoQD}K<>;M2D07*naR4PC?iL?RI zI-Kef6Gb-Sx6AT2>$;LSg%H1r@9R~37+ZE9_{aaSU*Z#=`rN&HF{}6|fnSJIoVAW( zrNraCf+R5?Fp9Z*DDdF{!AA`YKJxLu7WX@GmxEZQk#;UC1hOlwGSGE1b4Wz@D>2T* zfWb+N_8d}Y+GtN$%7LY-A`l%?D{00a9aF&AAY-^I4__(LBw=xBi9~B^wHkeW{RF;8 ztJUQ8jhoEPPSfZ#aCya0X#<6D0IvXH;Adny2~iSp;_@3jwdwcqtA?#lI!?dnF?L+D z>2XJ>LT(;wa{9s&pDJ#lxFN(;EMZO(M+V>JdGe99YLeU~Atq|(aGvIK|IXw7 zjSq18N=#p0Z$=b!0*N4(S6n;a;En%m(JjTz8%<1FVRUzm(#{*?=7;FJx}Txk2!oFU zXl$*QU~><43F@<&RtIW*5*u13&m`oOqPzz>9gq50n>96`bq`;H(4&3*%Pd{q&Dx#M zQ|P;e(;iB6pgBa;+)dCki`TONQgwIfS#+!f6z+~mTqbn?1-#c*v5HmvQ@}6G`qf&y z;?=&2djqK?&WbEq%}JcgQo;lVg#m2F(w06QXNiqPCk~?xzDe;#isx%I35`_HN=meh zhjAW(ue!TONxRixVQGmtP3WohQZ83W(uAe?dD0}tVyX1@Q>F(e9I>sl%|1odj&MGB zo}BM>?Pxn3zWfq{Vn3VL-N(?VV`PhC^03F?b}07x_yxh_)Fj8h9}pHcVji{(uJx(c z9Xj#oR2rCimFbHig}kOREC_QxmyT$*?p;HVcor3USSN4>L?AFibNo$@>*rFcy^gi( z`moxOuLvr)cCfxsqj$KB*JBVBi#H(f2cR+KSiB)2Ra8rsz<0Ff4V`gAZN#VDaNInt zsg~Q+p1DY2bPRp|^DIr)=zZWD6bEl(ti{78kPnkg?Zy)hF>oc@OFM_c;*sJZ@I($J z1yU>*^*iozt60SkEQV=#q_b1ZC~)%^gxM;H!IA|EOF+g3HCCAw~#VhmxNKCsY!_D z6V|Q|*z!3Kn|H|AF?CVWS=8)%t_;4!X^oT+7A0riX>sdlNVV6aw`Qo+1WWTCB2f&u zA$m#@u&9bft_7_Wix*Qm^^D}1t2hcpXg4g)wqWqSJPTuju@jcmZ_xYCPLdyNBhNlV zW7?;%?}tRyIY@gcx;%kCh-mI0wN3nW?;;B=ge%~oQ7*+7Iee)QGDS#*5+J%h^qD)| zD)DF)t60U~$31k4`)CLpoo1V$SU|YD3#!2PG%vn(n~tAnO)aE8HOKc)H`y_g z_>-fvh7&-ZAic_teLrkgq18IhfGp-3)YAk``JbMwqD%;BWZ zP+va-13lz&MSSUzrX8Xrq0{NmYBfosgpONe@m!7PwttxepIytsV#@Zd+o%-!xOVe0 zBKG;}!9PIe66WVx$j(}ZH)USzOP4gg>pet|W&F*Qq6$bO0XBqYNU~^A?E*GQNCU^f znjAwr1*J8DTv3uQc#K_gq&A{|#1geN`OqV=mN&m`2TE7kU5 zAN*40Dy?D_tN1zMXQo5{IQX8&#dluf_$|%0O~aXH8psN|;mwz>bIt}lwxzE?LFkCki^=B%fgch%pP5#k$)!B=u}_SpkW&jjj={<>4{UyrVnHx9<`GIs|5oTsC|VPSkpqgUi*4+rqgX5u#TFMyq;?eh zd|X?h+ZG`lp_1eV6=BJdFFOL?F?-ubG$L|Kf+$IuzTjB6SYWV65R@IM6^M=?EDLnU zF}T)I8#2fthYdWEX@|-S{JbOd1%6l!b7(etqH-zRma#_OR=xX!QWQ+scpKC19dxl~^Ou#wT%t)H$?{ak_)F zZG75zDkcalLgq*kLn9WL45`8f7K9ie|R+9{j9qecC`p-~5 z-)1^V8Cky$pEfs6q?Gz3y}JXZ&N$LB$?j)*c=zZS0U!uzFIsfR(YrmR)~{$=k4D2W zaW+9#6+N3JDzKD>9V+KBbxzQ1wFqR6#XvEzPLM1*lE_hSrS#&F&kLNk_)SN0O%U`( zq-~F|2+lWnPLi)$#;<55u1aj|NERJYThdy9Vrtm`r5t71!|}Hr@s1}ffK)|ggsTqV#87?hfI&n^4`^mPaWLA%()v}Xhf_r1})&V<70?*Lp-;+%*C^}xe*0? zJ$5||B7+RFDBuPTlj!05Xp-{m0J*1LY>u5(YTF10)iY{M5oZZK>+rP@X{Rxwc z<4m}lG+Q%_&5tuRdkqtNRM#aW-iy3*q{hI_9rX4&2G+yKMh}sMTzM&?{gkCx@W_`0 zH;y{C9Z0DR3Z_p8`t}v*+gQNNCKQw;@hm!pWLA)?LB8c-B28n#v2ezs3X0;O!^RRJ z9hDt=#?s7!+iS@LX3~^JSQ-_Jbp?)Z(*AEGw0P5gd53i=VaW!V< zd=6n1wwVwV0R`INgWuJcm7{1NA?LBPh-Hh@bmtT%)pZ8#R33XtS;<^acTu*zV zOtb(dZIG`B)^91GY?&l3U`0$5jaTWA69$}*Mc{;6)-SAL6{}dq2kED8-;4x87$zoTHgBmi(KI}~t;T%aaA0SZGpA;F{@fCxR$%X7$P4dH@b>LC z8~O|MmwYasnc=00l*_Xb+eS*9y*S0&bCS<(FLLPQB3GM+W+&z5WP|r-H5cY0HV+mk zdcQbt_j{GLrc<-BLw9!B%RL*n8=C?Nq11#@{+k;LF| zimba&HJzw~NfV@ySdlqE+1OyPcws;>SE3XY$@jv7yUwAPJQ_1Y3=CD+xnna29^A!& z{g2R_+ep%oEY%lL)k#YIlSH@HQc8MhFC>hQ$K-l)_!`D9HBi+6844E1pcp`TgP=W` zvAlEnkmeOlF7(Lf{LF<=c;t!#p%tF;(MAx>$4E~SRy>5Y2qd!O(VD4aN-4RX0Xhp= zKuwyaD5J>vhJ2t%b;6=|n&$ayj90MMSWqB&^Y+M0v)k5vx-;D ze!x;fOJk*h^u2eh;BBCzkg%EJUdL;>!43X8KJ+zoYoTjkgi2NsC;DgJQw$Ck$ObO^ z#wz4|hoSB)`CP!xO$EmL^Ne+580gBfcc{RQfdYLc53LQIy#@A-6xh+5p}Ue{*QOGC zMvC-RGi=*bVpngL;m!;@M=I>HGU(aox;Wk`$vij99G2+IH`!Dj&N`s7vZ`HYtgnz z%hti9Na2(13=zf4oPSMn`9ud7&ZjI)#|Si|oBG(hb1#GCotUV=n!QYvnV=ADq1*34 zYQy4Gj9>PVXl5=f;bjAa^l2?Yx!2-$N|FUZwi8;D0vd*lA0PsOjSY^JIKGQj?N)6Q zCB$`0kXN_?fTgu$3H$=tOo6ZtHg1dKm{dY&ah1iF9{H}6spOkPQzxl+*C>=-MsC~0 z%4*EjR|HCX5L;{vEu&bx<}kOcxwg{a%IsB=+B~27hr?{YyNmhLEw22TCF4svK5i-B z?BS{k;cTAzYD%LS(+ubFgBqhFLnu*Zb)n9BGo)@7@T*G{swq-tup4GEZ@JyI?Ni?w z&|9q**7BZXXZ^}~4*%#1qk9H_@xhw5m^gWsLT@+kTXhszU0p`I1q%L8$NA+eXSfn( z=*|E1+}CR>v}A@-aQ%W{b9tFGO_{!UnPP8+wfQ>bj`wsJ|An~v=8M>32gkqm0=b*F zlKVNQx_s&cuN_)s^rpUF`dBx(!TaGu59n|0)BK!DG|^USZMRn0wu+qD_#y8rByEXT z*Hz>^`IG%fDad+?oTu9TyGr7#_PV_1DssNUQ*9@!oUh2bAAPIO^$F-HX%eAL+x|2) z8eu|6TS(EwktwbaXkpQ`fA4R3p9?gS6l+tYO>ks_r(#^2fK9L@#A%96QxFMC7y_m7 zR14n?iNlEI3put1`$;<@TDn0PhIqL;rt}6&^9`1+ z51nWfE>2#8Yw?u8qDWhiCMjM~;N>hPQixQNtO;xr(nO$*KpF=fCP*CU`?4aqjxOZX>(rWSShq&bPw6UESzp)8E-bR#TE+%Tl>1jHbtw=T zENzE2Aq5J7BV8QF!+D$j`qitG{P|yggEvl{W@LDnY&P@GG1FM#&@(Uc%(DT31{kI|4h>X$B&>*=O>`6$2tFa9-hY%jGpU*@ad_z`|#gn^zM z*cL|~f0FOKe2mq(YdrJZGi3V4aaPapM_+v$UF@go$CxDI#G$8o;zuvym#aMg#2@p- z(K;BA zra$rM+EU`BZ+@Ncy?Bg~E!&8%p5V)0`BUaXpZ-Fg^|dBbGbdQ2N?+xje}@~~;Cl0s z1oU5)pH+c-FXgzmUfXc}e#h(Y`1h~)Tz?U+Pe8Y%X^J%(YZH`-k=Ecy3(7&`5F16B z+6|l32w@_uPDqRBX=;#Y*rc0!2nxv5>SCJG)p*iEAe2ZwgsHmN1aRUco4X&?H zzC=0#B_+}d(w4!blAuH36~PsjXkDN~i!cJLKpJo{xXK}$ckuJ8_`MOAo_&$Q9U~;g zENtBy2+`tma4;~nh#Sb>EX1#4FvWWS5fdaTSV zRxcYCCljt-n!-9Ywrna<%47*!hS|jhmg~#N%qm?YYxE6=lu7}{wEfZL#;jaQiIUEH zR=L0U@+LIzVPU6oO$*yIlC;l|1+PV zyzm+)CMK9|X>QrGljXy2((p6Xzjl>OS2rE3F)`g>a4^g6{rAGe}fxbCqDEl_ZwVaglOArA}msZbyVBz z#!9qFu*M*?MF@+KAf?61cI%TgF~Y_hvwKadHO5$!Ov$SdYZ9!J&?KfAOX5aAq+PUC zpd6HE>3i7nRkB`= ztFMM!IXy#VAmrMs-Sqa}MQ*^*$UwojbR~+Ykzf`B2Fk-k-RrE+tuy&*z>ca%q1Ui} z+2ZwULhB+MDURckrYWLj@UjBs3Y2a4lqXR@xMZoHwUloH6$t!%O1c8BaN1@;j>RS$ zYts&-DO9`ibY=x{ZIQ~RhnYC!v$R}eax!LVy+)(9N-JI>n~`Mlc~;k!>GBI~DVC@m zcd&&VY0e*0u=I>d`bSfOEJ%|fi4v4! zK{`ll+Fu)jLeTaPdpma@xRY-`{#WF4d3N7?^Sj1O97~2fhuJb)tKCMIC9 z#g?7-a`A3Q%WE`0w9x_|3;QGoTbkk(mhXu{ee=O$cIQNUF|V(IG`W&74%URkTK zsb`s`>1AH}{&~8`_n_7iqSW9!mcFquwvTpkY55Wb7iMNJ^8DGe^z7QsY?2Vh2CY+u z$2T#)wU6o9Q&=FJo#3@sr@7_Uo3UX70O@21>>^W>GstWgJ~0<2CSWO||CYPSs~7me z3(v4e3{lPHSzf)!>~f9NDRcYoGGG0_pX9T@_q(jEoM!957H-*b6J-e)g|aO!oH@@z zT~qBVaQWO3=E4-wv5ihoF|)8l5T^L9%goGW&bL|wxiUI#a%N(JxM9d-B|)ab^5Qj? z)<@_o=YEBEf*ai6-F&1obc-PH930vXY`*7!F&KE)TDOJvXN83gy=%{NaHJ(Q@BTah zqPl{0I zhHIxS;pHOn)quqb!RiH{qA0NS<{X{lF-z$(b4wB3`2s!JJk2?nnmO~5HwysSgHLt$hJHX)D- zn+Uu>;g`YBT38gMSfnfPeE~+`Is(rZMBWm)-Wv7GRo2vlOwEpP=Hxn;uB_59O_FdO z(mDlYDETE64jJLGV@DqyJtb@@hbU@tRf&U3eKlroevbL2OXPe>F8YLt{%Ey3E2b#m2qwUY{0xhUa)6iCNGInFyA`GO!|@18x}v}@;kTA?b( zC8!iARP$uJ``I$q$+1I6$ZxuXyKmoy*I8!M;0V?JZU*}Y$vOhz1SIhaVPP}lr53N8 zoZ^m8e1?It3n+qOj$&7tN-j@VrA(<~h|aDe)5~@C?Yo1qkzT6RE~@1+g?ye&vBFqq znbyoYy}P?vU2U>=-`$K4b+MI-pB3Pn1A>3G7sH5$Vjdo?2@$YuPdbhlkmjL*Kqr*+}G!W z124QbLs;$L(XDyX)Sv_;23#db4cMr`pS`feT?Ymj&1(MS2UBdjZHU`@T~Z@(qB?)@ z)ExKUHN?%G9$1Usuw?Z&OrDLlB-cra=6cwtCTU8m)ok0RZmglFTBB1zXPI7tb^gissMk~nD*n+DbhzHVpo}bwLoPoAlq+Qe*>DU2I(o3u*9*zOAUpBWG1>wnDJR! z?q+c{C2ZA6lZYq{Nx~MimXTJIH5rt2$T?XyjaTX4+JSKd*0C7J&|0vp&aX2yKTR`U zBwvs?UO<@C2-Om~5zE$F%M5Jp01QH>D4|hOw=HWO7e~1$*F!lzj+;R$58*gCUV!Td zWP`tZ>$8hjPIBs6mirEjeblFNgB$!>@S)qMN+GB%%wd8Ky2?JwbJJ+AgL2VFnUtrF z&T{WSFL(J7j~|@n_Irm3>me8GmMv9}t8;bgIE?oQ4!=2%YzSU{W{IAi{WK@$IUT@F zRi94Zu$Dk?PH}A|ph_FW2!l~ztiU7& zs|^;>w&t{kR+`cbHDN4?v_q06s3gI+5w6lWj)Rgep*1AJ;yD={8)LBajs%Pk1)RGy z$Hmvb#)%8xqI7DQ-GjHYW6P~5T|h7S^k&BB9BeREpJ#Ud5;n@SWyf|1Bho15#EBCu z)?&JPH&N;BV|67YU(S&&1>~)R=PTByHC1iMcNn~qB90U`5Xc1b1&_tqCBFLfSNQIw zKcF)|M7iTOlCu{Omp%G>de{|j!F4>OugP|J?d(NMQpiYjORyLw%&&H{bj47Qma!tj zaa@dwP;nC-H&7-ea57{>KzF`K-=+>q<9Uz*VIgh^)}~?>=H{7Qxr*>2{EVdDXwq80 zL~%qhdPkX&om~_%S>hu*eHvij1b@(Rk8TzQt z;s!VP_2pL{(1lVY%ac6w@*%eDsxmQgm5+Vqi*#(bM^$~v_g}fjVm6|c4QS0S@Xwx| z<={-nWA~48cQNIEeqx%B-_u8DIm2=*;lyl<&3!Q=u7g>r^ZVa8!~RVf4qs0Az5RLq z@Nk`f`5){gD+T$CgChl?nVxU(%I7YsWB>pl07*naRKhwhbailNrp2Lojgg8==NI`` zhZiUn9K2$gM@I}_eIq1ZsZriK$p7|e-@9yHKQcB>LWqqPJQE>JiqZ*58X;_g673#o zBPBNRvAR7o7uq1SLF)uki_`*7fDsyDn%FePO%0BTK|w@@wa7;sh3gvvuYJ5$;?oQr zqJ|FYMP)} zpwd}k|Gs@(yf96(8B?F1rny?8u$;v&_>_tTilrR&ykK=fQdl+QY>cNASLQPzw(1G>tc3~jBD8OpY^PKl&G+v4)XG*@RYQcvbdM2pmG zB5aHDXu#H6t8Cdni0|bfHMGJO$_k{CXaNEji^15W-L)YEMp!g$>)Ushb^hHfwgA3B zLBA40yyX^rgB#r7J@}Oebe*R3?7W?OqUZUm=g;ua-~R^;S3IJ`qOi0y+&bRD7fTHe zUs&MP6Z1T?BKXG-4C2`)hc9V{DuRnsO?tJVyEn(K?tnYDSGaa~iCb@~vT|vP2|Bo? zQ0IUBUY!T;9HGl=YjY>s68H*K%9Vwf1NU}u;^;hI34Q+aN2>fkKb~i;;^LPw{Da*& z{`Y5A_~v}dUH5Edcuw=xm0vHdSj$`XnFfSFN)M|H%2=!gBL!ADq$UGGVNDZKiNGO3 zky^lLOsdgIg3)Pv4LPsKq>?0tX6(|8pp{6pNwLacQWs-<64Rd9E0Iv~V;ryDA04;? z={gK`Zsn$3k~3GAxOVw4FJF3&AHFKcnqEfpJLt&`Y;?Gqre$wRD=)uan&**n0O6+in@9f3%x?;9+7z zk~R^-LnsMOd!4+U{b>g)D`i_#KqxE{gGE`3_yCWSR;R>~!O#BUVl;o72v^rgoq&Aa zzb^Yr;t(r6Jm;ftg}uQI-p+?olIRq)(b!ZYb-QiNiuUYK zNd+P$wTe{dNo0(2Ta?|94pO9$B-W)C32LDv))M7N0@olMi82PTtR-#39Bsh!9Bvuu z;*M?oSQWFd*x<;K^PHY|otI`q;w2AXm*@_<*;KfRxy1qUg(?G`gN$@lY4{$~3kz7A z;OFwBjgVF&By=65@8aV?(?gd$wCAwgh>@aB!OxOQ`)Q`=4IwWvi`n)PVr7D%4B0h@rV$Ag1 z1gFox%(<(lxZ1izlC9CTEyM1QZ|A`Mx6|8GMH)>Uw@6wbWq_0pR!G?RE`-9tAf(<{ zPw_zsECP^VC5X29jQDH((6!b`DG~26g{@CsD=ol~>Ar)FlPyvqb)6#;~70tEDdCpB-Vd9N*)aD{GX^D#LrYk$bKxLfGoi~v) z-DneGl_kg(34%Ntf%XIzizgLIDyr2Y&BZ02Kk^(kRbx6n#X|B50rLG=k77`W~7g%4f5rz%$1X`!GS}l|^M6D))au}@)(A&{VwOYbj z$(89d9G-ZVGc(7y*0_w$X*TcgWB-Gj*s*7f&T1ENY)DgqP+5?Gn?brht^(-_Pzr^M z0)%PL#idlRAt2*PP_kV_>N+mU@lbAnbUj=zAe+hlWK7J=&hnkdze^AVeEzeKQ7RSR zF=kp*6MXC6J;U0t$HyP}5<}?}fBdbd7})wTZrhw8Q|aL9)k_rnw&1PI(zR_H(WS%u zH~;M4@bEwV9~fLa!&9$b=7ER5z@G66Sk1!ulRWjp1baXE8x-rWbLi#Eh)w(X%pLuF z_nVL7j^EDSqNF^wne{6dL3zA!@EDy3?xM3f!AmFSc;Gi4qo>2+{42+(?B0i&e4Q1? z=j`F${8vF4|ax>(ZZTTYR?*!7Lg{Ps#g68lJ@MF1W#JR zW|J^(A_WL-C`yk_#Vz!CMWSS#V`q+XYVuXiE*zqjX;IzM$Ne|`26yg%kS*0)h*k|N zhw99%)=7j0Wst5#I0lgyI0cF4C-_-`${1AE;N=Z65Lj1YJdln=356B5Ju?T|PXwY} z1i3MHe><-oJw~*OJVJ$14x8M?PYQ10dKdmd!iPIG!*@|&N(pQFz{&cNp1r5au3U;ptpXthGF zWXkN0kC5B^NyO|CPP{hC%v{KQ_m;VM@FjMQZDH=hIo4yr@_L4y$u++AZ@ z`K`xx6UPgjJ#~WS{0u8gYxM7bfU&iCp8d1$(=i^<*FVU`iPz}fwh!Ng965CiXW(A? zhKk&E=UwdR>)`N_1)lo$ak9U2Kgf`JW({550l@UpGep%qZyvtL_D}BM;`1lizij|7 zS7ra5cQMix^U9mceEK&(%b8b>bEF}exK`tVPweB+O9$EhTaS>;Uf|-{89w&K`$#XI zrZT*X?&u7srdRP6SNPG3FViuUW9jr&_J90-&K*3>6UR?+XkrGV&oZ`SJG=Vxzp{J6 z4Q>Fx2EsD>Z7pGM{jAN~{G{xAc}rQ+x4mdmja$s|k3LZZtqJu{Z+cq4dwXlzI_2*; z{>HaBv2^!U_(y|PV8eU-16<#)6k!pvedCt`l**78mqdERCc)SUv}s?jkZ2*$5{y-7 z>7#W*Dhz($kx_`Jd>ko|F5I+joSoz2)EiA!S8J@SEVH;c$I8MgGs{0QIlCD7f7W zxSME(8P=y~@w>W6Yip$IYcyx>0n+e#w zWt^@}MJmM%6R*F)>Gd~}N&BI}^Ri_90>0;w34DSeLxwDF>>`Cmxf0=lpV#C{F0Lnu z-4>BsV@)m~vzq?FE|4BpIv63^#v(r_VRpxDxAE=A|B4_8xM|nUcfB7)f=#_!xNX-E z2M--&y%VBVizJK~9>0^T2VbJ+-cNAp=_jdv;oocD6fy;}o+3&OI*w?yA}p%?|0>l4 zxi0qIw}+dyZ)5iLvuwG2AJfl2M|Sfg>{+?UbKicM@xeMLkG{sr)H2nsQN}h8QqHbX zC=apkzT4TgXA1z^_TS1AfB6>_cRa*c?i>s24Ki^|E?;74wZ`FBUSn}qw^RQr$ANqA zVeyFN)^q0k|DH9{JU5m=M9 zms^a1I3kiEu2cx8-O}Yqg&zbA4-c?o`!r?WVMQy!%1X2G^tHS<^rIF^cEWTw8r)n=15(@9Qt>JcGH>dp_s1_WU>UlM>ZQEJxM0xQz&*&D(19-M~!bi{tP{1ck$@0H&e)3 zx`#%oh>z3VU2SjVm*^cXvZngk)nDVur;qT!!=Iz-0L9IG>ET=X!I24uN47F{)4gP* zVQ}XIq{%v`SKz=ycQRI3;hR4^!oc`0whs@G#2)#L?%VY;xDzxSm_nLhL!&mO(P?z``zy~to#xHe01>~?P7JI?7>XQ}&Dw&xnG zZQI3;9h;bX=?K|sFPV)DcmLjT65k~m)Cldc<>NbP%Vv2DjO^iJ=yB_gE*^jCY3{oJ z^X#l#;k8!|^3y>Abk70CdmLoO;`MCfKt*!)$TRdf+qiYSpQF#6 zu1qd-%ljx_xWNs6Sw6%y^!JAl0#^yFZC9%Qbx0|&=BI*~ zQi=`rv|s&sz8|j38hV=0Y}C<7N*X7`aY7PD=r|!s5|St)ZbgJ)OrjICNolrf7+QF~ z#PcPsX2R-n1j@lWh9pU8h87*SFi8U)*HE^FQf*B>YuhF-i490?Kq5SJYKh|nEj5mE zkiy$Ae=#WKwENdx7lB0UlqikCT7&>Y+t$=t2?{*VrH~H@@(xDB^7Jax6HV6VHF{aI zwz$ap$`rnnA?FH~*XL=pmXSi^$qI#RnUQQS=hsg%Bb#J1J%mY}-f|ZU>x(p6Yn1#h z0#%}DyV;w)pHb0GUihrxVnjk zKhl6Mq{2~xBzbphnAL{q)fgGLY$`g$KNHm3Qm~3NN?B>8{4&Kw3-dVXdUcEV0Il`rBE2Ov81%Mp*l)~{n9M44=NaMI|#wz7T1vhQ`Qe#jS@Dt+DQkzVeJ|5DTbnqG#e32(H5pl9e zIS>dTX{Kw$c7=vnWF@&qNRv3NVWlCEE|J!VLJ{TqR0>6;7F?@eBP;R*s!XICRE$G! z)THQjplt_QXx7s;A{&u+Iw-ll^!huHI!9mwda{FbXv z$dw9sfsc|3n~6xA25a?ITFDwZNLf~kXeY+F?KN}Zf$%NH(RdZj&V7RnY#Js`4Ay9* zQn*UCI}wD3R1U7^{;dq?Ym1Bcr7D5@!JEv(^;JR}P%i%R($!Wcq!wJ~efq~+L!4?{ z*Zn)H6mWwNn~$(4?wv@bXfDsOux=UK+)olGKbc{BuHwYmY5tFsAzMl=kL>T`rk*Td zNK%XINU$l#CRZ7&=2%{h@G>4*PXU^#l@^(NMHse-YB6Emha}I! za-Gm>1}Z6eAuvG|DFYy-*=mwR5i*Jqs-6B7wC!jGX?G(KQj8te+tcpn16B;M8$mszpwJv&Wfv`VhWwk{Zt{{aaD>osGAx-Ld za+Ri8!AXl`WjB$nVcd4xS3O;0##~~hHH({Oh-{NUN?e-~+GRq!MjSN|X-wI7xE5Zd znfOSZBkOjMl_eaJ!*x1HqLhqlXsLB3!}H9f4rvskt%O7Y4#p-V>rIkcLj7v1E$oBE z$rzLmeh}ame57&_p25vJ2v;BzMYhAGr@KhsNR?`5fkGumt62wQQI5bEg_bVDx`+)M z;J>YuIy8}*BU%=w!tmq6{;rWrSo-crNGU8gMa?y693s_qg=YSj$h96 z$aoQB%&&(s+|ObK!V0XkSYZ$bum&SFR;3_QP$|Yq5LV*?IqBj!rd@TOrlcyRvs^&8 z3=8u$nl(#Sg=l9TT%W9vSkDk9s=faWg)T?7Rn5R+MZ4HgN`X)TLQ14b$>k&_bx2dt z7~&`-P7@NF;#h?vCDw<;gxIMJ)!F@w$ven*_%yQ(W>#icTV2EP4AopOMeC!Zl(nda zYh7~2ArUSZpCHi`Y((PJndJ;K{uDL8jxsKhX|>zVOh8xBp_olsTTO@}!>m#)ScP=* z_?{w%L!x8mnlrrFx`-o>pd5t}Ae{C?Vqz04mc+)ySQOw%2a7;kjkMqx58)`hG>e~j z$XJmR18)Q%S2yY#egAb{gCmjkN-$K6n7sNs3Ua?fLL?Fj`}|KF50} zmd08GzmVHdqAo5(10L zXW73g!)r%pu!-S<@wvNSaq#FO4{phD{K5iLYcabD36abb%`bDM!{e^;67Fi9Q&*cj zwxtvCE`!Ry3B2=+g_IjJceAk$F3^b}LxAI2;(CL8#g%SoOe3X=UYU9DcXwbG@s~|#R)lS6=>0+fox(Y{v<0wQdh?^-<(xmC9BvX>^ zYkL{be2i+>2urmlQ_FMI>kT3u<7Y~AWpgO)5;p4?ZScsEk|HBXHgRyNknM{YxJ@(b zT;kd9zs72_PQjNro{zLX0)wZ($wD^gP>&=KgR)p-AtUj z!nK)sqBtRG7!VpMEy}j9PY7_WC6*ei1Q^h%!1ZGCxdK7Sqo=3B_}CD`Bi#)2cTldD zC=@dka(S|TfkdaY;uda_pzTk6&V>jtc*HnJ(^iT@XdK}n+s39K-rwkKOkU*J;dSo$ z;w``UMkZx`YL4#Vf&c%{{sQRrH5M8^xzP>)CQl#cT0G2wef=MJ->1IwBE>rnuxaS$ zE5R+De3dJzpZzzFyz`Bu#_1)^&fd!VKJL=hNeV-KEL@qTYuA={-Ar|JorRf*T=4_` z4&w#B_U&);u}}Q=jevfGzm|_KCz-;6UhU;E=y~M&HZ^sIm*(%=VeAs*`l zL_*@Jh;mlb@Ff``smD!hWLRE_n7lTH&P4R~R~V>PaeNPh!4VOpb+pzXEJ`YbP-qb# zoB)d;(v~P~k)|oeSacF&WP}k3nah1_UA>>Nj@zN2n4eu`d3BX2)wqGe3rcuy4mW~U zBSZ)fM+k(KBqqe~Fm(4TvfD!ZZ85nXsHn~Sr5`K+K+@aXF;(AQg}qmn0+DdT1;Bsf^(B8*3EU9(oNw=){b$CVOf zTbKu(Y1bcEi4qb=w7uH3kf@(jxNpe*zKx}&CBF5Yzao>#@VUnxC6{}rZKsr$Yv+#f z<*%RR{)fNFfS%$1{q|GzkKfI$qgjGNk*S$!ik-tawRt*5Hxo@B=U@E(zvbb7`aiO% zIl+^MF7v>rKF9XqJlL4?2aog0)pKmQ?ICU(F7y23-(*sk`TV2zvoN#7&`1YUSC`3W zEw3LrLAHOCo!dq@_tMjxTy)qz>~Z$+Q(U~*!zUm6B%PjM^4w9}fj#)OD=eBEOJ`o; z#3X$2^A9m!n`flEmn-uN6kW}!Bd6%zaT|Nax*%TX=_me-?7$|z^vFF-9)FP^9+}|2 zM}LPc9WB28?I&4XX)(Sd<@kjhdp8A~IyS?u{ad+w{73xPUp~RV`fvXUjq@+^!sVFH zeD*PhHVW!4pMI6^J%5V39{e2JdlFtb_$pI%_>FrH@TY(9`?w$f_xa2%&@TuE(QphQ;TsXo@hcEKU&;BOSg(Dn1+u+_u?x!mwX|7Cj^w@E(OecK& zw;!XA6(0Y=iwxX+8{0(0k<%AhZk*$Vi#}iQW1jl+udy5q@P$Y2Z9OV8$Aj1F|ZL%c=Mwm%*1FS5PE$CH+s*_cc@V9%xk z#jHn1$w#e6%rq^deFb)J$#eA7B2$TEM_-F z8)Wc;BEDC|_48Qo|6}h>qwF}(JHOw%RMlO(Z|~Umod5{#6lhTtC6Tset==RZTd^lI z<9x_ul9Qa1FLrWr&V2CLGdVu-BpEy7$;9zQ@_1xPwnSvFh z@}a7`0T84`5>NwB{myChUFz1Ys@u5F`)vQG6I3T{7f?5r*cgnBG1lTuo3@x^Zp5?v z^n>itzrjHFHtI=}k2FBkv-AOJ~3K~#S3y!$S;ZQsqZ{?&|*3^QAwB?u!To#yO? zR8&GuxeRe)s6<6R^TE=pp8Z~oa2>APbc!v`+VYz;E4*RZv-$-%ddae63W$F>?T zKYN(Q#0gH1X$CqS7fuhc|E*UT*mM^IYK+rklCF*>=V!}oT~%Y~*dg9N*Q7Omp0g*8 zF%_)j_Em~g`(NOhDbJoY%X#wPA!g2= z?_STD{jc%n*&(`DZKflh#uX|w2G3$lo#VrGwr*a9Z%=W?cd{jHa(*IU$L1oh?>ob` zZ5v?x7(aON1b5uMi;*{8;Ox+O&W#t?wcK&y+z6FY7n^pi=iH%_G^fvU@I;Gpa*7MK zo9g%wGt+IRk6z&Q;WrS~&8!Va_>+J4x4e1mEoM45aM!wCE*yN4r%u6L8#*{~V32)B zp5O;hyw2(ADcq@1n&ALDw|6jV0;;panBL8-au+#rc9@gT9%Ie@n|bALU!-eOh3DTI zWpj6f;aSV-8a(!&zs_s3Wl9roGt6?T?Gp@Nm}PXd!R>eN;ryFF=HGqu8BQE~o1SeS zVNEUf{%g?Vzz_0A;!5ITPmb3k~)Wh-4W1XkJqZ_80@V_W^KtWwFBHg#qEvUV(E+6i%g6={hO_J>5N= zYbatXX|x=kF-jdpoLWZW%QzokTtIAMj7u=Kj*T78d!%;=A#lRdYBwnlZe(ZcldSK* z1FJ3LGgHKgp;ReQ6a}<2=rANs3fQ{G#W6E+gH|w1vi2;wPUG8xb|c9a zG@zAYZf1^Bxy+U=+t_>m1N8SV<4^z1pW@mH)D2Dv3dM*hfSG!WH7i%~yI=h>D_8U~ zJu}DPq`}gQMIz&bnN|x&jBs^?8$$Xff)@R(NS$^N-~}R+OUC=GNs&OZATU7z`K`lV3r zVxYH&PX-yM>1ze1KQ>{g`^(B<$>>Cz#{(bc~*!r)9pjkk8#)Jh@#ri7+it&`bKm+V>@^5ga}`lK!7@30`XY`g@r? z(xycRE0(RKw>HlBSVCXl0M%m1v@;~C%#J;`v!?VW=d1V8f9g5T)teYs=97CL;=7Ok zi0{tzQd_x&NABLi_S@1qup%tbv!a*&Ucu0Xaa>Dq*Mq;v9lJJSUp_(Sj-9Msc8cEd zCIvS}(%DUy7~%YQOy9EYY~8$^vED7*yLUCGf)@9#pX8-87kK%{bL@QJLC%d%;VU)n zzvCg)>Bo5a!~`9xgPyJ~Cf;nZZqr>naQ9X==xI(KA7lNdwRwg9P0Z4H=#YL$4!E_nFvBYS5iamEf%P3y&>q#@dRj7uNS!rsm~oK5iFAbk27EO_IvF3GmFm?Xhj zK|M|woSq;kdU~oMff5wUC1lu1(iSvYE$VTTxZTFJVuVQmOCUf83Q!OP(`%rBvxI_xf;{0G84@r<1>o!M#yx_ z(`*_{JC)NlN+VST85B@q5uw7gu7PyHgCO<3RftrAFbt7WAcaIpiBu}>D3B7Rb#`B& z9_) zz^e6|=?EpLkc&e{88577xjWC_{rCWT9{fC;mX*MR^^(rjoj4-4?_S5gpL~ZmhAp4{ z%%`c^^Zf9oH`%!TPByRYqg1L=trjRnnv=)gBCc)Xo*iqcl&h4>Az`V5-tGznMb5nW zDy4OIvTNH0j=ubTo;y6lL!bRkc9bsi#8c1Fv;IzQ->`y8xr3gLv~^Drvw#1O8S=~c z!Y_Y{*5CpD{>hiw^RX}T(H&hp_Sj=g1ijpI*WE006TCXq;^X)1NXOqqMW*+?NqOUD zk~6RI_`y*={>zWjUkZ_>ZtTQqzV-d**>Lw~SYNC2>`$KJT)o04AAOXf9psfWvuxY2 zf!>}D%9SeBN=Q_yQ3)00?q2$Os+=D?%7v*8KKGeV(bXR2;HeQdY}r9qIiNXliszqw zmD7`+kNxJO?CR^}(4qb4Y8RW=uA$sprBsv{UE}t3Jv{mNw>jVL3&H`PiP0gm-McN3p_ji(Ns6D1ol3bT zNh&g9+Nm)&G1eH2P4Fhh+Zbm(-lPiyV`4<&h?|ynqm8KxCMKHfKXZz6?QvGG>7lP; zsT33))oy%PAx<>0aTsTCv+Gke0)bL2sG^*l)DiU3)1s7MkO(&$06 z0gbsi&W@bq#OPTjF?^1h#sqPrj!#+$Kr4`1 zqk}XQ?oh-|Vyz^xlEfNpVo(Sa7AwIkiT0oaO`%vIipuF+dXe>QIM7-W79&cf3Sm?t z)C#R62#F3fVHgr*!&rhKAdEt^4$xYmbb!(Ufljw8m2N#L(bM~%=KBX{`1RlXG~Lm4 zFe`IIeB-ab!=_*OJhyM{d*3gA>y@XO)SKCR$MS1-Naqgzm}7Dq5AVD-#fLdRU)(T# z=sHLXe=Z9nT1Pu8a~A@X$ik8DVw0tSceB^;^}3szEDsLMPq0e|c6dZKW>aR9x)3&< z=^-5oi*o|uHBvz0JW@-9cO;EA!a_j6*z`0fM$a>6oAh=R&~csCj>k$qL z!Xadm8k#}}LIhZvcri!gaKP51zsc^ky9oP0R)MNxraj4lw~ugS@D0vSy~RZP0_6cq z=ZYe=feu!6cA!-k-lXbs+8KmvAcaK;hxCpD9rsiN%>_>RC%c7=%QHh$t!$1OZxUtW9WJgHsA!2p2}53L!vv zJn4c<2$6oCPTlgJQ*GHMetmt<^)cpv)n)Ge<Nq!x5>O2yN*$fl$_12#N=KEc@ev9t0-L5eq!d{PfIx>Oj1x3l z4NPM22CQphoxvm1)Q)#pk&bKC+NZ|XL9^W=u^KHT-a8B+@q{`+h3VgU6h`PENaxOH zefcJdkx~&vAu0^gk+ULAGkGt;Yw#+aQinzmz>PbV{F+f)_TgLD-V0UeVD<8je|W=p zA&3eeOmOBs&|x7r=Q-DmCHY&@N^Ig{3vpS3=Q<8HSpNgS4W4OV^ z-`)pl`;uUBV26+bq0+INA_Ol47>V#2AuY}&cqb7qAa1red2W!gxf#w4jWRSg#>{Mk z5i1yzhB%yNLto6Q{%LwDidu0QQ8B>au+5990rdtYA(9%DNI#EaCt>b2&@4I8<6eNi$F@@Zk0y z{K-*HWP%w2m}*<@-qy{wnHG(P;q{S8j!na(ySte@HO@sNNLN*R7|Y>s=j^|GO+Z>W6w2PZp+EIcbRK^HC3^OH!d6NFOXF&L-Q zoVf(!921juE{snQYp8UTs5FXbVOZ7cSks@dXYB?m(N?6isl1M9W8*r;SR4`+M0gn@ zQ~`7eBSH`XLMUP(8Q2KFzOkQX;{k+^sTKvr(9>)-iCZzw%#zGbpb!K)z=i>$SVgK% z5?v(m0kQE|7o(&_1ckJh-ZqiWpaYK#6e_IXeMCEP#MTk}v@K7&fTAfNjUtQ+4D=6> z%*;?Ibzqz&5*8K56bccMlvK+RDojOptOMf{yj3VA5VD1|24UNH;ZRZ|gAh~+Yc=ge z(Kd#LX%WW`ydVrBf*>ReLZp%eVMrk?qk^=8Jq$y%R!AWbKp+%C2WTA-MG;a;RG!zBx|(Q|Tga;{(rqdgw1PpjF1mUWj^eisEiwSOGHvdX5CVB$iP|MdJg z6O9(Vv0?f0B1h&7XO54uf6}wHYv`*rcbYN*I<% zNPDz>J4wUdDny~tA|OHHM1Uj!5~NQM)~BOVNeHk=s}LfgyCM*_Kw=HEb1}+mv~Y+p zKo<*mRirH<5*vUhzsvOYLY_S*Cnx6$mc*bt@7cYzi+eipovbz>DJvqnp zoaGmG4{*oI0xLq#_g@+2`zL4l%FYhrTPE%<5Xda|nzlN5WE!$f97$}Mn5#40Y|&0E z^|?73%{m>$GGQg6oOpUGj^46FFY84pL2NB?yMeYAOo9;>DMP#}U|c}rEs05Ru8jsH z7B3u9I)qPuoCEJH-nMCa2U4I!+FGW92%)=hzDS%1+QQ+jMOufIGf3x=4sZ#Q7-2n9 zKoAJDuHXb1(*kS2TM9Z|7*OfNcu%|Skfx1LF<}%?3>Bqfg}%N4nv>(`LJ{ypIKnug zQjDk-B|3p50qYb3iP8~DmGD+z0Glm1trHmI(%J6LgOr4YB3ekKawsi;0P9>oAGHdqvw@(iVMz^8$y$+O&FI3WpAK+MOX&&sr*p zmp;{c7Z3?SAUzt36p}!u-nvr(9)VUC9Z1k3HQXx1D}j~@Ez?lHl4(9&$aE^85SM|_P0sS2T?KmwlN{nOGnm+z;eP0@xckr8^ zD3g@}<3uO--T??nTyIb-N9bbA`qdTItm@={T-k-SX<^Yl+xqa%(KhZD$viH4Y1XFm zmt_`;ZCQw&fG9*aG{PIo6-jSrg+P>0Dn{YJhFF)%vr3`R!r;Ba`&2jFA~DWlt;IQs z5FvpI7M06{PjkRBGekN_1yZ5{GU~*M5MvWUmkQok?=Ut=U!xRC*O00OdJgFw;evhg zC?raS2pM3tLijXq>_t|skH`B2;Sz8b;SIt$@CM;6UZiQ8K(q;kqgIWWsMV+x(`s@p zB-9H;8l;*-E1kBSNrT4`svwJgXq=N6=kZcvMVMB-i%fmbI}oWiu3Xl6AVEt_pde5l zDIHR%Q~^!}2o-<|5kezHkhK}c1f+{3B^^R)qynV{N(HH)j7kM&M3#5I!DS3{a&nf4 zCC#C?V@t8KNXh#JV-930a1MLPbQE)yNhv~j9D-sb*|WW$`&O5TV{_T}opp9$pSKce zcbW&Oaiq7U5GaujP3f#?gfE~o&k^siHtqX2!hvmPp<#u05-$Zpq$*^RfKV3j*5bTJ z$ zgi;_CHkbEl~z%5^w?5dyFtx zA7H(}y0m&-D1~zdAuI)L!CR1|1}!sAGBf&KWG!=Eq-|E#VSSR-V0$DUq{{O88jVjw z^K!AEPXwG$B1)nY&yh>-PD}fFIWS|i& zYLxqIWNT{i(l##7V%7ir!XeU7DVM2rbx>3x)L<;2bm2^agg12&f$_-ln`ho31mnRC`1r~lz_{GY&=psq|n)Q6S78ZWn{g0wn{4 zayT0zqy=k{)~0#wK&5lky+e2dEUM7?w8Jp%OBWs~972Ig6Z%H=8Qtv0xm9 zaFj|VIy<{4=@9E3fmB3^p-?P-p(pw(;;Zgo(Q1Vy*^={UdCGBR5AHF!sE3=0Cw)OCd zEnS2X@Q&Dc!XWeTv)LC(VpES*3h<70;)p^Gc#?L)PzFXS-3fy)aG3_?JhIs^V;#*H53TE&qC-jHH6{{r8AdBUebflirQSJ zSMI^4GhP=)*QSdDnSHK~MP)pH$u;l`BVuvs03IR4!W!vs zM<$_C4GF^%N*Sz^3+E+-$j0m1^g7~6i~0p3yPg7%Py#g{`W6fSEbK!FIsd1bEjkoJ zX_O2y?|c5537p8ha4FM_Qq8gcx>h7$FQ zbzU7$*tV{ltyRZ&ULB=eE3u;%Q7lSMpC0FAOYsY9OFaGd7|kH!;~T3B-rUk57VIBc zcvwh*$_l5Pb7^n-e5g1J7klRkL@HOPatwcewhyY%fc!;}Vo9g8DN zrBrD+*n6DJjF<<17g0{T&58UDI`**6$t{FdFI(q_Fg$TIXPFdBsp|} zM~4bCJHcoFmlp`j1^&Cwt>%kct0)g%B#cdY_U!Ed6dfhU_nsQ0Rq9~q#27D3NM1ft zr_dwW=K@w3%h94@|KUM`{%-0=M`;fR{P^e$gDuCe?e1blcaeBvia*+Sk5KUqSY zZzEPjjP6gE?s8n1)7-a0ap>4Z5+Ql*?OBe-p3##f4o@X~{G+Q;0?blewE1f{??w95 zcqjo*2!wM8rLr8iTPQs6nZ@(C)DWe99>zJGw`t24;IvPp-YyI2N}mv^0O1@`x%5ARU$m9;`Rz-0JHLpT7xr1awpoN=!TM)DwNeYk zZDdgtAzikuT6nXxzdk*Hd38AETh!)PQ!E}&E-aIilXEqe#GrfUn6Ux>{7XCV;%&Nj ztmeLOmVHBQKC!U~BBWaOynJMu6?-~pOwKZzD1LEIKf?#kuz$SGJsW#iw!-kE$%tQ8 zZH_f8rH&#WyStYoFQ4J)nHfqwWxl$(#>Og?62VFhUIcvg;bnB{CWG~WPkpqTXTLkh z;Y4!J9V^%~Ao3>cj^VEMR1a| zAPp=&oF@PPAOJ~3K~z}u5*HHSnK}1vK1z@G5M&zR(z{H4*CV`GSOm<6pckUyEG`f( zJ#}%3!2y@O#g$&9NTUG@k6k5gk$JSK;dI$Ui=ky9-Q}Ck-@d|IU6AV+0_PnC

Ll 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 erfJ#@;Qt4^00cuw12`N20000 Date: Wed, 20 Mar 2024 15:52:39 +0100 Subject: [PATCH 13/62] MDL-79368 theme_boost: Update screenshot image --- theme/boost/pix/screenshot.png | Bin 72236 -> 131945 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/theme/boost/pix/screenshot.png b/theme/boost/pix/screenshot.png index 1bbcd867009c303ab55ce5372fbf04a9df7d242f..811aff559a776dc058bca9075e44f9912750260c 100644 GIT binary patch literal 131945 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

@L!P+)QO9*f+2!j zmgfWX#F1C98OvG)FLldZ;g->}zQ7LjSSnf5I*XJQY%VefZu&%Q-0=vJH*T}+#|>$y zH?_~>1%-}7FN<^9>(!_VCG?YsBEUhWGL7u@a2e26TkLx2(!^Ia!59&v zPPOKZC->^gS$!9}hr%$ec_i+HEp~W05dXwWe?mLha&{fUs!X5w)ZKB$b^cbH2BFj^ zrOp8fP7A;m&ZX*%*3p)u;ky}D-66r?^M8Lw@-H7Ai{%! zs|~IM8~5mOh*k^NFEScfuZYeic+Iat#~;TqCh&Nno~G|c7YHKFS2Jl3kNvbT!|iU6 z=sTY>0u@)+m~UFw4yjr|NOWtbP4bz(%YQYP@GMDiE`F1H1zm~`o-Hzb97H({ zK9!u6T088Zy*~00>QcJ0Dii1GeS2^&2^`vVoW%JjRY);|aHK*=FGD1ArQr0zeZpHm zL7$E|xj6n?i_s%7Sz!AU&~aC85ZcY|S!NT%`&Azos8A54(y#Nv^5xvU-1-ASZTf zW}zjXl_)~<{`ALK=PekLiE1B{o%y$F`)KMH9{Ujm9YdqQQw3<8*+fz+mp}&aHm7@~ z0(9*0E-8X4io>bsy#WjRdnTJ#fe6*y$h9Gd%GZHr)hWoc;$FVH(4`8Ht^)Kw%l>{Xen|(Pg+$j(9~p= z*e7~ohX0(^_=!}@R#cL^`dT=c>d1&4ZtBgj18k>u`znOnfbz1Xj$P^T^7i1vYLoH1 zkbu^_9U2S@TJ$IP074o&6KIll^3XtlA+~$MfkTeQp?ofaT$||}wrgwC3O5p7B!uKx zY1DSB_GuHhGE6@-zWRYBngthJKK2yw$!^V4F$@9MQ+%9)3u8na|DxDPNwf;s2K3eF zjS-;sOkjp`Wr_34toIoO#0lwzj82pckJL(6D1!(IV&S>BjVcMTRoB{M()vmHe(xwE zaM|P+lr$uU#D?C;PON9Q>gWB#RB(RmZfUR0jtl;(qEI-9g?XQnkKEFWzUS<{^Gd%W zq@BLaBx2=hs&)PKHV#zt?BKGJ)Ilh3dlQhE*rOQGDl%%&}Eec^BKtoF&Nnp9{Yonx^hFAja< zf;tvEE!(L`mTg-|`pxyLurkcG@Ae>+>ilwBWBc3S4ngm|)dxp+K<8lF1&JiUt*BW9 ziPC3hakO2}2bKS_Qm0DYzL!}*!&!+~l>gOrlWQdLqABDXG{|r=NK8N8eCg?J<#3nh zVDN1+mdLVajokr zk~X;WAd6!TIT>&rg9vU5BX+Tr>HU-+s?Y*rPEQ>lJ^y4xw+@UOz(CC^l-Fm9v`ZF` z4olf%_`0^ znLl#)UvMG7OOWt9rTApnUpqI^jjz{Ujw2jms4W*`_tWbOBrR6r2F;@im!Qpe94Co& zuVlpFh4vIaZrk!d9(8L)i|e=frMFBR)DcPZ? z3)X9RelKcdx(Lje1Y%oA8n;BXk1n(OBvG#v;@H&Iq4n!b?=e{z<~AZHdsp9)QC$e* zTKb!@xn1j2;O|nRHGlf{xq2fZ8T$pjE>K@Ct||UsN)@0cUie)OAY7ts<@*gpi9R9E zU{TEd#tcL1kM8xn{2+2X{SoA;ZtTTJD}qY;$|e@dAw-LKJ$5iUm^JQ%S;IX|m(W&5 zhHkG9Gh8QTB;-s($^QIH8Mr499x?|nq@*hZ7S{T>$XX}*Z>}V z3MH+e8iY(v*E%f*gK0v~_h=FCcypx4?lKPP8LQJq0{jA4V@u1UPoS$BbvGVX{K=RX zu9i?yy%dAUUaHbYPPFxXDq=4$pF11*Rd%{HJ7*r>hVVSJ%ytZP s38wF*@Af<9 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 YT>2-wn1)VM0RGd_GSDngzaI4e023@QuK)l5 literal 72236 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

)%8f_wkiK$j<@0&W3cZ-|7 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 XIJTgRl)_Sv00000NkvXXu0mjfo7OvU From 27c1692867399eccc1aa7d54e0c01bbd47f6e689 Mon Sep 17 00:00:00 2001 From: Mathew May Date: Thu, 10 Aug 2023 13:00:26 +0800 Subject: [PATCH 14/62] MDL-78885 gradereport_grader: Collapsing a11y fixes --- grade/report/grader/amd/build/collapse.min.js | 2 +- .../grader/amd/build/collapse.min.js.map | 2 +- grade/report/grader/amd/src/collapse.js | 20 ++++++++++++++----- .../collapse/collapseresultitems.mustache | 4 ++-- .../tests/behat/column_collapsing.feature | 2 -- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/grade/report/grader/amd/build/collapse.min.js b/grade/report/grader/amd/build/collapse.min.js index d465a83330ea2..abd99864b5822 100644 --- a/grade/report/grader/amd/build/collapse.min.js +++ b/grade/report/grader/amd/build/collapse.min.js @@ -1,3 +1,3 @@ -define("gradereport_grader/collapse",["exports","gradereport_grader/collapse/repository","core/comboboxsearch/search_combobox","core/templates","core/utils","jquery","core/str","core/custom_interaction_events","core/localstorage","core/loadingicon","core/notification","core/pending"],(function(_exports,Repository,_search_combobox,_templates,_utils,_jquery,_str,_custom_interaction_events,_localstorage,_loadingicon,_notification,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Repository=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Repository),_search_combobox=_interopRequireDefault(_search_combobox),_jquery=_interopRequireDefault(_jquery),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_localstorage=_interopRequireDefault(_localstorage),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const selectors_component=".collapse-columns",selectors_formDropdown=".columnsdropdownform",selectors_formItems={cancel:"cancel",save:"save",checked:'input[type="checkbox"]:checked',currentlyUnchecked:'input[type="checkbox"]:not([data-action="selectall"])'},selectors_hider="hide",selectors_expand="expand",selectors_colVal="[data-col]",selectors_itemVal="[data-itemid]",selectors_content='[data-collapse="content"]',selectors_sort='[data-collapse="sort"]',selectors_expandbutton='[data-collapse="expandbutton"]',selectors_rangerowcell='[data-collapse="rangerowcell"]',selectors_avgrowcell='[data-collapse="avgrowcell"]',selectors_menu='[data-collapse="menu"]',selectors_icons=".data-collapse_gradeicons",selectors_count='[data-collapse="count"]',selectors_placeholder='.collapsecolumndropdown [data-region="placeholder"]',selectors_fullDropdown=".collapsecolumndropdown",countIndicator=document.querySelector(selectors_count);class ColumnSearch extends _search_combobox.default{static init(userID,courseID,defaultSort){return new ColumnSearch(userID,courseID,defaultSort)}constructor(userID,courseID,defaultSort){super(),_defineProperty(this,"userID",-1),_defineProperty(this,"courseID",null),_defineProperty(this,"defaultSort",""),_defineProperty(this,"nodes",[]),_defineProperty(this,"gradeStrings",null),_defineProperty(this,"userStrings",null),_defineProperty(this,"stringMap",[]),this.userID=userID,this.courseID=courseID,this.defaultSort=defaultSort,this.component=document.querySelector(selectors_component);const pendingPromise=new _pending.default;(0,_loadingicon.addIconToContainer)(document.querySelector(".gradeparent")).then((loader=>{setTimeout((()=>{this.getDataset().forEach((item=>{this.nodesUpdate(item)})),this.renderDefault(),loader.remove(),document.querySelector(".gradereport-grader-table").classList.remove("d-none")}),10)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}componentSelector(){return".collapse-columns"}dropdownSelector(){return".searchresultitemscontainer"}triggerSelector(){return".collapsecolumn"}getDataset(){if(!this.dataset){const cols=this.fetchDataset();this.dataset=JSON.parse(cols)?JSON.parse(cols).split(","):[]}return this.datasetSize=this.dataset.length,this.dataset}fetchDataset(){return _localstorage.default.get("gradereport_grader_collapseditems_".concat(this.courseID,"_").concat(this.userID))}setPreferences(){_localstorage.default.set("gradereport_grader_collapseditems_".concat(this.courseID,"_").concat(this.userID),JSON.stringify(this.getDataset().join(",")))}registerClickHandlers(){this.component.addEventListener("click",this.clickHandler.bind(this)),document.addEventListener("click",this.docClickHandler.bind(this))}clickHandler(e){super.clickHandler(e),e.target.closest(selectors_fullDropdown)&&e.stopPropagation()}async docClickHandler(e){var _e$target$closest3;if(e.target.dataset.hider===selectors_hider){var _e$target$closest,_e$target$closest2;e.preventDefault();const desiredToHide=e.target.closest(selectors_colVal)?null===(_e$target$closest=e.target.closest(selectors_colVal))||void 0===_e$target$closest?void 0:_e$target$closest.dataset.col:null===(_e$target$closest2=e.target.closest(selectors_itemVal))||void 0===_e$target$closest2?void 0:_e$target$closest2.dataset.itemid;-1===this.getDataset().indexOf(desiredToHide)&&this.getDataset().push(desiredToHide),await this.prefcountpipe(),this.nodesUpdate(desiredToHide)}if((null===(_e$target$closest3=e.target.closest("button"))||void 0===_e$target$closest3?void 0:_e$target$closest3.dataset.hider)===selectors_expand){var _e$target$closest4,_e$target$closest5,_e$target$closest6,_e$target$closest7;e.preventDefault();const desiredToHide=e.target.closest(selectors_colVal)?null===(_e$target$closest4=e.target.closest(selectors_colVal))||void 0===_e$target$closest4?void 0:_e$target$closest4.dataset.col:null===(_e$target$closest5=e.target.closest(selectors_itemVal))||void 0===_e$target$closest5?void 0:_e$target$closest5.dataset.itemid,idx=this.getDataset().indexOf(desiredToHide);this.getDataset().splice(idx,1),await this.prefcountpipe(),this.nodesUpdate(null===(_e$target$closest6=e.target.closest(selectors_colVal))||void 0===_e$target$closest6?void 0:_e$target$closest6.dataset.col),this.nodesUpdate(null===(_e$target$closest7=e.target.closest(selectors_colVal))||void 0===_e$target$closest7?void 0:_e$target$closest7.dataset.itemid)}}async keyHandler(e){if(super.keyHandler(e),"Tab"===e.key)e.target.closest(this.selectors.input)&&(e.preventDefault(),this.clearSearchButton.focus({preventScroll:!0}))}registerInputEvents(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{if(this.getSearchTerm()===this.searchInput.value&&this.searchResultsVisible())return void window.console.warn("Search term matches input value - skipping");this.setSearchTerms(this.searchInput.value),""===this.searchInput.value?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none");const pendingPromise=new _pending.default;await this.filterrenderpipe().then((()=>(pendingPromise.resolve(),!0)))}),300,{pending:!0}))}registerFormEvents(){const form=this.component.querySelector(selectors_formDropdown),events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);const selectall=form.querySelector('[data-action="selectall"]');events.forEach((event=>{const submitBtn=form.querySelector('[data-action="'.concat(selectors_formItems.save,'"'));form.addEventListener(event,(e=>{e.stopPropagation();const input=e.target.closest("input");if(input){selectall.checked&&!input.checked&&(selectall.checked=!1);const checkedCount=Array.from(form.querySelectorAll(selectors_formItems.checked)).length;submitBtn.disabled=checkedCount<=0}}),!1),this.searchInput.addEventListener(event,(e=>e.stopPropagation())),this.clearSearchButton.addEventListener(event,(async e=>{e.stopPropagation(),this.searchInput.value="",this.setSearchTerms(this.searchInput.value),await this.filterrenderpipe()})),selectall.addEventListener(event,(e=>{if(e.stopPropagation(),selectall.checked){Array.from(form.querySelectorAll(selectors_formItems.currentlyUnchecked)).forEach((item=>{item.checked=!0})),submitBtn.disabled=!1}else{Array.from(form.querySelectorAll(selectors_formItems.checked)).forEach((item=>{item.checked=!1})),submitBtn.disabled=!0}}))})),form.addEventListener("submit",(async e=>{if(e.preventDefault(),e.submitter.dataset.action===selectors_formItems.cancel)return void(0,_jquery.default)(this.component).dropdown("toggle");[...form.elements].filter((item=>item.checked)).forEach((item=>{const idx=this.getDataset().indexOf(item.dataset.collapse);this.getDataset().splice(idx,1),this.nodesUpdate(item.dataset.collapse)})),selectall.checked=!1,e.submitter.disabled=!0,await this.prefcountpipe()}))}nodesUpdate(item){const colNodesToHide=[...document.querySelectorAll('[data-col="'.concat(item,'"]'))],itemIDNodesToHide=[...document.querySelectorAll('[data-itemid="'.concat(item,'"]'))];this.nodes=[...colNodesToHide,...itemIDNodesToHide],this.updateDisplay()}async prefcountpipe(){this.setPreferences(),this.countUpdate(),await this.filterrenderpipe()}async filterDataset(filterableData){const stringUserMap=await this.fetchRequiredUserStrings(),stringGradeMap=await this.fetchRequiredGradeStrings(),customFieldMap=this.fetchCustomFieldValues();this.stringMap=new Map([...stringGradeMap,...stringUserMap,...customFieldMap]);const searching=filterableData.map((s=>{var _mapObj$itemname,_mapObj$category;const mapObj=this.stringMap.get(s);return void 0===mapObj?{key:s,string:s}:{key:s,string:null!==(_mapObj$itemname=mapObj.itemname)&&void 0!==_mapObj$itemname?_mapObj$itemname:this.stringMap.get(s),category:null!==(_mapObj$category=mapObj.category)&&void 0!==_mapObj$category?_mapObj$category:""}}));return""===this.getPreppedSearchTerm()?searching:searching.filter((col=>col.string.toString().toLowerCase().includes(this.getPreppedSearchTerm())))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((column=>{var _column$string,_column$category;return{name:column.key,displayName:null!==(_column$string=column.string)&&void 0!==_column$string?_column$string:column.key,category:null!==(_column$category=column.category)&&void 0!==_column$category?_column$category:""}})))}updateDisplay(){this.nodes.forEach((element=>{const content=element.querySelector(selectors_content),sort=element.querySelector(selectors_sort),expandButton=element.querySelector(selectors_expandbutton),rangeRowCell=element.querySelector(selectors_rangerowcell),avgRowCell=element.querySelector(selectors_avgrowcell),nodeSet=[element.querySelector(selectors_menu),element.querySelector(selectors_icons),content];if(element.classList.contains("cell"))if(null!==sort&&(window.location=this.defaultSort),null===content){const rowCell=null!=avgRowCell?avgRowCell:rangeRowCell;null==rowCell||rowCell.classList.toggle("d-none"),null==rowCell||rowCell.setAttribute("aria-hidden",null!=rowCell&&rowCell.classList.contains("d-none")?"true":"false")}else content.classList.contains("d-none")?(element.classList.remove("collapsed"),content.childNodes.length>1&&content.classList.add("d-flex"),nodeSet.forEach((node=>{null==node||node.classList.remove("d-none"),null==node||node.setAttribute("aria-hidden","false")})),null==expandButton||expandButton.classList.add("d-none"),null==expandButton||expandButton.setAttribute("aria-hidden","true")):(element.classList.add("collapsed"),content.classList.remove("d-flex"),nodeSet.forEach((node=>{null==node||node.classList.add("d-none"),null==node||node.setAttribute("aria-hidden","true")})),null==expandButton||expandButton.classList.remove("d-none"),null==expandButton||expandButton.setAttribute("aria-hidden","false"))}))}countUpdate(){countIndicator.textContent=this.getDatasetSize(),this.getDatasetSize()>0?(this.component.parentElement.classList.add("d-flex"),this.component.parentElement.classList.remove("d-none")):(this.component.parentElement.classList.remove("d-flex"),this.component.parentElement.classList.add("d-none"))}async renderDefault(){this.setMatchedResults(await this.filterDataset(this.getDataset())),this.filterMatchDataset(),this.countUpdate();const{html:html,js:js}=await(0,_templates.renderForPromise)("gradereport_grader/collapse/collapsebody",{results:this.getMatchedResults(),userid:this.userID});(0,_templates.replaceNode)(selectors_placeholder,html,js),this.updateNodes(),this.registerFormEvents(),this.registerInputEvents(),this.$component.on("shown.bs.dropdown",(()=>{this.searchInput.focus({preventScroll:!0})}))}async renderDropdown(){const selectall=this.component.querySelector(selectors_formDropdown).querySelector('[data-action="selectall"]'),{html:html,js:js}=await(0,_templates.renderForPromise)("gradereport_grader/collapse/collapseresults",{results:this.getMatchedResults(),searchTerm:this.getSearchTerm()});selectall.disabled=0===this.getMatchedResults().length,(0,_templates.replaceNodeContents)(this.getHTMLElements().searchDropdown,html,js)}fetchCustomFieldValues(){return[...document.querySelectorAll("[data-collapse-name]")].map((field=>[field.parentElement.dataset.col,field.dataset.collapseName]))}fetchRequiredUserStrings(){if(!this.userStrings){const requiredStrings=["username","firstname","lastname","email","city","country","department","institution","idnumber","phone1","phone2"];this.userStrings=(0,_str.getStrings)(requiredStrings.map((key=>({key:key})))).then((stringArray=>new Map(requiredStrings.map(((key,index)=>[key,stringArray[index]])))))}return this.userStrings}fetchRequiredGradeStrings(){return this.gradeStrings||(this.gradeStrings=Repository.gradeItems(this.courseID).then((result=>new Map(result.gradeItems.map((key=>[key.id,key])))))),this.gradeStrings}}return _exports.default=ColumnSearch,_exports.default})); +define("gradereport_grader/collapse",["exports","gradereport_grader/collapse/repository","core/comboboxsearch/search_combobox","core/templates","core/utils","jquery","core/str","core/custom_interaction_events","core/localstorage","core/loadingicon","core/notification","core/pending"],(function(_exports,Repository,_search_combobox,_templates,_utils,_jquery,_str,_custom_interaction_events,_localstorage,_loadingicon,_notification,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Repository=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Repository),_search_combobox=_interopRequireDefault(_search_combobox),_jquery=_interopRequireDefault(_jquery),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_localstorage=_interopRequireDefault(_localstorage),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);const selectors_component=".collapse-columns",selectors_formDropdown=".columnsdropdownform",selectors_formItems={cancel:"cancel",save:"save",checked:'input[type="checkbox"]:checked',currentlyUnchecked:'input[type="checkbox"]:not([data-action="selectall"])'},selectors_hider="hide",selectors_expand="expand",selectors_colVal="[data-col]",selectors_itemVal="[data-itemid]",selectors_content='[data-collapse="content"]',selectors_sort='[data-collapse="sort"]',selectors_expandbutton='[data-collapse="expandbutton"]',selectors_rangerowcell='[data-collapse="rangerowcell"]',selectors_avgrowcell='[data-collapse="avgrowcell"]',selectors_menu='[data-collapse="menu"]',selectors_icons=".data-collapse_gradeicons",selectors_count='[data-collapse="count"]',selectors_placeholder='.collapsecolumndropdown [data-region="placeholder"]',selectors_fullDropdown=".collapsecolumndropdown",countIndicator=document.querySelector(selectors_count);class ColumnSearch extends _search_combobox.default{static init(userID,courseID,defaultSort){return new ColumnSearch(userID,courseID,defaultSort)}constructor(userID,courseID,defaultSort){super(),_defineProperty(this,"userID",-1),_defineProperty(this,"courseID",null),_defineProperty(this,"defaultSort",""),_defineProperty(this,"nodes",[]),_defineProperty(this,"gradeStrings",null),_defineProperty(this,"userStrings",null),_defineProperty(this,"stringMap",[]),this.userID=userID,this.courseID=courseID,this.defaultSort=defaultSort,this.component=document.querySelector(selectors_component);const pendingPromise=new _pending.default;(0,_loadingicon.addIconToContainer)(document.querySelector(".gradeparent")).then((loader=>{setTimeout((()=>{this.getDataset().forEach((item=>{this.nodesUpdate(item)})),this.renderDefault(),loader.remove(),document.querySelector(".gradereport-grader-table").classList.remove("d-none")}),10)})).then((()=>pendingPromise.resolve())).catch(_notification.default.exception)}componentSelector(){return".collapse-columns"}dropdownSelector(){return".searchresultitemscontainer"}triggerSelector(){return".collapsecolumn"}getDataset(){if(!this.dataset){const cols=this.fetchDataset();this.dataset=JSON.parse(cols)?JSON.parse(cols).split(","):[]}return this.datasetSize=this.dataset.length,this.dataset}fetchDataset(){return _localstorage.default.get("gradereport_grader_collapseditems_".concat(this.courseID,"_").concat(this.userID))}setPreferences(){_localstorage.default.set("gradereport_grader_collapseditems_".concat(this.courseID,"_").concat(this.userID),JSON.stringify(this.getDataset().join(",")))}registerClickHandlers(){this.component.addEventListener("click",this.clickHandler.bind(this)),document.addEventListener("click",this.docClickHandler.bind(this))}clickHandler(e){super.clickHandler(e),e.target.closest(selectors_fullDropdown)&&e.stopPropagation()}async docClickHandler(e){var _e$target$closest3;if(e.target.dataset.hider===selectors_hider){var _e$target$closest,_e$target$closest2;e.preventDefault();const desiredToHide=e.target.closest(selectors_colVal)?null===(_e$target$closest=e.target.closest(selectors_colVal))||void 0===_e$target$closest?void 0:_e$target$closest.dataset.col:null===(_e$target$closest2=e.target.closest(selectors_itemVal))||void 0===_e$target$closest2?void 0:_e$target$closest2.dataset.itemid;-1===this.getDataset().indexOf(desiredToHide)&&this.getDataset().push(desiredToHide),await this.prefcountpipe(),this.nodesUpdate(desiredToHide)}if((null===(_e$target$closest3=e.target.closest("button"))||void 0===_e$target$closest3?void 0:_e$target$closest3.dataset.hider)===selectors_expand){var _e$target$closest4,_e$target$closest5,_e$target$closest6,_e$target$closest7;e.preventDefault();const desiredToHide=e.target.closest(selectors_colVal)?null===(_e$target$closest4=e.target.closest(selectors_colVal))||void 0===_e$target$closest4?void 0:_e$target$closest4.dataset.col:null===(_e$target$closest5=e.target.closest(selectors_itemVal))||void 0===_e$target$closest5?void 0:_e$target$closest5.dataset.itemid,idx=this.getDataset().indexOf(desiredToHide);this.getDataset().splice(idx,1),await this.prefcountpipe(),this.nodesUpdate(null===(_e$target$closest6=e.target.closest(selectors_colVal))||void 0===_e$target$closest6?void 0:_e$target$closest6.dataset.col),this.nodesUpdate(null===(_e$target$closest7=e.target.closest(selectors_colVal))||void 0===_e$target$closest7?void 0:_e$target$closest7.dataset.itemid)}}async keyHandler(e){if(super.keyHandler(e),"Tab"===e.key)e.target.closest(this.selectors.input)&&(this.clearSearchButton.classList.contains("d-none")||(e.preventDefault(),this.clearSearchButton.focus({preventScroll:!0})))}registerInputEvents(){this.searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{if(this.getSearchTerm()===this.searchInput.value&&this.searchResultsVisible())return void window.console.warn("Search term matches input value - skipping");this.setSearchTerms(this.searchInput.value),""===this.searchInput.value?this.clearSearchButton.classList.add("d-none"):this.clearSearchButton.classList.remove("d-none");const pendingPromise=new _pending.default;await this.filterrenderpipe().then((()=>(pendingPromise.resolve(),!0)))}),300,{pending:!0}))}registerFormEvents(){const form=this.component.querySelector(selectors_formDropdown),events=["click",_custom_interaction_events.default.events.activate,_custom_interaction_events.default.events.keyboardActivate];_custom_interaction_events.default.define(document,events);const selectall=form.querySelector('[data-action="selectall"]');events.forEach((event=>{const submitBtn=form.querySelector('[data-action="'.concat(selectors_formItems.save,'"'));form.addEventListener(event,(e=>{e.stopPropagation();const input=e.target.closest("input");if(input){selectall.checked&&!input.checked&&(selectall.checked=!1);const checkedCount=Array.from(form.querySelectorAll(selectors_formItems.checked)).length;submitBtn.disabled=checkedCount<=0}}),!1),this.searchInput.addEventListener(event,(e=>e.stopPropagation())),this.clearSearchButton.addEventListener(event,(async e=>{e.stopPropagation(),this.searchInput.value="",this.setSearchTerms(this.searchInput.value),await this.filterrenderpipe()})),selectall.addEventListener(event,(e=>{if(e.stopPropagation(),selectall.checked){Array.from(form.querySelectorAll(selectors_formItems.currentlyUnchecked)).forEach((item=>{item.checked=!0})),submitBtn.disabled=!1}else{Array.from(form.querySelectorAll(selectors_formItems.checked)).forEach((item=>{item.checked=!1})),submitBtn.disabled=!0}}))})),form.addEventListener("submit",(async e=>{if(e.preventDefault(),e.submitter.dataset.action===selectors_formItems.cancel)return void(0,_jquery.default)(this.component).dropdown("toggle");[...form.elements].filter((item=>item.checked)).forEach((item=>{const idx=this.getDataset().indexOf(item.dataset.collapse);this.getDataset().splice(idx,1),this.nodesUpdate(item.dataset.collapse)})),selectall.checked=!1,e.submitter.disabled=!0,await this.prefcountpipe()}))}nodesUpdate(item){const colNodesToHide=[...document.querySelectorAll('[data-col="'.concat(item,'"]'))],itemIDNodesToHide=[...document.querySelectorAll('[data-itemid="'.concat(item,'"]'))];this.nodes=[...colNodesToHide,...itemIDNodesToHide],this.updateDisplay()}async prefcountpipe(){this.setPreferences(),this.countUpdate(),await this.filterrenderpipe()}async filterDataset(filterableData){const stringUserMap=await this.fetchRequiredUserStrings(),stringGradeMap=await this.fetchRequiredGradeStrings(),customFieldMap=this.fetchCustomFieldValues();this.stringMap=new Map([...stringGradeMap,...stringUserMap,...customFieldMap]);const searching=filterableData.map((s=>{var _mapObj$itemname,_mapObj$category;const mapObj=this.stringMap.get(s);return void 0===mapObj?{key:s,string:s}:{key:s,string:null!==(_mapObj$itemname=mapObj.itemname)&&void 0!==_mapObj$itemname?_mapObj$itemname:this.stringMap.get(s),category:null!==(_mapObj$category=mapObj.category)&&void 0!==_mapObj$category?_mapObj$category:""}}));return""===this.getPreppedSearchTerm()?searching:searching.filter((col=>col.string.toString().toLowerCase().includes(this.getPreppedSearchTerm())))}filterMatchDataset(){this.setMatchedResults(this.getMatchedResults().map((column=>{var _column$string,_column$category;return{name:column.key,displayName:null!==(_column$string=column.string)&&void 0!==_column$string?_column$string:column.key,category:null!==(_column$category=column.category)&&void 0!==_column$category?_column$category:""}})))}updateDisplay(){this.nodes.forEach((element=>{const content=element.querySelector(selectors_content),sort=element.querySelector(selectors_sort),expandButton=element.querySelector(selectors_expandbutton),rangeRowCell=element.querySelector(selectors_rangerowcell),avgRowCell=element.querySelector(selectors_avgrowcell),nodeSet=[element.querySelector(selectors_menu),element.querySelector(selectors_icons),content];if(element.classList.contains("cell"))if(null!==sort&&(window.location=this.defaultSort),null===content){const rowCell=null!=avgRowCell?avgRowCell:rangeRowCell;null==rowCell||rowCell.classList.toggle("d-none"),null==rowCell||rowCell.setAttribute("aria-hidden",null!=rowCell&&rowCell.classList.contains("d-none")?"true":"false")}else content.classList.contains("d-none")?(element.classList.remove("collapsed"),content.childNodes.length>1&&content.classList.add("d-flex"),nodeSet.forEach((node=>{null==node||node.classList.remove("d-none"),null==node||node.setAttribute("aria-hidden","false")})),null==expandButton||expandButton.classList.add("d-none"),null==expandButton||expandButton.setAttribute("aria-hidden","true")):(element.classList.add("collapsed"),content.classList.remove("d-flex"),nodeSet.forEach((node=>{null==node||node.classList.add("d-none"),null==node||node.setAttribute("aria-hidden","true")})),null==expandButton||expandButton.classList.remove("d-none"),null==expandButton||expandButton.setAttribute("aria-hidden","false"))}))}countUpdate(){countIndicator.textContent=this.getDatasetSize(),this.getDatasetSize()>0?(this.component.parentElement.classList.add("d-flex"),this.component.parentElement.classList.remove("d-none")):(this.component.parentElement.classList.remove("d-flex"),this.component.parentElement.classList.add("d-none"))}async renderDefault(){this.setMatchedResults(await this.filterDataset(this.getDataset())),this.filterMatchDataset(),this.countUpdate();const{html:html,js:js}=await(0,_templates.renderForPromise)("gradereport_grader/collapse/collapsebody",{results:this.getMatchedResults(),userid:this.userID});(0,_templates.replaceNode)(selectors_placeholder,html,js),this.updateNodes(),this.registerFormEvents(),this.registerInputEvents(),this.$component.on("shown.bs.dropdown",(()=>{this.searchInput.focus({preventScroll:!0}),this.selectallEnable()}))}async renderDropdown(){const{html:html,js:js}=await(0,_templates.renderForPromise)("gradereport_grader/collapse/collapseresults",{results:this.getMatchedResults(),searchTerm:this.getSearchTerm()});this.selectallEnable(),(0,_templates.replaceNodeContents)(this.getHTMLElements().searchDropdown,html,js)}selectallEnable(){this.component.querySelector(selectors_formDropdown).querySelector('[data-action="selectall"]').disabled=0===this.getMatchedResults().length}fetchCustomFieldValues(){return[...document.querySelectorAll("[data-collapse-name]")].map((field=>[field.parentElement.dataset.col,field.dataset.collapseName]))}fetchRequiredUserStrings(){if(!this.userStrings){const requiredStrings=["username","firstname","lastname","email","city","country","department","institution","idnumber","phone1","phone2"];this.userStrings=(0,_str.getStrings)(requiredStrings.map((key=>({key:key})))).then((stringArray=>new Map(requiredStrings.map(((key,index)=>[key,stringArray[index]])))))}return this.userStrings}fetchRequiredGradeStrings(){return this.gradeStrings||(this.gradeStrings=Repository.gradeItems(this.courseID).then((result=>new Map(result.gradeItems.map((key=>[key.id,key])))))),this.gradeStrings}}return _exports.default=ColumnSearch,_exports.default})); //# sourceMappingURL=collapse.min.js.map \ No newline at end of file diff --git a/grade/report/grader/amd/build/collapse.min.js.map b/grade/report/grader/amd/build/collapse.min.js.map index 877f26953a490..a0dbd08b69721 100644 --- a/grade/report/grader/amd/build/collapse.min.js.map +++ b/grade/report/grader/amd/build/collapse.min.js.map @@ -1 +1 @@ -{"version":3,"file":"collapse.min.js","sources":["../src/collapse.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Allow the user to show and hide columns of the report at will.\n *\n * @module gradereport_grader/collapse\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport * as Repository from 'gradereport_grader/collapse/repository';\nimport search_combobox from 'core/comboboxsearch/search_combobox';\nimport {renderForPromise, replaceNodeContents, replaceNode} from 'core/templates';\nimport {debounce} from 'core/utils';\nimport $ from 'jquery';\nimport {getStrings} from 'core/str';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport storage from 'core/localstorage';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\n\n// Contain our selectors within this file until they could be of use elsewhere.\nconst selectors = {\n component: '.collapse-columns',\n formDropdown: '.columnsdropdownform',\n formItems: {\n cancel: 'cancel',\n save: 'save',\n checked: 'input[type=\"checkbox\"]:checked',\n currentlyUnchecked: 'input[type=\"checkbox\"]:not([data-action=\"selectall\"])',\n },\n hider: 'hide',\n expand: 'expand',\n colVal: '[data-col]',\n itemVal: '[data-itemid]',\n content: '[data-collapse=\"content\"]',\n sort: '[data-collapse=\"sort\"]',\n expandbutton: '[data-collapse=\"expandbutton\"]',\n rangerowcell: '[data-collapse=\"rangerowcell\"]',\n avgrowcell: '[data-collapse=\"avgrowcell\"]',\n menu: '[data-collapse=\"menu\"]',\n icons: '.data-collapse_gradeicons',\n count: '[data-collapse=\"count\"]',\n placeholder: '.collapsecolumndropdown [data-region=\"placeholder\"]',\n fullDropdown: '.collapsecolumndropdown',\n};\n\nconst countIndicator = document.querySelector(selectors.count);\n\nexport default class ColumnSearch extends search_combobox {\n\n userID = -1;\n courseID = null;\n defaultSort = '';\n\n nodes = [];\n\n gradeStrings = null;\n userStrings = null;\n stringMap = [];\n\n static init(userID, courseID, defaultSort) {\n return new ColumnSearch(userID, courseID, defaultSort);\n }\n\n constructor(userID, courseID, defaultSort) {\n super();\n this.userID = userID;\n this.courseID = courseID;\n this.defaultSort = defaultSort;\n this.component = document.querySelector(selectors.component);\n\n const pendingPromise = new Pending();\n // Display a loader whilst collapsing appropriate columns (based on the locally stored state for the current user).\n addIconToContainer(document.querySelector('.gradeparent')).then((loader) => {\n setTimeout(() => {\n // Get the users' checked columns to change.\n this.getDataset().forEach((item) => {\n this.nodesUpdate(item);\n });\n this.renderDefault();\n\n // Once the grade categories have been re-collapsed, remove the loader and display the Gradebook setup content.\n loader.remove();\n document.querySelector('.gradereport-grader-table').classList.remove('d-none');\n }, 10);\n }).then(() => pendingPromise.resolve()).catch(Notification.exception);\n }\n\n /**\n * The overall div that contains the searching widget.\n *\n * @returns {string}\n */\n componentSelector() {\n return '.collapse-columns';\n }\n\n /**\n * The dropdown div that contains the searching widget result space.\n *\n * @returns {string}\n */\n dropdownSelector() {\n return '.searchresultitemscontainer';\n }\n\n /**\n * The triggering div that contains the searching widget.\n *\n * @returns {string}\n */\n triggerSelector() {\n return '.collapsecolumn';\n }\n\n /**\n * Return the dataset that we will be searching upon.\n *\n * @returns {Array}\n */\n getDataset() {\n if (!this.dataset) {\n const cols = this.fetchDataset();\n this.dataset = JSON.parse(cols) ? JSON.parse(cols).split(',') : [];\n }\n this.datasetSize = this.dataset.length;\n return this.dataset;\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {string}\n */\n fetchDataset() {\n return storage.get(`gradereport_grader_collapseditems_${this.courseID}_${this.userID}`);\n }\n\n /**\n * Given a user performs an action, update the users' preferences.\n */\n setPreferences() {\n storage.set(`gradereport_grader_collapseditems_${this.courseID}_${this.userID}`,\n JSON.stringify(this.getDataset().join(','))\n );\n }\n\n /**\n * Register clickable event listeners.\n */\n registerClickHandlers() {\n // Register click events within the component.\n this.component.addEventListener('click', this.clickHandler.bind(this));\n\n document.addEventListener('click', this.docClickHandler.bind(this));\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n clickHandler(e) {\n super.clickHandler(e);\n // Prevent BS from closing the dropdown if they click elsewhere within the dropdown besides the form.\n if (e.target.closest(selectors.fullDropdown)) {\n e.stopPropagation();\n }\n }\n\n /**\n * Externally defined click function to improve memory handling.\n *\n * @param {MouseEvent} e\n * @returns {Promise}\n */\n async docClickHandler(e) {\n if (e.target.dataset.hider === selectors.hider) {\n e.preventDefault();\n const desiredToHide = e.target.closest(selectors.colVal) ?\n e.target.closest(selectors.colVal)?.dataset.col :\n e.target.closest(selectors.itemVal)?.dataset.itemid;\n const idx = this.getDataset().indexOf(desiredToHide);\n if (idx === -1) {\n this.getDataset().push(desiredToHide);\n }\n await this.prefcountpipe();\n\n this.nodesUpdate(desiredToHide);\n }\n\n if (e.target.closest('button')?.dataset.hider === selectors.expand) {\n e.preventDefault();\n const desiredToHide = e.target.closest(selectors.colVal) ?\n e.target.closest(selectors.colVal)?.dataset.col :\n e.target.closest(selectors.itemVal)?.dataset.itemid;\n const idx = this.getDataset().indexOf(desiredToHide);\n this.getDataset().splice(idx, 1);\n\n await this.prefcountpipe();\n\n this.nodesUpdate(e.target.closest(selectors.colVal)?.dataset.col);\n this.nodesUpdate(e.target.closest(selectors.colVal)?.dataset.itemid);\n }\n }\n\n /**\n * The handler for when a user presses a key within the component.\n *\n * @param {KeyboardEvent} e The triggering event that we are working with.\n */\n async keyHandler(e) {\n super.keyHandler(e);\n\n // Switch the key presses to handle keyboard nav.\n switch (e.key) {\n case 'Tab':\n if (e.target.closest(this.selectors.input)) {\n e.preventDefault();\n this.clearSearchButton.focus({preventScroll: true});\n }\n break;\n }\n }\n\n /**\n * Handle any keyboard inputs.\n */\n registerInputEvents() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(async() => {\n if (this.getSearchTerm() === this.searchInput.value && this.searchResultsVisible()) {\n window.console.warn(`Search term matches input value - skipping`);\n // Debounce can happen multiple times quickly.\n return;\n }\n this.setSearchTerms(this.searchInput.value);\n // We can also require a set amount of input before search.\n if (this.searchInput.value === '') {\n // Hide the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.add('d-none');\n } else {\n // Display the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.remove('d-none');\n }\n const pendingPromise = new Pending();\n // User has given something for us to filter against.\n await this.filterrenderpipe().then(() => {\n pendingPromise.resolve();\n return true;\n });\n }, 300, {pending: true}));\n }\n\n /**\n * Handle the form submission within the dropdown.\n */\n registerFormEvents() {\n const form = this.component.querySelector(selectors.formDropdown);\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n const selectall = form.querySelector('[data-action=\"selectall\"]');\n\n // Register clicks & keyboard form handling.\n events.forEach((event) => {\n const submitBtn = form.querySelector(`[data-action=\"${selectors.formItems.save}\"`);\n form.addEventListener(event, (e) => {\n // Stop Bootstrap from being clever.\n e.stopPropagation();\n const input = e.target.closest('input');\n if (input) {\n // If the user is unchecking an item, we need to uncheck the select all if it's checked.\n if (selectall.checked && !input.checked) {\n selectall.checked = false;\n }\n const checkedCount = Array.from(form.querySelectorAll(selectors.formItems.checked)).length;\n // Check if any are clicked or not then change disabled.\n submitBtn.disabled = checkedCount <= 0;\n }\n }, false);\n\n // Stop Bootstrap from being clever.\n this.searchInput.addEventListener(event, e => e.stopPropagation());\n this.clearSearchButton.addEventListener(event, async(e) => {\n e.stopPropagation();\n this.searchInput.value = '';\n this.setSearchTerms(this.searchInput.value);\n await this.filterrenderpipe();\n });\n selectall.addEventListener(event, (e) => {\n // Stop Bootstrap from being clever.\n e.stopPropagation();\n if (!selectall.checked) {\n const touncheck = Array.from(form.querySelectorAll(selectors.formItems.checked));\n touncheck.forEach(item => {\n item.checked = false;\n });\n submitBtn.disabled = true;\n } else {\n const currentUnchecked = Array.from(form.querySelectorAll(selectors.formItems.currentlyUnchecked));\n currentUnchecked.forEach(item => {\n item.checked = true;\n });\n submitBtn.disabled = false;\n }\n });\n });\n\n form.addEventListener('submit', async(e) => {\n e.preventDefault();\n if (e.submitter.dataset.action === selectors.formItems.cancel) {\n $(this.component).dropdown('toggle');\n return;\n }\n // Get the users' checked columns to change.\n const checkedItems = [...form.elements].filter(item => item.checked);\n checkedItems.forEach((item) => {\n const idx = this.getDataset().indexOf(item.dataset.collapse);\n this.getDataset().splice(idx, 1);\n this.nodesUpdate(item.dataset.collapse);\n });\n // Reset the check all & submit to false just in case.\n selectall.checked = false;\n e.submitter.disabled = true;\n await this.prefcountpipe();\n });\n }\n\n nodesUpdate(item) {\n const colNodesToHide = [...document.querySelectorAll(`[data-col=\"${item}\"]`)];\n const itemIDNodesToHide = [...document.querySelectorAll(`[data-itemid=\"${item}\"]`)];\n this.nodes = [...colNodesToHide, ...itemIDNodesToHide];\n this.updateDisplay();\n }\n\n /**\n * Update the user preferences, count display then render the results.\n *\n * @returns {Promise}\n */\n async prefcountpipe() {\n this.setPreferences();\n this.countUpdate();\n await this.filterrenderpipe();\n }\n\n /**\n * Dictate to the search component how and what we want to match upon.\n *\n * @param {Array} filterableData\n * @returns {Array} An array of objects containing the system reference and the user readable value.\n */\n async filterDataset(filterableData) {\n const stringUserMap = await this.fetchRequiredUserStrings();\n const stringGradeMap = await this.fetchRequiredGradeStrings();\n // Custom user profile fields are not in our string map and need a bit of extra love.\n const customFieldMap = this.fetchCustomFieldValues();\n this.stringMap = new Map([...stringGradeMap, ...stringUserMap, ...customFieldMap]);\n\n const searching = filterableData.map(s => {\n const mapObj = this.stringMap.get(s);\n if (mapObj === undefined) {\n return {key: s, string: s};\n }\n return {\n key: s,\n string: mapObj.itemname ?? this.stringMap.get(s),\n category: mapObj.category ?? '',\n };\n });\n // Sometimes we just want to show everything.\n if (this.getPreppedSearchTerm() === '') {\n return searching;\n }\n // Other times we want to actually filter the content.\n return searching.filter((col) => {\n return col.string.toString().toLowerCase().includes(this.getPreppedSearchTerm());\n });\n }\n\n /**\n * Given we have a subset of the dataset, set the field that we matched upon to inform the end user.\n */\n filterMatchDataset() {\n this.setMatchedResults(\n this.getMatchedResults().map((column) => {\n return {\n name: column.key,\n displayName: column.string ?? column.key,\n category: column.category ?? '',\n };\n })\n );\n }\n\n /**\n * With an array of nodes, switch their classes and values.\n */\n updateDisplay() {\n this.nodes.forEach((element) => {\n const content = element.querySelector(selectors.content);\n const sort = element.querySelector(selectors.sort);\n const expandButton = element.querySelector(selectors.expandbutton);\n const rangeRowCell = element.querySelector(selectors.rangerowcell);\n const avgRowCell = element.querySelector(selectors.avgrowcell);\n const nodeSet = [\n element.querySelector(selectors.menu),\n element.querySelector(selectors.icons),\n content\n ];\n\n // This can be further improved to reduce redundant similar calls.\n if (element.classList.contains('cell')) {\n // The column is actively being sorted, lets reset that and reload the page.\n if (sort !== null) {\n window.location = this.defaultSort;\n }\n if (content === null) {\n // If it's not a content cell, it must be an overall average or a range cell.\n const rowCell = avgRowCell ?? rangeRowCell;\n\n rowCell?.classList.toggle('d-none');\n rowCell?.setAttribute('aria-hidden',\n rowCell?.classList.contains('d-none') ? 'true' : 'false');\n } else if (content.classList.contains('d-none')) {\n // We should always have content but some cells do not contain menus or other actions.\n element.classList.remove('collapsed');\n // If there are many nodes, apply the following.\n if (content.childNodes.length > 1) {\n content.classList.add('d-flex');\n }\n nodeSet.forEach(node => {\n node?.classList.remove('d-none');\n node?.setAttribute('aria-hidden', 'false');\n });\n expandButton?.classList.add('d-none');\n expandButton?.setAttribute('aria-hidden', 'true');\n } else {\n element.classList.add('collapsed');\n content.classList.remove('d-flex');\n nodeSet.forEach(node => {\n node?.classList.add('d-none');\n node?.setAttribute('aria-hidden', 'true');\n });\n expandButton?.classList.remove('d-none');\n expandButton?.setAttribute('aria-hidden', 'false');\n }\n }\n });\n }\n\n /**\n * Update the visual count of collapsed columns or hide the count all together.\n */\n countUpdate() {\n countIndicator.textContent = this.getDatasetSize();\n if (this.getDatasetSize() > 0) {\n this.component.parentElement.classList.add('d-flex');\n this.component.parentElement.classList.remove('d-none');\n } else {\n this.component.parentElement.classList.remove('d-flex');\n this.component.parentElement.classList.add('d-none');\n }\n }\n\n /**\n * Build the content then replace the node by default we want our form to exist.\n */\n async renderDefault() {\n this.setMatchedResults(await this.filterDataset(this.getDataset()));\n this.filterMatchDataset();\n\n // Update the collapsed button pill.\n this.countUpdate();\n const {html, js} = await renderForPromise('gradereport_grader/collapse/collapsebody', {\n 'results': this.getMatchedResults(),\n 'userid': this.userID,\n });\n replaceNode(selectors.placeholder, html, js);\n this.updateNodes();\n\n // Given we now have the body, we can set up more triggers.\n this.registerFormEvents();\n this.registerInputEvents();\n\n // Add a small BS listener so that we can set the focus correctly on open.\n this.$component.on('shown.bs.dropdown', () => {\n this.searchInput.focus({preventScroll: true});\n });\n }\n\n /**\n * Build the content then replace the node.\n */\n async renderDropdown() {\n const form = this.component.querySelector(selectors.formDropdown);\n const selectall = form.querySelector('[data-action=\"selectall\"]');\n const {html, js} = await renderForPromise('gradereport_grader/collapse/collapseresults', {\n 'results': this.getMatchedResults(),\n 'searchTerm': this.getSearchTerm(),\n });\n selectall.disabled = this.getMatchedResults().length === 0;\n replaceNodeContents(this.getHTMLElements().searchDropdown, html, js);\n }\n\n /**\n * If we have any custom user profile fields, grab their system & readable names to add to our string map.\n *\n * @returns {array} An array of associated string arrays ready for our map.\n */\n fetchCustomFieldValues() {\n const customFields = document.querySelectorAll('[data-collapse-name]');\n // Cast from NodeList to array to grab all the values.\n return [...customFields].map(field => [field.parentElement.dataset.col, field.dataset.collapseName]);\n }\n\n /**\n * Given the set of profile fields we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n fetchRequiredUserStrings() {\n if (!this.userStrings) {\n const requiredStrings = [\n 'username',\n 'firstname',\n 'lastname',\n 'email',\n 'city',\n 'country',\n 'department',\n 'institution',\n 'idnumber',\n 'phone1',\n 'phone2',\n ];\n this.userStrings = getStrings(requiredStrings.map((key) => ({key})))\n .then((stringArray) => new Map(\n requiredStrings.map((key, index) => ([key, stringArray[index]]))\n ));\n }\n return this.userStrings;\n }\n\n /**\n * Given the set of gradable items we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n fetchRequiredGradeStrings() {\n if (!this.gradeStrings) {\n this.gradeStrings = Repository.gradeItems(this.courseID)\n .then((result) => new Map(\n result.gradeItems.map(key => ([key.id, key]))\n ));\n }\n return this.gradeStrings;\n }\n}\n"],"names":["selectors","cancel","save","checked","currentlyUnchecked","countIndicator","document","querySelector","ColumnSearch","search_combobox","userID","courseID","defaultSort","constructor","component","pendingPromise","Pending","then","loader","setTimeout","getDataset","forEach","item","nodesUpdate","renderDefault","remove","classList","resolve","catch","Notification","exception","componentSelector","dropdownSelector","triggerSelector","this","dataset","cols","fetchDataset","JSON","parse","split","datasetSize","length","storage","get","setPreferences","set","stringify","join","registerClickHandlers","addEventListener","clickHandler","bind","docClickHandler","e","target","closest","stopPropagation","hider","preventDefault","desiredToHide","_e$target$closest","col","_e$target$closest2","itemid","indexOf","push","prefcountpipe","_e$target$closest4","_e$target$closest5","idx","splice","_e$target$closest6","_e$target$closest7","keyHandler","key","input","clearSearchButton","focus","preventScroll","registerInputEvents","searchInput","async","getSearchTerm","value","searchResultsVisible","window","console","warn","setSearchTerms","add","filterrenderpipe","pending","registerFormEvents","form","events","CustomEvents","activate","keyboardActivate","define","selectall","event","submitBtn","checkedCount","Array","from","querySelectorAll","disabled","submitter","action","dropdown","elements","filter","collapse","colNodesToHide","itemIDNodesToHide","nodes","updateDisplay","countUpdate","filterableData","stringUserMap","fetchRequiredUserStrings","stringGradeMap","fetchRequiredGradeStrings","customFieldMap","fetchCustomFieldValues","stringMap","Map","searching","map","s","mapObj","undefined","string","itemname","category","getPreppedSearchTerm","toString","toLowerCase","includes","filterMatchDataset","setMatchedResults","getMatchedResults","column","name","displayName","element","content","sort","expandButton","rangeRowCell","avgRowCell","nodeSet","contains","location","rowCell","toggle","setAttribute","childNodes","node","textContent","getDatasetSize","parentElement","filterDataset","html","js","updateNodes","$component","on","getHTMLElements","searchDropdown","field","collapseName","userStrings","requiredStrings","stringArray","index","gradeStrings","Repository","gradeItems","result","id"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"collapse.min.js","sources":["../src/collapse.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Allow the user to show and hide columns of the report at will.\n *\n * @module gradereport_grader/collapse\n * @copyright 2023 Mathew May \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport * as Repository from 'gradereport_grader/collapse/repository';\nimport search_combobox from 'core/comboboxsearch/search_combobox';\nimport {renderForPromise, replaceNodeContents, replaceNode} from 'core/templates';\nimport {debounce} from 'core/utils';\nimport $ from 'jquery';\nimport {getStrings} from 'core/str';\nimport CustomEvents from \"core/custom_interaction_events\";\nimport storage from 'core/localstorage';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\n\n// Contain our selectors within this file until they could be of use elsewhere.\nconst selectors = {\n component: '.collapse-columns',\n formDropdown: '.columnsdropdownform',\n formItems: {\n cancel: 'cancel',\n save: 'save',\n checked: 'input[type=\"checkbox\"]:checked',\n currentlyUnchecked: 'input[type=\"checkbox\"]:not([data-action=\"selectall\"])',\n },\n hider: 'hide',\n expand: 'expand',\n colVal: '[data-col]',\n itemVal: '[data-itemid]',\n content: '[data-collapse=\"content\"]',\n sort: '[data-collapse=\"sort\"]',\n expandbutton: '[data-collapse=\"expandbutton\"]',\n rangerowcell: '[data-collapse=\"rangerowcell\"]',\n avgrowcell: '[data-collapse=\"avgrowcell\"]',\n menu: '[data-collapse=\"menu\"]',\n icons: '.data-collapse_gradeicons',\n count: '[data-collapse=\"count\"]',\n placeholder: '.collapsecolumndropdown [data-region=\"placeholder\"]',\n fullDropdown: '.collapsecolumndropdown',\n};\n\nconst countIndicator = document.querySelector(selectors.count);\n\nexport default class ColumnSearch extends search_combobox {\n\n userID = -1;\n courseID = null;\n defaultSort = '';\n\n nodes = [];\n\n gradeStrings = null;\n userStrings = null;\n stringMap = [];\n\n static init(userID, courseID, defaultSort) {\n return new ColumnSearch(userID, courseID, defaultSort);\n }\n\n constructor(userID, courseID, defaultSort) {\n super();\n this.userID = userID;\n this.courseID = courseID;\n this.defaultSort = defaultSort;\n this.component = document.querySelector(selectors.component);\n\n const pendingPromise = new Pending();\n // Display a loader whilst collapsing appropriate columns (based on the locally stored state for the current user).\n addIconToContainer(document.querySelector('.gradeparent')).then((loader) => {\n setTimeout(() => {\n // Get the users' checked columns to change.\n this.getDataset().forEach((item) => {\n this.nodesUpdate(item);\n });\n this.renderDefault();\n\n // Once the grade categories have been re-collapsed, remove the loader and display the Gradebook setup content.\n loader.remove();\n document.querySelector('.gradereport-grader-table').classList.remove('d-none');\n }, 10);\n }).then(() => pendingPromise.resolve()).catch(Notification.exception);\n }\n\n /**\n * The overall div that contains the searching widget.\n *\n * @returns {string}\n */\n componentSelector() {\n return '.collapse-columns';\n }\n\n /**\n * The dropdown div that contains the searching widget result space.\n *\n * @returns {string}\n */\n dropdownSelector() {\n return '.searchresultitemscontainer';\n }\n\n /**\n * The triggering div that contains the searching widget.\n *\n * @returns {string}\n */\n triggerSelector() {\n return '.collapsecolumn';\n }\n\n /**\n * Return the dataset that we will be searching upon.\n *\n * @returns {Array}\n */\n getDataset() {\n if (!this.dataset) {\n const cols = this.fetchDataset();\n this.dataset = JSON.parse(cols) ? JSON.parse(cols).split(',') : [];\n }\n this.datasetSize = this.dataset.length;\n return this.dataset;\n }\n\n /**\n * Get the data we will be searching against in this component.\n *\n * @returns {string}\n */\n fetchDataset() {\n return storage.get(`gradereport_grader_collapseditems_${this.courseID}_${this.userID}`);\n }\n\n /**\n * Given a user performs an action, update the users' preferences.\n */\n setPreferences() {\n storage.set(`gradereport_grader_collapseditems_${this.courseID}_${this.userID}`,\n JSON.stringify(this.getDataset().join(','))\n );\n }\n\n /**\n * Register clickable event listeners.\n */\n registerClickHandlers() {\n // Register click events within the component.\n this.component.addEventListener('click', this.clickHandler.bind(this));\n\n document.addEventListener('click', this.docClickHandler.bind(this));\n }\n\n /**\n * The handler for when a user interacts with the component.\n *\n * @param {MouseEvent} e The triggering event that we are working with.\n */\n clickHandler(e) {\n super.clickHandler(e);\n // Prevent BS from closing the dropdown if they click elsewhere within the dropdown besides the form.\n if (e.target.closest(selectors.fullDropdown)) {\n e.stopPropagation();\n }\n }\n\n /**\n * Externally defined click function to improve memory handling.\n *\n * @param {MouseEvent} e\n * @returns {Promise}\n */\n async docClickHandler(e) {\n if (e.target.dataset.hider === selectors.hider) {\n e.preventDefault();\n const desiredToHide = e.target.closest(selectors.colVal) ?\n e.target.closest(selectors.colVal)?.dataset.col :\n e.target.closest(selectors.itemVal)?.dataset.itemid;\n const idx = this.getDataset().indexOf(desiredToHide);\n if (idx === -1) {\n this.getDataset().push(desiredToHide);\n }\n await this.prefcountpipe();\n\n this.nodesUpdate(desiredToHide);\n }\n\n if (e.target.closest('button')?.dataset.hider === selectors.expand) {\n e.preventDefault();\n const desiredToHide = e.target.closest(selectors.colVal) ?\n e.target.closest(selectors.colVal)?.dataset.col :\n e.target.closest(selectors.itemVal)?.dataset.itemid;\n const idx = this.getDataset().indexOf(desiredToHide);\n this.getDataset().splice(idx, 1);\n\n await this.prefcountpipe();\n\n this.nodesUpdate(e.target.closest(selectors.colVal)?.dataset.col);\n this.nodesUpdate(e.target.closest(selectors.colVal)?.dataset.itemid);\n }\n }\n\n /**\n * The handler for when a user presses a key within the component.\n *\n * @param {KeyboardEvent} e The triggering event that we are working with.\n */\n async keyHandler(e) {\n super.keyHandler(e);\n\n // Switch the key presses to handle keyboard nav.\n switch (e.key) {\n case 'Tab':\n if (e.target.closest(this.selectors.input)) {\n if (!this.clearSearchButton.classList.contains('d-none')) {\n e.preventDefault();\n this.clearSearchButton.focus({preventScroll: true});\n }\n }\n break;\n }\n }\n\n /**\n * Handle any keyboard inputs.\n */\n registerInputEvents() {\n // Register & handle the text input.\n this.searchInput.addEventListener('input', debounce(async() => {\n if (this.getSearchTerm() === this.searchInput.value && this.searchResultsVisible()) {\n window.console.warn(`Search term matches input value - skipping`);\n // Debounce can happen multiple times quickly.\n return;\n }\n this.setSearchTerms(this.searchInput.value);\n // We can also require a set amount of input before search.\n if (this.searchInput.value === '') {\n // Hide the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.add('d-none');\n } else {\n // Display the \"clear\" search button in the search bar.\n this.clearSearchButton.classList.remove('d-none');\n }\n const pendingPromise = new Pending();\n // User has given something for us to filter against.\n await this.filterrenderpipe().then(() => {\n pendingPromise.resolve();\n return true;\n });\n }, 300, {pending: true}));\n }\n\n /**\n * Handle the form submission within the dropdown.\n */\n registerFormEvents() {\n const form = this.component.querySelector(selectors.formDropdown);\n const events = [\n 'click',\n CustomEvents.events.activate,\n CustomEvents.events.keyboardActivate\n ];\n CustomEvents.define(document, events);\n\n const selectall = form.querySelector('[data-action=\"selectall\"]');\n\n // Register clicks & keyboard form handling.\n events.forEach((event) => {\n const submitBtn = form.querySelector(`[data-action=\"${selectors.formItems.save}\"`);\n form.addEventListener(event, (e) => {\n // Stop Bootstrap from being clever.\n e.stopPropagation();\n const input = e.target.closest('input');\n if (input) {\n // If the user is unchecking an item, we need to uncheck the select all if it's checked.\n if (selectall.checked && !input.checked) {\n selectall.checked = false;\n }\n const checkedCount = Array.from(form.querySelectorAll(selectors.formItems.checked)).length;\n // Check if any are clicked or not then change disabled.\n submitBtn.disabled = checkedCount <= 0;\n }\n }, false);\n\n // Stop Bootstrap from being clever.\n this.searchInput.addEventListener(event, e => e.stopPropagation());\n this.clearSearchButton.addEventListener(event, async(e) => {\n e.stopPropagation();\n this.searchInput.value = '';\n this.setSearchTerms(this.searchInput.value);\n await this.filterrenderpipe();\n });\n selectall.addEventListener(event, (e) => {\n // Stop Bootstrap from being clever.\n e.stopPropagation();\n if (!selectall.checked) {\n const touncheck = Array.from(form.querySelectorAll(selectors.formItems.checked));\n touncheck.forEach(item => {\n item.checked = false;\n });\n submitBtn.disabled = true;\n } else {\n const currentUnchecked = Array.from(form.querySelectorAll(selectors.formItems.currentlyUnchecked));\n currentUnchecked.forEach(item => {\n item.checked = true;\n });\n submitBtn.disabled = false;\n }\n });\n });\n\n form.addEventListener('submit', async(e) => {\n e.preventDefault();\n if (e.submitter.dataset.action === selectors.formItems.cancel) {\n $(this.component).dropdown('toggle');\n return;\n }\n // Get the users' checked columns to change.\n const checkedItems = [...form.elements].filter(item => item.checked);\n checkedItems.forEach((item) => {\n const idx = this.getDataset().indexOf(item.dataset.collapse);\n this.getDataset().splice(idx, 1);\n this.nodesUpdate(item.dataset.collapse);\n });\n // Reset the check all & submit to false just in case.\n selectall.checked = false;\n e.submitter.disabled = true;\n await this.prefcountpipe();\n });\n }\n\n nodesUpdate(item) {\n const colNodesToHide = [...document.querySelectorAll(`[data-col=\"${item}\"]`)];\n const itemIDNodesToHide = [...document.querySelectorAll(`[data-itemid=\"${item}\"]`)];\n this.nodes = [...colNodesToHide, ...itemIDNodesToHide];\n this.updateDisplay();\n }\n\n /**\n * Update the user preferences, count display then render the results.\n *\n * @returns {Promise}\n */\n async prefcountpipe() {\n this.setPreferences();\n this.countUpdate();\n await this.filterrenderpipe();\n }\n\n /**\n * Dictate to the search component how and what we want to match upon.\n *\n * @param {Array} filterableData\n * @returns {Array} An array of objects containing the system reference and the user readable value.\n */\n async filterDataset(filterableData) {\n const stringUserMap = await this.fetchRequiredUserStrings();\n const stringGradeMap = await this.fetchRequiredGradeStrings();\n // Custom user profile fields are not in our string map and need a bit of extra love.\n const customFieldMap = this.fetchCustomFieldValues();\n this.stringMap = new Map([...stringGradeMap, ...stringUserMap, ...customFieldMap]);\n\n const searching = filterableData.map(s => {\n const mapObj = this.stringMap.get(s);\n if (mapObj === undefined) {\n return {key: s, string: s};\n }\n return {\n key: s,\n string: mapObj.itemname ?? this.stringMap.get(s),\n category: mapObj.category ?? '',\n };\n });\n // Sometimes we just want to show everything.\n if (this.getPreppedSearchTerm() === '') {\n return searching;\n }\n // Other times we want to actually filter the content.\n return searching.filter((col) => {\n return col.string.toString().toLowerCase().includes(this.getPreppedSearchTerm());\n });\n }\n\n /**\n * Given we have a subset of the dataset, set the field that we matched upon to inform the end user.\n */\n filterMatchDataset() {\n this.setMatchedResults(\n this.getMatchedResults().map((column) => {\n return {\n name: column.key,\n displayName: column.string ?? column.key,\n category: column.category ?? '',\n };\n })\n );\n }\n\n /**\n * With an array of nodes, switch their classes and values.\n */\n updateDisplay() {\n this.nodes.forEach((element) => {\n const content = element.querySelector(selectors.content);\n const sort = element.querySelector(selectors.sort);\n const expandButton = element.querySelector(selectors.expandbutton);\n const rangeRowCell = element.querySelector(selectors.rangerowcell);\n const avgRowCell = element.querySelector(selectors.avgrowcell);\n const nodeSet = [\n element.querySelector(selectors.menu),\n element.querySelector(selectors.icons),\n content\n ];\n\n // This can be further improved to reduce redundant similar calls.\n if (element.classList.contains('cell')) {\n // The column is actively being sorted, lets reset that and reload the page.\n if (sort !== null) {\n window.location = this.defaultSort;\n }\n if (content === null) {\n // If it's not a content cell, it must be an overall average or a range cell.\n const rowCell = avgRowCell ?? rangeRowCell;\n\n rowCell?.classList.toggle('d-none');\n rowCell?.setAttribute('aria-hidden',\n rowCell?.classList.contains('d-none') ? 'true' : 'false');\n } else if (content.classList.contains('d-none')) {\n // We should always have content but some cells do not contain menus or other actions.\n element.classList.remove('collapsed');\n // If there are many nodes, apply the following.\n if (content.childNodes.length > 1) {\n content.classList.add('d-flex');\n }\n nodeSet.forEach(node => {\n node?.classList.remove('d-none');\n node?.setAttribute('aria-hidden', 'false');\n });\n expandButton?.classList.add('d-none');\n expandButton?.setAttribute('aria-hidden', 'true');\n } else {\n element.classList.add('collapsed');\n content.classList.remove('d-flex');\n nodeSet.forEach(node => {\n node?.classList.add('d-none');\n node?.setAttribute('aria-hidden', 'true');\n });\n expandButton?.classList.remove('d-none');\n expandButton?.setAttribute('aria-hidden', 'false');\n }\n }\n });\n }\n\n /**\n * Update the visual count of collapsed columns or hide the count all together.\n */\n countUpdate() {\n countIndicator.textContent = this.getDatasetSize();\n if (this.getDatasetSize() > 0) {\n this.component.parentElement.classList.add('d-flex');\n this.component.parentElement.classList.remove('d-none');\n } else {\n this.component.parentElement.classList.remove('d-flex');\n this.component.parentElement.classList.add('d-none');\n }\n }\n\n /**\n * Build the content then replace the node by default we want our form to exist.\n */\n async renderDefault() {\n this.setMatchedResults(await this.filterDataset(this.getDataset()));\n this.filterMatchDataset();\n\n // Update the collapsed button pill.\n this.countUpdate();\n const {html, js} = await renderForPromise('gradereport_grader/collapse/collapsebody', {\n 'results': this.getMatchedResults(),\n 'userid': this.userID,\n });\n replaceNode(selectors.placeholder, html, js);\n this.updateNodes();\n\n // Given we now have the body, we can set up more triggers.\n this.registerFormEvents();\n this.registerInputEvents();\n\n // Add a small BS listener so that we can set the focus correctly on open.\n this.$component.on('shown.bs.dropdown', () => {\n this.searchInput.focus({preventScroll: true});\n this.selectallEnable();\n });\n }\n\n /**\n * Build the content then replace the node.\n */\n async renderDropdown() {\n const {html, js} = await renderForPromise('gradereport_grader/collapse/collapseresults', {\n 'results': this.getMatchedResults(),\n 'searchTerm': this.getSearchTerm(),\n });\n this.selectallEnable();\n replaceNodeContents(this.getHTMLElements().searchDropdown, html, js);\n }\n\n /**\n * Given we render the dropdown, Determine if we want to enable the select all checkbox.\n */\n selectallEnable() {\n const form = this.component.querySelector(selectors.formDropdown);\n const selectall = form.querySelector('[data-action=\"selectall\"]');\n selectall.disabled = this.getMatchedResults().length === 0;\n }\n\n /**\n * If we have any custom user profile fields, grab their system & readable names to add to our string map.\n *\n * @returns {array} An array of associated string arrays ready for our map.\n */\n fetchCustomFieldValues() {\n const customFields = document.querySelectorAll('[data-collapse-name]');\n // Cast from NodeList to array to grab all the values.\n return [...customFields].map(field => [field.parentElement.dataset.col, field.dataset.collapseName]);\n }\n\n /**\n * Given the set of profile fields we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n fetchRequiredUserStrings() {\n if (!this.userStrings) {\n const requiredStrings = [\n 'username',\n 'firstname',\n 'lastname',\n 'email',\n 'city',\n 'country',\n 'department',\n 'institution',\n 'idnumber',\n 'phone1',\n 'phone2',\n ];\n this.userStrings = getStrings(requiredStrings.map((key) => ({key})))\n .then((stringArray) => new Map(\n requiredStrings.map((key, index) => ([key, stringArray[index]]))\n ));\n }\n return this.userStrings;\n }\n\n /**\n * Given the set of gradable items we can possibly search, fetch their strings,\n * so we can report to screen readers the field that matched.\n *\n * @returns {Promise}\n */\n fetchRequiredGradeStrings() {\n if (!this.gradeStrings) {\n this.gradeStrings = Repository.gradeItems(this.courseID)\n .then((result) => new Map(\n result.gradeItems.map(key => ([key.id, key]))\n ));\n }\n return this.gradeStrings;\n }\n}\n"],"names":["selectors","cancel","save","checked","currentlyUnchecked","countIndicator","document","querySelector","ColumnSearch","search_combobox","userID","courseID","defaultSort","constructor","component","pendingPromise","Pending","then","loader","setTimeout","getDataset","forEach","item","nodesUpdate","renderDefault","remove","classList","resolve","catch","Notification","exception","componentSelector","dropdownSelector","triggerSelector","this","dataset","cols","fetchDataset","JSON","parse","split","datasetSize","length","storage","get","setPreferences","set","stringify","join","registerClickHandlers","addEventListener","clickHandler","bind","docClickHandler","e","target","closest","stopPropagation","hider","preventDefault","desiredToHide","_e$target$closest","col","_e$target$closest2","itemid","indexOf","push","prefcountpipe","_e$target$closest4","_e$target$closest5","idx","splice","_e$target$closest6","_e$target$closest7","keyHandler","key","input","clearSearchButton","contains","focus","preventScroll","registerInputEvents","searchInput","async","getSearchTerm","value","searchResultsVisible","window","console","warn","setSearchTerms","add","filterrenderpipe","pending","registerFormEvents","form","events","CustomEvents","activate","keyboardActivate","define","selectall","event","submitBtn","checkedCount","Array","from","querySelectorAll","disabled","submitter","action","dropdown","elements","filter","collapse","colNodesToHide","itemIDNodesToHide","nodes","updateDisplay","countUpdate","filterableData","stringUserMap","fetchRequiredUserStrings","stringGradeMap","fetchRequiredGradeStrings","customFieldMap","fetchCustomFieldValues","stringMap","Map","searching","map","s","mapObj","undefined","string","itemname","category","getPreppedSearchTerm","toString","toLowerCase","includes","filterMatchDataset","setMatchedResults","getMatchedResults","column","name","displayName","element","content","sort","expandButton","rangeRowCell","avgRowCell","nodeSet","location","rowCell","toggle","setAttribute","childNodes","node","textContent","getDatasetSize","parentElement","filterDataset","html","js","updateNodes","$component","on","selectallEnable","getHTMLElements","searchDropdown","field","collapseName","userStrings","requiredStrings","stringArray","index","gradeStrings","Repository","gradeItems","result","id"],"mappings":"8/DAmCMA,oBACS,oBADTA,uBAEY,uBAFZA,oBAGS,CACPC,OAAQ,SACRC,KAAM,OACNC,QAAS,iCACTC,mBAAoB,yDAPtBJ,gBASK,OATLA,iBAUM,SAVNA,iBAWM,aAXNA,kBAYO,gBAZPA,kBAaO,4BAbPA,eAcI,yBAdJA,uBAeY,iCAfZA,uBAgBY,iCAhBZA,qBAiBU,+BAjBVA,eAkBI,yBAlBJA,gBAmBK,4BAnBLA,gBAoBK,0BApBLA,sBAqBW,sDArBXA,uBAsBY,0BAGZK,eAAiBC,SAASC,cAAcP,uBAEzBQ,qBAAqBC,qCAY1BC,OAAQC,SAAUC,oBACnB,IAAIJ,aAAaE,OAAQC,SAAUC,aAG9CC,YAAYH,OAAQC,SAAUC,oDAdpB,mCACC,yCACG,iCAEN,wCAEO,yCACD,uCACF,SAQHF,OAASA,YACTC,SAAWA,cACXC,YAAcA,iBACdE,UAAYR,SAASC,cAAcP,2BAElCe,eAAiB,IAAIC,qDAERV,SAASC,cAAc,iBAAiBU,MAAMC,SAC7DC,YAAW,UAEFC,aAAaC,SAASC,YAClBC,YAAYD,cAEhBE,gBAGLN,OAAOO,SACPnB,SAASC,cAAc,6BAA6BmB,UAAUD,OAAO,YACtE,OACJR,MAAK,IAAMF,eAAeY,YAAWC,MAAMC,sBAAaC,WAQ/DC,0BACW,oBAQXC,yBACW,8BAQXC,wBACW,kBAQXb,iBACSc,KAAKC,QAAS,OACTC,KAAOF,KAAKG,oBACbF,QAAUG,KAAKC,MAAMH,MAAQE,KAAKC,MAAMH,MAAMI,MAAM,KAAO,eAE/DC,YAAcP,KAAKC,QAAQO,OACzBR,KAAKC,QAQhBE,sBACWM,sBAAQC,gDAAyCV,KAAKvB,qBAAYuB,KAAKxB,SAMlFmC,uCACYC,gDAAyCZ,KAAKvB,qBAAYuB,KAAKxB,QACnE4B,KAAKS,UAAUb,KAAKd,aAAa4B,KAAK,OAO9CC,6BAESnC,UAAUoC,iBAAiB,QAAShB,KAAKiB,aAAaC,KAAKlB,OAEhE5B,SAAS4C,iBAAiB,QAAShB,KAAKmB,gBAAgBD,KAAKlB,OAQjEiB,aAAaG,SACHH,aAAaG,GAEfA,EAAEC,OAAOC,QAAQxD,yBACjBsD,EAAEG,wCAUYH,6BACdA,EAAEC,OAAOpB,QAAQuB,QAAU1D,gBAAiB,0CAC5CsD,EAAEK,uBACIC,cAAgBN,EAAEC,OAAOC,QAAQxD,4CACnCsD,EAAEC,OAAOC,QAAQxD,sDAAjB6D,kBAAoC1B,QAAQ2B,+BAC5CR,EAAEC,OAAOC,QAAQxD,wDAAjB+D,mBAAqC5B,QAAQ6B,QAEpC,IADD9B,KAAKd,aAAa6C,QAAQL,qBAE7BxC,aAAa8C,KAAKN,qBAErB1B,KAAKiC,qBAEN5C,YAAYqC,8CAGjBN,EAAEC,OAAOC,QAAQ,kEAAWrB,QAAQuB,SAAU1D,iBAAkB,iFAChEsD,EAAEK,uBACIC,cAAgBN,EAAEC,OAAOC,QAAQxD,6CACnCsD,EAAEC,OAAOC,QAAQxD,uDAAjBoE,mBAAoCjC,QAAQ2B,+BAC5CR,EAAEC,OAAOC,QAAQxD,wDAAjBqE,mBAAqClC,QAAQ6B,OAC3CM,IAAMpC,KAAKd,aAAa6C,QAAQL,oBACjCxC,aAAamD,OAAOD,IAAK,SAExBpC,KAAKiC,qBAEN5C,uCAAY+B,EAAEC,OAAOC,QAAQxD,uDAAjBwE,mBAAoCrC,QAAQ2B,UACxDvC,uCAAY+B,EAAEC,OAAOC,QAAQxD,uDAAjByE,mBAAoCtC,QAAQ6B,0BASpDV,YACPoB,WAAWpB,GAIR,QADDA,EAAEqB,IAEErB,EAAEC,OAAOC,QAAQtB,KAAKlC,UAAU4E,SAC3B1C,KAAK2C,kBAAkBnD,UAAUoD,SAAS,YAC3CxB,EAAEK,sBACGkB,kBAAkBE,MAAM,CAACC,eAAe,MAUjEC,2BAESC,YAAYhC,iBAAiB,SAAS,oBAASiC,aAC5CjD,KAAKkD,kBAAoBlD,KAAKgD,YAAYG,OAASnD,KAAKoD,mCACxDC,OAAOC,QAAQC,wDAIdC,eAAexD,KAAKgD,YAAYG,OAEN,KAA3BnD,KAAKgD,YAAYG,WAEZR,kBAAkBnD,UAAUiE,IAAI,eAGhCd,kBAAkBnD,UAAUD,OAAO,gBAEtCV,eAAiB,IAAIC,uBAErBkB,KAAK0D,mBAAmB3E,MAAK,KAC/BF,eAAeY,WACR,OAEZ,IAAK,CAACkE,SAAS,KAMtBC,2BACUC,KAAO7D,KAAKpB,UAAUP,cAAcP,wBACpCgG,OAAS,CACX,QACAC,mCAAaD,OAAOE,SACpBD,mCAAaD,OAAOG,qDAEXC,OAAO9F,SAAU0F,cAExBK,UAAYN,KAAKxF,cAAc,6BAGrCyF,OAAO3E,SAASiF,cACNC,UAAYR,KAAKxF,sCAA+BP,oBAAoBE,WAC1E6F,KAAK7C,iBAAiBoD,OAAQhD,IAE1BA,EAAEG,wBACImB,MAAQtB,EAAEC,OAAOC,QAAQ,YAC3BoB,MAAO,CAEHyB,UAAUlG,UAAYyE,MAAMzE,UAC5BkG,UAAUlG,SAAU,SAElBqG,aAAeC,MAAMC,KAAKX,KAAKY,iBAAiB3G,oBAAoBG,UAAUuC,OAEpF6D,UAAUK,SAAWJ,cAAgB,MAE1C,QAGEtB,YAAYhC,iBAAiBoD,OAAOhD,GAAKA,EAAEG,yBAC3CoB,kBAAkB3B,iBAAiBoD,OAAOnB,MAAAA,IAC3C7B,EAAEG,uBACGyB,YAAYG,MAAQ,QACpBK,eAAexD,KAAKgD,YAAYG,aAC/BnD,KAAK0D,sBAEfS,UAAUnD,iBAAiBoD,OAAQhD,OAE/BA,EAAEG,kBACG4C,UAAUlG,QAMR,CACsBsG,MAAMC,KAAKX,KAAKY,iBAAiB3G,oBAAoBI,qBAC7DiB,SAAQC,OACrBA,KAAKnB,SAAU,KAEnBoG,UAAUK,UAAW,MAXD,CACFH,MAAMC,KAAKX,KAAKY,iBAAiB3G,oBAAoBG,UAC7DkB,SAAQC,OACdA,KAAKnB,SAAU,KAEnBoG,UAAUK,UAAW,SAWjCb,KAAK7C,iBAAiB,UAAUiC,MAAAA,OAC5B7B,EAAEK,iBACEL,EAAEuD,UAAU1E,QAAQ2E,SAAW9G,oBAAoBC,sCACjDiC,KAAKpB,WAAWiG,SAAS,UAIV,IAAIhB,KAAKiB,UAAUC,QAAO3F,MAAQA,KAAKnB,UAC/CkB,SAASC,aACZgD,IAAMpC,KAAKd,aAAa6C,QAAQ3C,KAAKa,QAAQ+E,eAC9C9F,aAAamD,OAAOD,IAAK,QACzB/C,YAAYD,KAAKa,QAAQ+E,aAGlCb,UAAUlG,SAAU,EACpBmD,EAAEuD,UAAUD,UAAW,QACjB1E,KAAKiC,mBAInB5C,YAAYD,YACF6F,eAAiB,IAAI7G,SAASqG,sCAA+BrF,aAC7D8F,kBAAoB,IAAI9G,SAASqG,yCAAkCrF,kBACpE+F,MAAQ,IAAIF,kBAAmBC,wBAC/BE,2CASAzE,sBACA0E,oBACCrF,KAAK0D,uCASK4B,sBACVC,oBAAsBvF,KAAKwF,2BAC3BC,qBAAuBzF,KAAK0F,4BAE5BC,eAAiB3F,KAAK4F,8BACvBC,UAAY,IAAIC,IAAI,IAAIL,kBAAmBF,iBAAkBI,uBAE5DI,UAAYT,eAAeU,KAAIC,gDAC3BC,OAASlG,KAAK6F,UAAUnF,IAAIuF,eACnBE,IAAXD,OACO,CAACzD,IAAKwD,EAAGG,OAAQH,GAErB,CACHxD,IAAKwD,EACLG,gCAAQF,OAAOG,sDAAYrG,KAAK6F,UAAUnF,IAAIuF,GAC9CK,kCAAUJ,OAAOI,sDAAY,aAID,KAAhCtG,KAAKuG,uBACER,UAGJA,UAAUhB,QAAQnD,KACdA,IAAIwE,OAAOI,WAAWC,cAAcC,SAAS1G,KAAKuG,0BAOjEI,0BACSC,kBACD5G,KAAK6G,oBAAoBb,KAAKc,mDACnB,CACHC,KAAMD,OAAOrE,IACbuE,mCAAaF,OAAOV,gDAAUU,OAAOrE,IACrC6D,kCAAUQ,OAAOR,sDAAY,QAS7ClB,qBACSD,MAAMhG,SAAS8H,gBACVC,QAAUD,QAAQ5I,cAAcP,mBAChCqJ,KAAOF,QAAQ5I,cAAcP,gBAC7BsJ,aAAeH,QAAQ5I,cAAcP,wBACrCuJ,aAAeJ,QAAQ5I,cAAcP,wBACrCwJ,WAAaL,QAAQ5I,cAAcP,sBACnCyJ,QAAU,CACZN,QAAQ5I,cAAcP,gBACtBmJ,QAAQ5I,cAAcP,iBACtBoJ,YAIAD,QAAQzH,UAAUoD,SAAS,WAEd,OAATuE,OACA9D,OAAOmE,SAAWxH,KAAKtB,aAEX,OAAZwI,QAAkB,OAEZO,QAAUH,MAAAA,WAAAA,WAAcD,aAE9BI,MAAAA,SAAAA,QAASjI,UAAUkI,OAAO,UAC1BD,MAAAA,SAAAA,QAASE,aAAa,cAClBF,MAAAA,SAAAA,QAASjI,UAAUoD,SAAS,UAAY,OAAS,cAC9CsE,QAAQ1H,UAAUoD,SAAS,WAElCqE,QAAQzH,UAAUD,OAAO,aAErB2H,QAAQU,WAAWpH,OAAS,GAC5B0G,QAAQ1H,UAAUiE,IAAI,UAE1B8D,QAAQpI,SAAQ0I,OACZA,MAAAA,MAAAA,KAAMrI,UAAUD,OAAO,UACvBsI,MAAAA,MAAAA,KAAMF,aAAa,cAAe,YAEtCP,MAAAA,cAAAA,aAAc5H,UAAUiE,IAAI,UAC5B2D,MAAAA,cAAAA,aAAcO,aAAa,cAAe,UAE1CV,QAAQzH,UAAUiE,IAAI,aACtByD,QAAQ1H,UAAUD,OAAO,UACzBgI,QAAQpI,SAAQ0I,OACZA,MAAAA,MAAAA,KAAMrI,UAAUiE,IAAI,UACpBoE,MAAAA,MAAAA,KAAMF,aAAa,cAAe,WAEtCP,MAAAA,cAAAA,aAAc5H,UAAUD,OAAO,UAC/B6H,MAAAA,cAAAA,aAAcO,aAAa,cAAe,aAS1DtC,cACIlH,eAAe2J,YAAc9H,KAAK+H,iBAC9B/H,KAAK+H,iBAAmB,QACnBnJ,UAAUoJ,cAAcxI,UAAUiE,IAAI,eACtC7E,UAAUoJ,cAAcxI,UAAUD,OAAO,iBAEzCX,UAAUoJ,cAAcxI,UAAUD,OAAO,eACzCX,UAAUoJ,cAAcxI,UAAUiE,IAAI,sCAQ1CmD,wBAAwB5G,KAAKiI,cAAcjI,KAAKd,oBAChDyH,0BAGAtB,oBACC6C,KAACA,KAADC,GAAOA,UAAY,+BAAiB,2CAA4C,SACvEnI,KAAK6G,2BACN7G,KAAKxB,oCAEPV,sBAAuBoK,KAAMC,SACpCC,mBAGAxE,0BACAb,2BAGAsF,WAAWC,GAAG,qBAAqB,UAC/BtF,YAAYH,MAAM,CAACC,eAAe,SAClCyF,kDAQHL,KAACA,KAADC,GAAOA,UAAY,+BAAiB,8CAA+C,SAC1EnI,KAAK6G,+BACF7G,KAAKkD,uBAElBqF,qDACevI,KAAKwI,kBAAkBC,eAAgBP,KAAMC,IAMrEI,kBACiBvI,KAAKpB,UAAUP,cAAcP,wBACnBO,cAAc,6BAC3BqG,SAA+C,IAApC1E,KAAK6G,oBAAoBrG,OAQlDoF,+BAGW,IAFcxH,SAASqG,iBAAiB,yBAEtBuB,KAAI0C,OAAS,CAACA,MAAMV,cAAc/H,QAAQ2B,IAAK8G,MAAMzI,QAAQ0I,gBAS1FnD,+BACSxF,KAAK4I,YAAa,OACbC,gBAAkB,CACpB,WACA,YACA,WACA,QACA,OACA,UACA,aACA,cACA,WACA,SACA,eAECD,aAAc,mBAAWC,gBAAgB7C,KAAKvD,OAAUA,IAAAA,SACxD1D,MAAM+J,aAAgB,IAAIhD,IACvB+C,gBAAgB7C,KAAI,CAACvD,IAAKsG,QAAW,CAACtG,IAAKqG,YAAYC,oBAG5D/I,KAAK4I,YAShBlD,mCACS1F,KAAKgJ,oBACDA,aAAeC,WAAWC,WAAWlJ,KAAKvB,UAC1CM,MAAMoK,QAAW,IAAIrD,IAClBqD,OAAOD,WAAWlD,KAAIvD,KAAQ,CAACA,IAAI2G,GAAI3G,WAG5CzC,KAAKgJ"} \ No newline at end of file diff --git a/grade/report/grader/amd/src/collapse.js b/grade/report/grader/amd/src/collapse.js index 7599203cef48b..4ba1f0c1ba158 100644 --- a/grade/report/grader/amd/src/collapse.js +++ b/grade/report/grader/amd/src/collapse.js @@ -230,8 +230,10 @@ export default class ColumnSearch extends search_combobox { switch (e.key) { case 'Tab': if (e.target.closest(this.selectors.input)) { - e.preventDefault(); - this.clearSearchButton.focus({preventScroll: true}); + if (!this.clearSearchButton.classList.contains('d-none')) { + e.preventDefault(); + this.clearSearchButton.focus({preventScroll: true}); + } } break; } @@ -505,6 +507,7 @@ export default class ColumnSearch extends search_combobox { // Add a small BS listener so that we can set the focus correctly on open. this.$component.on('shown.bs.dropdown', () => { this.searchInput.focus({preventScroll: true}); + this.selectallEnable(); }); } @@ -512,16 +515,23 @@ export default class ColumnSearch extends search_combobox { * Build the content then replace the node. */ async renderDropdown() { - const form = this.component.querySelector(selectors.formDropdown); - const selectall = form.querySelector('[data-action="selectall"]'); const {html, js} = await renderForPromise('gradereport_grader/collapse/collapseresults', { 'results': this.getMatchedResults(), 'searchTerm': this.getSearchTerm(), }); - selectall.disabled = this.getMatchedResults().length === 0; + this.selectallEnable(); replaceNodeContents(this.getHTMLElements().searchDropdown, html, js); } + /** + * Given we render the dropdown, Determine if we want to enable the select all checkbox. + */ + selectallEnable() { + const form = this.component.querySelector(selectors.formDropdown); + const selectall = form.querySelector('[data-action="selectall"]'); + selectall.disabled = this.getMatchedResults().length === 0; + } + /** * If we have any custom user profile fields, grab their system & readable names to add to our string map. * diff --git a/grade/report/grader/templates/collapse/collapseresultitems.mustache b/grade/report/grader/templates/collapse/collapseresultitems.mustache index d883c1ff02b79..e2fce0fbbd6a2 100644 --- a/grade/report/grader/templates/collapse/collapseresultitems.mustache +++ b/grade/report/grader/templates/collapse/collapseresultitems.mustache @@ -27,8 +27,8 @@ } }}

  • - -