diff --git a/availability/condition/date/classes/frontend.php b/availability/condition/date/classes/frontend.php index abca3e6446b..ce91d037990 100644 --- a/availability/condition/date/classes/frontend.php +++ b/availability/condition/date/classes/frontend.php @@ -49,7 +49,7 @@ class frontend extends \core_availability\frontend { protected function get_javascript_strings() { return array('ajaxerror', 'direction_before', 'direction_from', 'direction_until', - 'direction_label'); + 'direction_label', 'error_dateconflict'); } /** diff --git a/availability/condition/date/lang/en/availability_date.php b/availability/condition/date/lang/en/availability_date.php index 98072a0d57f..9f0fc6c2889 100644 --- a/availability/condition/date/lang/en/availability_date.php +++ b/availability/condition/date/lang/en/availability_date.php @@ -28,6 +28,7 @@ $string['direction_from'] = 'from'; $string['direction_label'] = 'Direction'; $string['direction_until'] = 'until'; $string['description'] = 'Prevent access until (or from) a specified date and time.'; +$string['error_dateconflict'] = 'Conflicts with other date restrictions'; $string['full_from'] = 'It is after {$a}'; $string['full_from_date'] = 'It is on or after {$a}'; $string['full_until'] = 'It is before {$a}'; diff --git a/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-debug.js b/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-debug.js index c4eb06b60d1..e1ab688d539 100644 --- a/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-debug.js +++ b/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-debug.js @@ -142,5 +142,84 @@ M.availability_date.form.fillValue = function(value, node) { value.t = parseInt(node.getData('time'), 10); }; +/** + * List out Date node value in an array node. + * + * This will go through all array node and list from earlier date node to current date node. + * + * @method convertTreeDateValue + * @param {array} tree Tree node to convert + * @param {array} arrayDateNode + * @param {array} currentNode current node. + * + * @return {array} arrayDateNode + */ +M.availability_date.form.convertTreeDateValue = function(tree, arrayDateNode, currentNode) { + var shouldSkip = false; + tree.forEach(function(node) { + if (shouldSkip) { + return; + } + if (node.type === 'date') { + // We go through all tree node, if we meet the current node then return. + if (node.t === parseInt(currentNode.getData('time'), 10) + && currentNode.one('select[name=direction]').get('value') == node.d) { + shouldSkip = true; + return; + } + arrayDateNode.push(node); + } else if (node.type === undefined) { + M.availability_date.form.convertTreeDateValue(node.c, arrayDateNode, currentNode); + } + }); + return arrayDateNode; +}; + +/** + * Check current node. + * + * This will check current date node with all date node in tree node. + * + * @method checkConditionDate + * @param {array} currentNode The curent node. + * + * @return {boolean} error Return true if the date is conflict. + */ +M.availability_date.form.checkConditionDate = function(currentNode) { + var error = false; + if (M.core_availability.form.rootList.getValue().op === '&') { + var jsValue = M.core_availability.form.rootList.getValue().c; + var arrayDateNode = M.availability_date.form.convertTreeDateValue(jsValue, [], currentNode); + var currentNodeDirection = currentNode.one('select[name=direction]').get('value'); + var currentNodeTime = parseInt(currentNode.getData('time'), 10); + arrayDateNode.forEach(function(checkNode) { + // Validate if the date is conflict. + if (checkNode.d === '<') { + if (currentNodeDirection === '>=' && currentNodeTime >= checkNode.t) { + error = true; + } + } else { + if (currentNodeDirection === '<' && currentNodeTime <= checkNode.t) { + error = true; + } + } + return error; + }); + return error; + } else { + if (currentNode.one('div > .badge-warning')) { + currentNode.one('div > .badge-warning').remove(); + } + return error; + } +}; + +M.availability_date.form.fillErrors = function(errors, node) { + var error = M.availability_date.form.checkConditionDate(node); + if (error) { + errors.push('availability_date:error_dateconflict'); + } +}; + }, '@VERSION@', {"requires": ["base", "node", "event", "io", "moodle-core_availability-form"]}); diff --git a/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-min.js b/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-min.js index 04b294fb600..57b4ba56ae3 100644 --- a/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-min.js +++ b/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form-min.js @@ -1 +1 @@ -YUI.add("moodle-availability_date-form",function(o,e){M.availability_date=M.availability_date||{},M.availability_date.form=o.Object(M.core_availability.plugin),M.availability_date.form.initInner=function(e,a){this.html=e,this.defaultTime=a},M.availability_date.form.getNode=function(e){var t,i,a=''+M.util.get_string("direction_before","availability_date")+' "+this.html,n=o.Node.create(""+a+"");return e.t!==undefined?(n.setData("time",e.t),n.all("select:not([name=direction])").each(function(e){e.set("disabled",!0)}),a=M.cfg.wwwroot+"/availability/condition/date/ajax.php?action=fromtime&time="+e.t,o.io(a,{on:{success:function(e,a){var t,i,l=o.JSON.parse(a.responseText);for(t in l)(i=n.one("select[name=x\\["+t+"\\]]")).set("value",""+l[t]),i.set("disabled",!1)},failure:function(){window.alert(M.util.get_string("ajaxerror","availability_date"))}}})):n.setData("time",this.defaultTime),e.d!==undefined&&n.one("select[name=direction]").set("value",e.d),M.availability_date.form.addedEvents||(M.availability_date.form.addedEvents=!0,(a=o.one(".availability-field")).delegate("change",function(){M.core_availability.form.update()},".availability_date select[name=direction]"),a.delegate("change",function(){M.availability_date.form.updateTime(this.ancestor("span.availability_date"))},".availability_date select:not([name=direction])")),n.one("a[href=#]")&&(M.form.dateselector.init_single_date_selector(n),t=n.one("select[name=x\\[year\\]]"),i=t.set,t.set=function(e,a){i.call(t,e,a),"selectedIndex"===e&&setTimeout(function(){M.availability_date.form.updateTime(n)},0)}),n},M.availability_date.form.updateTime=function(t){var e=M.cfg.wwwroot+"/availability/condition/date/ajax.php?action=totime&year="+t.one("select[name=x\\[year\\]]").get("value")+"&month="+t.one("select[name=x\\[month\\]]").get("value")+"&day="+t.one("select[name=x\\[day\\]]").get("value")+"&hour="+t.one("select[name=x\\[hour\\]]").get("value")+"&minute="+t.one("select[name=x\\[minute\\]]").get("value");o.io(e,{on:{success:function(e,a){t.setData("time",a.responseText),M.core_availability.form.update()},failure:function(){window.alert(M.util.get_string("ajaxerror","availability_date"))}}})},M.availability_date.form.fillValue=function(e,a){e.d=a.one("select[name=direction]").get("value"),e.t=parseInt(a.getData("time"),10)}},"@VERSION@",{requires:["base","node","event","io","moodle-core_availability-form"]}); \ No newline at end of file +YUI.add("moodle-availability_date-form",function(o,e){M.availability_date=M.availability_date||{},M.availability_date.form=o.Object(M.core_availability.plugin),M.availability_date.form.initInner=function(e,a){this.html=e,this.defaultTime=a},M.availability_date.form.getNode=function(e){var t,i,a=''+M.util.get_string("direction_before","availability_date")+' "+this.html,n=o.Node.create(""+a+"");return e.t!==undefined?(n.setData("time",e.t),n.all("select:not([name=direction])").each(function(e){e.set("disabled",!0)}),a=M.cfg.wwwroot+"/availability/condition/date/ajax.php?action=fromtime&time="+e.t,o.io(a,{on:{success:function(e,a){var t,i,l=o.JSON.parse(a.responseText);for(t in l)(i=n.one("select[name=x\\["+t+"\\]]")).set("value",""+l[t]),i.set("disabled",!1)},failure:function(){window.alert(M.util.get_string("ajaxerror","availability_date"))}}})):n.setData("time",this.defaultTime),e.d!==undefined&&n.one("select[name=direction]").set("value",e.d),M.availability_date.form.addedEvents||(M.availability_date.form.addedEvents=!0,(a=o.one(".availability-field")).delegate("change",function(){M.core_availability.form.update()},".availability_date select[name=direction]"),a.delegate("change",function(){M.availability_date.form.updateTime(this.ancestor("span.availability_date"))},".availability_date select:not([name=direction])")),n.one("a[href=#]")&&(M.form.dateselector.init_single_date_selector(n),t=n.one("select[name=x\\[year\\]]"),i=t.set,t.set=function(e,a){i.call(t,e,a),"selectedIndex"===e&&setTimeout(function(){M.availability_date.form.updateTime(n)},0)}),n},M.availability_date.form.updateTime=function(t){var e=M.cfg.wwwroot+"/availability/condition/date/ajax.php?action=totime&year="+t.one("select[name=x\\[year\\]]").get("value")+"&month="+t.one("select[name=x\\[month\\]]").get("value")+"&day="+t.one("select[name=x\\[day\\]]").get("value")+"&hour="+t.one("select[name=x\\[hour\\]]").get("value")+"&minute="+t.one("select[name=x\\[minute\\]]").get("value");o.io(e,{on:{success:function(e,a){t.setData("time",a.responseText),M.core_availability.form.update()},failure:function(){window.alert(M.util.get_string("ajaxerror","availability_date"))}}})},M.availability_date.form.fillValue=function(e,a){e.d=a.one("select[name=direction]").get("value"),e.t=parseInt(a.getData("time"),10)},M.availability_date.form.convertTreeDateValue=function(e,a,t){var i=!1;return e.forEach(function(e){i||("date"===e.type?e.t!==parseInt(t.getData("time"),10)||t.one("select[name=direction]").get("value")!=e.d?a.push(e):i=!0:e.type===undefined&&M.availability_date.form.convertTreeDateValue(e.c,a,t))}),a},M.availability_date.form.checkConditionDate=function(e){var a,t,i,l=!1;return"&"===M.core_availability.form.rootList.getValue().op?(a=M.core_availability.form.rootList.getValue().c,a=M.availability_date.form.convertTreeDateValue(a,[],e),t=e.one("select[name=direction]").get("value"),i=parseInt(e.getData("time"),10),a.forEach(function(e){return"<"===e.d?">="===t&&i>=e.t&&(l=!0):"<"===t&&i<=e.t&&(l=!0),l})):e.one("div > .badge-warning")&&e.one("div > .badge-warning").remove(),l},M.availability_date.form.fillErrors=function(e,a){M.availability_date.form.checkConditionDate(a)&&e.push("availability_date:error_dateconflict")}},"@VERSION@",{requires:["base","node","event","io","moodle-core_availability-form"]}); \ No newline at end of file diff --git a/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form.js b/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form.js index c4eb06b60d1..e1ab688d539 100644 --- a/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form.js +++ b/availability/condition/date/yui/build/moodle-availability_date-form/moodle-availability_date-form.js @@ -142,5 +142,84 @@ M.availability_date.form.fillValue = function(value, node) { value.t = parseInt(node.getData('time'), 10); }; +/** + * List out Date node value in an array node. + * + * This will go through all array node and list from earlier date node to current date node. + * + * @method convertTreeDateValue + * @param {array} tree Tree node to convert + * @param {array} arrayDateNode + * @param {array} currentNode current node. + * + * @return {array} arrayDateNode + */ +M.availability_date.form.convertTreeDateValue = function(tree, arrayDateNode, currentNode) { + var shouldSkip = false; + tree.forEach(function(node) { + if (shouldSkip) { + return; + } + if (node.type === 'date') { + // We go through all tree node, if we meet the current node then return. + if (node.t === parseInt(currentNode.getData('time'), 10) + && currentNode.one('select[name=direction]').get('value') == node.d) { + shouldSkip = true; + return; + } + arrayDateNode.push(node); + } else if (node.type === undefined) { + M.availability_date.form.convertTreeDateValue(node.c, arrayDateNode, currentNode); + } + }); + return arrayDateNode; +}; + +/** + * Check current node. + * + * This will check current date node with all date node in tree node. + * + * @method checkConditionDate + * @param {array} currentNode The curent node. + * + * @return {boolean} error Return true if the date is conflict. + */ +M.availability_date.form.checkConditionDate = function(currentNode) { + var error = false; + if (M.core_availability.form.rootList.getValue().op === '&') { + var jsValue = M.core_availability.form.rootList.getValue().c; + var arrayDateNode = M.availability_date.form.convertTreeDateValue(jsValue, [], currentNode); + var currentNodeDirection = currentNode.one('select[name=direction]').get('value'); + var currentNodeTime = parseInt(currentNode.getData('time'), 10); + arrayDateNode.forEach(function(checkNode) { + // Validate if the date is conflict. + if (checkNode.d === '<') { + if (currentNodeDirection === '>=' && currentNodeTime >= checkNode.t) { + error = true; + } + } else { + if (currentNodeDirection === '<' && currentNodeTime <= checkNode.t) { + error = true; + } + } + return error; + }); + return error; + } else { + if (currentNode.one('div > .badge-warning')) { + currentNode.one('div > .badge-warning').remove(); + } + return error; + } +}; + +M.availability_date.form.fillErrors = function(errors, node) { + var error = M.availability_date.form.checkConditionDate(node); + if (error) { + errors.push('availability_date:error_dateconflict'); + } +}; + }, '@VERSION@', {"requires": ["base", "node", "event", "io", "moodle-core_availability-form"]}); diff --git a/availability/condition/date/yui/src/form/js/form.js b/availability/condition/date/yui/src/form/js/form.js index 1d1656c6ac2..ca0ca736d44 100644 --- a/availability/condition/date/yui/src/form/js/form.js +++ b/availability/condition/date/yui/src/form/js/form.js @@ -139,3 +139,82 @@ M.availability_date.form.fillValue = function(value, node) { value.d = node.one('select[name=direction]').get('value'); value.t = parseInt(node.getData('time'), 10); }; + +/** + * List out Date node value in an array node. + * + * This will go through all array node and list from earlier date node to current date node. + * + * @method convertTreeDateValue + * @param {array} tree Tree node to convert + * @param {array} arrayDateNode + * @param {array} currentNode current node. + * + * @return {array} arrayDateNode + */ +M.availability_date.form.convertTreeDateValue = function(tree, arrayDateNode, currentNode) { + var shouldSkip = false; + tree.forEach(function(node) { + if (shouldSkip) { + return; + } + if (node.type === 'date') { + // We go through all tree node, if we meet the current node then return. + if (node.t === parseInt(currentNode.getData('time'), 10) + && currentNode.one('select[name=direction]').get('value') == node.d) { + shouldSkip = true; + return; + } + arrayDateNode.push(node); + } else if (node.type === undefined) { + M.availability_date.form.convertTreeDateValue(node.c, arrayDateNode, currentNode); + } + }); + return arrayDateNode; +}; + +/** + * Check current node. + * + * This will check current date node with all date node in tree node. + * + * @method checkConditionDate + * @param {array} currentNode The curent node. + * + * @return {boolean} error Return true if the date is conflict. + */ +M.availability_date.form.checkConditionDate = function(currentNode) { + var error = false; + if (M.core_availability.form.rootList.getValue().op === '&') { + var jsValue = M.core_availability.form.rootList.getValue().c; + var arrayDateNode = M.availability_date.form.convertTreeDateValue(jsValue, [], currentNode); + var currentNodeDirection = currentNode.one('select[name=direction]').get('value'); + var currentNodeTime = parseInt(currentNode.getData('time'), 10); + arrayDateNode.forEach(function(checkNode) { + // Validate if the date is conflict. + if (checkNode.d === '<') { + if (currentNodeDirection === '>=' && currentNodeTime >= checkNode.t) { + error = true; + } + } else { + if (currentNodeDirection === '<' && currentNodeTime <= checkNode.t) { + error = true; + } + } + return error; + }); + return error; + } else { + if (currentNode.one('div > .badge-warning')) { + currentNode.one('div > .badge-warning').remove(); + } + return error; + } +}; + +M.availability_date.form.fillErrors = function(errors, node) { + var error = M.availability_date.form.checkConditionDate(node); + if (error) { + errors.push('availability_date:error_dateconflict'); + } +}; diff --git a/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-debug.js b/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-debug.js index f7eb56944a4..34c0df0af14 100644 --- a/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-debug.js +++ b/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-debug.js @@ -971,7 +971,21 @@ M.core_availability.Item.prototype.fillErrors = function(errors) { // If any errors were added, add the marker to this item. var errorLabel = this.node.one('> .badge-warning'); if (errors.length !== before && !errorLabel.get('firstChild')) { - errorLabel.appendChild(document.createTextNode(M.util.get_string('invalid', 'availability'))); + var errorString = ''; + // Fetch the last error code from the array of errors and split using the ':' delimiter. + var langString = errors[errors.length - 1].split(':'); + var component = langString[0]; + var identifier = langString[1]; + // If get_string can't find the string, it will return the string in this format. + var undefinedString = '[[' + identifier + ',' + component + ']]'; + // Get the lang string. + errorString = M.util.get_string(identifier, component); + if (errorString === undefinedString) { + // Use a generic invalid input message when the error lang string cannot be loaded. + errorString = M.util.get_string('invalid', 'availability'); + } + // Show the error string. + errorLabel.appendChild(document.createTextNode(errorString)); } else if (errors.length === before && errorLabel.get('firstChild')) { errorLabel.get('firstChild').remove(); } diff --git a/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-min.js b/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-min.js index 741ff3fac1d..7d341375412 100644 --- a/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-min.js +++ b/availability/yui/build/moodle-core_availability-form/moodle-core_availability-form-min.js @@ -1,3 +1,3 @@ YUI.add("moodle-core_availability-form",function(d,i){M.core_availability=M.core_availability||{},M.core_availability.form={plugins:{},field:null,mainDiv:null,rootList:null,idCounter:0,restrictByGroup:null,init:function(i){var t,e,a,l,n;for(t in i)e=i[t],(a=M[e[0]].form).init.apply(a,e);if(this.field=d.one("#id_availabilityconditionsjson"),this.field.setAttribute("aria-hidden","true"),this.mainDiv=d.Node.create('
'),this.field.insert(this.mainDiv,"after"),n=null,""!==(l=this.field.get("value")))try{n=d.JSON.parse(l)}catch(o){this.field.set("value","")}this.rootList=new M.core_availability.List(n,!0),this.mainDiv.appendChild(this.rootList.node),this.update(),this.rootList.renumber(),this.mainDiv.setAttribute("aria-live","polite"),this.field.ancestor("form").on("submit",function(){this.mainDiv.all("input,textarea,select").set("disabled",!0)},this),this.restrictByGroup=d.one("#restrictbygroup"),this.restrictByGroup&&(this.restrictByGroup.on("click",this.addRestrictByGroup,this),l=d.one("#id_groupmode"),n=d.one("#id_groupingid"),l&&l.on("change",this.updateRestrictByGroup,this),n&&n.on("change",this.updateRestrictByGroup,this),this.updateRestrictByGroup())},update:function(){var i=this.rootList.getValue(),t=[];this.rootList.fillErrors(t),0!==t.length&&(i.errors=t),this.field.set("value",d.JSON.stringify(i)),this.updateRestrictByGroup()},updateRestrictByGroup:function(){var i,t,e,a;this.restrictByGroup&&("&"!==this.rootList.getValue().op||(this.rootList.hasItemOfType("group")||this.rootList.hasItemOfType("grouping"))?this.restrictByGroup.set("disabled",!0):(i=d.one("#id_groupmode"),t=d.one("#id_groupingid"),e=1===Number(this.restrictByGroup.getData("groupavailability")),a=1===Number(this.restrictByGroup.getData("groupingavailability")),i&&0!==Number(i.get("value"))&&e||t&&0!==Number(t.get("value"))&&a?this.restrictByGroup.set("disabled",!1):this.restrictByGroup.set("disabled",!0)))},addRestrictByGroup:function(i){var t,e,a,l;i.preventDefault(),i=d.one("#id_groupmode"),t=d.one("#id_groupingid"),e=1===Number(this.restrictByGroup.getData("groupavailability")),a=1===Number(this.restrictByGroup.getData("groupingavailability")),t&&0!==Number(t.get("value"))&&a?l=new M.core_availability.Item({type:"grouping",id:Number(t.get("value"))},!0):i&&e&&(l=new M.core_availability.Item({type:"group"},!0)),null!==l&&(this.rootList.addChild(l),this.update(),this.rootList.renumber(),this.rootList.updateHtml())}},M.core_availability.plugin={allowAdd:!1,init:function(i,t,e){i=i.replace(/^availability_/,"");this.allowAdd=t,(M.core_availability.form.plugins[i]=this).initInner.apply(this,e)},initInner:function(){},getNode:function(){throw"getNode not implemented"},fillValue:function(){throw"fillValue not implemented"},fillErrors:function(){},focusAfterAdd:function(i){i.one("input:not([disabled]),select:not([disabled])").focus()}},M.core_availability.List=function(i,t,e){var a,l,n;if(this.children=[],t!==undefined&&(this.root=t),this.node=d.Node.create('