');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],d=0;d1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},b}),b.define("select2/selection/allowClear",["jquery","../keys"],function(a,b){function c(){}return c.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(a){d._handleClear(a)}),b.on("keypress",function(a){d._handleKeyboardClear(a,b)})},c.prototype._handleClear=function(a,b){if(!this.options.get("disabled")){var c=this.$selection.find(".select2-selection__clear");if(0!==c.length){b.stopPropagation();for(var d=c.data("data"),e=0;e0||0===c.length)){var d=a('×');d.data("data",c),this.$selection.find(".select2-selection__rendered").prepend(d)}},c}),b.define("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return this._transferTabIndex(),d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.trigger("focus")}),b.on("close",function(){e.$search.val(""),e.$search.removeAttr("aria-activedescendant"),e.$search.trigger("focus")}),b.on("enable",function(){e.$search.prop("disabled",!1),e._transferTabIndex()}),b.on("disable",function(){e.$search.prop("disabled",!0)}),b.on("focus",function(a){e.$search.trigger("focus")}),b.on("results:focus",function(a){e.$search.attr("aria-activedescendant",a.id)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e._handleBlur(a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}});var f=document.documentMode,g=f&&11>=f;this.$selection.on("input.searchcheck",".select2-search--inline",function(a){return g?void e.$selection.off("input.search input.searchcheck"):void e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(a){if(g&&"input"===a.type)return void e.$selection.off("input.search input.searchcheck");var b=a.which;b!=c.SHIFT&&b!=c.CTRL&&b!=c.ALT&&b!=c.TAB&&e.handleSearch(a)})},d.prototype._transferTabIndex=function(a){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){var c=this.$search[0]==document.activeElement;this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),c&&this.$search.focus()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.$search.val(b.text),this.handleSearch()},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(a,c){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(a){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(a,b){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(a,b){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change");if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f=0){var k=f.filter(d(j)),l=this.item(k),m=c.extend(!0,{},j,l),n=this.option(m);k.replaceWith(n)}else{var o=this.option(j);if(j.children){var p=this.convertToOptions(j.children);b.appendMany(o,p)}h.push(o)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(a,b){this.ajaxOptions=this._applyDefaults(b.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),d.__super__.constructor.call(this,a,b)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return c.extend({},a,{q:a.term})},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){e.trigger("results:message",{message:"errorLoading"})});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url.call(this.$element,a)),"function"==typeof f.data&&(f.data=f.data.call(this.$element,a)),this.ajaxOptions.delay&&""!==a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");void 0!==f&&(this.createTag=f);var g=d.get("insertTag");if(void 0!==g&&(this.insertTag=g),b.call(this,c,d),a.isArray(e))for(var h=0;h0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.bind=function(){},c.prototype.position=function(a,b){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a,b){function c(){}return c.prototype.render=function(b){var c=b.call(this),d=a('');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},c.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(b){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},c.prototype.handleSearch=function(a){if(!this._keyUpPrevented){var b=this.$search.val();this.trigger("query",{term:b})}this._keyUpPrevented=!1},c.prototype.showSearch=function(a,b){return!0},c}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a(''),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(b,c,d){this.$dropdownParent=d.get("dropdownParent")||a(document.body),b.call(this,c,d)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.destroy=function(a){a.call(this),this.$dropdownContainer.remove()},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a(""),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(a){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c,d){var e=this,f="scroll.select2."+d.id,g="resize.select2."+d.id,h="orientationchange.select2."+d.id,i=this.$container.parents().filter(b.hasScroll);i.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),i.on(f,function(b){var c=a(this).data("select2-scroll-position");a(this).scrollTop(c.y)}),a(window).on(f+" "+g+" "+h,function(a){e._positionDropdown(),e._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c,d){var e="scroll.select2."+d.id,f="resize.select2."+d.id,g="orientationchange.select2."+d.id,h=this.$container.parents().filter(b.hasScroll);h.off(e),a(window).off(e+" "+f+" "+g)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=this.$container.offset();f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.topf.bottom+h.height,l={left:f.left,top:g.bottom},m=this.$dropdownParent;"static"===m.css("position")&&(m=m.offsetParent());var n=m.offset();l.top-=n.top,l.left-=n.left,c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){var a={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(a.minWidth=a.width,a.width="auto"),this.$dropdown.css(a)},c.prototype._showDropdown=function(a){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.multiple?l.selectionAdapter=e:l.selectionAdapter=d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(a.prop("dir")?this.options.dir=a.prop("dir"):a.closest("[dir]").prop("dir")?this.options.dir=a.closest("[dir]").prop("dir"):this.options.dir="ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b=b.replace(/(:|\.|\[|\]|,)/g,""),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this._sync=c.bind(this._syncAttributes,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._sync);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._sync)}),this._observer.observe(this.$element[0],{attributes:!0,subtree:!1})):this.$element[0].addEventListener&&this.$element[0].addEventListener("DOMAttrModified",b._sync,!1)},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle","focus"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("focus",function(a){b.focus(a)}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open",{}),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ESC||c===d.TAB||c===d.UP&&b.altKey?(a.close(),b.preventDefault()):c===d.ENTER?(a.trigger("results:select",{}),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle",{}),b.preventDefault()):c===d.UP?(a.trigger("results:previous",{}),b.preventDefault()):c===d.DOWN&&(a.trigger("results:next",{}),b.preventDefault()):(c===d.ENTER||c===d.SPACE||c===d.DOWN&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(void 0===b&&(b={}),a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||this.trigger("query",{})},e.prototype.close=function(){this.isOpen()&&this.trigger("close",{})},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},e.prototype.focus=function(a){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._sync),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&this.$element[0].removeEventListener("DOMAttrModified",this._sync,!1),this._sync=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null},e.prototype.render=function(){var b=a('');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),b.define("jquery-mousewheel",["jquery"],function(a){return a}),b.define("jquery.select2",["jquery","jquery-mousewheel","./select2/core","./select2/defaults"],function(a,b,c,d){if(null==a.fn.select2){var e=["open","close","destroy"];a.fn.select2=function(b){if(b=b||{},"object"==typeof b)return this.each(function(){var d=a.extend(!0,{},b);new c(a(this),d)}),this;if("string"==typeof b){var d;return this.each(function(){var c=a(this).data("select2");null==c&&window.console&&console.error&&console.error("The select2('"+b+"') method was called on an element that is not using Select2.");var e=Array.prototype.slice.call(arguments,1);d=c[b].apply(c,e)}),a.inArray(b,e)>-1?this:d}throw new Error("Invalid arguments for Select2: "+b)}}return null==a.fn.select2.defaults&&(a.fn.select2.defaults=d),c}),{define:b.define,require:b.require}}(),c=b.require("jquery.select2");return a.fn.select2.amd=b,c});
+/*! Select2 4.0.3 | https://github.com/select2/select2/blob/master/LICENSE.md */!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.slice(0,n.length-1).concat(a),k=0;k0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){var d=v.call(arguments,0);return"string"!=typeof d[0]&&1===d.length&&d.push(null),n.apply(b,d.concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;hc;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},c.bind=function(a,b){return function(){a.apply(b,arguments)}},c._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e":">",'"':""","'":"'","/":"/"};return"string"!=typeof a?a:String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})},c.appendMany=function(b,c){if("1.7"===a.fn.jquery.substr(0,3)){var d=a();a.map(c,function(a){d=d.add(a)}),c=d}b.append(c)},c}),b.define("select2/results",["jquery","./utils"],function(a,b){function c(a,b,d){this.$element=a,this.data=d,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('
+ = e('%s set a new internal link for the task %s',
+ $this->text->e($author),
+ $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
+ ) ?>
+ = $this->dt->datetime($date_creation) ?>
+
+
+
+ = e(
+ 'This task is now linked to the task %s with the relation "%s"',
+ $this->url->link(t('#%d', $task_link['opposite_task_id']), 'TaskViewController', 'show', array('task_id' => $task_link['opposite_task_id'])),
+ $this->text->e($task_link['label'])
+ ) ?>
+
+
diff --git a/app/Template/event/task_internal_link_delete.php b/app/Template/event/task_internal_link_delete.php
new file mode 100644
index 00000000..e537bf81
--- /dev/null
+++ b/app/Template/event/task_internal_link_delete.php
@@ -0,0 +1,16 @@
+
+ = e('%s removed an internal link for the task %s',
+ $this->text->e($author),
+ $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
+ ) ?>
+ = $this->dt->datetime($date_creation) ?>
+
+
+
+ = e(
+ 'The link with the relation "%s" to the task %s have been removed',
+ $this->text->e($task_link['label']),
+ $this->url->link(t('#%d', $task_link['opposite_task_id']), 'TaskViewController', 'show', array('task_id' => $task_link['opposite_task_id']))
+ ) ?>
+
+ = e(
+ 'The link with the relation "%s" to the task %s have been removed',
+ $this->text->e($task_link['label']),
+ $this->url->link(t('#%d', $task_link['opposite_task_id']), 'TaskViewController', 'show', array('task_id' => $task_link['opposite_task_id']))
+ ) ?>
+
+
+= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?>
diff --git a/tests/units/Action/TaskAssignCategoryLinkTest.php b/tests/units/Action/TaskAssignCategoryLinkTest.php
index d7e68f72..b9d7e9d9 100644
--- a/tests/units/Action/TaskAssignCategoryLinkTest.php
+++ b/tests/units/Action/TaskAssignCategoryLinkTest.php
@@ -2,12 +2,12 @@
require_once __DIR__.'/../Base.php';
+use Kanboard\EventBuilder\TaskLinkEventBuilder;
use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\TaskFinderModel;
use Kanboard\Model\ProjectModel;
use Kanboard\Model\TaskLinkModel;
use Kanboard\Model\CategoryModel;
-use Kanboard\Event\TaskLinkEvent;
use Kanboard\Action\TaskAssignCategoryLink;
class TaskAssignCategoryLinkTest extends Base
@@ -18,6 +18,7 @@ class TaskAssignCategoryLinkTest extends Base
$taskFinderModel = new TaskFinderModel($this->container);
$projectModel = new ProjectModel($this->container);
$categoryModel = new CategoryModel($this->container);
+ $taskLinkModel = new TaskLinkModel($this->container);
$action = new TaskAssignCategoryLink($this->container);
$action->setProjectId(1);
@@ -27,13 +28,12 @@ class TaskAssignCategoryLinkTest extends Base
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $categoryModel->create(array('name' => 'C1', 'project_id' => 1)));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'T1', 'project_id' => 1)));
+ $this->assertEquals(2, $taskCreationModel->create(array('title' => 'T2', 'project_id' => 1)));
+ $this->assertEquals(1, $taskLinkModel->create(1, 2, 2));
- $event = new TaskLinkEvent(array(
- 'project_id' => 1,
- 'task_id' => 1,
- 'opposite_task_id' => 2,
- 'link_id' => 2,
- ));
+ $event = TaskLinkEventBuilder::getInstance($this->container)
+ ->withTaskLinkId(1)
+ ->build();
$this->assertTrue($action->execute($event, TaskLinkModel::EVENT_CREATE_UPDATE));
@@ -44,51 +44,58 @@ class TaskAssignCategoryLinkTest extends Base
public function testWhenLinkDontMatch()
{
$taskCreationModel = new TaskCreationModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
$projectModel = new ProjectModel($this->container);
$categoryModel = new CategoryModel($this->container);
+ $taskLinkModel = new TaskLinkModel($this->container);
$action = new TaskAssignCategoryLink($this->container);
$action->setProjectId(1);
$action->setParam('category_id', 1);
- $action->setParam('link_id', 1);
+ $action->setParam('link_id', 2);
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $categoryModel->create(array('name' => 'C1', 'project_id' => 1)));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'T1', 'project_id' => 1)));
+ $this->assertEquals(2, $taskCreationModel->create(array('title' => 'T2', 'project_id' => 1)));
+ $this->assertEquals(1, $taskLinkModel->create(1, 2, 1));
- $event = new TaskLinkEvent(array(
- 'project_id' => 1,
- 'task_id' => 1,
- 'opposite_task_id' => 2,
- 'link_id' => 2,
- ));
+ $event = TaskLinkEventBuilder::getInstance($this->container)
+ ->withTaskLinkId(1)
+ ->build();
$this->assertFalse($action->execute($event, TaskLinkModel::EVENT_CREATE_UPDATE));
+
+ $task = $taskFinderModel->getById(1);
+ $this->assertEquals(0, $task['category_id']);
}
public function testThatExistingCategoryWillNotChange()
{
$taskCreationModel = new TaskCreationModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
$projectModel = new ProjectModel($this->container);
$categoryModel = new CategoryModel($this->container);
+ $taskLinkModel = new TaskLinkModel($this->container);
$action = new TaskAssignCategoryLink($this->container);
$action->setProjectId(1);
- $action->setParam('category_id', 2);
+ $action->setParam('category_id', 1);
$action->setParam('link_id', 2);
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $categoryModel->create(array('name' => 'C1', 'project_id' => 1)));
- $this->assertEquals(2, $categoryModel->create(array('name' => 'C2', 'project_id' => 1)));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'T1', 'project_id' => 1, 'category_id' => 1)));
+ $this->assertEquals(2, $taskCreationModel->create(array('title' => 'T2', 'project_id' => 1)));
+ $this->assertEquals(1, $taskLinkModel->create(1, 2, 2));
- $event = new TaskLinkEvent(array(
- 'project_id' => 1,
- 'task_id' => 1,
- 'opposite_task_id' => 2,
- 'link_id' => 2,
- ));
+ $event = TaskLinkEventBuilder::getInstance($this->container)
+ ->withTaskLinkId(1)
+ ->build();
$this->assertFalse($action->execute($event, TaskLinkModel::EVENT_CREATE_UPDATE));
+
+ $task = $taskFinderModel->getById(1);
+ $this->assertEquals(1, $task['category_id']);
}
}
diff --git a/tests/units/Action/TaskAssignColorLinkTest.php b/tests/units/Action/TaskAssignColorLinkTest.php
index 07d0969b..27364bc9 100644
--- a/tests/units/Action/TaskAssignColorLinkTest.php
+++ b/tests/units/Action/TaskAssignColorLinkTest.php
@@ -2,7 +2,7 @@
require_once __DIR__.'/../Base.php';
-use Kanboard\Event\GenericEvent;
+use Kanboard\EventBuilder\TaskLinkEventBuilder;
use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\TaskFinderModel;
use Kanboard\Model\ProjectModel;
@@ -13,42 +13,55 @@ class TaskAssignColorLinkTest extends Base
{
public function testChangeColor()
{
- $projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$taskFinderModel = new TaskFinderModel($this->container);
-
- $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
- $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
-
- $event = new GenericEvent(array('project_id' => 1, 'task_id' => 1, 'link_id' => 1));
+ $projectModel = new ProjectModel($this->container);
+ $taskLinkModel = new TaskLinkModel($this->container);
$action = new TaskAssignColorLink($this->container);
$action->setProjectId(1);
+ $action->setParam('link_id', 2);
$action->setParam('color_id', 'red');
- $action->setParam('link_id', 1);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
+ $this->assertEquals(1, $taskCreationModel->create(array('title' => 'T1', 'project_id' => 1)));
+ $this->assertEquals(2, $taskCreationModel->create(array('title' => 'T2', 'project_id' => 1)));
+ $this->assertEquals(1, $taskLinkModel->create(1, 2, 2));
+
+ $event = TaskLinkEventBuilder::getInstance($this->container)
+ ->withTaskLinkId(1)
+ ->build();
$this->assertTrue($action->execute($event, TaskLinkModel::EVENT_CREATE_UPDATE));
$task = $taskFinderModel->getById(1);
- $this->assertNotEmpty($task);
$this->assertEquals('red', $task['color_id']);
}
public function testWithWrongLink()
{
- $projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
-
- $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
- $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
-
- $event = new GenericEvent(array('project_id' => 1, 'task_id' => 1, 'link_id' => 2));
+ $taskFinderModel = new TaskFinderModel($this->container);
+ $projectModel = new ProjectModel($this->container);
+ $taskLinkModel = new TaskLinkModel($this->container);
$action = new TaskAssignColorLink($this->container);
$action->setProjectId(1);
+ $action->setParam('link_id', 2);
$action->setParam('color_id', 'red');
- $action->setParam('link_id', 1);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
+ $this->assertEquals(1, $taskCreationModel->create(array('title' => 'T1', 'project_id' => 1)));
+ $this->assertEquals(2, $taskCreationModel->create(array('title' => 'T2', 'project_id' => 1)));
+ $this->assertEquals(1, $taskLinkModel->create(1, 2, 1));
+
+ $event = TaskLinkEventBuilder::getInstance($this->container)
+ ->withTaskLinkId(1)
+ ->build();
$this->assertFalse($action->execute($event, TaskLinkModel::EVENT_CREATE_UPDATE));
+
+ $task = $taskFinderModel->getById(1);
+ $this->assertEquals('yellow', $task['color_id']);
}
}
diff --git a/tests/units/EventBuilder/TaskLinkEventBuilderTest.php b/tests/units/EventBuilder/TaskLinkEventBuilderTest.php
new file mode 100644
index 00000000..7364d651
--- /dev/null
+++ b/tests/units/EventBuilder/TaskLinkEventBuilderTest.php
@@ -0,0 +1,70 @@
+container);
+ $taskLinkEventBuilder->withTaskLinkId(42);
+ $this->assertNull($taskLinkEventBuilder->build());
+ }
+
+ public function testBuild()
+ {
+ $taskLinkModel = new TaskLinkModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $projectModel = new ProjectModel($this->container);
+ $taskLinkEventBuilder = new TaskLinkEventBuilder($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
+ $this->assertEquals(1, $taskCreationModel->create(array('title' => 'task 1', 'project_id' => 1)));
+ $this->assertEquals(2, $taskCreationModel->create(array('title' => 'task 2', 'project_id' => 1)));
+ $this->assertEquals(1, $taskLinkModel->create(1, 2, 1));
+
+ $event = $taskLinkEventBuilder->withTaskLinkId(1)->build();
+
+ $this->assertInstanceOf('Kanboard\Event\TaskLinkEvent', $event);
+ $this->assertNotEmpty($event['task_link']);
+ $this->assertNotEmpty($event['task']);
+ }
+
+ public function testBuildTitle()
+ {
+ $taskLinkModel = new TaskLinkModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $projectModel = new ProjectModel($this->container);
+ $taskLinkEventBuilder = new TaskLinkEventBuilder($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
+ $this->assertEquals(1, $taskCreationModel->create(array('title' => 'task 1', 'project_id' => 1)));
+ $this->assertEquals(2, $taskCreationModel->create(array('title' => 'task 2', 'project_id' => 1)));
+ $this->assertEquals(1, $taskLinkModel->create(1, 2, 1));
+
+ $eventData = $taskLinkEventBuilder->withTaskLinkId(1)->build();
+
+ $title = $taskLinkEventBuilder->buildTitleWithAuthor('Foobar', TaskLinkModel::EVENT_CREATE_UPDATE, $eventData->getAll());
+ $this->assertEquals('Foobar set a new internal link for the task #1', $title);
+
+ $title = $taskLinkEventBuilder->buildTitleWithAuthor('Foobar', TaskLinkModel::EVENT_DELETE, $eventData->getAll());
+ $this->assertEquals('Foobar removed an internal link for the task #1', $title);
+
+ $title = $taskLinkEventBuilder->buildTitleWithAuthor('Foobar', 'not found', $eventData->getAll());
+ $this->assertSame('', $title);
+
+ $title = $taskLinkEventBuilder->buildTitleWithoutAuthor(TaskLinkModel::EVENT_CREATE_UPDATE, $eventData->getAll());
+ $this->assertEquals('A new internal link for the task #1 have been defined', $title);
+
+ $title = $taskLinkEventBuilder->buildTitleWithoutAuthor(TaskLinkModel::EVENT_DELETE, $eventData->getAll());
+ $this->assertEquals('Internal link removed for the task #1', $title);
+
+ $title = $taskLinkEventBuilder->buildTitleWithoutAuthor('not found', $eventData->getAll());
+ $this->assertSame('', $title);
+ }
+}
diff --git a/tests/units/Job/TaskLinkEventJobTest.php b/tests/units/Job/TaskLinkEventJobTest.php
new file mode 100644
index 00000000..1949316a
--- /dev/null
+++ b/tests/units/Job/TaskLinkEventJobTest.php
@@ -0,0 +1,65 @@
+container);
+ $taskLinkEventJob->withParams(123, 'foobar');
+
+ $this->assertSame(array(123, 'foobar'), $taskLinkEventJob->getJobParams());
+ }
+
+ public function testWithMissingLink()
+ {
+ $this->container['dispatcher']->addListener(TaskLinkModel::EVENT_CREATE_UPDATE, function() {});
+
+ $taskLinkEventJob = new TaskLinkEventJob($this->container);
+ $taskLinkEventJob->execute(42, TaskLinkModel::EVENT_CREATE_UPDATE);
+
+ $called = $this->container['dispatcher']->getCalledListeners();
+ $this->assertEmpty($called);
+ }
+
+ public function testTriggerCreationEvents()
+ {
+ $this->container['dispatcher']->addListener(TaskLinkModel::EVENT_CREATE_UPDATE, function() {});
+
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $projectModel = new ProjectModel($this->container);
+ $taskLinkModel = new TaskLinkModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
+ $this->assertEquals(1, $taskCreationModel->create(array('title' => 'task 1', 'project_id' => 1)));
+ $this->assertEquals(2, $taskCreationModel->create(array('title' => 'task 2', 'project_id' => 1)));
+ $this->assertEquals(1, $taskLinkModel->create(1, 2, 1));
+
+ $called = $this->container['dispatcher']->getCalledListeners();
+ $this->assertArrayHasKey(TaskLinkModel::EVENT_CREATE_UPDATE.'.closure', $called);
+ }
+
+ public function testTriggerDeleteEvents()
+ {
+ $this->container['dispatcher']->addListener(TaskLinkModel::EVENT_DELETE, function() {});
+
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $projectModel = new ProjectModel($this->container);
+ $taskLinkModel = new TaskLinkModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
+ $this->assertEquals(1, $taskCreationModel->create(array('title' => 'task 1', 'project_id' => 1)));
+ $this->assertEquals(2, $taskCreationModel->create(array('title' => 'task 2', 'project_id' => 1)));
+ $this->assertEquals(1, $taskLinkModel->create(1, 2, 1));
+ $this->assertTrue($taskLinkModel->remove(1));
+
+ $called = $this->container['dispatcher']->getCalledListeners();
+ $this->assertArrayHasKey(TaskLinkModel::EVENT_DELETE.'.closure', $called);
+ }
+}
diff --git a/tests/units/Model/NotificationModelTest.php b/tests/units/Model/NotificationModelTest.php
index 889f3349..0bd9db6e 100644
--- a/tests/units/Model/NotificationModelTest.php
+++ b/tests/units/Model/NotificationModelTest.php
@@ -7,6 +7,7 @@ use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\SubtaskModel;
use Kanboard\Model\CommentModel;
use Kanboard\Model\TaskFileModel;
+use Kanboard\Model\TaskLinkModel;
use Kanboard\Model\TaskModel;
use Kanboard\Model\ProjectModel;
use Kanboard\Model\NotificationModel;
@@ -23,47 +24,38 @@ class NotificationModelTest extends Base
$subtaskModel = new SubtaskModel($this->container);
$commentModel = new CommentModel($this->container);
$taskFileModel = new TaskFileModel($this->container);
+ $taskLinkModel = new TaskLinkModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1)));
+ $this->assertEquals(2, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1)));
$this->assertEquals(1, $subtaskModel->create(array('title' => 'test', 'task_id' => 1)));
$this->assertEquals(1, $commentModel->create(array('comment' => 'test', 'task_id' => 1, 'user_id' => 1)));
$this->assertEquals(1, $taskFileModel->create(1, 'test', 'blah', 123));
+ $this->assertEquals(1, $taskLinkModel->create(1, 2, 1));
$task = $taskFinderModel->getDetails(1);
$subtask = $subtaskModel->getById(1, true);
$comment = $commentModel->getById(1);
$file = $commentModel->getById(1);
+ $tasklink = $taskLinkModel->getById(1);
- $this->assertNotEmpty($task);
- $this->assertNotEmpty($subtask);
- $this->assertNotEmpty($comment);
- $this->assertNotEmpty($file);
-
- foreach (NotificationSubscriber::getSubscribedEvents() as $event_name => $values) {
- $title = $notificationModel->getTitleWithoutAuthor($event_name, array(
- 'task' => $task,
- 'comment' => $comment,
- 'subtask' => $subtask,
- 'file' => $file,
- 'changes' => array()
- ));
-
- $this->assertNotEmpty($title);
-
- $title = $notificationModel->getTitleWithAuthor('foobar', $event_name, array(
+ foreach (NotificationSubscriber::getSubscribedEvents() as $eventName => $values) {
+ $eventData = array(
'task' => $task,
'comment' => $comment,
'subtask' => $subtask,
'file' => $file,
+ 'task_link' => $tasklink,
'changes' => array()
- ));
+ );
- $this->assertNotEmpty($title);
+ $this->assertNotEmpty($notificationModel->getTitleWithoutAuthor($eventName, $eventData));
+ $this->assertNotEmpty($notificationModel->getTitleWithAuthor('Foobar', $eventName, $eventData));
}
$this->assertNotEmpty($notificationModel->getTitleWithoutAuthor(TaskModel::EVENT_OVERDUE, array('tasks' => array(array('id' => 1)))));
- $this->assertNotEmpty($notificationModel->getTitleWithoutAuthor('unkown', array()));
+ $this->assertNotEmpty($notificationModel->getTitleWithoutAuthor('unknown', array()));
}
public function testGetTaskIdFromEvent()
@@ -75,6 +67,7 @@ class NotificationModelTest extends Base
$subtaskModel = new SubtaskModel($this->container);
$commentModel = new CommentModel($this->container);
$taskFileModel = new TaskFileModel($this->container);
+ $taskLinkModel = new TaskLinkModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1)));
@@ -86,18 +79,20 @@ class NotificationModelTest extends Base
$subtask = $subtaskModel->getById(1, true);
$comment = $commentModel->getById(1);
$file = $commentModel->getById(1);
+ $tasklink = $taskLinkModel->getById(1);
$this->assertNotEmpty($task);
$this->assertNotEmpty($subtask);
$this->assertNotEmpty($comment);
$this->assertNotEmpty($file);
- foreach (NotificationSubscriber::getSubscribedEvents() as $event_name => $values) {
- $task_id = $notificationModel->getTaskIdFromEvent($event_name, array(
+ foreach (NotificationSubscriber::getSubscribedEvents() as $eventName => $values) {
+ $task_id = $notificationModel->getTaskIdFromEvent($eventName, array(
'task' => $task,
'comment' => $comment,
'subtask' => $subtask,
'file' => $file,
+ 'task_link' => $tasklink,
'changes' => array()
));
diff --git a/tests/units/Model/TaskLinkModelTest.php b/tests/units/Model/TaskLinkModelTest.php
index 78590891..01a7888b 100644
--- a/tests/units/Model/TaskLinkModelTest.php
+++ b/tests/units/Model/TaskLinkModelTest.php
@@ -9,6 +9,34 @@ use Kanboard\Model\ProjectModel;
class TaskLinkModelTest extends Base
{
+ public function testGeyById()
+ {
+ $taskLinkModel = new TaskLinkModel($this->container);
+ $projectModel = new ProjectModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test')));
+ $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A')));
+ $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'B')));
+ $this->assertEquals(1, $taskLinkModel->create(1, 2, 6));
+
+ $taskLink = $taskLinkModel->getById(1);
+ $this->assertEquals(1, $taskLink['id']);
+ $this->assertEquals(1, $taskLink['task_id']);
+ $this->assertEquals(2, $taskLink['opposite_task_id']);
+ $this->assertEquals(6, $taskLink['link_id']);
+ $this->assertEquals(7, $taskLink['opposite_link_id']);
+ $this->assertEquals('is a child of', $taskLink['label']);
+
+ $taskLink = $taskLinkModel->getById(2);
+ $this->assertEquals(2, $taskLink['id']);
+ $this->assertEquals(2, $taskLink['task_id']);
+ $this->assertEquals(1, $taskLink['opposite_task_id']);
+ $this->assertEquals(7, $taskLink['link_id']);
+ $this->assertEquals(6, $taskLink['opposite_link_id']);
+ $this->assertEquals('is a parent of', $taskLink['label']);
+ }
+
// Check postgres issue: "Cardinality violation: 7 ERROR: more than one row returned by a subquery used as an expression"
public function testGetTaskWithMultipleMilestoneLink()
{
diff --git a/tests/units/Notification/MailNotificationTest.php b/tests/units/Notification/MailNotificationTest.php
new file mode 100644
index 00000000..6579d9bc
--- /dev/null
+++ b/tests/units/Notification/MailNotificationTest.php
@@ -0,0 +1,117 @@
+container);
+ $projectModel = new ProjectModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $subtaskModel = new SubtaskModel($this->container);
+ $commentModel = new CommentModel($this->container);
+ $fileModel = new TaskFileModel($this->container);
+ $taskLinkModel = new TaskLinkModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test')));
+ $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1)));
+ $this->assertEquals(2, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1)));
+ $this->assertEquals(1, $subtaskModel->create(array('title' => 'test', 'task_id' => 1)));
+ $this->assertEquals(1, $commentModel->create(array('comment' => 'test', 'task_id' => 1, 'user_id' => 1)));
+ $this->assertEquals(1, $fileModel->create(1, 'test', 'blah', 123));
+ $this->assertEquals(1, $taskLinkModel->create(1, 2, 1));
+
+ $task = $taskFinderModel->getDetails(1);
+ $subtask = $subtaskModel->getById(1, true);
+ $comment = $commentModel->getById(1);
+ $file = $commentModel->getById(1);
+ $tasklink = $taskLinkModel->getById(1);
+
+ $this->assertNotEmpty($task);
+ $this->assertNotEmpty($subtask);
+ $this->assertNotEmpty($comment);
+ $this->assertNotEmpty($file);
+
+ foreach (NotificationSubscriber::getSubscribedEvents() as $eventName => $values) {
+ $eventData = array(
+ 'task' => $task,
+ 'comment' => $comment,
+ 'subtask' => $subtask,
+ 'file' => $file,
+ 'task_link' => $tasklink,
+ 'changes' => array()
+ );
+ $this->assertNotEmpty($mailNotification->getMailContent($eventName, $eventData));
+ $this->assertNotEmpty($mailNotification->getMailSubject($eventName, $eventData));
+ }
+ }
+
+ public function testSendWithEmailAddress()
+ {
+ $mailNotification = new MailNotification($this->container);
+ $projectModel = new ProjectModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $userModel = new UserModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test')));
+ $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1)));
+ $this->assertTrue($userModel->update(array('id' => 1, 'email' => 'test@localhost')));
+
+ $this->container['emailClient'] = $this
+ ->getMockBuilder('\Kanboard\Core\Mail\Client')
+ ->setConstructorArgs(array($this->container))
+ ->setMethods(array('send'))
+ ->getMock();
+
+ $this->container['emailClient']
+ ->expects($this->once())
+ ->method('send')
+ ->with(
+ $this->equalTo('test@localhost'),
+ $this->equalTo('admin'),
+ $this->equalTo('[test][New task] test (#1)'),
+ $this->stringContains('test')
+ );
+
+ $mailNotification->notifyUser($userModel->getById(1), TaskModel::EVENT_CREATE, array('task' => $taskFinderModel->getDetails(1)));
+ }
+
+ public function testSendWithoutEmailAddress()
+ {
+ $mailNotification = new MailNotification($this->container);
+ $projectModel = new ProjectModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $userModel = new UserModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test')));
+ $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1)));
+
+ $this->container['emailClient'] = $this
+ ->getMockBuilder('\Kanboard\Core\Mail\Client')
+ ->setConstructorArgs(array($this->container))
+ ->setMethods(array('send'))
+ ->getMock();
+
+ $this->container['emailClient']
+ ->expects($this->never())
+ ->method('send');
+
+ $mailNotification->notifyUser($userModel->getById(1), TaskModel::EVENT_CREATE, array('task' => $taskFinderModel->getDetails(1)));
+ }
+}
diff --git a/tests/units/Notification/MailTest.php b/tests/units/Notification/MailTest.php
deleted file mode 100644
index 9f077ac8..00000000
--- a/tests/units/Notification/MailTest.php
+++ /dev/null
@@ -1,117 +0,0 @@
-container);
- $p = new ProjectModel($this->container);
- $tf = new TaskFinderModel($this->container);
- $tc = new TaskCreationModel($this->container);
- $s = new SubtaskModel($this->container);
- $c = new CommentModel($this->container);
- $f = new TaskFileModel($this->container);
-
- $this->assertEquals(1, $p->create(array('name' => 'test')));
- $this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1)));
- $this->assertEquals(1, $s->create(array('title' => 'test', 'task_id' => 1)));
- $this->assertEquals(1, $c->create(array('comment' => 'test', 'task_id' => 1, 'user_id' => 1)));
- $this->assertEquals(1, $f->create(1, 'test', 'blah', 123));
-
- $task = $tf->getDetails(1);
- $subtask = $s->getById(1, true);
- $comment = $c->getById(1);
- $file = $c->getById(1);
-
- $this->assertNotEmpty($task);
- $this->assertNotEmpty($subtask);
- $this->assertNotEmpty($comment);
- $this->assertNotEmpty($file);
-
- foreach (NotificationSubscriber::getSubscribedEvents() as $event => $values) {
- $this->assertNotEmpty($en->getMailContent($event, array(
- 'task' => $task,
- 'comment' => $comment,
- 'subtask' => $subtask,
- 'file' => $file,
- 'changes' => array())
- ));
-
- $this->assertNotEmpty($en->getMailSubject($event, array(
- 'task' => $task,
- 'comment' => $comment,
- 'subtask' => $subtask,
- 'file' => $file,
- 'changes' => array())
- ));
- }
- }
-
- public function testSendWithEmailAddress()
- {
- $en = new MailNotification($this->container);
- $p = new ProjectModel($this->container);
- $tf = new TaskFinderModel($this->container);
- $tc = new TaskCreationModel($this->container);
- $u = new UserModel($this->container);
-
- $this->assertEquals(1, $p->create(array('name' => 'test')));
- $this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1)));
- $this->assertTrue($u->update(array('id' => 1, 'email' => 'test@localhost')));
-
- $this->container['emailClient'] = $this
- ->getMockBuilder('\Kanboard\Core\Mail\Client')
- ->setConstructorArgs(array($this->container))
- ->setMethods(array('send'))
- ->getMock();
-
- $this->container['emailClient']
- ->expects($this->once())
- ->method('send')
- ->with(
- $this->equalTo('test@localhost'),
- $this->equalTo('admin'),
- $this->equalTo('[test][New task] test (#1)'),
- $this->stringContains('test')
- );
-
- $en->notifyUser($u->getById(1), TaskModel::EVENT_CREATE, array('task' => $tf->getDetails(1)));
- }
-
- public function testSendWithoutEmailAddress()
- {
- $en = new MailNotification($this->container);
- $p = new ProjectModel($this->container);
- $tf = new TaskFinderModel($this->container);
- $tc = new TaskCreationModel($this->container);
- $u = new UserModel($this->container);
-
- $this->assertEquals(1, $p->create(array('name' => 'test')));
- $this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1)));
-
- $this->container['emailClient'] = $this
- ->getMockBuilder('\Kanboard\Core\Mail\Client')
- ->setConstructorArgs(array($this->container))
- ->setMethods(array('send'))
- ->getMock();
-
- $this->container['emailClient']
- ->expects($this->never())
- ->method('send');
-
- $en->notifyUser($u->getById(1), TaskModel::EVENT_CREATE, array('task' => $tf->getDetails(1)));
- }
-}
diff --git a/tests/units/Notification/WebhookNotificationTest.php b/tests/units/Notification/WebhookNotificationTest.php
new file mode 100644
index 00000000..6fbc349c
--- /dev/null
+++ b/tests/units/Notification/WebhookNotificationTest.php
@@ -0,0 +1,29 @@
+container);
+ $projectModel = new ProjectModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $this->container['dispatcher']->addSubscriber(new NotificationSubscriber($this->container));
+
+ $configModel->save(array('webhook_url' => 'http://localhost/?task-creation'));
+
+ $this->container['httpClient']
+ ->expects($this->once())
+ ->method('postJson')
+ ->with($this->stringContains('http://localhost/?task-creation&token='), $this->anything());
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test')));
+ $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
+ }
+}
diff --git a/tests/units/Notification/WebhookTest.php b/tests/units/Notification/WebhookTest.php
deleted file mode 100644
index 5a9eb1c7..00000000
--- a/tests/units/Notification/WebhookTest.php
+++ /dev/null
@@ -1,29 +0,0 @@
-container);
- $p = new ProjectModel($this->container);
- $tc = new TaskCreationModel($this->container);
- $this->container['dispatcher']->addSubscriber(new NotificationSubscriber($this->container));
-
- $c->save(array('webhook_url' => 'http://localhost/?task-creation'));
-
- $this->container['httpClient']
- ->expects($this->once())
- ->method('postJson')
- ->with($this->stringContains('http://localhost/?task-creation&token='), $this->anything());
-
- $this->assertEquals(1, $p->create(array('name' => 'test')));
- $this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'test')));
- }
-}
--
cgit v1.2.3
From 2a42e0e1aae35a9bb7abf054155b516ffab701d4 Mon Sep 17 00:00:00 2001
From: Frederic Guillot
Date: Sat, 23 Jul 2016 18:10:05 -0400
Subject: Added a new automatic action to set due date
---
ChangeLog | 5 +-
app/Action/TaskAssignDueDateOnCreation.php | 96 ++++++++++++++++++++++
app/ServiceProvider/ActionProvider.php | 2 +
.../Action/TaskAssignDueDateOnCreationTest.php | 37 +++++++++
tests/units/Action/TaskUpdateStartDateTest.php | 3 +-
5 files changed, 140 insertions(+), 3 deletions(-)
create mode 100644 app/Action/TaskAssignDueDateOnCreation.php
create mode 100644 tests/units/Action/TaskAssignDueDateOnCreationTest.php
(limited to 'ChangeLog')
diff --git a/ChangeLog b/ChangeLog
index ee57c86c..01ad5fbd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,7 +3,9 @@ Version 1.0.32 (unreleased)
New features:
-* New automated action to close tasks without activity in a specific column
+* New automated actions:
+ - Close tasks without activity in a specific column
+ - Set due date automatically
* Added internal task links to activity stream
* Added new event for removed comments
* Added search filter for task priority
@@ -11,6 +13,7 @@ New features:
Improvements:
+* Internal events management refactoring
* Handle header X-Real-IP to get IP address
* Display project name for task auto-complete fields
* Make search attributes not case sensitive
diff --git a/app/Action/TaskAssignDueDateOnCreation.php b/app/Action/TaskAssignDueDateOnCreation.php
new file mode 100644
index 00000000..79ff765c
--- /dev/null
+++ b/app/Action/TaskAssignDueDateOnCreation.php
@@ -0,0 +1,96 @@
+ t('Duration in days')
+ );
+ }
+
+ /**
+ * Get the required parameter for the event
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getEventRequiredParameters()
+ {
+ return array(
+ 'task_id',
+ 'task' => array(
+ 'project_id',
+ ),
+ );
+ }
+
+ /**
+ * Execute the action (set the task color)
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool True if the action was executed or false when not executed
+ */
+ public function doAction(array $data)
+ {
+ $values = array(
+ 'id' => $data['task_id'],
+ 'date_due' => strtotime('+'.$this->getParam('duration').'days'),
+ );
+
+ return $this->taskModificationModel->update($values, false);
+ }
+
+ /**
+ * Check if the event data meet the action condition
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool
+ */
+ public function hasRequiredCondition(array $data)
+ {
+ return true;
+ }
+}
diff --git a/app/ServiceProvider/ActionProvider.php b/app/ServiceProvider/ActionProvider.php
index 9383be12..c76555fa 100644
--- a/app/ServiceProvider/ActionProvider.php
+++ b/app/ServiceProvider/ActionProvider.php
@@ -3,6 +3,7 @@
namespace Kanboard\ServiceProvider;
use Kanboard\Action\TaskAssignColorPriority;
+use Kanboard\Action\TaskAssignDueDateOnCreation;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Kanboard\Core\Action\ActionManager;
@@ -80,6 +81,7 @@ class ActionProvider implements ServiceProviderInterface
$container['actionManager']->register(new TaskMoveColumnUnAssigned($container));
$container['actionManager']->register(new TaskOpen($container));
$container['actionManager']->register(new TaskUpdateStartDate($container));
+ $container['actionManager']->register(new TaskAssignDueDateOnCreation($container));
return $container;
}
diff --git a/tests/units/Action/TaskAssignDueDateOnCreationTest.php b/tests/units/Action/TaskAssignDueDateOnCreationTest.php
new file mode 100644
index 00000000..26c0584e
--- /dev/null
+++ b/tests/units/Action/TaskAssignDueDateOnCreationTest.php
@@ -0,0 +1,37 @@
+container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
+ $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
+
+ $event = TaskEventBuilder::getInstance($this->container)
+ ->withTaskId(1)
+ ->buildEvent();
+
+ $action = new TaskAssignDueDateOnCreation($this->container);
+ $action->setProjectId(1);
+ $action->setParam('duration', 4);
+
+ $this->assertTrue($action->execute($event, TaskModel::EVENT_CREATE));
+
+ $task = $taskFinderModel->getById(1);
+ $this->assertNotEmpty($task);
+ $this->assertEquals(date('Y-m-d', strtotime('+4days')), date('Y-m-d', $task['date_due']));
+ }
+}
diff --git a/tests/units/Action/TaskUpdateStartDateTest.php b/tests/units/Action/TaskUpdateStartDateTest.php
index 8d609b3e..05fac100 100644
--- a/tests/units/Action/TaskUpdateStartDateTest.php
+++ b/tests/units/Action/TaskUpdateStartDateTest.php
@@ -2,7 +2,6 @@
require_once __DIR__.'/../Base.php';
-use Kanboard\Event\GenericEvent;
use Kanboard\Event\TaskEvent;
use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\TaskFinderModel;
@@ -12,7 +11,7 @@ use Kanboard\Action\TaskUpdateStartDate;
class TaskUpdateStartDateTest extends Base
{
- public function testClose()
+ public function testAction()
{
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
--
cgit v1.2.3
From 9b2a32af78ef8fb5424398dc57e3c3f906026272 Mon Sep 17 00:00:00 2001
From: Frederic Guillot
Date: Sat, 23 Jul 2016 18:33:31 -0400
Subject: Add new automatic action to move a task to another column when closed
---
ChangeLog | 1 +
app/Action/Base.php | 2 +-
app/Action/CommentCreation.php | 2 +-
app/Action/CommentCreationMoveTaskColumn.php | 2 +-
app/Action/TaskAssignCategoryColor.php | 2 +-
app/Action/TaskAssignCategoryLabel.php | 2 +-
app/Action/TaskAssignCategoryLink.php | 2 +-
app/Action/TaskAssignColorCategory.php | 2 +-
app/Action/TaskAssignColorColumn.php | 2 +-
app/Action/TaskAssignColorLink.php | 2 +-
app/Action/TaskAssignColorPriority.php | 2 +-
app/Action/TaskAssignColorUser.php | 2 +-
app/Action/TaskAssignCurrentUser.php | 2 +-
app/Action/TaskAssignCurrentUserColumn.php | 2 +-
app/Action/TaskAssignDueDateOnCreation.php | 2 +-
app/Action/TaskAssignSpecificUser.php | 2 +-
app/Action/TaskAssignUser.php | 2 +-
app/Action/TaskClose.php | 2 +-
app/Action/TaskCloseColumn.php | 2 +-
app/Action/TaskCloseNoActivity.php | 2 +-
app/Action/TaskCloseNoActivityColumn.php | 2 +-
app/Action/TaskCreation.php | 2 +-
app/Action/TaskDuplicateAnotherProject.php | 2 +-
app/Action/TaskEmail.php | 2 +-
app/Action/TaskEmailNoActivity.php | 2 +-
app/Action/TaskMoveAnotherProject.php | 2 +-
app/Action/TaskMoveColumnAssigned.php | 2 +-
app/Action/TaskMoveColumnCategoryChange.php | 2 +-
app/Action/TaskMoveColumnClosed.php | 102 ++++++++++++++++++++++++
app/Action/TaskMoveColumnUnAssigned.php | 2 +-
app/Action/TaskOpen.php | 2 +-
app/Action/TaskUpdateStartDate.php | 2 +-
app/Model/TaskPositionModel.php | 19 ++---
app/ServiceProvider/ActionProvider.php | 2 +
tests/units/Action/TaskMoveColumnClosedTest.php | 91 +++++++++++++++++++++
35 files changed, 236 insertions(+), 39 deletions(-)
create mode 100644 app/Action/TaskMoveColumnClosed.php
create mode 100644 tests/units/Action/TaskMoveColumnClosedTest.php
(limited to 'ChangeLog')
diff --git a/ChangeLog b/ChangeLog
index 01ad5fbd..c9aebc48 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -6,6 +6,7 @@ New features:
* New automated actions:
- Close tasks without activity in a specific column
- Set due date automatically
+ - Move a task to another column when closed
* Added internal task links to activity stream
* Added new event for removed comments
* Added search filter for task priority
diff --git a/app/Action/Base.php b/app/Action/Base.php
index e0ed8bde..9a502a08 100644
--- a/app/Action/Base.php
+++ b/app/Action/Base.php
@@ -7,7 +7,7 @@ use Kanboard\Event\GenericEvent;
/**
* Base class for automatic actions
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
abstract class Base extends \Kanboard\Core\Base
diff --git a/app/Action/CommentCreation.php b/app/Action/CommentCreation.php
index 60ca24f7..301d2cf9 100644
--- a/app/Action/CommentCreation.php
+++ b/app/Action/CommentCreation.php
@@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Create automatically a comment from a webhook
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class CommentCreation extends Base
diff --git a/app/Action/CommentCreationMoveTaskColumn.php b/app/Action/CommentCreationMoveTaskColumn.php
index 8ab792ad..d5bdd807 100644
--- a/app/Action/CommentCreationMoveTaskColumn.php
+++ b/app/Action/CommentCreationMoveTaskColumn.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Add a comment of the triggering event to the task description.
*
- * @package action
+ * @package Kanboard\Action
* @author Oren Ben-Kiki
*/
class CommentCreationMoveTaskColumn extends Base
diff --git a/app/Action/TaskAssignCategoryColor.php b/app/Action/TaskAssignCategoryColor.php
index 2df90b2c..9228e1ff 100644
--- a/app/Action/TaskAssignCategoryColor.php
+++ b/app/Action/TaskAssignCategoryColor.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Set a category automatically according to the color
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCategoryColor extends Base
diff --git a/app/Action/TaskAssignCategoryLabel.php b/app/Action/TaskAssignCategoryLabel.php
index 48299010..c390414e 100644
--- a/app/Action/TaskAssignCategoryLabel.php
+++ b/app/Action/TaskAssignCategoryLabel.php
@@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Set a category automatically according to a label
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCategoryLabel extends Base
diff --git a/app/Action/TaskAssignCategoryLink.php b/app/Action/TaskAssignCategoryLink.php
index d4a4c0ec..6c4b6c96 100644
--- a/app/Action/TaskAssignCategoryLink.php
+++ b/app/Action/TaskAssignCategoryLink.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskLinkModel;
/**
* Set a category automatically according to a task link
*
- * @package action
+ * @package Kanboard\Action
* @author Olivier Maridat
* @author Frederic Guillot
*/
diff --git a/app/Action/TaskAssignColorCategory.php b/app/Action/TaskAssignColorCategory.php
index 91860be4..a136ffd2 100644
--- a/app/Action/TaskAssignColorCategory.php
+++ b/app/Action/TaskAssignColorCategory.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a color to a specific category
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorCategory extends Base
diff --git a/app/Action/TaskAssignColorColumn.php b/app/Action/TaskAssignColorColumn.php
index 6c674b1f..da6e3aed 100644
--- a/app/Action/TaskAssignColorColumn.php
+++ b/app/Action/TaskAssignColorColumn.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a color to a task
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorColumn extends Base
diff --git a/app/Action/TaskAssignColorLink.php b/app/Action/TaskAssignColorLink.php
index 9759f622..19c37afe 100644
--- a/app/Action/TaskAssignColorLink.php
+++ b/app/Action/TaskAssignColorLink.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskLinkModel;
/**
* Assign a color to a specific task link
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorLink extends Base
diff --git a/app/Action/TaskAssignColorPriority.php b/app/Action/TaskAssignColorPriority.php
index 57000ba8..37f7ffed 100644
--- a/app/Action/TaskAssignColorPriority.php
+++ b/app/Action/TaskAssignColorPriority.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a color to a priority
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorPriority extends Base
diff --git a/app/Action/TaskAssignColorUser.php b/app/Action/TaskAssignColorUser.php
index 385db793..468d0198 100644
--- a/app/Action/TaskAssignColorUser.php
+++ b/app/Action/TaskAssignColorUser.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a color to a specific user
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorUser extends Base
diff --git a/app/Action/TaskAssignCurrentUser.php b/app/Action/TaskAssignCurrentUser.php
index 997aa98f..dee5e7db 100644
--- a/app/Action/TaskAssignCurrentUser.php
+++ b/app/Action/TaskAssignCurrentUser.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a task to the logged user
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCurrentUser extends Base
diff --git a/app/Action/TaskAssignCurrentUserColumn.php b/app/Action/TaskAssignCurrentUserColumn.php
index e4eade33..60ada7ef 100644
--- a/app/Action/TaskAssignCurrentUserColumn.php
+++ b/app/Action/TaskAssignCurrentUserColumn.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a task to the logged user on column change
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCurrentUserColumn extends Base
diff --git a/app/Action/TaskAssignDueDateOnCreation.php b/app/Action/TaskAssignDueDateOnCreation.php
index 79ff765c..5c6e2b61 100644
--- a/app/Action/TaskAssignDueDateOnCreation.php
+++ b/app/Action/TaskAssignDueDateOnCreation.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Set the due date of task
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignDueDateOnCreation extends Base
diff --git a/app/Action/TaskAssignSpecificUser.php b/app/Action/TaskAssignSpecificUser.php
index 2c7dcacd..daf9e1df 100644
--- a/app/Action/TaskAssignSpecificUser.php
+++ b/app/Action/TaskAssignSpecificUser.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a task to a specific user
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignSpecificUser extends Base
diff --git a/app/Action/TaskAssignUser.php b/app/Action/TaskAssignUser.php
index 9ea22986..8727b672 100644
--- a/app/Action/TaskAssignUser.php
+++ b/app/Action/TaskAssignUser.php
@@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Assign a task to someone
*
- * @package action
+ * @package Kanboard\Actionv
* @author Frederic Guillot
*/
class TaskAssignUser extends Base
diff --git a/app/Action/TaskClose.php b/app/Action/TaskClose.php
index 91e8cf43..e476e9ba 100644
--- a/app/Action/TaskClose.php
+++ b/app/Action/TaskClose.php
@@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Close automatically a task
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskClose extends Base
diff --git a/app/Action/TaskCloseColumn.php b/app/Action/TaskCloseColumn.php
index 4f1ffc92..523996f4 100644
--- a/app/Action/TaskCloseColumn.php
+++ b/app/Action/TaskCloseColumn.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Close automatically a task in a specific column
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCloseColumn extends Base
diff --git a/app/Action/TaskCloseNoActivity.php b/app/Action/TaskCloseNoActivity.php
index 5a10510f..ea724d8c 100644
--- a/app/Action/TaskCloseNoActivity.php
+++ b/app/Action/TaskCloseNoActivity.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Close automatically a task after when inactive
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCloseNoActivity extends Base
diff --git a/app/Action/TaskCloseNoActivityColumn.php b/app/Action/TaskCloseNoActivityColumn.php
index 7af0b7fc..b2ee5224 100644
--- a/app/Action/TaskCloseNoActivityColumn.php
+++ b/app/Action/TaskCloseNoActivityColumn.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Close automatically a task after inactive and in an defined column
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCloseNoActivityColumn extends Base
diff --git a/app/Action/TaskCreation.php b/app/Action/TaskCreation.php
index 0620afd3..01d91228 100644
--- a/app/Action/TaskCreation.php
+++ b/app/Action/TaskCreation.php
@@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Create automatically a task from a webhook
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCreation extends Base
diff --git a/app/Action/TaskDuplicateAnotherProject.php b/app/Action/TaskDuplicateAnotherProject.php
index d6d8d51f..0ad7713c 100644
--- a/app/Action/TaskDuplicateAnotherProject.php
+++ b/app/Action/TaskDuplicateAnotherProject.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Duplicate a task to another project
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskDuplicateAnotherProject extends Base
diff --git a/app/Action/TaskEmail.php b/app/Action/TaskEmail.php
index 526e9aa8..fdfe7987 100644
--- a/app/Action/TaskEmail.php
+++ b/app/Action/TaskEmail.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Email a task to someone
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskEmail extends Base
diff --git a/app/Action/TaskEmailNoActivity.php b/app/Action/TaskEmailNoActivity.php
index c60702fb..cac4281e 100644
--- a/app/Action/TaskEmailNoActivity.php
+++ b/app/Action/TaskEmailNoActivity.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Email a task with no activity
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskEmailNoActivity extends Base
diff --git a/app/Action/TaskMoveAnotherProject.php b/app/Action/TaskMoveAnotherProject.php
index 148b6b0c..0fa22b1b 100644
--- a/app/Action/TaskMoveAnotherProject.php
+++ b/app/Action/TaskMoveAnotherProject.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Move a task to another project
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskMoveAnotherProject extends Base
diff --git a/app/Action/TaskMoveColumnAssigned.php b/app/Action/TaskMoveColumnAssigned.php
index 1c1f657a..1cfe6743 100644
--- a/app/Action/TaskMoveColumnAssigned.php
+++ b/app/Action/TaskMoveColumnAssigned.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Move a task to another column when an assignee is set
*
- * @package action
+ * @package Kanboard\Action
* @author Francois Ferrand
*/
class TaskMoveColumnAssigned extends Base
diff --git a/app/Action/TaskMoveColumnCategoryChange.php b/app/Action/TaskMoveColumnCategoryChange.php
index 4c2b289a..13d6ee4f 100644
--- a/app/Action/TaskMoveColumnCategoryChange.php
+++ b/app/Action/TaskMoveColumnCategoryChange.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Move a task to another column when the category is changed
*
- * @package action
+ * @package Kanboard\Action
* @author Francois Ferrand
*/
class TaskMoveColumnCategoryChange extends Base
diff --git a/app/Action/TaskMoveColumnClosed.php b/app/Action/TaskMoveColumnClosed.php
new file mode 100644
index 00000000..3f3e2124
--- /dev/null
+++ b/app/Action/TaskMoveColumnClosed.php
@@ -0,0 +1,102 @@
+ t('Destination column'),
+ );
+ }
+
+ /**
+ * Get the required parameter for the event
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getEventRequiredParameters()
+ {
+ return array(
+ 'task_id',
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ 'swimlane_id',
+ 'is_active',
+ )
+ );
+ }
+
+ /**
+ * Execute the action (move the task to another column)
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool True if the action was executed or false when not executed
+ */
+ public function doAction(array $data)
+ {
+ return $this->taskPositionModel->movePosition(
+ $data['task']['project_id'],
+ $data['task']['id'],
+ $this->getParam('dest_column_id'),
+ 1,
+ $data['task']['swimlane_id'],
+ false,
+ false
+ );
+ }
+
+ /**
+ * Check if the event data meet the action condition
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool
+ */
+ public function hasRequiredCondition(array $data)
+ {
+ return $data['task']['column_id'] != $this->getParam('dest_column_id') && $data['task']['is_active'] == 0;
+ }
+}
diff --git a/app/Action/TaskMoveColumnUnAssigned.php b/app/Action/TaskMoveColumnUnAssigned.php
index 0e9a8a16..ab63d624 100644
--- a/app/Action/TaskMoveColumnUnAssigned.php
+++ b/app/Action/TaskMoveColumnUnAssigned.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Move a task to another column when an assignee is cleared
*
- * @package action
+ * @package Kanboard\Action
* @author Francois Ferrand
*/
class TaskMoveColumnUnAssigned extends Base
diff --git a/app/Action/TaskOpen.php b/app/Action/TaskOpen.php
index 8e847b8e..49017831 100644
--- a/app/Action/TaskOpen.php
+++ b/app/Action/TaskOpen.php
@@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Open automatically a task
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskOpen extends Base
diff --git a/app/Action/TaskUpdateStartDate.php b/app/Action/TaskUpdateStartDate.php
index cc016da1..160f6ee5 100644
--- a/app/Action/TaskUpdateStartDate.php
+++ b/app/Action/TaskUpdateStartDate.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Set the start date of task
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskUpdateStartDate extends Base
diff --git a/app/Model/TaskPositionModel.php b/app/Model/TaskPositionModel.php
index d6d2a0af..3d95a763 100644
--- a/app/Model/TaskPositionModel.php
+++ b/app/Model/TaskPositionModel.php
@@ -16,15 +16,16 @@ class TaskPositionModel extends Base
* Move a task to another column or to another position
*
* @access public
- * @param integer $project_id Project id
- * @param integer $task_id Task id
- * @param integer $column_id Column id
- * @param integer $position Position (must be >= 1)
- * @param integer $swimlane_id Swimlane id
- * @param boolean $fire_events Fire events
- * @return boolean
+ * @param integer $project_id Project id
+ * @param integer $task_id Task id
+ * @param integer $column_id Column id
+ * @param integer $position Position (must be >= 1)
+ * @param integer $swimlane_id Swimlane id
+ * @param boolean $fire_events Fire events
+ * @param bool $onlyOpen Do not move closed tasks
+ * @return bool
*/
- public function movePosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0, $fire_events = true)
+ public function movePosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0, $fire_events = true, $onlyOpen = true)
{
if ($position < 1) {
return false;
@@ -32,7 +33,7 @@ class TaskPositionModel extends Base
$task = $this->taskFinderModel->getById($task_id);
- if ($task['is_active'] == TaskModel::STATUS_CLOSED) {
+ if ($onlyOpen && $task['is_active'] == TaskModel::STATUS_CLOSED) {
return true;
}
diff --git a/app/ServiceProvider/ActionProvider.php b/app/ServiceProvider/ActionProvider.php
index c76555fa..cbc60679 100644
--- a/app/ServiceProvider/ActionProvider.php
+++ b/app/ServiceProvider/ActionProvider.php
@@ -4,6 +4,7 @@ namespace Kanboard\ServiceProvider;
use Kanboard\Action\TaskAssignColorPriority;
use Kanboard\Action\TaskAssignDueDateOnCreation;
+use Kanboard\Action\TaskMoveColumnClosed;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Kanboard\Core\Action\ActionManager;
@@ -78,6 +79,7 @@ class ActionProvider implements ServiceProviderInterface
$container['actionManager']->register(new TaskMoveAnotherProject($container));
$container['actionManager']->register(new TaskMoveColumnAssigned($container));
$container['actionManager']->register(new TaskMoveColumnCategoryChange($container));
+ $container['actionManager']->register(new TaskMoveColumnClosed($container));
$container['actionManager']->register(new TaskMoveColumnUnAssigned($container));
$container['actionManager']->register(new TaskOpen($container));
$container['actionManager']->register(new TaskUpdateStartDate($container));
diff --git a/tests/units/Action/TaskMoveColumnClosedTest.php b/tests/units/Action/TaskMoveColumnClosedTest.php
new file mode 100644
index 00000000..318b995d
--- /dev/null
+++ b/tests/units/Action/TaskMoveColumnClosedTest.php
@@ -0,0 +1,91 @@
+container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
+ $this->assertEquals(2, $projectModel->create(array('name' => 'test2')));
+ $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test', 'is_active' => 0)));
+
+ $event = TaskEventBuilder::getInstance($this->container)
+ ->withTaskId(1)
+ ->buildEvent();
+
+ $action = new TaskMoveColumnClosed($this->container);
+ $action->setProjectId(1);
+ $action->setParam('dest_column_id', 2);
+
+ $this->assertTrue($action->execute($event, TaskModel::EVENT_CLOSE));
+
+ $task = $taskFinderModel->getById(1);
+ $this->assertNotEmpty($task);
+ $this->assertEquals('test', $task['title']);
+ $this->assertEquals(2, $task['column_id']);
+ }
+
+ public function testWhenTaskIsOpen()
+ {
+ $projectModel = new ProjectModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
+ $this->assertEquals(2, $projectModel->create(array('name' => 'test2')));
+ $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
+
+ $event = TaskEventBuilder::getInstance($this->container)
+ ->withTaskId(1)
+ ->buildEvent();
+
+ $action = new TaskMoveColumnClosed($this->container);
+ $action->setProjectId(1);
+ $action->setParam('dest_column_id', 2);
+
+ $this->assertFalse($action->execute($event, TaskModel::EVENT_CLOSE));
+
+ $task = $taskFinderModel->getById(1);
+ $this->assertNotEmpty($task);
+ $this->assertEquals('test', $task['title']);
+ $this->assertEquals(1, $task['column_id']);
+ }
+
+ public function testWhenTaskIsAlreadyInDestinationColumn()
+ {
+ $projectModel = new ProjectModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
+ $this->assertEquals(2, $projectModel->create(array('name' => 'test2')));
+ $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test', 'is_active' => 0, 'column_id' => 2)));
+
+ $event = TaskEventBuilder::getInstance($this->container)
+ ->withTaskId(1)
+ ->buildEvent();
+
+ $action = new TaskMoveColumnClosed($this->container);
+ $action->setProjectId(1);
+ $action->setParam('dest_column_id', 2);
+
+ $this->assertFalse($action->execute($event, TaskModel::EVENT_CLOSE));
+
+ $task = $taskFinderModel->getById(1);
+ $this->assertNotEmpty($task);
+ $this->assertEquals('test', $task['title']);
+ $this->assertEquals(2, $task['column_id']);
+ }
+}
--
cgit v1.2.3
From ca45b5592b17d3675a22b7aca49ea49dd9dd57ea Mon Sep 17 00:00:00 2001
From: Frederic Guillot
Date: Sat, 23 Jul 2016 18:59:00 -0400
Subject: Add new automatic action to move the task to another column when not
moved
---
ChangeLog | 1 +
app/Action/TaskMoveColumnNotMovedPeriod.php | 104 +++++++++++++++++++++
app/ServiceProvider/ActionProvider.php | 2 +
.../Action/TaskMoveColumnNotMovedPeriodTest.php | 50 ++++++++++
4 files changed, 157 insertions(+)
create mode 100644 app/Action/TaskMoveColumnNotMovedPeriod.php
create mode 100644 tests/units/Action/TaskMoveColumnNotMovedPeriodTest.php
(limited to 'ChangeLog')
diff --git a/ChangeLog b/ChangeLog
index c9aebc48..1bc0eed3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,7 @@ New features:
- Close tasks without activity in a specific column
- Set due date automatically
- Move a task to another column when closed
+ - Move a task to another column when not moved during a given period
* Added internal task links to activity stream
* Added new event for removed comments
* Added search filter for task priority
diff --git a/app/Action/TaskMoveColumnNotMovedPeriod.php b/app/Action/TaskMoveColumnNotMovedPeriod.php
new file mode 100644
index 00000000..87e7e405
--- /dev/null
+++ b/app/Action/TaskMoveColumnNotMovedPeriod.php
@@ -0,0 +1,104 @@
+ t('Duration in days'),
+ 'src_column_id' => t('Source column'),
+ 'dest_column_id' => t('Destination column'),
+ );
+ }
+
+ /**
+ * Get the required parameter for the event
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getEventRequiredParameters()
+ {
+ return array('tasks');
+ }
+
+ /**
+ * Execute the action (close the task)
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool True if the action was executed or false when not executed
+ */
+ public function doAction(array $data)
+ {
+ $results = array();
+ $max = $this->getParam('duration') * 86400;
+
+ foreach ($data['tasks'] as $task) {
+ $duration = time() - $task['date_moved'];
+
+ if ($duration > $max && $task['column_id'] == $this->getParam('src_column_id')) {
+ $results[] = $this->taskPositionModel->movePosition(
+ $task['project_id'],
+ $task['id'],
+ $this->getParam('dest_column_id'),
+ 1,
+ $task['swimlane_id'],
+ false
+ );
+ }
+ }
+
+ return in_array(true, $results, true);
+ }
+
+ /**
+ * Check if the event data meet the action condition
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool
+ */
+ public function hasRequiredCondition(array $data)
+ {
+ return count($data['tasks']) > 0;
+ }
+}
diff --git a/app/ServiceProvider/ActionProvider.php b/app/ServiceProvider/ActionProvider.php
index cbc60679..946fbf41 100644
--- a/app/ServiceProvider/ActionProvider.php
+++ b/app/ServiceProvider/ActionProvider.php
@@ -5,6 +5,7 @@ namespace Kanboard\ServiceProvider;
use Kanboard\Action\TaskAssignColorPriority;
use Kanboard\Action\TaskAssignDueDateOnCreation;
use Kanboard\Action\TaskMoveColumnClosed;
+use Kanboard\Action\TaskMoveColumnNotMovedPeriod;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Kanboard\Core\Action\ActionManager;
@@ -80,6 +81,7 @@ class ActionProvider implements ServiceProviderInterface
$container['actionManager']->register(new TaskMoveColumnAssigned($container));
$container['actionManager']->register(new TaskMoveColumnCategoryChange($container));
$container['actionManager']->register(new TaskMoveColumnClosed($container));
+ $container['actionManager']->register(new TaskMoveColumnNotMovedPeriod($container));
$container['actionManager']->register(new TaskMoveColumnUnAssigned($container));
$container['actionManager']->register(new TaskOpen($container));
$container['actionManager']->register(new TaskUpdateStartDate($container));
diff --git a/tests/units/Action/TaskMoveColumnNotMovedPeriodTest.php b/tests/units/Action/TaskMoveColumnNotMovedPeriodTest.php
new file mode 100644
index 00000000..7fa16cf2
--- /dev/null
+++ b/tests/units/Action/TaskMoveColumnNotMovedPeriodTest.php
@@ -0,0 +1,50 @@
+container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
+ $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
+ $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test', 'column_id' => 3)));
+ $this->assertEquals(3, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test', 'column_id' => 2)));
+
+ $this->container['db']->table(TaskModel::TABLE)->in('id', array(2, 3))->update(array('date_moved' => strtotime('-10days')));
+
+ $tasks = $taskFinderModel->getAll(1);
+ $event = new TaskListEvent(array('tasks' => $tasks, 'project_id' => 1));
+
+ $action = new TaskMoveColumnNotMovedPeriod($this->container);
+ $action->setProjectId(1);
+ $action->setParam('duration', 2);
+ $action->setParam('src_column_id', 2);
+ $action->setParam('dest_column_id', 3);
+
+ $this->assertTrue($action->execute($event, TaskModel::EVENT_DAILY_CRONJOB));
+
+ $task = $taskFinderModel->getById(1);
+ $this->assertNotEmpty($task);
+ $this->assertEquals(1, $task['column_id']);
+
+ $task = $taskFinderModel->getById(2);
+ $this->assertNotEmpty($task);
+ $this->assertEquals(3, $task['column_id']);
+
+ $task = $taskFinderModel->getById(3);
+ $this->assertNotEmpty($task);
+ $this->assertEquals(3, $task['column_id']);
+ }
+}
--
cgit v1.2.3
From 506ebf3bac302a63be7c32a03b872a9eefa689fc Mon Sep 17 00:00:00 2001
From: Frederic Guillot
Date: Sun, 24 Jul 2016 10:08:57 -0400
Subject: Fixed typo in template that prevent project permissions to be
duplicated
---
ChangeLog | 1 +
app/Template/project_creation/create.php | 2 +-
app/Template/project_view/duplicate.php | 2 +-
3 files changed, 3 insertions(+), 2 deletions(-)
(limited to 'ChangeLog')
diff --git a/ChangeLog b/ChangeLog
index 1bc0eed3..6da73200 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -24,6 +24,7 @@ Improvements:
Bug fixes:
+* Fixed typo in template that prevent project permissions to be duplicated
* Fixed search query with multiple assignees (nested OR conditions)
* Fixed Markdown editor auto-grow on the task form (Safari)
* Fixed compatibility issue with PHP 5.3 for OAuthUserProvider class
diff --git a/app/Template/project_creation/create.php b/app/Template/project_creation/create.php
index d00883ba..b90b15c4 100644
--- a/app/Template/project_creation/create.php
+++ b/app/Template/project_creation/create.php
@@ -19,7 +19,7 @@
= t('Which parts of the project do you want to duplicate?') ?>
= t('Subtasks exportation for "%s"', $project['name']) ?>
+
= t('Subtasks export') ?>
= t('This report contains all subtasks information for the given date range.') ?>
@@ -21,4 +21,4 @@
-
\ No newline at end of file
+
diff --git a/app/Template/export/summary.php b/app/Template/export/summary.php
index 60aa306f..d9362a9b 100644
--- a/app/Template/export/summary.php
+++ b/app/Template/export/summary.php
@@ -1,5 +1,5 @@
-
= t('Daily project summary export for "%s"', $project['name']) ?>
+
= t('Daily project summary export') ?>
= t('This export contains the number of tasks per column grouped per day.') ?>
@@ -21,4 +21,4 @@
-
\ No newline at end of file
+
diff --git a/app/Template/export/tasks.php b/app/Template/export/tasks.php
index bed8ab90..ae411326 100644
--- a/app/Template/export/tasks.php
+++ b/app/Template/export/tasks.php
@@ -1,5 +1,5 @@
-
= t('Tasks exportation for "%s"', $project['name']) ?>
+
= t('Tasks exportation') ?>
= t('This report contains all tasks information for the given date range.') ?>