diff --git a/admin/tool/lp/amd/build/base.min.js b/admin/tool/lp/amd/build/base.min.js new file mode 100644 index 00000000000..c9e4818dbfb --- /dev/null +++ b/admin/tool/lp/amd/build/base.min.js @@ -0,0 +1 @@ +define(["jquery"],function(a){var b=function(){this._eventNode=a("
")};return b.prototype._eventNode=null,b.prototype.on=function(a,b){this._eventNode.on(a,b)},b.prototype._trigger=function(a,b){this._eventNode.trigger(a,[b])},b}); \ No newline at end of file diff --git a/admin/tool/lp/amd/build/planactions.min.js b/admin/tool/lp/amd/build/planactions.min.js index c6bc873a38b..530665ca73b 100644 --- a/admin/tool/lp/amd/build/planactions.min.js +++ b/admin/tool/lp/amd/build/planactions.min.js @@ -1 +1 @@ -define(["jquery","core/templates","core/ajax","core/notification","core/str","tool_lp/menubar"],function(a,b,c,d,e,f){var g=function(a){if(this._type=a,"plan"===a)this._region='[data-region="plan-page"]',this._planNode='[data-region="plan-page"]',this._template="tool_lp/plan_page",this._contextMethod="tool_lp_data_for_plan_page";else{if("plans"!==a)throw new TypeError("Unexpected type.");this._region='[data-region="plans"]',this._planNode='[data-region="plan-node"]',this._template="tool_lp/plans_page",this._contextMethod="tool_lp_data_for_plans_page"}};return g.prototype._contextMethod=null,g.prototype._planNode=null,g.prototype._region=null,g.prototype._template=null,g.prototype._type=null,g.prototype._getContextArgs=function(a){var b=this,c={};return"plan"===b._type?c={planid:a.id}:"plans"===b._type&&(c={userid:a.userid}),c},g.prototype._renderView=function(c){var e=this;b.render(e._template,c).done(function(c,d){a(e._region).replaceWith(c),b.runTemplateJS(d)}.bind(e)).fail(d.exception)},g.prototype._callAndRefresh=function(b,e){var f=this;return b.push({methodname:f._contextMethod,args:f._getContextArgs(e)}),a.when.apply(a.when,c.call(b)).then(function(){f._renderView.call(f,arguments[arguments.length-1])}).fail(d.exception)},g.prototype._doDelete=function(a){var b=this,c=[{methodname:"tool_lp_delete_plan",args:{id:a.id}}];b._callAndRefresh(c,a)},g.prototype.deletePlan=function(a){var b,f=this;b=c.call([{methodname:"tool_lp_read_plan",args:{id:a.id}}]),b[0].done(function(b){e.get_strings([{key:"confirm",component:"moodle"},{key:"deleteplan",component:"tool_lp",param:b.name},{key:"delete",component:"moodle"},{key:"cancel",component:"moodle"}]).done(function(b){d.confirm(b[0],b[1],b[2],b[3],function(){f._doDelete(a)}.bind(f))}).fail(d.exception)}).fail(d.exception)},g.prototype._deletePlanHandler=function(b){b.preventDefault();var c=this._findPlanData(a(b.target));this.deletePlan(c)},g.prototype._doReopenPlan=function(a){var b=this,c=[{methodname:"tool_lp_reopen_plan",args:{planid:a.id}}];b._callAndRefresh(c,a)},g.prototype.reopenPlan=function(a){var b=this,f=c.call([{methodname:"tool_lp_read_plan",args:{id:a.id}}]);f[0].done(function(c){e.get_strings([{key:"confirm",component:"moodle"},{key:"reopenplanconfirm",component:"tool_lp",param:c.name},{key:"reopenplan",component:"tool_lp"},{key:"cancel",component:"moodle"}]).done(function(c){d.confirm(c[0],c[1],c[2],c[3],function(){b._doReopenPlan(a)}.bind(b))}).fail(d.exception)}).fail(d.exception)},g.prototype._reopenPlanHandler=function(b){b.preventDefault();var c=this._findPlanData(a(b.target));this.reopenPlan(c)},g.prototype._doCompletePlan=function(a){var b=this,c=[{methodname:"tool_lp_complete_plan",args:{planid:a.id}}];b._callAndRefresh(c,a)},g.prototype.completePlan=function(a){var b=this,f=c.call([{methodname:"tool_lp_read_plan",args:{id:a.id}}]);f[0].done(function(c){e.get_strings([{key:"confirm",component:"moodle"},{key:"completeplanconfirm",component:"tool_lp",param:c.name},{key:"completeplan",component:"tool_lp"},{key:"cancel",component:"moodle"}]).done(function(c){d.confirm(c[0],c[1],c[2],c[3],function(){b._doCompletePlan(a)}.bind(b))}).fail(d.exception)}).fail(d.exception)},g.prototype._completePlanHandler=function(b){b.preventDefault();var c=this._findPlanData(a(b.target));this.completePlan(c)},g.prototype._doUnlinkPlan=function(a){var b=this,c=[{methodname:"tool_lp_unlink_plan_from_template",args:{planid:a.id}}];b._callAndRefresh(c,a)},g.prototype.unlinkPlan=function(a){var b=this,f=c.call([{methodname:"tool_lp_read_plan",args:{id:a.id}}]);f[0].done(function(c){e.get_strings([{key:"confirm",component:"moodle"},{key:"unlinkplantemplateconfirm",component:"tool_lp",param:c.name},{key:"unlinkplantemplate",component:"tool_lp"},{key:"cancel",component:"moodle"}]).done(function(c){d.confirm(c[0],c[1],c[2],c[3],function(){b._doUnlinkPlan(a)}.bind(b))}).fail(d.exception)}).fail(d.exception)},g.prototype._unlinkPlanHandler=function(b){b.preventDefault();var c=this._findPlanData(a(b.target));this.unlinkPlan(c)},g.prototype._findPlanData=function(b){var c,d=b.parentsUntil(a(this._region).parent(),this._planNode);if(1!=d.length)throw new Error("The plan node was not located.");if(c=d.data(),"undefined"==typeof c||"undefined"==typeof c.id)throw new Error("Plan data could not be found.");return c},g.prototype.enhanceMenubar=function(a){var b=this;f.enhance(a,{'[data-action="plan-delete"]':b._deletePlanHandler.bind(b),'[data-action="plan-complete"]':b._completePlanHandler.bind(b),'[data-action="plan-reopen"]':b._reopenPlanHandler.bind(b),'[data-action="plan-unlink"]':b._unlinkPlanHandler.bind(b)})},g.prototype.registerEvents=function(){var b=a(this._region),c=this;b.find('[data-action="plan-delete"]').click(c._deletePlanHandler.bind(c)),b.find('[data-action="plan-complete"]').click(c._completePlanHandler.bind(c)),b.find('[data-action="plan-reopen"]').click(c._reopenPlanHandler.bind(c)),b.find('[data-action="plan-unlink"]').click(c._unlinkPlanHandler.bind(c))},g}); \ No newline at end of file +define(["jquery","core/templates","core/ajax","core/notification","core/str","tool_lp/menubar"],function(a,b,c,d,e,f){var g=function(a){if(this._type=a,"plan"===a)this._region='[data-region="plan-page"]',this._planNode='[data-region="plan-page"]',this._template="tool_lp/plan_page",this._contextMethod="tool_lp_data_for_plan_page";else{if("plans"!==a)throw new TypeError("Unexpected type.");this._region='[data-region="plans"]',this._planNode='[data-region="plan-node"]',this._template="tool_lp/plans_page",this._contextMethod="tool_lp_data_for_plans_page"}};return g.prototype._contextMethod=null,g.prototype._planNode=null,g.prototype._region=null,g.prototype._template=null,g.prototype._type=null,g.prototype._getContextArgs=function(a){var b=this,c={};return"plan"===b._type?c={planid:a.id}:"plans"===b._type&&(c={userid:a.userid}),c},g.prototype.refresh=function(b){var c=this._findPlanData(a(b));this._callAndRefresh([],c)},g.prototype._renderView=function(c){var e=this;b.render(e._template,c).done(function(c,d){a(e._region).replaceWith(c),b.runTemplateJS(d)}.bind(e)).fail(d.exception)},g.prototype._callAndRefresh=function(b,e){var f=this;return b.push({methodname:f._contextMethod,args:f._getContextArgs(e)}),a.when.apply(a.when,c.call(b)).then(function(){f._renderView.call(f,arguments[arguments.length-1])}).fail(d.exception)},g.prototype._doDelete=function(a){var b=this,c=[{methodname:"tool_lp_delete_plan",args:{id:a.id}}];b._callAndRefresh(c,a)},g.prototype.deletePlan=function(a){var b,f=this;b=c.call([{methodname:"tool_lp_read_plan",args:{id:a.id}}]),b[0].done(function(b){e.get_strings([{key:"confirm",component:"moodle"},{key:"deleteplan",component:"tool_lp",param:b.name},{key:"delete",component:"moodle"},{key:"cancel",component:"moodle"}]).done(function(b){d.confirm(b[0],b[1],b[2],b[3],function(){f._doDelete(a)}.bind(f))}).fail(d.exception)}).fail(d.exception)},g.prototype._deletePlanHandler=function(b){b.preventDefault();var c=this._findPlanData(a(b.target));this.deletePlan(c)},g.prototype._doReopenPlan=function(a){var b=this,c=[{methodname:"tool_lp_reopen_plan",args:{planid:a.id}}];b._callAndRefresh(c,a)},g.prototype.reopenPlan=function(a){var b=this,f=c.call([{methodname:"tool_lp_read_plan",args:{id:a.id}}]);f[0].done(function(c){e.get_strings([{key:"confirm",component:"moodle"},{key:"reopenplanconfirm",component:"tool_lp",param:c.name},{key:"reopenplan",component:"tool_lp"},{key:"cancel",component:"moodle"}]).done(function(c){d.confirm(c[0],c[1],c[2],c[3],function(){b._doReopenPlan(a)}.bind(b))}).fail(d.exception)}).fail(d.exception)},g.prototype._reopenPlanHandler=function(b){b.preventDefault();var c=this._findPlanData(a(b.target));this.reopenPlan(c)},g.prototype._doCompletePlan=function(a){var b=this,c=[{methodname:"tool_lp_complete_plan",args:{planid:a.id}}];b._callAndRefresh(c,a)},g.prototype.completePlan=function(a){var b=this,f=c.call([{methodname:"tool_lp_read_plan",args:{id:a.id}}]);f[0].done(function(c){e.get_strings([{key:"confirm",component:"moodle"},{key:"completeplanconfirm",component:"tool_lp",param:c.name},{key:"completeplan",component:"tool_lp"},{key:"cancel",component:"moodle"}]).done(function(c){d.confirm(c[0],c[1],c[2],c[3],function(){b._doCompletePlan(a)}.bind(b))}).fail(d.exception)}).fail(d.exception)},g.prototype._completePlanHandler=function(b){b.preventDefault();var c=this._findPlanData(a(b.target));this.completePlan(c)},g.prototype._doUnlinkPlan=function(a){var b=this,c=[{methodname:"tool_lp_unlink_plan_from_template",args:{planid:a.id}}];b._callAndRefresh(c,a)},g.prototype.unlinkPlan=function(a){var b=this,f=c.call([{methodname:"tool_lp_read_plan",args:{id:a.id}}]);f[0].done(function(c){e.get_strings([{key:"confirm",component:"moodle"},{key:"unlinkplantemplateconfirm",component:"tool_lp",param:c.name},{key:"unlinkplantemplate",component:"tool_lp"},{key:"cancel",component:"moodle"}]).done(function(c){d.confirm(c[0],c[1],c[2],c[3],function(){b._doUnlinkPlan(a)}.bind(b))}).fail(d.exception)}).fail(d.exception)},g.prototype._unlinkPlanHandler=function(b){b.preventDefault();var c=this._findPlanData(a(b.target));this.unlinkPlan(c)},g.prototype._findPlanData=function(b){var c,d=b.parentsUntil(a(this._region).parent(),this._planNode);if(1!=d.length)throw new Error("The plan node was not located.");if(c=d.data(),"undefined"==typeof c||"undefined"==typeof c.id)throw new Error("Plan data could not be found.");return c},g.prototype.enhanceMenubar=function(a){var b=this;f.enhance(a,{'[data-action="plan-delete"]':b._deletePlanHandler.bind(b),'[data-action="plan-complete"]':b._completePlanHandler.bind(b),'[data-action="plan-reopen"]':b._reopenPlanHandler.bind(b),'[data-action="plan-unlink"]':b._unlinkPlanHandler.bind(b)})},g.prototype.registerEvents=function(){var b=a(this._region),c=this;b.find('[data-action="plan-delete"]').click(c._deletePlanHandler.bind(c)),b.find('[data-action="plan-complete"]').click(c._completePlanHandler.bind(c)),b.find('[data-action="plan-reopen"]').click(c._reopenPlanHandler.bind(c)),b.find('[data-action="plan-unlink"]').click(c._unlinkPlanHandler.bind(c))},g}); \ No newline at end of file diff --git a/admin/tool/lp/amd/build/user_competency_workflow.min.js b/admin/tool/lp/amd/build/user_competency_workflow.min.js new file mode 100644 index 00000000000..91f979eaf81 --- /dev/null +++ b/admin/tool/lp/amd/build/user_competency_workflow.min.js @@ -0,0 +1 @@ +define(["jquery","core/templates","core/ajax","core/notification","core/str","tool_lp/menubar","tool_lp/base"],function(a,b,c,d,e,f,g){var h=function(){g.prototype.constructor.apply(this,[])};return h.prototype=Object.create(g.prototype),h.prototype._nodeSelector='[data-node="user-competency"]',h.prototype._cancelReviewRequest=function(a){var b={methodname:"tool_lp_user_competency_cancel_review_request",args:{userid:a.userid,competencyid:a.competencyid}};c.call([b])[0].then(function(){this._trigger("review-request-cancelled",a),this._trigger("status-changed",a)}.bind(this),function(){this._trigger("error-occured",a)}.bind(this))},h.prototype.cancelReviewRequest=function(a){this._cancelReviewRequest(a)},h.prototype._cancelReviewRequestHandler=function(b){b.preventDefault();var c=this._findUserCompetencyData(a(b.target));this.cancelReviewRequest(c)},h.prototype._requestReview=function(a){var b={methodname:"tool_lp_user_competency_request_review",args:{userid:a.userid,competencyid:a.competencyid}};c.call([b])[0].then(function(){this._trigger("review-requested",a),this._trigger("status-changed",a)}.bind(this),function(){this._trigger("error-occured",a)}.bind(this))},h.prototype.requestReview=function(a){this._requestReview(a)},h.prototype._requestReviewHandler=function(b){b.preventDefault();var c=this._findUserCompetencyData(a(b.target));this.requestReview(c)},h.prototype._startReview=function(a){var b={methodname:"tool_lp_user_competency_start_review",args:{userid:a.userid,competencyid:a.competencyid}};c.call([b])[0].then(function(){this._trigger("review-started",a),this._trigger("status-changed",a)}.bind(this),function(){this._trigger("error-occured",a)}.bind(this))},h.prototype.startReview=function(a){this._startReview(a)},h.prototype._startReviewHandler=function(b){b.preventDefault();var c=this._findUserCompetencyData(a(b.target));this.startReview(c)},h.prototype._stopReview=function(a){var b={methodname:"tool_lp_user_competency_stop_review",args:{userid:a.userid,competencyid:a.competencyid}};c.call([b])[0].then(function(){this._trigger("review-stopped",a),this._trigger("status-changed",a)}.bind(this),function(){this._trigger("error-occured",a)}.bind(this))},h.prototype.stopReview=function(a){this._stopReview(a)},h.prototype._stopReviewHandler=function(b){b.preventDefault();var c=this._findUserCompetencyData(a(b.target));this.stopReview(c)},h.prototype.enhanceMenubar=function(a){f.enhance(a,{'[data-action="request-review"]':this._requestReviewHandler.bind(this),'[data-action="cancel-review-request"]':this._cancelReviewRequestHandler.bind(this)})},h.prototype._findUserCompetencyData=function(a){var b,c=a.parents(this._nodeSelector);if(1!=c.length)throw new Error("The evidence node was not located.");if(b=c.data(),"undefined"==typeof b||"undefined"==typeof b.userid||"undefined"==typeof b.competencyid)throw new Error("User competency data could not be found.");return b},h.prototype.enhanceMenubar=function(a){f.enhance(a,{'[data-action="request-review"]':this._requestReviewHandler.bind(this),'[data-action="cancel-review-request"]':this._cancelReviewRequestHandler.bind(this),'[data-action="start-review"]':this._startReviewHandler.bind(this),'[data-action="stop-review"]':this._stopReviewHandler.bind(this)})},h.prototype.registerEvents=function(b){var c=a(b);c.find('[data-action="request-review"]').click(this._requestReviewHandler.bind(this)),c.find('[data-action="cancel-review-request"]').click(this._cancelReviewRequestHandler.bind(this)),c.find('[data-action="start-review"]').click(this._startReviewHandler.bind(this)),c.find('[data-action="stop-review"]').click(this._stopReviewHandler.bind(this))},h}); \ No newline at end of file diff --git a/admin/tool/lp/amd/src/base.js b/admin/tool/lp/amd/src/base.js new file mode 100644 index 00000000000..2168e760be3 --- /dev/null +++ b/admin/tool/lp/amd/src/base.js @@ -0,0 +1,59 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * Base javascript module. + * + * @module tool_lp/base + * @package tool_lp + * @copyright 2015 Frédéric Massart - FMCorz.net + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define(['jquery'], function($) { + + /** + * Base class. + */ + var Base = function() { + this._eventNode = $('
'); + }; + + /** @type {Node} The node we attach the events to. */ + Base.prototype._eventNode = null; + + /** + * Register an event listener. + * + * @param {String} type The event type. + * @param {Function} handler The event listener. + * @method on + */ + Base.prototype.on = function(type, handler) { + this._eventNode.on(type, handler); + }; + + /** + * Trigger an event. + * + * @param {String} type The type of event. + * @param {Object} The data to pass to the listeners. + * @method _trigger + */ + Base.prototype._trigger = function(type, data) { + this._eventNode.trigger(type, [data]); + }; + + return /** @alias module:tool_lp/base */ Base; +}); diff --git a/admin/tool/lp/amd/src/planactions.js b/admin/tool/lp/amd/src/planactions.js index edc3880285a..dba37c80034 100644 --- a/admin/tool/lp/amd/src/planactions.js +++ b/admin/tool/lp/amd/src/planactions.js @@ -93,6 +93,18 @@ define(['jquery', return args; }; + /** + * Refresh the plan view. + * + * This is useful when you only want to refresh the view. + * + * @param {String} selector The node to search the plan data from. + */ + PlanActions.prototype.refresh = function(selector) { + var planData = this._findPlanData($(selector)); + this._callAndRefresh([], planData); + }; + /** * Callback to render the region template. * diff --git a/admin/tool/lp/amd/src/user_competency_workflow.js b/admin/tool/lp/amd/src/user_competency_workflow.js new file mode 100644 index 00000000000..4534dac67ac --- /dev/null +++ b/admin/tool/lp/amd/src/user_competency_workflow.js @@ -0,0 +1,301 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/** + * User competency workflow. + * + * @module tool_lp/user_competency_workflow + * @package tool_lp + * @copyright 2015 Frédéric Massart - FMCorz.net + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define(['jquery', + 'core/templates', + 'core/ajax', + 'core/notification', + 'core/str', + 'tool_lp/menubar', + 'tool_lp/base'], + function($, Templates, Ajax, Notification, Str, Menubar, Base) { + + /** + * UserCompetencyWorkflow class. + * + * @param {String} selector The node containing the buttons to switch mode. + */ + var UserCompetencyWorkflow = function() { + Base.prototype.constructor.apply(this, []); + }; + UserCompetencyWorkflow.prototype = Object.create(Base.prototype); + + /** @type {String} The selector to find the user competency data. */ + UserCompetencyWorkflow.prototype._nodeSelector = '[data-node="user-competency"]'; + + /** + * Cancel a review request and refresh the view. + * + * @param {Object} data The user competency data. + * @return {Void} + * @method _cancelReviewRequest + */ + UserCompetencyWorkflow.prototype._cancelReviewRequest = function(data) { + var call = { + methodname: 'tool_lp_user_competency_cancel_review_request', + args: { + userid: data.userid, + competencyid: data.competencyid + } + }; + + Ajax.call([call])[0].then(function() { + this._trigger('review-request-cancelled', data); + this._trigger('status-changed', data); + }.bind(this), function() { + this._trigger('error-occured', data); + }.bind(this)); + }; + + /** + * Cancel a review request an refresh the view. + * + * @param {Object} data The user competency data. + * @return {Void} + * @method cancelReviewRequest + */ + UserCompetencyWorkflow.prototype.cancelReviewRequest = function(data) { + this._cancelReviewRequest(data); + }; + + /** + * Cancel a review request handler. + * + * @param {Event} e The event. + * @return {Void} + * @method _cancelReviewRequestHandler + */ + UserCompetencyWorkflow.prototype._cancelReviewRequestHandler = function(e) { + e.preventDefault(); + var data = this._findUserCompetencyData($(e.target)); + this.cancelReviewRequest(data); + }; + + /** + * Request a review and refresh the view. + * + * @param {Object} data The user competency data. + * @return {Void} + * @method _requestReview + */ + UserCompetencyWorkflow.prototype._requestReview = function(data) { + var call = { + methodname: 'tool_lp_user_competency_request_review', + args: { + userid: data.userid, + competencyid: data.competencyid + } + }; + + Ajax.call([call])[0].then(function() { + this._trigger('review-requested', data); + this._trigger('status-changed', data); + }.bind(this), function() { + this._trigger('error-occured', data); + }.bind(this)); + }; + + /** + * Request a review. + * + * @param {Object} data The user competency data. + * @return {Void} + * @method requestReview + */ + UserCompetencyWorkflow.prototype.requestReview = function(data) { + this._requestReview(data); + }; + + /** + * Request a review handler. + * + * @param {Event} e The event. + * @return {Void} + * @method _requestReviewHandler + */ + UserCompetencyWorkflow.prototype._requestReviewHandler = function(e) { + e.preventDefault(); + var data = this._findUserCompetencyData($(e.target)); + this.requestReview(data); + }; + + /** + * Start a review and refresh the view. + * + * @param {Object} data The user competency data. + * @return {Void} + * @method _startReview + */ + UserCompetencyWorkflow.prototype._startReview = function(data) { + var call = { + methodname: 'tool_lp_user_competency_start_review', + args: { + userid: data.userid, + competencyid: data.competencyid + } + }; + + Ajax.call([call])[0].then(function() { + this._trigger('review-started', data); + this._trigger('status-changed', data); + }.bind(this), function() { + this._trigger('error-occured', data); + }.bind(this)); + }; + + /** + * Start a review. + * + * @param {Object} data The user competency data. + * @return {Void} + * @method startReview + */ + UserCompetencyWorkflow.prototype.startReview = function(data) { + this._startReview(data); + }; + + /** + * Start a review handler. + * + * @param {Event} e The event. + * @return {Void} + * @method _startReviewHandler + */ + UserCompetencyWorkflow.prototype._startReviewHandler = function(e) { + e.preventDefault(); + var data = this._findUserCompetencyData($(e.target)); + this.startReview(data); + }; + + /** + * Stop a review and refresh the view. + * + * @param {Object} data The user competency data. + * @return {Void} + * @method _stopReview + */ + UserCompetencyWorkflow.prototype._stopReview = function(data) { + var call = { + methodname: 'tool_lp_user_competency_stop_review', + args: { + userid: data.userid, + competencyid: data.competencyid + } + }; + + Ajax.call([call])[0].then(function() { + this._trigger('review-stopped', data); + this._trigger('status-changed', data); + }.bind(this), function() { + this._trigger('error-occured', data); + }.bind(this)); + }; + + /** + * Stop a review. + * + * @param {Object} data The user competency data. + * @return {Void} + * @method stopReview + */ + UserCompetencyWorkflow.prototype.stopReview = function(data) { + this._stopReview(data); + }; + + /** + * Stop a review handler. + * + * @param {Event} e The event. + * @return {Void} + * @method _stopReviewHandler + */ + UserCompetencyWorkflow.prototype._stopReviewHandler = function(e) { + e.preventDefault(); + var data = this._findUserCompetencyData($(e.target)); + this.stopReview(data); + }; + + /** + * Enhance a menu bar. + * + * @param {String} selector Menubar selector. + */ + UserCompetencyWorkflow.prototype.enhanceMenubar = function(selector) { + Menubar.enhance(selector, { + '[data-action="request-review"]': this._requestReviewHandler.bind(this), + '[data-action="cancel-review-request"]': this._cancelReviewRequestHandler.bind(this), + }); + }; + + /** + * Find the user competency data from a node. + * + * @param {Node} node The node to search from. + * @return {Object} User competency data. + */ + UserCompetencyWorkflow.prototype._findUserCompetencyData = function(node) { + var parent = node.parents(this._nodeSelector), + data; + + if (parent.length != 1) { + throw new Error('The evidence node was not located.'); + } + + data = parent.data(); + if (typeof data === 'undefined' || typeof data.userid === 'undefined' || typeof data.competencyid === 'undefined') { + throw new Error('User competency data could not be found.'); + } + + return data; + }; + + /** + * Enhance a menu bar. + * + * @param {String} selector Menubar selector. + */ + UserCompetencyWorkflow.prototype.enhanceMenubar = function(selector) { + Menubar.enhance(selector, { + '[data-action="request-review"]': this._requestReviewHandler.bind(this), + '[data-action="cancel-review-request"]': this._cancelReviewRequestHandler.bind(this), + '[data-action="start-review"]': this._startReviewHandler.bind(this), + '[data-action="stop-review"]': this._stopReviewHandler.bind(this), + }); + }; + + /** + * Register the events in the region. + * + * @param {String} selector The base selector to search nodes in and attach events. + */ + UserCompetencyWorkflow.prototype.registerEvents = function(selector) { + var wrapper = $(selector); + + wrapper.find('[data-action="request-review"]').click(this._requestReviewHandler.bind(this)); + wrapper.find('[data-action="cancel-review-request"]').click(this._cancelReviewRequestHandler.bind(this)); + wrapper.find('[data-action="start-review"]').click(this._startReviewHandler.bind(this)); + wrapper.find('[data-action="stop-review"]').click(this._stopReviewHandler.bind(this)); + }; + + return /** @alias module:tool_lp/user_competency_actions */ UserCompetencyWorkflow; +}); diff --git a/admin/tool/lp/classes/api.php b/admin/tool/lp/classes/api.php index 6e168aab5c3..2bd11b4bdd0 100644 --- a/admin/tool/lp/classes/api.php +++ b/admin/tool/lp/classes/api.php @@ -2044,6 +2044,7 @@ class api { throw new coding_exception('A user competency plan is missing'); } else { $uc = user_competency::create_relation($plan->get_userid(), $competency->get_id()); + $uc->create(); } } @@ -2256,6 +2257,110 @@ class api { return $competencyfrom->update(); } + /** + * Cancel a user competency review request. + * + * @param int $userid The user ID. + * @param int $competencyid The competency ID. + * @return bool + */ + public static function user_competency_cancel_review_request($userid, $competencyid) { + if (!plan::can_read_user($userid)) { + $context = context_user::instance($userid); + throw new required_capability_exception($context, 'tool/lp:planview', 'nopermissions', ''); + } + + $uc = user_competency::get_record(array('userid' => $userid, 'competencyid' => $competencyid)); + if (!$uc || $uc->get_status() != user_competency::STATUS_WAITING_FOR_REVIEW) { + throw new coding_exception('The competency can not be cancel review request at this stage.'); + } else if (!$uc->can_request_review()) { + throw new required_capability_exception($context, 'tool/lp:userevidencemanage', 'nopermissions', ''); + } + + $uc->set_status(user_competency::STATUS_IDLE); + return $uc->update(); + } + + /** + * Request a user competency review. + * + * @param int $userid The user ID. + * @param int $competencyid The competency ID. + * @return bool + */ + public static function user_competency_request_review($userid, $competencyid) { + if (!plan::can_read_user($userid)) { + $context = context_user::instance($userid); + throw new required_capability_exception($context, 'tool/lp:planview', 'nopermissions', ''); + } + + $uc = user_competency::get_record(array('userid' => $userid, 'competencyid' => $competencyid)); + if (!$uc) { + $uc = user_competency::create_relation($userid, $competencyid); + $uc->create(); + } + + if ($uc->get_status() != user_competency::STATUS_IDLE) { + throw new coding_exception('The competency can not be sent for review at this stage.'); + } else if (!$uc->can_request_review()) { + throw new required_capability_exception($context, 'tool/lp:userevidencemanage', 'nopermissions', ''); + } + + $uc->set_status(user_competency::STATUS_WAITING_FOR_REVIEW); + return $uc->update(); + } + + /** + * Start a user competency review. + * + * @param int $userid The user ID. + * @param int $competencyid The competency ID. + * @return bool + */ + public static function user_competency_start_review($userid, $competencyid) { + global $USER; + + if (!plan::can_read_user($userid)) { + $context = context_user::instance($userid); + throw new required_capability_exception($context, 'tool/lp:planview', 'nopermissions', ''); + } + + $uc = user_competency::get_record(array('userid' => $userid, 'competencyid' => $competencyid)); + if (!$uc || $uc->get_status() != user_competency::STATUS_WAITING_FOR_REVIEW) { + throw new coding_exception('The competency review can not be started at this stage.'); + } else if (!$uc->can_review()) { + throw new required_capability_exception($context, 'tool/lp:competencygrade', 'nopermissions', ''); + } + + $uc->set_status(user_competency::STATUS_IN_REVIEW); + $uc->set_reviewerid($USER->id); + return $uc->update(); + } + + /** + * Stop a user competency review. + * + * @param int $userid The user ID. + * @param int $competencyid The competency ID. + * @return bool + */ + public static function user_competency_stop_review($userid, $competencyid) { + if (!plan::can_read_user($userid)) { + $context = context_user::instance($userid); + throw new required_capability_exception($context, 'tool/lp:planview', 'nopermissions', ''); + } + + $uc = user_competency::get_record(array('userid' => $userid, 'competencyid' => $competencyid)); + if (!$uc || $uc->get_status() != user_competency::STATUS_IN_REVIEW) { + throw new coding_exception('The competency review can not be stopped at this stage.'); + } else if (!$uc->can_review()) { + throw new required_capability_exception($context, 'tool/lp:competencygrade', 'nopermissions', ''); + } + + $uc->set_status(user_competency::STATUS_IDLE); + return $uc->update(); + } + /** * List all the related competencies. * diff --git a/admin/tool/lp/classes/external.php b/admin/tool/lp/classes/external.php index f9d834acfa5..f03a53d4820 100644 --- a/admin/tool/lp/classes/external.php +++ b/admin/tool/lp/classes/external.php @@ -2678,6 +2678,163 @@ class external extends external_api { return new external_value(PARAM_BOOL, 'True if successful.'); } + /** + * Returns description of external function parameters. + * + * @return \external_function_parameters + */ + public static function user_competency_cancel_review_request_parameters() { + return new external_function_parameters(array( + 'userid' => new external_value(PARAM_INT, 'The user ID'), + 'competencyid' => new external_value(PARAM_INT, 'The competency ID'), + )); + } + + /** + * External function user_competency_cancel_review_request. + * + * @param int $id The ID description. + * @return boolean + */ + public static function user_competency_cancel_review_request($userid, $competencyid) { + $params = self::validate_parameters(self::user_competency_request_review_parameters(), array( + 'userid' => $userid, + 'competencyid' => $competencyid + )); + + $context = context_user::instance($params['userid']); + self::validate_context($context); + + return api::user_competency_cancel_review_request($userid, $competencyid); + } + + /** + * Returns description of external function result value. + * + * @return \external_function_parameters + */ + public static function user_competency_cancel_review_request_returns() { + new external_value(PARAM_BOOL, 'The success'); + } + + /** + * Returns description of external function parameters. + * + * @return \external_function_parameters + */ + public static function user_competency_request_review_parameters() { + return new external_function_parameters(array( + 'userid' => new external_value(PARAM_INT, 'The user ID'), + 'competencyid' => new external_value(PARAM_INT, 'The competency ID'), + )); + } + + /** + * External function user_competency_request_review. + * + * @param int $userid The user ID. + * @param int $competencyid The competency ID. + * @return boolean + */ + public static function user_competency_request_review($userid, $competencyid) { + $params = self::validate_parameters(self::user_competency_request_review_parameters(), array( + 'userid' => $userid, + 'competencyid' => $competencyid + )); + + $context = context_user::instance($params['userid']); + self::validate_context($context); + + return api::user_competency_request_review($userid, $competencyid); + } + + /** + * Returns description of external function result value. + * + * @return \external_function_parameters + */ + public static function user_competency_request_review_returns() { + new external_value(PARAM_BOOL, 'The success'); + } + + /** + * Returns description of external function parameters. + * + * @return \external_function_parameters + */ + public static function user_competency_start_review_parameters() { + return new external_function_parameters(array( + 'userid' => new external_value(PARAM_INT, 'The user ID'), + 'competencyid' => new external_value(PARAM_INT, 'The competency ID'), + )); + } + + /** + * External function user_competency_start_review. + * + * @param int $id The ID description. + * @return boolean + */ + public static function user_competency_start_review($userid, $competencyid) { + $params = self::validate_parameters(self::user_competency_request_review_parameters(), array( + 'userid' => $userid, + 'competencyid' => $competencyid + )); + + $context = context_user::instance($params['userid']); + self::validate_context($context); + + return api::user_competency_start_review($userid, $competencyid); + } + + /** + * Returns description of external function result value. + * + * @return \external_function_parameters + */ + public static function user_competency_start_review_returns() { + new external_value(PARAM_BOOL, 'The success'); + } + + /** + * Returns description of external function parameters. + * + * @return \external_function_parameters + */ + public static function user_competency_stop_review_parameters() { + return new external_function_parameters(array( + 'userid' => new external_value(PARAM_INT, 'The user ID'), + 'competencyid' => new external_value(PARAM_INT, 'The competency ID'), + )); + } + + /** + * External function user_competency_stop_review. + * + * @param int $id The ID description. + * @return boolean + */ + public static function user_competency_stop_review($userid, $competencyid) { + $params = self::validate_parameters(self::user_competency_request_review_parameters(), array( + 'userid' => $userid, + 'competencyid' => $competencyid + )); + + $context = context_user::instance($params['userid']); + self::validate_context($context); + + return api::user_competency_stop_review($userid, $competencyid); + } + + /** + * Returns description of external function result value. + * + * @return \external_function_parameters + */ + public static function user_competency_stop_review_returns() { + new external_value(PARAM_BOOL, 'The success'); + } + /** * Count the competencies (visible to this user) in this learning plan template. * diff --git a/admin/tool/lp/classes/external/comment_area_exporter.php b/admin/tool/lp/classes/external/comment_area_exporter.php index 7b49e97d93f..43f5f23abb5 100644 --- a/admin/tool/lp/classes/external/comment_area_exporter.php +++ b/admin/tool/lp/classes/external/comment_area_exporter.php @@ -24,6 +24,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace tool_lp\external; +require_once($CFG->dirroot . '/comment/lib.php'); + defined('MOODLE_INTERNAL') || die(); use comment; @@ -51,6 +53,7 @@ class comment_area_exporter extends exporter { $data->courseid = $comment->get_courseid(); $data->contextid = $comment->get_context()->id; $data->cid = $comment->get_cid(); + parent::__construct($data, $related); } diff --git a/admin/tool/lp/classes/external/user_competency_exporter.php b/admin/tool/lp/classes/external/user_competency_exporter.php index 64836124d14..a65014479d6 100644 --- a/admin/tool/lp/classes/external/user_competency_exporter.php +++ b/admin/tool/lp/classes/external/user_competency_exporter.php @@ -24,6 +24,7 @@ namespace tool_lp\external; defined('MOODLE_INTERNAL') || die(); +use core_user; use renderer_base; use stdClass; use tool_lp\user_competency; @@ -68,17 +69,66 @@ class user_competency_exporter extends persistent_exporter { } $result->statusname = $statusname; + $result->canrequestreview = $this->persistent->can_request_review(); + $result->canreview = $this->persistent->can_review(); + + $result->isstatusidle = $this->persistent->get_status() == user_competency::STATUS_IDLE; + $result->isstatusinreview = $this->persistent->get_status() == user_competency::STATUS_IN_REVIEW; + $result->isstatuswaitingforreview = $this->persistent->get_status() == user_competency::STATUS_WAITING_FOR_REVIEW; + + $result->isrequestreviewallowed = $result->canrequestreview && $result->isstatusidle; + $result->iscancelreviewrequestallowed = $result->canrequestreview && $result->isstatuswaitingforreview; + $result->isstartreviewallowed = $result->canreview && $result->isstatuswaitingforreview; + $result->isstopreviewallowed = $result->canreview && $result->isstatusinreview; + + if (!empty($result->isstatusinreview)) { + // TODO Make this more efficient. + $userexporter = new user_summary_exporter(core_user::get_user($this->persistent->get_reviewerid(), '*', MUST_EXIST)); + $result->reviewer = $userexporter->export($output); + } + return (array) $result; } protected static function define_other_properties() { return array( + 'canrequestreview' => array( + 'type' => PARAM_BOOL, + ), + 'canreview' => array( + 'type' => PARAM_BOOL, + ), 'gradename' => array( 'type' => PARAM_TEXT ), + 'isrequestreviewallowed' => array( + 'type' => PARAM_BOOL, + ), + 'iscancelreviewrequestallowed' => array( + 'type' => PARAM_BOOL, + ), + 'isstartreviewallowed' => array( + 'type' => PARAM_BOOL, + ), + 'isstopreviewallowed' => array( + 'type' => PARAM_BOOL, + ), + 'isstatusidle' => array( + 'type' => PARAM_BOOL, + ), + 'isstatusinreview' => array( + 'type' => PARAM_BOOL, + ), + 'isstatuswaitingforreview' => array( + 'type' => PARAM_BOOL, + ), 'proficiencyname' => array( 'type' => PARAM_RAW ), + 'reviewer' => array( + 'type' => user_summary_exporter::read_properties_definition(), + 'optional' => true + ), 'statusname' => array( 'type' => PARAM_RAW ), diff --git a/admin/tool/lp/classes/external/user_competency_summary_exporter.php b/admin/tool/lp/classes/external/user_competency_summary_exporter.php index 179171949c7..53f21cdeda5 100644 --- a/admin/tool/lp/classes/external/user_competency_summary_exporter.php +++ b/admin/tool/lp/classes/external/user_competency_summary_exporter.php @@ -26,6 +26,7 @@ namespace tool_lp\external; use context_user; use renderer_base; use stdClass; +use tool_lp\user_competency; /** * Class for exporting user competency data with additional related data. @@ -76,7 +77,11 @@ class user_competency_summary_exporter extends exporter { 'evidence' => array( 'type' => evidence_exporter::read_properties_definition(), 'multiple' => true - ) + ), + 'commentarea' => array( + 'type' => comment_area_exporter::read_properties_definition(), + 'optional' => true + ), ); } @@ -143,6 +148,13 @@ class user_competency_summary_exporter extends exporter { $result->evidence = $allevidence; } + $usercompetency = !empty($this->related['usercompetency']) ? $this->related['usercompetency'] : null; + + if (!empty($usercompetency)) { + $commentareaexporter = new comment_area_exporter($usercompetency->get_comment_object()); + $result->commentarea = $commentareaexporter->export($output); + } + return (array) $result; } } diff --git a/admin/tool/lp/classes/output/user_competency_summary_in_plan.php b/admin/tool/lp/classes/output/user_competency_summary_in_plan.php index e0a8b2ba3bc..6d3581d1d08 100644 --- a/admin/tool/lp/classes/output/user_competency_summary_in_plan.php +++ b/admin/tool/lp/classes/output/user_competency_summary_in_plan.php @@ -22,6 +22,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace tool_lp\output; +defined('MOODLE_INTERNAL') || die(); use renderable; use templatable; @@ -64,7 +65,7 @@ class user_competency_summary_in_plan implements renderable, templatable { global $DB; $plan = api::read_plan($this->planid); - $pc = api::get_plan_competency($this->planid, $this->competencyid); + $pc = api::get_plan_competency($plan, $this->competencyid); $competency = $pc->competency; $usercompetency = $pc->usercompetency; $usercompetencyplan = $pc->usercompetencyplan; diff --git a/admin/tool/lp/classes/user_competency.php b/admin/tool/lp/classes/user_competency.php index 6977d34ada9..da51f170332 100644 --- a/admin/tool/lp/classes/user_competency.php +++ b/admin/tool/lp/classes/user_competency.php @@ -24,6 +24,9 @@ namespace tool_lp; defined('MOODLE_INTERNAL') || die(); +use coding_exception; +use context_user; +use comment; use lang_string; /** @@ -86,6 +89,24 @@ class user_competency extends persistent { ); } + /** + * Can the current user send the user competency for review? + * + * @return bool + */ + public function can_request_review() { + return static::can_request_review_user($this->get_userid()); + } + + /** + * Can the current user review the user competency? + * + * @return bool + */ + public function can_review() { + return static::can_review_user($this->get_userid()); + } + /** * Human readable status name. * @@ -131,6 +152,30 @@ class user_competency extends persistent { return $list; } + /** + * Get the comment object. + * + * @return comment + */ + public function get_comment_object() { + global $CFG; + require_once($CFG->dirroot . '/comment/lib.php'); + + if (!$this->get_id()) { + throw new coding_exception('The user competency record must exist.'); + } + + $comment = new comment((object) array( + 'context' => $this->get_context(), + 'component' => 'tool_lp', + 'itemid' => $this->get_id(), + 'area' => 'user_competency', + 'showcount' => true, + )); + $comment->set_fullwidth(true); + return $comment; + } + /** * Return the competency Object. * @@ -140,6 +185,15 @@ class user_competency extends persistent { return new competency($this->get_competencyid()); } + /** + * Get the context. + * + * @return context The context. + */ + public function get_context() { + return context_user::instance($this->get_userid()); + } + /** * Validate the user ID. * @@ -230,6 +284,28 @@ class user_competency extends persistent { return true; } + /** + * Can the current user send a user's competency for review? + * + * @param int $userid The user ID the competency belongs to. + * @return bool + */ + public static function can_request_review_user($userid) { + // We can request a review when we can attach evidence of prior learning. + return user_evidence::can_manage_user($userid); + } + + /** + * Can the current user send review the user competency? + * + * @param int $userid The user ID the competency belongs to. + * @return bool + */ + public static function can_review_user($userid) { + // We can review a competency when we can grade them. + return has_capability('tool/lp:competencygrade', context_user::instance($userid)); + } + /** * Create a new user_competency object. * diff --git a/admin/tool/lp/db/access.php b/admin/tool/lp/db/access.php index 8eb87441871..7142c2f5cf8 100644 --- a/admin/tool/lp/db/access.php +++ b/admin/tool/lp/db/access.php @@ -28,6 +28,7 @@ defined('MOODLE_INTERNAL') || die(); $capabilities = array( + // Competencies. 'tool/lp:competencymanage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSECAT, @@ -43,6 +44,22 @@ $capabilities = array( ), 'clonepermissionsfrom' => 'moodle/block:view' ), + 'tool/lp:competencysuggestgrade' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSE, // And CONTEXT_USER. + 'archetypes' => array( + 'teacher' => CAP_ALLOW + ), + ), + 'tool/lp:competencygrade' => array( + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSE, // And CONTEXT_USER. + 'archetypes' => array( + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ), + ), + // Course competencies. 'tool/lp:coursecompetencymanage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, @@ -68,6 +85,7 @@ $capabilities = array( ), 'clonepermissionsfrom' => 'moodle/block:view' ), + // User plans. 'tool/lp:planmanage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, @@ -125,6 +143,7 @@ $capabilities = array( ), 'clonepermissionsfrom' => 'moodle/site:config' ), + // Template. 'tool/lp:templatemanage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSECAT, @@ -163,19 +182,4 @@ $capabilities = array( ), 'clonepermissionsfrom' => 'moodle/site:config' ), - 'tool/lp:competencysuggestgrade' => array( - 'captype' => 'write', - 'contextlevel' => CONTEXT_COURSE, // And CONTEXT_USER. - 'archetypes' => array( - 'teacher' => CAP_ALLOW - ), - ), - 'tool/lp:competencygrade' => array( - 'captype' => 'write', - 'contextlevel' => CONTEXT_COURSE, // And CONTEXT_USER. - 'archetypes' => array( - 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ), - ), ); diff --git a/admin/tool/lp/db/messages.php b/admin/tool/lp/db/messages.php new file mode 100644 index 00000000000..0140afe7162 --- /dev/null +++ b/admin/tool/lp/db/messages.php @@ -0,0 +1,29 @@ +. + +/** + * Message producers. + * + * @package tool_lp + * @copyright 2015 Frédéric Massart - FMCorz.net + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$messageproviders = array ( + 'user_competency_comment' => array(), +); \ No newline at end of file diff --git a/admin/tool/lp/db/services.php b/admin/tool/lp/db/services.php index 8649e409b7d..396924c350f 100644 --- a/admin/tool/lp/db/services.php +++ b/admin/tool/lp/db/services.php @@ -662,6 +662,45 @@ $functions = array( 'capabilities' => 'tool/lp:userevidencemanageown', 'ajax' => true, ), + + // User competency. + 'tool_lp_user_competency_cancel_review_request' => array( + 'classname' => 'tool_lp\external', + 'methodname' => 'user_competency_cancel_review_request', + 'classpath' => '', + 'description' => 'Cancel a review request.', + 'type' => 'write', + 'capabilities' => 'tool/lp:userevidencemanageown', + 'ajax' => true, + ), + 'tool_lp_user_competency_request_review' => array( + 'classname' => 'tool_lp\external', + 'methodname' => 'user_competency_request_review', + 'classpath' => '', + 'description' => 'Request a review.', + 'type' => 'write', + 'capabilities' => 'tool/lp:userevidencemanageown', + 'ajax' => true, + ), + 'tool_lp_user_competency_start_review' => array( + 'classname' => 'tool_lp\external', + 'methodname' => 'user_competency_start_review', + 'classpath' => '', + 'description' => 'Start a review.', + 'type' => 'write', + 'capabilities' => 'tool/lp:competencygrade', + 'ajax' => true, + ), + 'tool_lp_user_competency_stop_review' => array( + 'classname' => 'tool_lp\external', + 'methodname' => 'user_competency_stop_review', + 'classpath' => '', + 'description' => 'Stop a review.', + 'type' => 'write', + 'capabilities' => 'tool/lp:competencygrade', + 'ajax' => true, + ), + 'tool_lp_grade_competency_in_plan' => array( 'classname' => 'tool_lp\external', 'methodname' => 'grade_competency_in_plan', diff --git a/admin/tool/lp/lang/en/tool_lp.php b/admin/tool/lp/lang/en/tool_lp.php index d6c1bda9f55..3770c10f41d 100644 --- a/admin/tool/lp/lang/en/tool_lp.php +++ b/admin/tool/lp/lang/en/tool_lp.php @@ -23,6 +23,7 @@ */ $string['actions'] = 'Actions'; +$string['acommentwaspostedonacompetency'] = 'A comment was posted on a competency.'; $string['addcohorts'] = 'Add cohorts'; $string['addcompetency'] = 'Add competency'; $string['addingcompetencywillresetparentrule'] = 'Adding a new competency will remove the rule set on \'{$a}\'. Do you want to continue?'; @@ -37,6 +38,7 @@ $string['allchildrenarecomplete'] = 'All children are complete'; $string['aplanswerecreated'] = '{$a} plans were created.'; $string['aplanswerecreatedmoremayrequiresync'] = '{$a} plans were created, more will be created during the next synchronization.'; $string['assigncohorts'] = 'Assign cohorts'; +$string['cancelreviewrequest'] = 'Cancel review request'; $string['cannotaddrules'] = 'This competency cannot be configured.'; $string['cannotcreateuserplanswhentemplateduedateispassed'] = 'New user plans can not be created: this template\'s due date has, or is about to, expire.'; $string['cannotcreateuserplanswhentemplatehidden'] = 'New user plans can not be created while this template is hidden.'; @@ -156,6 +158,7 @@ $string['lp:userevidencemanage'] = 'Manage evidence of prior learning'; $string['lp:userevidencemanageown'] = 'Manage own evidence of prior learning'; $string['lp:userevidenceread'] = 'View evidence of prior learning of a user'; $string['managecompetenciesandframeworks'] = 'Manage competencies and frameworks'; +$string['messageprovider:user_competency_comment'] = 'Comment posted on a competency.'; $string['move'] = 'Move'; $string['movecompetency'] = 'Move competency'; $string['movecompetencyafter'] = 'Move competency after \'{$a}\''; @@ -201,6 +204,7 @@ $string['proficient'] = 'Proficient'; $string['rate'] = 'Rate'; $string['rating'] = 'Rating'; $string['relatedcompetencies'] = 'Related competencies'; +$string['requestreview'] = 'Request review'; $string['reopenplan'] = 'Reopen this learning plan'; $string['reopenplanconfirm'] = 'Reopen the plan \'{$a}\'? The status of the users competencies that was recorded at the time the plan was previously completed will be deleted, and the plan will become active again.'; $string['savechanges'] = 'Save changes'; @@ -213,8 +217,10 @@ $string['selectedcompetency'] = 'Selected competency'; $string['selectcohortstosync'] = 'Select cohorts to sync'; $string['selectuserstocreateplansfor'] = 'Select users to create plans for'; $string['shortname'] = 'Name'; +$string['startreview'] = 'Start review'; $string['state'] = 'State'; $string['status'] = 'Status'; +$string['stopreview'] = 'Stop review'; $string['stopsyncingcohort'] = 'Stop syncing cohort'; $string['suggest'] = 'Suggest'; $string['syncplanscohorts'] = 'Sync plans from template cohorts'; diff --git a/admin/tool/lp/lib.php b/admin/tool/lp/lib.php index cfb9e8283c4..f765920a9c9 100644 --- a/admin/tool/lp/lib.php +++ b/admin/tool/lp/lib.php @@ -169,3 +169,64 @@ function tool_lp_pluginfile($course, $cm, $context, $filearea, $args, $forcedown send_stored_file($file, null, 0, $forcedownload); } + +/** + * Hook when a comment is added. + * + * @param stdClass $comment The comment. + * @param stdClass $params The parameters. + * @return array + */ +function tool_lp_comment_add($comment, $params) { + if ($params->commentarea == 'user_competency') { + $uc = new \tool_lp\user_competency($params->itemid); + + // When the owner comments we message the reviewer, otherwise we message the current user. + $recipient = $comment->userid == $uc->get_userid() ? $uc->get_reviewerid() : $uc->get_userid(); + if (!$recipient) { + return; + } + + $message = new stdClass(); + $message->component = 'tool_lp'; + $message->name = 'user_competency_comment'; + $message->notification = 1; + $message->userto = $recipient; + $message->userfrom = $comment->userid; + // TODO Make message useful. + $message->fullmessage = get_string('acommentwaspostedonacompetency', 'tool_lp'); + $message->fullmessageformat = FORMAT_PLAIN; + $message->fullmessagehtml = ''; + $message->smallmessage = get_string('acommentwaspostedonacompetency', 'tool_lp'); + // TODO Make URL useful. + $message->contexturl = new moodle_url('/admin/tool/lp/plans.php', array('userid' => $uc->get_userid())); + $message->contexturlname = get_string('userplans', 'tool_lp'); + + message_send($message); + } +} + +/** + * Return the permissions of for the comments. + * + * @param stdClass $params The parameters. + * @return array + */ +function tool_lp_comment_permissions($params) { + if ($params->commentarea == 'user_competency') { + $uc = new \tool_lp\user_competency($params->itemid); + $can = \tool_lp\plan::can_read_user($uc->get_userid()); + return array('post' => $can, 'view' => $can); + } + return array('post' => false, 'view' => false); +} + +/** + * Validates comments. + * + * @param stdClass $params The parameters. + * @return array + */ +function tool_lp_comment_validate($params) { + return true; +} diff --git a/admin/tool/lp/templates/plan_page.mustache b/admin/tool/lp/templates/plan_page.mustache index 96caba22fd4..3ffa4d5521e 100644 --- a/admin/tool/lp/templates/plan_page.mustache +++ b/admin/tool/lp/templates/plan_page.mustache @@ -66,15 +66,13 @@ {{#str}}proficient, tool_lp{{/str}} {{^plan.iscompleted}} {{#str}}status, tool_lp{{/str}} - {{/plan.iscompleted}} - {{#plan.canbeedited}} {{#str}}actions, tool_lp{{/str}} - {{/plan.canbeedited}} + {{/plan.iscompleted}} - + {{#competencies}} - + {{#plan.canbeedited}} @@ -91,13 +89,32 @@ {{usercompetency.proficiencyname}} {{usercompetency.statusname}} {{/plan.iscompleted}} - {{#plan.canbeedited}} + {{^plan.iscompleted}} - + - {{/plan.canbeedited}} + {{/plan.iscompleted}} {{/competencies}} @@ -114,19 +131,25 @@ -{{#plan.canmanage}} {{#js}} -require(['tool_lp/competencies', 'tool_lp/planactions'], function(mod, actionsMod) { - (new mod({{plan.id}}, 'plan', {{contextid}})); - +require(['tool_lp/competencies', 'tool_lp/planactions', 'tool_lp/user_competency_workflow'], function(mod, actionsMod, UserCompWorkflow) { var planActions = new actionsMod('plan'); - planActions.registerEvents(); + + {{#plan.canmanage}} + (new mod({{plan.id}}, 'plan', {{contextid}})); + planActions.registerEvents(); + {{/plan.canmanage}} + + var ucw = new UserCompWorkflow(); + ucw.enhanceMenubar('.user-competency-actions'); + ucw.on('status-changed', planActions.refresh.bind(planActions, '[data-region="plan-summary"]')); + ucw.on('error-occured', planActions.refresh.bind(planActions, '[data-region="plan-summary"]')); }); {{/js}} -{{/plan.canmanage}} {{#js}} require(['tool_lp/competencydialogue'], function(Compdialogue) { var competencydialogue = new Compdialogue({includerelated : true}); competencydialogue.watch('[data-region="plan-page"]'); + }); {{/js}} diff --git a/admin/tool/lp/templates/user_competency_summary_in_plan.mustache b/admin/tool/lp/templates/user_competency_summary_in_plan.mustache index 2efa2005332..63583d79be5 100644 --- a/admin/tool/lp/templates/user_competency_summary_in_plan.mustache +++ b/admin/tool/lp/templates/user_competency_summary_in_plan.mustache @@ -40,14 +40,25 @@ This template does not have an example context because it includes ajax functionality. }} {{#usercompetencysummary}} -
+
{{#competency}} {{> tool_lp/competency_summary }} {{/competency}}
-
{{#str}}rating, tool_lp{{/str}}
{{#usercompetency}} +
{{#str}}status, tool_lp{{/str}}
+
{{statusname}} + + {{#isstatusinreview}} + - {{reviewer.fullname}} + {{/isstatusinreview}} + + {{#isrequestreviewallowed}}{{/isrequestreviewallowed}} + {{#iscancelreviewrequestallowed}}{{/iscancelreviewrequestallowed}} + {{#isstartreviewallowed}}{{/isstartreviewallowed}} + {{#isstopreviewallowed}}{{/isstopreviewallowed}} +
{{#str}}proficient, tool_lp{{/str}}
@@ -56,9 +67,9 @@
{{#str}}rating, tool_lp{{/str}}
{{gradename}} - {{#cangradeorsuggest}}

+ {{#cangradeorsuggest}}
{{#cangrade}} @@ -68,25 +79,30 @@ {{/cansuggest}}
- +

+ {{/cangradeorsuggest}}
-

{{#js}} - require(['jquery', 'tool_lp/grade_user_competency_inline', 'tool_lp/user_competency_info'], function($, mod, info) { + require(['jquery', 'tool_lp/grade_user_competency_inline', 'tool_lp/user_competency_info', 'tool_lp/user_competency_workflow'], function($, mod, info, UserCompWorkflow) { var scaleConfig = JSON.parse('{{{competency.scaleconfiguration}}}'); - var inlineGrader = new mod('grade-competency-form-{{uniqid}}', scaleConfig, '{{competency.competency.id}}', '{{user.id}}', '{{plan.id}}', '', '{{#str}}chooserating, tool_lp{{/str}}'); var competencyElement = $(document.getElementById('grade-competency-form-{{uniqid}}')).closest('[data-region=user-competency-full-info]'); var infoReloader = new info(competencyElement, '{{competency.competency.id}}', '{{user.id}}', '{{plan.id}}'); + var ucw = new UserCompWorkflow(); + ucw.registerEvents('[data-region="user-competency-status"]'); + ucw.on('status-changed', infoReloader.reload.bind(infoReloader)); + ucw.on('error-occured', infoReloader.reload.bind(infoReloader)); + + var inlineGrader = new mod('grade-competency-form-{{uniqid}}', scaleConfig, '{{competency.competency.id}}', '{{user.id}}', '{{plan.id}}', '', '{{#str}}chooserating, tool_lp{{/str}}'); inlineGrader.on('competencyupdated', infoReloader.reload.bind(infoReloader)); }); {{/js}} - {{/cangradeorsuggest}} {{/usercompetency}} {{#usercompetencyplan}} +
{{#str}}rating, tool_lp{{/str}}
{{gradename}} - {{#str}}plancompleted, tool_lp{{/str}}
{{#cangradeorsuggest}} {{$gradeform}} @@ -101,6 +117,9 @@ {{/usercompetencyplan}}
+ {{#commentarea}} + {{>tool_lp/comment_area}} + {{/commentarea}}
{{#str}}evidence, tool_lp{{/str}}
diff --git a/admin/tool/lp/tests/externallib_test.php b/admin/tool/lp/tests/externallib_test.php index fee697f955d..3630e82c208 100644 --- a/admin/tool/lp/tests/externallib_test.php +++ b/admin/tool/lp/tests/externallib_test.php @@ -1405,7 +1405,7 @@ class tool_lp_external_testcase extends externallib_advanced_testcase { $plan = $lpg->create_plan(array('userid' => $this->user->id, 'templateid' => $tpl->get_id())); $uc1a = $lpg->create_user_competency(array('userid' => $this->user->id, 'competencyid' => $c1a->get_id(), - 'status' => user_competency::STATUS_IN_REVIEW)); + 'status' => user_competency::STATUS_IN_REVIEW, 'reviewerid' => $this->creator->id)); $uc1b = $lpg->create_user_competency(array('userid' => $this->user->id, 'competencyid' => $c1b->get_id())); $uc2b = $lpg->create_user_competency(array('userid' => $this->user->id, 'competencyid' => $c2b->get_id(), 'grade' => 2, 'proficiency' => 1));