From 7dfc288f93648fb512c7032aea719e6993434063 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Sun, 12 Jun 2016 12:26:08 +0200 Subject: [PATCH 001/599] setup view + load ace editor --- .../lib/ace/ace-src-min-noconflict.js | 10 + src/Umbraco.Web.UI.Client/lib/ace/mode-css.js | 1 + .../lib/ace/mode-javascript.js | 1 + .../lib/ace/mode-razor.js | 1 + .../components/umbaceeditor.directive.js | 341 ++++++++++++++++++ .../src/views/templates/edit.controller.js | 92 +++++ .../src/views/templates/edit.html | 94 +++++ src/Umbraco.Web/UI/JavaScript/JsInitialize.js | 5 + 8 files changed, 545 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/lib/ace/ace-src-min-noconflict.js create mode 100644 src/Umbraco.Web.UI.Client/lib/ace/mode-css.js create mode 100644 src/Umbraco.Web.UI.Client/lib/ace/mode-javascript.js create mode 100644 src/Umbraco.Web.UI.Client/lib/ace/mode-razor.js create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/templates/edit.html diff --git a/src/Umbraco.Web.UI.Client/lib/ace/ace-src-min-noconflict.js b/src/Umbraco.Web.UI.Client/lib/ace/ace-src-min-noconflict.js new file mode 100644 index 0000000000..b338717bfd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/ace/ace-src-min-noconflict.js @@ -0,0 +1,10 @@ +(function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.define=t,i.define.packaged=!0;if(!i.require||!i.require.packaged)r.original=i.require,i.require=r,i.require.packaged=!0}var ACE_NAMESPACE = "ace",e=function(){return this}();!e&&typeof window!="undefined"&&(e=window);if(!ACE_NAMESPACE&&typeof requirejs!="undefined")return;var t=function(e,n,r){if(typeof e!="string"){t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)};t.modules={},t.payloads={};var n=function(e,t,n){if(typeof t=="string"){var i=s(e,t);if(i!=undefined)return n&&n(),i}else if(Object.prototype.toString.call(t)==="[object Array]"){var o=[];for(var u=0,a=t.length;u1&&u(t,"")>-1&&(a=RegExp(this.source,r.replace.call(o(this),"g","")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;et.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),ace.define("ace/lib/es5-shim",["require","exports","module"],function(e,t,n){function r(){}function w(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}function H(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}function B(e){var t=typeof e;return e===null||t==="undefined"||t==="boolean"||t==="number"||t==="string"}function j(e){var t,n,r;if(B(e))return e;n=e.valueOf;if(typeof n=="function"){t=n.call(e);if(B(t))return t}r=e.toString;if(typeof r=="function"){t=r.call(e);if(B(t))return t}throw new TypeError}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError("Function.prototype.bind called on incompatible "+n);var i=u.call(arguments,1),s=function(){if(this instanceof s){var e=n.apply(this,i.concat(u.call(arguments)));return Object(e)===e?e:this}return n.apply(t,i.concat(u.call(arguments)))};return n.prototype&&(r.prototype=n.prototype,s.prototype=new r,r.prototype=null),s});var i=Function.prototype.call,s=Array.prototype,o=Object.prototype,u=s.slice,a=i.bind(o.toString),f=i.bind(o.hasOwnProperty),l,c,h,p,d;if(d=f(o,"__defineGetter__"))l=i.bind(o.__defineGetter__),c=i.bind(o.__defineSetter__),h=i.bind(o.__lookupGetter__),p=i.bind(o.__lookupSetter__);if([1,2].splice(0).length!=2)if(!function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t=[],n;t.splice.apply(t,e(20)),t.splice.apply(t,e(26)),n=t.length,t.splice(5,0,"XXX"),n+1==t.length;if(n+1==t.length)return!0}())Array.prototype.splice=function(e,t){var n=this.length;e>0?e>n&&(e=n):e==void 0?e=0:e<0&&(e=Math.max(n+e,0)),e+ta)for(h=l;h--;)this[f+h]=this[a+h];if(s&&e===c)this.length=c,this.push.apply(this,i);else{this.length=c+s;for(h=0;h>>0;if(a(t)!="[object Function]")throw new TypeError;while(++s>>0,s=Array(i),o=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var u=0;u>>0,s=[],o,u=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var f=0;f>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var s=0,o;if(arguments.length>=2)o=arguments[1];else do{if(s in r){o=r[s++];break}if(++s>=i)throw new TypeError("reduce of empty array with no initial value")}while(!0);for(;s>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var s,o=i-1;if(arguments.length>=2)s=arguments[1];else do{if(o in r){s=r[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}while(!0);do o in this&&(s=t.call(void 0,s,r[o],o,n));while(o--);return s});if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1)Array.prototype.indexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=H(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,H(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1};Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:o)});if(!Object.getOwnPropertyDescriptor){var y="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(y+t);if(!f(t,n))return;var r,i,s;r={enumerable:!0,configurable:!0};if(d){var u=t.__proto__;t.__proto__=o;var i=h(t,n),s=p(t,n);t.__proto__=u;if(i||s)return i&&(r.get=i),s&&(r.set=s),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var b;Object.prototype.__proto__===null?b=function(){return{__proto__:null}}:b=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=b();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var E=w({}),S=typeof document=="undefined"||w(document.createElement("div"));if(!E||!S)var x=Object.defineProperty}if(!Object.defineProperty||x){var T="Property description must be an object: ",N="Object.defineProperty called on non-object: ",C="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(N+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(T+r);if(x)try{return x.call(Object,t,n,r)}catch(i){}if(f(r,"value"))if(d&&(h(t,n)||p(t,n))){var s=t.__proto__;t.__proto__=o,delete t[n],t[n]=r.value,t.__proto__=s}else t[n]=r.value;else{if(!d)throw new TypeError(C);f(r,"get")&&l(t,n,r.get),f(r,"set")&&c(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)f(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(f(t,n))n+="?";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n=0?parseFloat((i.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((i.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=(window.Controllers||window.controllers)&&window.navigator.product==="Gecko",t.isOldGecko=t.isGecko&&parseInt((i.match(/rv\:(\d+)/)||[])[1],10)<4,t.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(i.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(i.split(" Chrome/")[1])||undefined,t.isAIR=i.indexOf("AdobeAIR")>=0,t.isIPad=i.indexOf("iPad")>=0,t.isTouchPad=i.indexOf("TouchPad")>=0,t.isChromeOS=i.indexOf(" CrOS ")>=0}),ace.define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function a(e,t,n){var a=u(t);if(!i.isMac&&s){s.OSKey&&(a|=8);if(s.altGr){if((3&a)==3)return;s.altGr=0}if(n===18||n===17){var f="location"in t?t.location:t.keyLocation;if(n===17&&f===1)s[n]==1&&(o=t.timeStamp);else if(n===18&&a===3&&f===2){var l=t.timeStamp-o;l<50&&(s.altGr=!0)}}}n in r.MODIFIER_KEYS&&(n=-1),a&8&&n>=91&&n<=93&&(n=-1);if(!a&&n===13){var f="location"in t?t.location:t.keyLocation;if(f===3){e(t,a,-n);if(t.defaultPrevented)return}}if(i.isChromeOS&&a&8){e(t,a,n);if(t.defaultPrevented)return;a&=-9}return!!a||n in r.FUNCTION_KEYS||n in r.PRINTABLE_KEYS?e(t,a,n):!1}function f(){s=Object.create(null),s.count=0,s.lastT=0}var r=e("./keys"),i=e("./useragent"),s=null,o=0;t.addListener=function(e,t,n){if(e.addEventListener)return e.addEventListener(t,n,!1);if(e.attachEvent){var r=function(){n.call(e,window.event)};n._wrapper=r,e.attachEvent("on"+t,r)}},t.removeListener=function(e,t,n){if(e.removeEventListener)return e.removeEventListener(t,n,!1);e.detachEvent&&e.detachEvent("on"+t,n._wrapper||n)},t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},t.preventDefault=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1},t.getButton=function(e){return e.type=="dblclick"?0:e.type=="contextmenu"||i.isMac&&e.ctrlKey&&!e.altKey&&!e.shiftKey?2:e.preventDefault?e.button:{1:0,2:2,4:1}[e.button]},t.capture=function(e,n,r){function i(e){n&&n(e),r&&r(e),t.removeListener(document,"mousemove",n,!0),t.removeListener(document,"mouseup",i,!0),t.removeListener(document,"dragstart",i,!0)}return t.addListener(document,"mousemove",n,!0),t.addListener(document,"mouseup",i,!0),t.addListener(document,"dragstart",i,!0),i},t.addTouchMoveListener=function(e,n){if("ontouchmove"in e){var r,i;t.addListener(e,"touchstart",function(e){var t=e.changedTouches[0];r=t.clientX,i=t.clientY}),t.addListener(e,"touchmove",function(e){var t=1,s=e.changedTouches[0];e.wheelX=-(s.clientX-r)/t,e.wheelY=-(s.clientY-i)/t,r=s.clientX,i=s.clientY,n(e)})}},t.addMouseWheelListener=function(e,n){"onmousewheel"in e?t.addListener(e,"mousewheel",function(e){var t=8;e.wheelDeltaX!==undefined?(e.wheelX=-e.wheelDeltaX/t,e.wheelY=-e.wheelDeltaY/t):(e.wheelX=0,e.wheelY=-e.wheelDelta/t),n(e)}):"onwheel"in e?t.addListener(e,"wheel",function(e){var t=.35;switch(e.deltaMode){case e.DOM_DELTA_PIXEL:e.wheelX=e.deltaX*t||0,e.wheelY=e.deltaY*t||0;break;case e.DOM_DELTA_LINE:case e.DOM_DELTA_PAGE:e.wheelX=(e.deltaX||0)*5,e.wheelY=(e.deltaY||0)*5}n(e)}):t.addListener(e,"DOMMouseScroll",function(e){e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=(e.detail||0)*5,e.wheelY=0):(e.wheelX=0,e.wheelY=(e.detail||0)*5),n(e)})},t.addMultiMouseDownListener=function(e,n,r,s){function c(e){t.getButton(e)!==0?o=0:e.detail>1?(o++,o>4&&(o=1)):o=1;if(i.isIE){var c=Math.abs(e.clientX-u)>5||Math.abs(e.clientY-a)>5;if(!f||c)o=1;f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),o==1&&(u=e.clientX,a=e.clientY)}e._clicks=o,r[s]("mousedown",e);if(o>4)o=0;else if(o>1)return r[s](l[o],e)}function h(e){o=2,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),r[s]("mousedown",e),r[s](l[o],e)}var o=0,u,a,f,l={2:"dblclick",3:"tripleclick",4:"quadclick"};Array.isArray(e)||(e=[e]),e.forEach(function(e){t.addListener(e,"mousedown",c),i.isOldIE&&t.addListener(e,"dblclick",h)})};var u=!i.isMac||!i.isOpera||"KeyboardEvent"in window?function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)}:function(e){return 0|(e.metaKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.ctrlKey?8:0)};t.getModifierString=function(e){return r.KEY_MODS[u(e)]},t.addCommandKeyListener=function(e,n){var r=t.addListener;if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var o=null;r(e,"keydown",function(e){o=e.keyCode}),r(e,"keypress",function(e){return a(n,e,o)})}else{var u=null;r(e,"keydown",function(e){var t=e.keyCode;s[t]=(s[t]||0)+1,t==91||t==92?s.OSKey=!0:s.OSKey&&e.timeStamp-s.lastT>200&&s.count==1&&f(),s[t]==1&&s.count++,s.lastT=e.timeStamp;var r=a(n,e,t);return u=e.defaultPrevented,r}),r(e,"keypress",function(e){u&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),u=null)}),r(e,"keyup",function(e){var t=e.keyCode;s[t]?s.count=Math.max(s.count-1,0):f();if(t==91||t==92)s.OSKey=!1;s[t]=null}),s||(f(),r(window,"focus",f))}};if(typeof window=="object"&&window.postMessage&&!i.isOldIE){var l=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+l;t.addListener(n,"message",function i(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,"message",i),e())}),n.postMessage(r,"*")}}t.nextFrame=typeof window=="object"&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),ace.define("ace/lib/lang",["require","exports","module"],function(e,t,n){"use strict";t.last=function(e){return e[e.length-1]},t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){var n="";while(t>0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n1),e.preventDefault()},this.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;n.$blockScrolling++,this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.renderer.scroller.setCapture&&n.renderer.scroller.setCapture(),n.setStyle("ace_selecting"),this.setState("select"),n.$blockScrolling--},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);t.$blockScrolling++;if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=f(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.$blockScrolling--,t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);n.$blockScrolling++;if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=f(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.$blockScrolling--,n.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=a(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>o||t-this.mousedownEvent.time>this.$focusTimout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState("select")):(i=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=i,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},this.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()},this.onTouchMove=function(e){var t=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()}}).call(u.prototype),t.DefaultHandlers=u}),ace.define("ace/tooltip",["require","exports","module","ace/lib/oop","ace/lib/dom"],function(e,t,n){"use strict";function s(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}var r=e("./lib/oop"),i=e("./lib/dom");(function(){this.$init=function(){return this.$element=i.createElement("div"),this.$element.className="ace_tooltip",this.$element.style.display="none",this.$parentNode.appendChild(this.$element),this.$element},this.getElement=function(){return this.$element||this.$init()},this.setText=function(e){i.setInnerText(this.getElement(),e)},this.setHtml=function(e){this.getElement().innerHTML=e},this.setPosition=function(e,t){this.getElement().style.left=e+"px",this.getElement().style.top=t+"px"},this.setClassName=function(e){i.addCssClass(this.getElement(),e)},this.show=function(e,t,n){e!=null&&this.setText(e),t!=null&&n!=null&&this.setPosition(t,n),this.isOpen||(this.getElement().style.display="block",this.isOpen=!0)},this.hide=function(){this.isOpen&&(this.getElement().style.display="none",this.isOpen=!1)},this.getHeight=function(){return this.getElement().offsetHeight},this.getWidth=function(){return this.getElement().offsetWidth}}).call(s.prototype),t.Tooltip=s}),ace.define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event","ace/tooltip"],function(e,t,n){"use strict";function u(e){function l(){var r=u.getDocumentPosition().row,s=n.$annotations[r];if(!s)return c();var o=t.session.getLength();if(r==o){var a=t.renderer.pixelToScreenCoordinates(0,u.y).row,l=u.$pos;if(a>t.session.documentToScreenRow(l.row,l.column))return c()}if(f==s)return;f=s.text.join("
"),i.setHtml(f),i.show(),t.on("mousewheel",c);if(e.$tooltipFollowsMouse)h(u);else{var p=u.domEvent.target,d=p.getBoundingClientRect(),v=i.getElement().style;v.left=d.right+"px",v.top=d.bottom+"px"}}function c(){o&&(o=clearTimeout(o)),f&&(i.hide(),f=null,t.removeEventListener("mousewheel",c))}function h(e){i.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,i=new a(t.container);e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i=="foldWidgets")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState("selectByLines"),e.captureMouse(r),r.preventDefault()});var o,u,f;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(r.hasCssClass(n,"ace_fold-widget"))return c();f&&e.$tooltipFollowsMouse&&h(t),u=t;if(o)return;o=setTimeout(function(){o=null,u&&!e.isMousePressed?l():c()},50)}),s.addListener(t.renderer.$gutter,"mouseout",function(e){u=null;if(!f||o)return;o=setTimeout(function(){o=null,c()},50)}),t.on("changeSession",c)}function a(e){o.call(this,e)}var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/event"),o=e("../tooltip").Tooltip;i.inherits(a,o),function(){this.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),o.prototype.setPosition.call(this,e,t)}}.call(a.prototype),t.GutterHandler=u}),ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}return this.$inSelection},this.getButton=function(){return r.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=i.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(s.prototype)}),ace.define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||e.column!=n.column;if(!S||i||s)t.$blockScrolling+=1,t.moveCursorToPosition(e),t.$blockScrolling-=1,S=r,x={x:p,y:d};else{var o=l(x.x,x.y,p,d);o>a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.$blockScrolling+=1,t.selection.fromOrientedRange(m),t.$blockScrolling-=1,t.isFocused()&&!w&&t.renderer.$cursorLayer.setBlinking(!t.getReadOnly()),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("img");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",s.isOpera&&(n.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.addEventListener("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",s.isOpera&&(t.container.appendChild(n),n.scrollTop=0),i.setDragImage&&i.setDragImage(n,0,0),s.isOpera&&t.container.removeChild(n),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.renderer.$cursorLayer.setBlinking(!0)}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e)),i.addListener(c,"dragend",this.onDragEnd.bind(e)),i.addListener(c,"dragenter",this.onDragEnter.bind(e)),i.addListener(c,"dragover",this.onDragOver.bind(e)),i.addListener(c,"dragleave",this.onDragLeave.bind(e)),i.addListener(c,"drop",this.onDrop.bind(e));var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.renderer.$cursorLayer.setBlinking(!this.editor.getReadOnly()),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var n=s.isWin?"default":"move";e.renderer.setCursorStyle(n),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),ace.define("ace/lib/net",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./dom");t.get=function(e,t){var n=new XMLHttpRequest;n.open("GET",e,!0),n.onreadystatechange=function(){n.readyState===4&&t(n.responseText)},n.send(null)},t.loadScript=function(e,t){var n=r.getDocumentHead(),i=document.createElement("script");i.src=e,n.appendChild(i),i.onload=i.onreadystatechange=function(e,n){if(n||!i.readyState||i.readyState=="loaded"||i.readyState=="complete")i=i.onload=i.onreadystatechange=null,n||t()}},t.qualifyURL=function(e){var t=document.createElement("a");return t.href=e,t.href}}),ace.define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;o1&&(i=n[n.length-2]);var o=a[t+"Path"];return o==null?o=a.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return a.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(n,r){var i,o;Array.isArray(n)&&(o=n[0],n=n[1]);try{i=e(n)}catch(u){}if(i&&!t.$loading[n])return r&&r(i);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var a=function(){e([n],function(e){t._emit("load.module",{name:n,module:e});var r=t.$loading[n];t.$loading[n]=null,r.forEach(function(t){t&&t(e)})})};if(!t.get("packaged"))return a();s.loadScript(t.moduleUrl(n,o),a)},t.init=f}),ace.define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event","ace/mouse/dragdrop_handler","ace/config"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("./default_handlers").DefaultHandlers,o=e("./default_gutter_handler").GutterHandler,u=e("./mouse_event").MouseEvent,a=e("./dragdrop_handler").DragdropHandler,f=e("../config"),l=function(e){var t=this;this.editor=e,new s(this),new o(this),new a(this);var n=function(t){var n=!document.hasFocus||!document.hasFocus()||!e.isFocused()&&document.activeElement==(e.textInput&&e.textInput.getElement());n&&window.focus(),e.focus()},u=e.renderer.getMouseEventTarget();r.addListener(u,"click",this.onMouseEvent.bind(this,"click")),r.addListener(u,"mousemove",this.onMouseMove.bind(this,"mousemove")),r.addMultiMouseDownListener([u,e.renderer.scrollBarV&&e.renderer.scrollBarV.inner,e.renderer.scrollBarH&&e.renderer.scrollBarH.inner,e.textInput&&e.textInput.getElement()].filter(Boolean),[400,300,250],this,"onMouseEvent"),r.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,"mousewheel")),r.addTouchMoveListener(e.container,this.onTouchMove.bind(this,"touchmove"));var f=e.renderer.$gutter;r.addListener(f,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),r.addListener(f,"click",this.onMouseEvent.bind(this,"gutterclick")),r.addListener(f,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),r.addListener(f,"mousemove",this.onMouseEvent.bind(this,"guttermousemove")),r.addListener(u,"mousedown",n),r.addListener(f,"mousedown",n),i.isIE&&e.renderer.scrollBarV&&(r.addListener(e.renderer.scrollBarV.element,"mousedown",n),r.addListener(e.renderer.scrollBarH.element,"mousedown",n)),e.on("mousemove",function(n){if(t.state||t.$dragDelay||!t.$dragEnabled)return;var r=e.renderer.screenToTextCoordinates(n.x,n.y),i=e.session.selection.getRange(),s=e.renderer;!i.isEmpty()&&i.insideStart(r.row,r.column)?s.setCursorStyle("default"):s.setCursorStyle("")})};(function(){this.onMouseEvent=function(e,t){this.editor._emit(e,new u(t,this.editor))},this.onMouseMove=function(e,t){var n=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!n||!n.length)return;this.editor._emit(e,new u(t,this.editor))},this.onMouseWheel=function(e,t){var n=new u(t,this.editor);n.speed=this.$scrollSpeed*2,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.onTouchMove=function(e,t){var n=new u(t,this.editor);n.speed=1,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){this.x=e.x,this.y=e.y,this.isMousePressed=!0;var n=this.editor.renderer;n.$keepTextAreaAtCursor&&(n.$keepTextAreaAtCursor=null);var s=this,o=function(e){if(!e)return;if(i.isWebKit&&!e.which&&s.releaseMouse)return s.releaseMouse();s.x=e.clientX,s.y=e.clientY,t&&t(e),s.mouseEvent=new u(e,s.editor),s.$mouseMoved=!0},a=function(e){clearInterval(l),f(),s[s.state+"End"]&&s[s.state+"End"](e),s.state="",n.$keepTextAreaAtCursor==null&&(n.$keepTextAreaAtCursor=!0,n.$moveTextAreaToCursor()),s.isMousePressed=!1,s.$onCaptureMouseMove=s.releaseMouse=null,e&&s.onMouseEvent("mouseup",e)},f=function(){s[s.state]&&s[s.state](),s.$mouseMoved=!1};if(i.isOldIE&&e.domEvent.type=="dblclick")return setTimeout(function(){a(e)});s.$onCaptureMouseMove=o,s.releaseMouse=r.capture(this.editor.container,o,a);var l=setInterval(f,20)},this.releaseMouse=null,this.cancelContextMenu=function(){var e=function(t){if(t&&t.domEvent&&t.domEvent.type!="contextmenu")return;this.editor.off("nativecontextmenu",e),t&&t.domEvent&&r.stopEvent(t.domEvent)}.bind(this);setTimeout(e,10),this.editor.on("nativecontextmenu",e)}}).call(l.prototype),f.defineOptions(l.prototype,"mouseHandler",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:i.isMac?150:0},dragEnabled:{initialValue:!0},focusTimout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=l}),ace.define("ace/mouse/fold_handler",["require","exports","module"],function(e,t,n){"use strict";function r(e){e.on("click",function(t){var n=t.getDocumentPosition(),r=e.session,i=r.getFoldAt(n.row,n.column,1);i&&(t.getAccelKey()?r.removeFold(i):r.expandFold(i),t.stop())}),e.on("gutterclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session;i.foldWidgets&&i.foldWidgets[r]&&e.session.onFoldWidgetClick(r,t),e.isFocused()||e.focus(),t.stop()}}),e.on("gutterdblclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session,s=i.getParentFoldRangeData(r,!0),o=s.range||s.firstRange;if(o){r=o.start.row;var u=i.getFoldAt(r,i.getLine(r).length,1);u?i.removeFold(u):(i.addFold("...",o),e.renderer.scrollCursorIntoView({row:o.start.row,column:0}))}t.stop()}})}t.FoldHandler=r}),ace.define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event"],function(e,t,n){"use strict";var r=e("../lib/keys"),i=e("../lib/event"),s=function(e){this.$editor=e,this.$data={editor:e},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0)},this.setKeyboardHandler=function(e){var t=this.$handlers;if(t[t.length-1]==e)return;while(t[t.length-1]&&t[t.length-1]!=this.$defaultHandler)this.removeKeyboardHandler(t[t.length-1]);this.addKeyboardHandler(e,1)},this.addKeyboardHandler=function(e,t){if(!e)return;typeof e=="function"&&!e.handleKeyboard&&(e.handleKeyboard=e);var n=this.$handlers.indexOf(e);n!=-1&&this.$handlers.splice(n,1),t==undefined?this.$handlers.push(e):this.$handlers.splice(t,0,e),n==-1&&e.attach&&e.attach(this.$editor)},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return t==-1?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.getStatusText=function(){var e=this.$data,t=e.editor;return this.$handlers.map(function(n){return n.getStatusText&&n.getStatusText(t,e)||""}).filter(Boolean).join(" ")},this.$callKeyboardHandlers=function(e,t,n,r){var s,o=!1,u=this.$editor.commands;for(var a=this.$handlers.length;a--;){s=this.$handlers[a].handleKeyboard(this.$data,e,t,n,r);if(!s||!s.command)continue;s.command=="null"?o=!0:o=u.exec(s.command,this.$editor,s.args,r),o&&r&&e!=-1&&s.passEvent!=1&&s.command.passEvent!=1&&i.stopEvent(r);if(o)break}return!o&&e==-1&&(s={command:"insertstring"},o=u.exec("insertstring",this.$editor,t)),o&&this.$editor._signal("keyboardActivity",s),o},this.onCommandKey=function(e,t,n){var i=r.keyCodeToString(n);this.$callKeyboardHandlers(t,i,n,e)},this.onTextInput=function(e){this.$callKeyboardHandlers(-1,e)}}).call(s.prototype),t.KeyBinding=s}),ace.define("ace/range",["require","exports","module"],function(e,t,n){"use strict";var r=function(e,t){return e.row-t.row||e.column-t.column},i=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.rowt)var r={row:t+1,column:0};else if(this.start.rowt.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.isEmpty()?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){var e=this.doc.getLength()-1;this.setSelectionAnchor(0,0),this.moveCursorTo(e,this.doc.getLine(e).length)},this.setRange=this.setSelectionRange=function(e,t){t?(this.setSelectionAnchor(e.end.row,e.end.column),this.selectTo(e.start.row,e.start.column)):(this.setSelectionAnchor(e.start.row,e.start.column),this.selectTo(e.end.row,e.end.column)),this.getRange().isEmpty()&&(this.$isEmpty=!0),this.$desiredColumn=null},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(e.column-n,e.column).split(" ").length-1==n?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var s=this.session.getFoldAt(e,t,1);if(s){this.moveCursorTo(s.end.row,s.end.column);return}if(i=this.session.nonTokenRe.exec(r))t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t);if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e0&&this.moveCursorWordLeft();return}if(o=this.session.tokenRe.exec(s))t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t,n=0,r,i=/\s/,s=this.session.tokenRe;s.lastIndex=0;if(t=this.session.tokenRe.exec(e))n=this.session.tokenRe.lastIndex;else{while((r=e[n])&&i.test(r))n++;if(n<1){s.lastIndex=0;while((r=e[n])&&!s.test(r)){s.lastIndex=0,n++;if(i.test(r)){if(n>2){n--;break}while((r=e[n])&&i.test(r))n++;if(n>2)break}}}}return s.lastIndex=0,n},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column);t===0&&(this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var r=this.session.screenToDocumentPosition(n.row+e,n.column);e!==0&&t===0&&r.row===this.lead.row&&r.column===this.lead.column&&this.session.lineWidgets&&this.session.lineWidgets[r.row]&&(r.row>0||e>0)&&r.row++,this.moveCursorTo(r.row,r.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0,this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),ace.define("ace/tokenizer",["require","exports","module","ace/config"],function(e,t,n){"use strict";var r=e("./config"),i=2e3,s=function(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:"text"},o="g",u=[];for(var a=0;a1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push("$")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}};(function(){this.$setMaxTokenCount=function(e){i=e|0},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;il){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;yi){c>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});while(l1&&n[0]!==r&&n.unshift("#tmp",r),{tokens:f,state:n.length?n:r}},this.reportError=r.reportError}).call(s.prototype),t.Tokenizer=s}),ace.define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/lang"),i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}};(function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n},this.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}}}).call(r.prototype),t.TokenIterator=r}),ace.define("ace/mode/text",["require","exports","module","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour","ace/unicode","ace/lib/lang","ace/token_iterator","ace/range"],function(e,t,n){"use strict";var r=e("../tokenizer").Tokenizer,i=e("./text_highlight_rules").TextHighlightRules,s=e("./behaviour").Behaviour,o=e("../unicode"),u=e("../lib/lang"),a=e("../token_iterator").TokenIterator,f=e("../range").Range,l=function(){this.HighlightRules=i,this.$behaviour=new s};(function(){this.tokenRe=new RegExp("^["+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]+","g"),this.nonTokenRe=new RegExp("^(?:[^"+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]|\\s])+","g"),this.getTokenizer=function(){return this.$tokenizer||(this.$highlightRules=this.$highlightRules||new this.HighlightRules,this.$tokenizer=new r(this.$highlightRules.getRules())),this.$tokenizer},this.lineCommentStart="",this.blockComment="",this.toggleCommentLines=function(e,t,n,r){function w(e){for(var t=n;t<=r;t++)e(i.getLine(t),t)}var i=t.doc,s=!0,o=!0,a=Infinity,f=t.getTabSize(),l=!1;if(!this.lineCommentStart){if(!this.blockComment)return!1;var c=this.blockComment.start,h=this.blockComment.end,p=new RegExp("^(\\s*)(?:"+u.escapeRegExp(c)+")"),d=new RegExp("(?:"+u.escapeRegExp(h)+")\\s*$"),v=function(e,t){if(g(e,t))return;if(!s||/\S/.test(e))i.insertInLine({row:t,column:e.length},h),i.insertInLine({row:t,column:a},c)},m=function(e,t){var n;(n=e.match(d))&&i.removeInLine(t,e.length-n[0].length,e.length),(n=e.match(p))&&i.removeInLine(t,n[1].length,n[0].length)},g=function(e,n){if(p.test(e))return!0;var r=t.getTokens(n);for(var i=0;i2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(ne.length&&(E=e.length)}),a==Infinity&&(a=E,s=!1,o=!1),l&&a%f!=0&&(a=Math.floor(a/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new a(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,l=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new f(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new a(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new f(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);l.start.row==c&&(l.start.column+=h),l.end.row==c&&(l.end.column+=h),t.selection.fromOrientedRange(l)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)e[t]&&(this.$embeds.push(t),this.$modes[t]=new e[t]);var n=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(var t=0;t=0&&t.row=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),ace.define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=t.Anchor=function(e,t,n){this.$onChange=this.onChange.bind(this),this.attach(e),typeof n=="undefined"?this.setPosition(t.row,t.column):this.setPosition(t,n)};(function(){function e(e,t,n){var r=n?e.column<=t.column:e.columnthis.row)return;var n=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(n.row,n.column,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){r.implement(this,s),this.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new u(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},this.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e0,r=t=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},this.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},this.applyDeltas=function(e){for(var t=0;t=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4&&this.$splitAndapplyLargeDelta(e,2e4),i(this.$lines,e,t),this._signal("change",e)},this.$splitAndapplyLargeDelta=function(e,t){var n=e.lines,r=n.length,i=e.start.row,s=e.start.column,o=0,u=0;do{o=u,u+=t-1;var a=n.slice(o,u);if(u>r){e.lines=a,e.start.row=i+o,e.start.column=s;break}a.push(""),this.applyDelta({start:this.pos(i+o,s),end:this.pos(i+u,s=0),action:e.action,lines:a},!0)}while(!0)},this.revertDelta=function(e){this.applyDelta({start:this.clonePos(e.start),end:this.clonePos(e.end),action:e.action=="insert"?"remove":"insert",lines:e.lines.slice()})},this.indexToPosition=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length;for(var i=t||0,s=n.length;i20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,s<=r&&n.fireUpdateEvent(s,r)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal("update",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.lines[t]=null;else if(e.action=="remove")this.lines.splice(t,n+1,null),this.states.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.lines.splice.apply(this.lines,r),this.states.splice.apply(this.states,r)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens}}).call(s.prototype),t.BackgroundTokenizer=s}),ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:"");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i)}}}).call(o.prototype),t.SearchHighlight=o}),ace.define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.rowthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},this.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.apply(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.rowr)break;l.start.row==r&&l.start.column>=t.column&&(l.start.column!=t.column||!this.$insertRight)&&(l.start.column+=o,l.start.row+=s);if(l.end.row==r&&l.end.column>=t.column){if(l.end.column==t.column&&this.$insertRight)continue;l.end.column==t.column&&o>0&&al.start.column&&l.end.column==u[a+1].start.column&&(l.end.column-=o),l.end.column+=o,l.end.row+=s}}if(s!=0&&a=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i=t){u=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column;if(u0&&(this.removeFolds(p),p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?(n=new r(0,0,this.getLength(),0),t=!0):typeof e=="number"?n=new r(e,0,e,this.getLine(e).length):"row"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRangeList(n);if(t)this.removeFolds(i);else{var s=i;while(s.length)this.expandFolds(s),s=this.getFoldsInRangeList(n)}if(i.length)return i},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(t=e){i=s.end.row;try{var o=this.addFold("...",s);o&&(o.collapseChildren=n)}catch(u){}}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error("invalid fold style: "+e+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==e)return;this.$foldStyle=e,e=="manual"&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)},this.$setFolding=function(e){if(this.$foldMode==e)return;this.$foldMode=e,this.off("change",this.$updateFoldWidgets),this.off("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets),this._signal("changeAnnotation");if(!e||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.$tokenizerUpdateFoldWidgets=this.tokenizerUpdateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets),this.on("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets)},this.getParentFoldRangeData=function(e,t){var n=this.foldWidgets;if(!n||t&&n[e])return{};var r=e-1,i;while(r>=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n==="end"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s){t.children||t.all?this.removeFold(s):this.expandFold(s);return}var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range)){this.removeFold(s);return}}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,f,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold("...",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold("...",n)}},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.foldWidgets[t]=null;else if(e.action=="remove")this.foldWidgets.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,r)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator;t.Folding=u}),ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}r.implement(this,o),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.$deltasFold.length&&(t.$deltas.push({group:"fold",deltas:t.$deltasFold}),t.$deltasFold=[]),t.$deltasDoc.length&&(t.$deltas.push({group:"doc",deltas:t.$deltasDoc}),t.$deltasDoc=[]),t.$deltas.length>0&&e.execute({action:"aceupdate",args:[t.$deltas,t],merge:t.mergeUndoDeltas}),t.mergeUndoDeltas=!1,t.$deltas=[]},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):" "},this.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption("tabSize",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption("overwrite",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(oe&&(e=t.screenWidth)}),this.lineWidgetWidth=e},this.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;ao){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=e.length-1;r!=-1;r--){var i=e[r];i.group=="doc"?(this.doc.revertDeltas(i.deltas),n=this.$getUndoSelection(i.deltas,!0,n)):i.deltas.forEach(function(e){this.addFolds(e.folds)},this)}return this.$fromUndo=!1,n&&this.$undoSelect&&!t&&this.selection.setSelectionRange(n),n},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=0;re.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,l=s.start,o=l.row-a.row,u=l.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.doc.insertInLine({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new f(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new f(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+i,u),o.length&&this.addFolds(o),i},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$useWrapMode&&this._signal("changeWrapMode")},this.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n=e.action,r=e.start,i=e.end,s=r.row,o=i.row,u=o-s,a=null;this.$updating=!0;if(u!=0)if(n==="remove"){this[t?"$wrapData":"$rowLengthCache"].splice(s,u);var f=this.$foldData;a=this.getFoldsInRange(e),this.removeFolds(a);var l=this.getFoldLine(i.row),c=0;if(l){l.addRemoveChars(i.row,i.column,r.column-i.column),l.shiftRow(-u);var h=this.getFoldLine(s);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c=i.row&&l.shiftRow(-u)}o=s}else{var p=Array(u);p.unshift(s,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(s),c=0;if(l){var v=l.range.compareInside(r.row,r.column);v==0?(l=l.split(r.row,r.column),l&&(l.shiftRow(u),l.addRemoveChars(o,0,i.column-r.column))):v==-1&&(l.addRemoveChars(s,0,i.column-r.column),l.shiftRow(u)),c=f.indexOf(l)+1}for(c;c=s&&l.shiftRow(u)}}else{u=Math.abs(e.start.column-e.end.column),n==="remove"&&(a=this.getFoldsInRange(e),this.removeFolds(a),u=-u);var l=this.getFoldLine(s);l&&l.addRemoveChars(s,r.column,u)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(s,o):this.$updateRowLengthCache(s,o),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var r=this.doc.getAllLines(),i=this.getTabSize(),s=this.$wrapData,o=this.$wrapLimit,a,f,l=e;t=Math.min(t,r.length-1);while(l<=t)f=this.getFoldLine(l,f),f?(a=[],f.walk(function(e,t,i,s){var o;if(e!=null){o=this.$getDisplayTokens(e,a.length),o[0]=n;for(var f=1;fr-b){var w=a+r-b;if(e[w-1]>=p&&e[w]>=p){y(w);continue}if(e[w]==n||e[w]==u){for(w;w!=a-1;w--)if(e[w]==n)break;if(w>a){y(w);continue}w=a+r;for(w;w>2)),a-1);while(w>E&&e[w]E&&e[w]E&&e[w]==l)w--}else while(w>E&&e[w]E){y(++w);continue}w=a+r,e[w]==t&&w--,y(w-b)}return s},this.$getDisplayTokens=function(n,r){var i=[],s;r=r||0;for(var o=0;o39&&u<48||u>57&&u<64?i.push(l):u>=4352&&m(u)?i.push(e,t):i.push(e)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i=4352&&m(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.lineWidgets=null,this.getRowLength=function(e){if(this.lineWidgets)var t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0;else t=0;return!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),n=this.$wrapData[t.row];return n.length&&n[0]=0)var o=a[f],r=this.$docRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getLength()-1,p=this.getNextFoldLine(r),d=p?p.start.row:Infinity;while(o<=e){u=this.getRowLength(r);if(o+u>e||r>=h)break;o+=u,r++,r>d&&(r=p.end.row+1,p=this.getNextFoldLine(r,p),d=p?p.start.row:Infinity),c&&(this.$docRowCache.push(r),this.$screenRowCache.push(o))}if(p&&p.start.row<=r)n=this.getFoldDisplayLine(p),r=p.start.row;else{if(o+u<=e||r>h)return{row:h,column:this.getLine(h).length};n=this.getLine(r),p=null}var v=0;if(this.$useWrapMode){var m=this.$wrapData[r];if(m){var g=Math.floor(e-o);s=m[g],g>0&&m.length&&(v=m.indent,i=m[g-1]||m[m.length-1],n=n.substring(i))}}return i+=this.$getStringScreenWidth(n,t-v)[1],this.$useWrapMode&&i>=s&&(i=s-1),p?p.idxToPosition(i):{row:r,column:i}},this.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);var v=0;if(this.$useWrapMode){var m=this.$wrapData[i];if(m){var g=0;while(d.length>=m[g])r++,g++;d=d.substring(m[g-1]||0,d.length),v=g>0?m.indent:0}}return{row:r,column:v+this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;ro&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},this.$setFontMetrics=function(e){if(!this.$enableVarChar)return;this.$getStringScreenWidth=function(t,n,r){if(n===0)return[0,0];n||(n=Infinity),r=r||0;var i,s;for(s=0;sn)break}return[r,s]}},this.destroy=function(){this.bgTokenizer&&(this.bgTokenizer.setDocument(null),this.bgTokenizer=null),this.$stopWorker()}}).call(p.prototype),e("./edit_session/folding").Folding.call(p.prototype),e("./edit_session/bracket_match").BracketMatch.call(p.prototype),s.defineOptions(p.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;this.$wrap=e;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}},get:function(){return this.getUseWrapMode()?this.$wrap==-1?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=e=="auto"?this.$mode.type!="text":e!="text",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$modified=!0,this.$resetRowCache(0),this.$updateWrapData(0,this.getLength()-1)))},initialValue:"auto"},indentedSoftWrap:{initialValue:!0},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){if(isNaN(e)||this.$tabSize===e)return;this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize")},initialValue:4,handlesSet:!0},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId}}}),t.EditSession=p}),ace.define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){this.$options={}};(function(){this.set=function(e){return i.mixin(this.$options,e),this},this.getOptions=function(){return r.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$options,n=this.$matchIterator(e,t);if(!n)return!1;var r=null;return n.forEach(function(e,n,i){if(!e.start){var o=e.offset+(i||0);r=new s(n,o,n,o+e.length);if(!e.length&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start))return r=null,!1}else r=e;return!0}),r},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;hv)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g=0;gE&&o[h].end.row==n.end.row)h--;o=o.slice(g,h+1);for(g=0,h=o.length;g=0;u--)if(i(o[u],t,s))return!0};else var u=function(e,t,s){var o=r.getMatchOffsets(e,n);for(var u=0;u=o;r--)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=u,o=s.row;r>=o;r--)if(n(e.getLine(r),r))return}:function(n){var r=s.row,i=e.getLine(r).substr(s.column);if(n(i,r,s.column))return;for(r+=1;r<=u;r++)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=o,u=s.row;r<=u;r++)if(n(e.getLine(r),r))return};return{forEach:a}}}).call(o.prototype),t.Search=o}),ace.define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function o(e,t){this.platform=t||(i.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function u(e,t){o.call(this,e,t),this.$singleCommand=!1}var r=e("../lib/keys"),i=e("../lib/useragent"),s=r.KEY_MODS;u.prototype=o.prototype,function(){function e(e){return typeof e=="object"&&e.bindKey&&e.bindKey.position||0}this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var n=e&&(typeof e=="string"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},this.bindKey=function(e,t,n){typeof e=="object"&&e&&(n==undefined&&(n=e.position),e=e[this.platform]);if(!e)return;if(typeof t=="function")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split("|").forEach(function(e){var r="";if(e.indexOf(" ")!=-1){var i=e.split(/\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=s[t.hashId]+t.key;r+=(r?" ":"")+n,this._addCommandToBinding(r,"chainKeys")},this),r+=" "}var o=this.parseKeys(e),u=s[o.hashId]+o.key;this._addCommandToBinding(r+u,t,n)},this)},this._addCommandToBinding=function(t,n,r){var i=this.commandKeyBinding,s;if(!n)delete i[t];else if(!i[t]||this.$singleCommand)i[t]=n;else{Array.isArray(i[t])?(s=i[t].indexOf(n))!=-1&&i[t].splice(s,1):i[t]=[i[t]],typeof r!="number"&&(r||n.isDefault?r=-100:r=e(n));var o=i[t];for(s=0;sr)break}o.splice(s,0,n)}},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n});if(typeof n!="object")return;n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=s[t]+n;return this.commandKeyBinding[r]},this.handleKeyboard=function(e,t,n,r){if(r<0)return;var i=s[t]+n,o=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=" "+i,o=this.commandKeyBinding[e.$keyChain]||o);if(o)if(o=="chainKeys"||o[o.length-1]=="chainKeys")return e.$keyChain=e.$keyChain||i,{command:"null"};if(e.$keyChain)if(!!t&&t!=4||n.length!=1){if(t==-1||r>0)e.$keyChain=""}else e.$keyChain=e.$keyChain.slice(0,-i.length-1);return{command:o}},this.getStatusText=function(e,t){return t.$keyChain||""}}.call(o.prototype),t.HashHandler=o,t.MultiHashHandler=u}),ace.define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../keyboard/hash_handler").MultiHashHandler,s=e("../lib/event_emitter").EventEmitter,o=function(e,t){i.call(this,t,e),this.byName=this.commands,this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit("exec",i),this._signal("afterExec",i),i.returnValue===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),ace.define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,n){"use strict";function o(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",bindKey:o("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",bindKey:o("Alt-E","Ctrl-E"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",bindKey:o("Alt-Shift-E","Ctrl-Shift-E"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",bindKey:o("Ctrl-L","Command-L"),exec:function(e){var t=parseInt(prompt("Enter line number:"),10);isNaN(t)||e.gotoLine(t)},readOnly:!0},{name:"fold",bindKey:o("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:o("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",bindKey:o("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",bindKey:o("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"foldall",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",bindKey:o("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",bindKey:o("Ctrl-Shift-Home","Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",bindKey:o("Shift-Up","Shift-Up"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",bindKey:o("Ctrl-Shift-End","Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",bindKey:o("Shift-Down","Shift-Down"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",bindKey:o("Alt-Shift-Left","Command-Shift-Left"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",bindKey:o("Shift-Left","Shift-Left"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",bindKey:o("Alt-Shift-Right","Command-Shift-Right"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",bindKey:o("Ctrl-P","Ctrl-P"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",bindKey:o("Ctrl-Shift-P","Ctrl-Shift-P"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",bindKey:o("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",exec:function(e){},readOnly:!0},{name:"cut",exec:function(e){var t=e.getSelectionRange();e._emit("cut",t),e.selection.isEmpty()||(e.session.remove(t),e.clearSelection())},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",bindKey:o("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",bindKey:o("Alt-Delete","Ctrl-K"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",bindKey:o("Ctrl-T","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"expandtoline",bindKey:o("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"joinlines",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\n\s*/," ").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=" "+c),f+=c}i.row+10?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;o0&&this.$blockScrolling--;var n=t&&t.scrollIntoView;if(n){switch(n){case"center-animate":n="animate";case"center":this.renderer.scrollCursorIntoView(null,.5);break;case"animate":case"cursor":this.renderer.scrollCursorIntoView();break;case"selectionPart":var r=this.selection.getRange(),i=this.renderer.layerConfig;(r.start.row>=i.lastRow||r.end.row<=i.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}n=="animate"&&this.renderer.animateScrolling(this.curOp.scrollTop)}this.prevOp=this.curOp,this.curOp=null}},this.$mergeableCommands=["backspace","del","insertstring"],this.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},this.setKeyboardHandler=function(e,t){if(e&&typeof e=="string"){this.$keybindingId=e;var n=this;g.loadModule(["keybinding",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off("change",this.$onDocumentChange),this.session.off("changeMode",this.$onChangeMode),this.session.off("tokenizerUpdate",this.$onTokenizerUpdate),this.session.off("changeTabSize",this.$onChangeTabSize),this.session.off("changeWrapLimit",this.$onChangeWrapLimit),this.session.off("changeWrapMode",this.$onChangeWrapMode),this.session.off("changeFold",this.$onChangeFold),this.session.off("changeFrontMarker",this.$onChangeFrontMarker),this.session.off("changeBackMarker",this.$onChangeBackMarker),this.session.off("changeBreakpoint",this.$onChangeBreakpoint),this.session.off("changeAnnotation",this.$onChangeAnnotation),this.session.off("changeOverwrite",this.$onCursorChange),this.session.off("changeScrollTop",this.$onScrollTopChange),this.session.off("changeScrollLeft",this.$onScrollLeftChange);var n=this.session.getSelection();n.off("changeCursor",this.$onCursorChange),n.off("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.$blockScrolling+=1,this.onCursorChange(),this.$blockScrolling-=1,this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),this.curOp=null,t&&t._signal("changeEditor",{oldEditor:this}),e&&e._signal("changeEditor",{editor:this})},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e,t){this.renderer.setTheme(e,t)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption("fontSize")||i.computedStyle(this.container,"fontSize")},this.setFontSize=function(e){this.setOption("fontSize",e)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=t.findMatchingBracket(e.getCursorPosition());if(n)var r=new p(n.row,n.column,n.row,n.column+1);else if(t.$mode.getMatching)var r=t.$mode.getMatching(e.session);r&&(t.$bracketHighlight=t.addMarker(r,"ace_bracket","text"))},50)},this.$highlightTags=function(){if(this.$highlightTagPending)return;var e=this;this.$highlightTagPending=!0,setTimeout(function(){e.$highlightTagPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=e.getCursorPosition(),r=new y(e.session,n.row,n.column),i=r.getCurrentToken();if(!i||!/\b(?:tag-open|tag-name)/.test(i.type)){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}if(i.type.indexOf("tag-open")!=-1){i=r.stepForward();if(!i)return}var s=i.value,o=0,u=r.stepBackward();if(u.value=="<"){do u=i,i=r.stepForward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="=0)}else{do i=u,u=r.stepBackward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new p(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column-1,r=t.end.column+1,i=e.getLine(t.start.row),s=i.length,o=i.substring(Math.max(n,0),Math.min(r,s));if(n>=0&&/^[\w\d]/.test(o)||r<=s&&/[\w\d]$/.test(o))return;o=i.substring(t.start.column,t.end.column);if(!/^[\w\d]+$/.test(o))return;var u=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:o});return u},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText();return this._signal("copy",e),e},this.onCopy=function(){this.commands.exec("copy",this)},this.onCut=function(){this.commands.exec("cut",this)},this.onPaste=function(e,t){var n={text:e,event:t};this.commands.exec("paste",this,n)},this.$handlePaste=function(e){typeof e=="string"&&(e={text:e}),this._signal("paste",e);var t=e.text;if(!this.inMultiSelectMode||this.inVirtualSelectionMode)this.insert(t);else{var n=t.split(/\r\n|\r|\n/),r=this.selection.rangeList.ranges;if(n.length>r.length||n.length<2||!n[1])return this.commands.exec("insertstring",this,t);for(var i=r.length;i--;){var s=r[i];s.isEmpty()||this.session.remove(s),this.session.insert(s.start,n[i])}}},this.execCommand=function(e,t){return this.commands.exec(e,this,t)},this.insert=function(e,t){var n=this.session,r=n.getMode(),i=this.getCursorPosition();if(this.getBehavioursEnabled()&&!t){var s=r.transformAction(n.getState(i.row),"insertion",this,n,e);s&&(e!==s.text&&(this.session.mergeUndoDeltas=!1,this.$mergeNextCommand=!1),e=s.text)}e==" "&&(e=this.session.getTabString());if(!this.selection.isEmpty()){var o=this.getSelectionRange();i=this.session.remove(o),this.clearSelection()}else if(this.session.getOverwrite()){var o=new p.fromPoints(i,i);o.end.column+=e.length,this.session.remove(o)}if(e=="\n"||e=="\r\n"){var u=n.getLine(i.row);if(i.column>u.search(/\S|$/)){var a=u.substr(i.column).search(/\S|$/);n.doc.removeInLine(i.row,i.column,i.column+a)}}this.clearSelection();var f=i.column,l=n.getState(i.row),u=n.getLine(i.row),c=r.checkOutdent(l,u,e),h=n.insert(i,e);s&&s.selection&&(s.selection.length==2?this.selection.setSelectionRange(new p(i.row,f+s.selection[0],i.row,f+s.selection[1])):this.selection.setSelectionRange(new p(i.row+s.selection[0],s.selection[1],i.row+s.selection[2],s.selection[3])));if(n.getDocument().isNewLine(e)){var d=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},d)}c&&r.autoOutdent(l,n,i.row)},this.onTextInput=function(e){this.keyBinding.onTextInput(e)},this.onCommandKey=function(e,t,n){this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},this.getScrollSpeed=function(){return this.getOption("scrollSpeed")},this.setDragDelay=function(e){this.setOption("dragDelay",e)},this.getDragDelay=function(){return this.getOption("dragDelay")},this.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},this.getSelectionStyle=function(){return this.getOption("selectionStyle")},this.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},this.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption("readOnly",e)},this.getReadOnly=function(){return this.getOption("readOnly")},this.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},this.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},this.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},this.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},this.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},this.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;tt.toLowerCase()?1:0});var r=new p(0,0,0,0);for(var i=e.first;i<=e.last;i++){var s=t.getLine(i);r.start.row=i,r.end.row=i,r.end.column=s.length,t.replace(r,n[i-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},this.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new p(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&np+1)break;p=d.last}l--,u=this.session.$moveLines(h,p,t?0:e),t&&e==-1&&(c=l+1);while(c<=l)o[c].moveBy(u,0),c++;t||(u=0),a+=u}i.fromOrientedRange(i.ranges[0]),i.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},this.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},this.onCompositionStart=function(e){this.renderer.showComposition(this.getCursorPosition())},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);this.$blockScrolling++,t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection()),this.$blockScrolling--;var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new y(this.session,n.row,n.column),i=r.getCurrentToken(),s=i||r.stepForward();if(!s)return;var o,u=!1,a={},f=n.column-s.start,l,c={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(s.value.match(/[{}()\[\]]/g))for(;f=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),this.$blockScrolling-=1,r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.$blockScrolling+=1,this.session.unfold(e),this.selection.setSelectionRange(e),this.$blockScrolling-=1;var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},this.undo=function(){this.$blockScrolling++,this.session.getUndoManager().undo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.$blockScrolling++,this.session.getUndoManager().redo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy()},this.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.topwindow.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.off("changeSelection",s),this.renderer.off("afterRender",u),this.renderer.off("beforeRender",o)}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!="wide",i.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e))}}).call(b.prototype),g.defineOptions(b.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.$resetCursorStyle()},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.keybindingId},handlesSet:!0},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",showLineNumbers:"renderer",showGutter:"renderer",displayIndentGuides:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"}),t.Editor=b}),ace.define("ace/undomanager",["require","exports","module"],function(e,t,n){"use strict";var r=function(){this.reset()};(function(){function e(e){return{action:e.action,start:e.start,end:e.end,lines:e.lines.length==1?null:e.lines,text:e.lines.length==1?e.lines[0]:null}}function t(e){return{action:e.action,start:e.start,end:e.end,lines:e.lines||[e.text]}}function n(e,t){var n=new Array(e.length);for(var r=0;r0},this.hasRedo=function(){return this.$redoStack.length>0},this.markClean=function(){this.dirtyCounter=0},this.isClean=function(){return this.dirtyCounter===0},this.$serializeDeltas=function(t){return n(t,e)},this.$deserializeDeltas=function(e){return n(e,t)}}).call(r.prototype),t.UndoManager=r}),ace.define("ace/layer/gutter",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/lang"),o=e("../lib/event_emitter").EventEmitter,u=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_gutter-layer",e.appendChild(this.element),this.setShowFoldWidgets(this.$showFoldWidgets),this.gutterWidth=0,this.$annotations=[],this.$updateAnnotations=this.$updateAnnotations.bind(this),this.$cells=[]};(function(){i.implement(this,o),this.setSession=function(e){this.session&&this.session.removeEventListener("change",this.$updateAnnotations),this.session=e,e&&e.on("change",this.$updateAnnotations)},this.addGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.addGutterDecoration"),this.session.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.removeGutterDecoration"),this.session.removeGutterDecoration(e,t)},this.setAnnotations=function(e){this.$annotations=[];for(var t=0;to&&(v=s.end.row+1,s=t.getNextFoldLine(v,s),o=s?s.start.row:Infinity);if(v>i){while(this.$cells.length>d+1)p=this.$cells.pop(),this.element.removeChild(p.element);break}p=this.$cells[++d],p||(p={element:null,textNode:null,foldWidget:null},p.element=r.createElement("div"),p.textNode=document.createTextNode(""),p.element.appendChild(p.textNode),this.element.appendChild(p.element),this.$cells[d]=p);var m="ace_gutter-cell ";a[v]&&(m+=a[v]),f[v]&&(m+=f[v]),this.$annotations[v]&&(m+=this.$annotations[v].className),p.element.className!=m&&(p.element.className=m);var g=t.getRowLength(v)*e.lineHeight+"px";g!=p.element.style.height&&(p.element.style.height=g);if(u){var y=u[v];y==null&&(y=u[v]=t.getFoldWidget(v))}if(y){p.foldWidget||(p.foldWidget=r.createElement("span"),p.element.appendChild(p.foldWidget));var m="ace_fold-widget ace_"+y;y=="start"&&v==o&&vn.right-t.right)return"foldWidgets"}}).call(u.prototype),t.Gutter=u}),ace.define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../range").Range,i=e("../lib/dom"),s=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){function e(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.update=function(e){var e=e||this.config;if(!e)return;this.config=e;var t=[];for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,i,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type=="text"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+" ace_start"+" ace_br15",e)}this.element.innerHTML=t.join("")},this.$getTop=function(e,t){return(e-t.firstRowScreen)*t.lineHeight},this.drawTextMarker=function(t,n,i,s,o){var u=this.session,a=n.start.row,f=n.end.row,l=a,c=0,h=0,p=u.getScreenLastRowColumn(l),d=new r(l,n.start.column,l,h);for(;l<=f;l++)d.start.row=d.end.row=l,d.start.column=l==a?n.start.column:u.getRowWrapIndent(l),d.end.column=p,c=h,h=p,p=l+1p,l==f),s,l==f?0:1,o)},this.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;i=i||"",e.push("
"),u=this.$getTop(t.end.row,r);var f=t.end.column*r.characterWidth;e.push("
"),o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var l=(t.start.column?1:0)|(t.end.column?0:8);e.push("
")},this.drawSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;e.push("
")},this.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),e.push("
")},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;e.push("
")}}).call(s.prototype),t.Marker=s}),ace.define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this)};(function(){r.implement(this,u),this.EOF_CHAR="\u00b6",this.EOL_CHAR_LF="\u00ac",this.EOL_CHAR_CRLF="\u00a4",this.EOL_CHAR=this.EOL_CHAR_LF,this.TAB_CHAR="\u2014",this.SPACE_CHAR="\u00b7",this.$padding=0,this.$updateEolChar=function(){var e=this.session.doc.getNewLineCharacter()=="\n"?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=e)return this.EOL_CHAR=e,!0},this.setPadding=function(e){this.$padding=e,this.element.style.padding="0 "+e+"px"},this.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},this.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},this.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},this.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},this.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},this.setSession=function(e){this.session=e,e&&this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;n"+s.stringRepeat(this.TAB_CHAR,n)+""):t.push(s.stringRepeat(" ",n));if(this.displayIndentGuides){this.$indentGuideRe=/\s\S| \t|\t |\s$/;var r="ace_indent-guide",i="",o="";if(this.showInvisibles){r+=" ace_invisible",i=" ace_invisible_space",o=" ace_invisible_tab";var u=s.stringRepeat(this.SPACE_CHAR,this.tabSize),a=s.stringRepeat(this.TAB_CHAR,this.tabSize)}else var u=s.stringRepeat(" ",this.tabSize),a=u;this.$tabStrings[" "]=""+u+"",this.$tabStrings[" "]=""+a+""}},this.updateLines=function(e,t,n){(this.config.lastRow!=e.lastRow||this.config.firstRow!=e.firstRow)&&this.scrollLines(e),this.config=e;var r=Math.max(t,e.firstRow),i=Math.min(n,e.lastRow),s=this.element.childNodes,o=0;for(var u=e.firstRow;uf&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),f=a?a.start.row:Infinity);if(u>i)break;var l=s[o++];if(l){var c=[];this.$renderLine(c,u,!this.$useLineGroups(),u==f?a:!1),l.style.height=e.lineHeight*this.session.getRowLength(u)+"px",l.innerHTML=c.join("")}u++}},this.scrollLines=function(e){var t=this.config;this.config=e;if(!t||t.lastRow0;r--)n.removeChild(n.firstChild);if(t.lastRow>e.lastRow)for(var r=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);r>0;r--)n.removeChild(n.lastChild);if(e.firstRowt.lastRow){var i=this.$renderLinesFragment(e,t.lastRow+1,e.lastRow);n.appendChild(i)}},this.$renderLinesFragment=function(e,t,n){var r=this.element.ownerDocument.createDocumentFragment(),s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=i.createElement("div"),f=[];this.$renderLine(f,s,!1,s==u?o:!1),a.innerHTML=f.join("");if(this.$useLineGroups())a.className="ace_line_group",r.appendChild(a),a.style.height=e.lineHeight*this.session.getRowLength(s)+"px";else while(a.firstChild)r.appendChild(a.firstChild);s++}return r},this.update=function(e){this.config=e;var t=[],n=e.firstRow,r=e.lastRow,i=n,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>r)break;this.$useLineGroups()&&t.push("
"),this.$renderLine(t,i,!1,i==o?s:!1),this.$useLineGroups()&&t.push("
"),i++}this.element.innerHTML=t.join("")},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var i=this,o=/\t|&|<|>|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\u3000\uFEFF\uFFF9-\uFFFC])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]/g,u=function(e,n,r,o,u){if(n)return i.showInvisibles?""+s.stringRepeat(i.SPACE_CHAR,e.length)+"":e;if(e=="&")return"&";if(e=="<")return"<";if(e==">")return">";if(e==" "){var a=i.session.getScreenTabSize(t+o);return t+=a-1,i.$tabStrings[a]}if(e=="\u3000"){var f=i.showInvisibles?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",l=i.showInvisibles?i.SPACE_CHAR:"";return t+=1,""+l+""}return r?""+i.SPACE_CHAR+"":(t+=1,""+e+"")},a=r.replace(o,u);if(!this.$textToken[n.type]){var f="ace_"+n.type.replace(/\./g," ace_"),l="";n.type=="fold"&&(l=" style='width:"+n.value.length*this.config.characterWidth+"px;' "),e.push("",a,"")}else e.push(a);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);return r<=0||r>=n?t:t[0]==" "?(r-=r%this.tabSize,e.push(s.stringRepeat(this.$tabStrings[" "],r/this.tabSize)),t.substr(r)):t[0]==" "?(e.push(s.stringRepeat(this.$tabStrings[" "],r)),t.substr(r)):t},this.$renderWrappedLine=function(e,t,n,r){var i=0,o=0,u=n[0],a=0;for(var f=0;f=u)a=this.$renderToken(e,a,l,c.substring(0,u-i)),c=c.substring(u-i),i=u,r||e.push("","
"),e.push(s.stringRepeat("\u00a0",n.indent)),o++,a=0,u=n[o]||Number.MAX_VALUE;c.length!=0&&(i+=c.length,a=this.$renderToken(e,a,l,c))}}},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;s");if(i.length){var s=this.session.getRowSplitData(t);s&&s.length?this.$renderWrappedLine(e,i,s,n):this.$renderSimpleLine(e,i)}this.showInvisibles&&(r&&(t=r.end.row),e.push("",t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,"")),n||e.push("
")},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.lengthn-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(sn?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(a.prototype),t.Text=a}),ace.define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i,s=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),i===undefined&&(i=!("opacity"in this.element.style)),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=(i?this.$updateVisibility:this.$updateOpacity).bind(this)};(function(){this.$updateVisibility=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.visibility=e?"":"hidden"},this.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.opacity=e?"":"0"},this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&!i&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.$updateCursors=this.$updateOpacity.bind(this),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking"),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible)return;this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+n.column*this.config.characterWidth,i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.update=function(e){this.config=e;var t=this.session.$selectionMarkers,n=0,r=0;if(t===undefined||t.length===0)t=[{cursor:null}];for(var n=0,i=t.length;ne.height+e.offset||s.top<0)&&n>1)continue;var o=(this.cursors[r++]||this.addCursor()).style;this.drawCursor?this.drawCursor(o,s,e,t[n],this.session):(o.left=s.left+"px",o.top=s.top+"px",o.width=e.characterWidth+"px",o.height=e.lineHeight+"px")}while(this.cursors.length>r)this.removeCursor();var u=this.session.getOverwrite();this.$setOverwrite(u),this.$pixelPos=s,this.restartTimer()},this.drawCursor=null,this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(s.prototype),t.Cursor=s}),ace.define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/event_emitter").EventEmitter,u=function(e){this.element=i.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+this.classSuffix,this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,s.addListener(this.element,"scroll",this.onScroll.bind(this)),s.addListener(this.element,"mousedown",s.preventDefault)};(function(){r.implement(this,o),this.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e}}).call(u.prototype);var a=function(e,t){u.call(this,e),this.scrollTop=0,t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+"px"};r.inherits(a,u),function(){this.classSuffix="-v",this.onScroll=function(){this.skipEvent||(this.scrollTop=this.element.scrollTop,this._emit("scroll",{data:this.scrollTop})),this.skipEvent=!1},this.getWidth=function(){return this.isVisible?this.width:0},this.setHeight=function(e){this.element.style.height=e+"px"},this.setInnerHeight=function(e){this.inner.style.height=e+"px"},this.setScrollHeight=function(e){this.inner.style.height=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=this.element.scrollTop=e)}}.call(a.prototype);var f=function(e,t){u.call(this,e),this.scrollLeft=0,this.height=t.$scrollbarWidth,this.inner.style.height=this.element.style.height=(this.height||15)+5+"px"};r.inherits(f,u),function(){this.classSuffix="-h",this.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},this.getHeight=function(){return this.isVisible?this.height:0},this.setWidth=function(e){this.element.style.width=e+"px"},this.setInnerWidth=function(e){this.inner.style.width=e+"px"},this.setScrollWidth=function(e){this.inner.style.width=e+"px"},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}.call(f.prototype),t.ScrollBar=a,t.ScrollBarV=a,t.ScrollBarH=f,t.VScrollBar=a,t.HScrollBar=f}),ace.define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){"use strict";var r=e("./lib/event"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.window=t||window};(function(){this.schedule=function(e){this.changes=this.changes|e;if(!this.pending&&this.changes){this.pending=!0;var t=this;r.nextFrame(function(){t.pending=!1;var e;while(e=t.changes)t.changes=0,t.onRender(e)},this.window)}}}).call(i.prototype),t.RenderLoop=i}),ace.define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=0,f=t.FontMetrics=function(e){this.el=i.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),a||this.$testFractionalRect(),this.$measureNode.innerHTML=s.stringRepeat("X",a),this.$characterSize={width:0,height:0},this.checkForSizeChanges()};(function(){r.implement(this,u),this.$characterSize={width:0,height:0},this.$testFractionalRect=function(){var e=i.createElement("div");this.$setMeasureNodeStyles(e.style),e.style.width="0.2px",document.documentElement.appendChild(e);var t=e.getBoundingClientRect().width;t>0&&t<1?a=50:a=100,e.parentNode.removeChild(e)},this.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",o.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(){var e=this.$measureSizes();if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=setInterval(function(){e.checkForSizeChanges()},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},this.$measureSizes=function(){if(a===50){var e=null;try{e=this.$measureNode.getBoundingClientRect()}catch(t){e={width:0,height:0}}var n={height:e.height,width:e.width/a}}else var n={height:this.$measureNode.clientHeight,width:this.$measureNode.clientWidth/a};return n.width===0||n.height===0?null:n},this.$measureCharWidth=function(e){this.$main.innerHTML=s.stringRepeat(e,a);var t=this.$main.getBoundingClientRect();return t.width/a},this.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)}}).call(f.prototype)}),ace.define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/lib/useragent","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./config"),o=e("./lib/useragent"),u=e("./layer/gutter").Gutter,a=e("./layer/marker").Marker,f=e("./layer/text").Text,l=e("./layer/cursor").Cursor,c=e("./scrollbar").HScrollBar,h=e("./scrollbar").VScrollBar,p=e("./renderloop").RenderLoop,d=e("./layer/font_metrics").FontMetrics,v=e("./lib/event_emitter").EventEmitter,m='.ace_editor {position: relative;overflow: hidden;font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'source-code-pro\', monospace;direction: ltr;}.ace_scroller {position: absolute;overflow: hidden;top: 0;bottom: 0;background-color: inherit;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;cursor: text;}.ace_content {position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;min-width: 100%;}.ace_dragging .ace_scroller:before{position: absolute;top: 0;left: 0;right: 0;bottom: 0;content: \'\';background: rgba(250, 250, 250, 0.01);z-index: 1000;}.ace_dragging.ace_dark .ace_scroller:before{background: rgba(0, 0, 0, 0.01);}.ace_selecting, .ace_selecting * {cursor: text !important;}.ace_gutter {position: absolute;overflow : hidden;width: auto;top: 0;bottom: 0;left: 0;cursor: default;z-index: 4;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");}.ace_scrollbar {position: absolute;right: 0;bottom: 0;z-index: 6;}.ace_scrollbar-inner {position: absolute;cursor: text;left: 0;top: 0;}.ace_scrollbar-v{overflow-x: hidden;overflow-y: scroll;top: 0;}.ace_scrollbar-h {overflow-x: scroll;overflow-y: hidden;left: 0;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;font: inherit;padding: 0 1px;margin: 0 -1px;text-indent: -1em;-ms-user-select: text;-moz-user-select: text;-webkit-user-select: text;user-select: text;white-space: pre!important;}.ace_text-input.ace_composition {background: inherit;color: inherit;z-index: 1000;opacity: 1;text-indent: 0;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;word-wrap: normal;white-space: pre;height: 100%;width: 100%;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;}.ace_text-layer {font: inherit !important;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;border-left: 2px solid;transform: translatez(0);}.ace_slim-cursors .ace_cursor {border-left-width: 1px;}.ace_overwrite-cursors .ace_cursor {border-left-width: 0;border-bottom: 1px solid;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_smooth-blinking .ace_cursor {-webkit-transition: opacity 0.18s;transition: opacity 0.18s;}.ace_editor.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;}.ace_line .ace_fold {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");}.ace_tooltip {background-color: #FFF;background-image: -webkit-linear-gradient(top, transparent, rgba(0, 0, 0, 0.1));background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;max-width: 100%;padding: 3px 4px;position: fixed;z-index: 999999;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;cursor: default;white-space: pre;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;pointer-events: none;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block; }.ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");}.ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}.ace_dark .ace_fold-widget {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");}.ace_dark .ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {-webkit-transition: opacity 0.4s ease 0.05s;transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {-webkit-transition: opacity 0.05s ease 0.05s;transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}.ace_br1 {border-top-left-radius : 3px;}.ace_br2 {border-top-right-radius : 3px;}.ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;}.ace_br4 {border-bottom-right-radius: 3px;}.ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;}.ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;}.ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;}.ace_br8 {border-bottom-left-radius : 3px;}.ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;}.ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;}.ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}';i.importCssString(m,"ace_editor.css");var g=function(e,t){var n=this;this.container=e||i.createElement("div"),this.$keepTextAreaAtCursor=!o.isOldIE,i.addCssClass(this.container,"ace_editor"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new u(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new a(this.content);var r=this.$textLayer=new f(this.content);this.canvas=r.element,this.$markerFront=new a(this.content),this.$cursorLayer=new l(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new h(this.container,this),this.scrollBarH=new c(this.container,this),this.scrollBarV.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new d(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.addEventListener("changeCharacterSize",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$loop=new p(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),s.resetOptions(this),s._emit("renderer",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,v),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin()},this.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode)},this.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},this.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar()},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.$changes=0,this.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},this.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null},this.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var i=0,s=this.$size,o={width:s.width,height:s.height,scrollerHeight:s.scrollerHeight,scrollerWidth:s.scrollerWidth};r&&(e||s.height!=r)&&(s.height=r,i|=this.CHANGE_SIZE,s.scrollerHeight=s.height,this.$horizScroll&&(s.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",i|=this.CHANGE_SCROLL);if(n&&(e||s.width!=n)){i|=this.CHANGE_SIZE,s.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,this.scrollBarH.element.style.left=this.scroller.style.left=t+"px",s.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()),this.scrollBarH.element.style.right=this.scroller.style.right=this.scrollBarV.getWidth()+"px",this.scroller.style.bottom=this.scrollBarH.getHeight()+"px";if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)i|=this.CHANGE_FULL}return s.$dirty=!n||!r,i&&this._signal("resize",o),i},this.onGutterResize=function(){var e=this.$showGutter?this.$gutter.offsetWidth:0;e!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,e,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):(this.$computeLayerConfig(),this.$loop.schedule(this.CHANGE_MARKER))},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption("showInvisibles",e)},this.getShowInvisibles=function(){return this.getOption("showInvisibles")},this.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},this.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},this.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},this.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},this.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},this.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},this.getShowGutter=function(){return this.getOption("showGutter")},this.setShowGutter=function(e){return this.setOption("showGutter",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.$updateGutterLineHighlight=function(){var e=this.$cursorLayer.$pixelPos,t=this.layerConfig.lineHeight;if(this.session.getUseWrapMode()){var n=this.session.selection.getCursor();n.column=0,e=this.$cursorLayer.getPixelPosition(n,!0),t*=this.session.getRowLength(n.row)}this.$gutterLineHighlight.style.top=e.top-this.layerConfig.offset+"px",this.$gutterLineHighlight.style.height=t+"px"},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=this.characterWidth*this.$printMarginColumn+this.$padding+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.scroller},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){if(!this.$keepTextAreaAtCursor)return;var e=this.layerConfig,t=this.$cursorLayer.$pixelPos.top,n=this.$cursorLayer.$pixelPos.left;t-=e.offset;var r=this.textarea.style,i=this.lineHeight;if(t<0||t>e.height-i){r.top=r.left="0";return}var s=this.characterWidth;if(this.$composition){var o=this.textarea.value.replace(/^\x01+/,"");s*=this.session.$getStringScreenWidth(o)[0]+2,i+=2}n-=this.scrollLeft,n>this.$size.scrollerWidth-s&&(n=this.$size.scrollerWidth-s),n+=this.gutterWidth,r.height=i+"px",r.width=s+"px",r.left=Math.min(n,this.$size.scrollerWidth-s)+"px",r.top=Math.min(t,this.$size.height-i)+"px"},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,n=this.session.documentToScreenRow(t,0)*e.lineHeight;return n-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},this.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},this.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$frozen=!1,this.freeze=function(){this.$frozen=!0},this.unfreeze=function(){this.$frozen=!1},this.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender");var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-this.layerConfig.firstRow)*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),this.$gutterLayer.element.style.marginTop=-n.offset+"px",this.content.style.marginTop=-n.offset+"px",this.content.style.width=n.width+2*this.$padding+"px",this.content.style.height=n.minHeight+"px"}e&this.CHANGE_H_SCROLL&&(this.content.style.marginLeft=-this.scrollLeft+"px",this.scroller.className=this.scrollLeft<=0?"ace_scroller":"ace_scroller ace_scroll-left");if(e&this.CHANGE_FULL){this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this._signal("afterRender");return}if(e&this.CHANGE_SCROLL){e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this.$moveTextAreaToCursor(),this._signal("afterRender");return}e&this.CHANGE_TEXT?(this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n):(e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER)&&this.$showGutter&&this.$gutterLayer.update(n),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal("afterRender")},this.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.min(t,Math.max((this.$minLines||1)*this.lineHeight,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(n+=this.scrollBarH.getHeight()),this.$maxPixelHeight&&n>this.$maxPixelHeight&&(n=this.$maxPixelHeight);var r=e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||r!=this.$vScroll){r!=this.$vScroll&&(this.$vScroll=r,this.scrollBarV.setVisible(r));var i=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,i,n),this.desiredHeight=n,this._signal("autosize")}},this.$computeLayerConfig=function(){var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.$getLongestLine(),o=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-s-2*this.$padding<0),u=this.$horizScroll!==o;u&&(this.$horizScroll=o,this.scrollBarH.setVisible(o));var a=this.$vScroll;this.$maxLines&&this.lineHeight>1&&this.$autosize();var f=this.scrollTop%this.lineHeight,l=t.scrollerHeight+this.lineHeight,c=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=c;var h=this.scrollMargin;this.session.setScrollTop(Math.max(-h.top,Math.min(this.scrollTop,i-t.scrollerHeight+h.bottom))),this.session.setScrollLeft(Math.max(-h.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+h.right)));var p=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+c<0||this.scrollTop>h.top),d=a!==p;d&&(this.$vScroll=p,this.scrollBarV.setVisible(p));var v=Math.ceil(l/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-f)/this.lineHeight)),g=m+v,y,b,w=this.lineHeight;m=e.screenToDocumentRow(m,0);var E=e.getFoldLine(m);E&&(m=E.start.row),y=e.documentToScreenRow(m,0),b=e.getRowLength(m)*w,g=Math.min(e.screenToDocumentRow(g,0),e.getLength()-1),l=t.scrollerHeight+e.getRowLength(g)*w+b,f=this.scrollTop-y*w;var S=0;this.layerConfig.width!=s&&(S=this.CHANGE_H_SCROLL);if(u||d)S=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),d&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:l,maxHeight:i,offset:f,gutterOffset:Math.max(0,Math.ceil((f+t.height-t.scrollerHeight)/w)),height:this.$size.scrollerHeight},S},this.$updateLines=function(){var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(ts?(t&&a+o>s+this.lineHeight&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-ui?(i=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},this.pixelToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=(e+this.scrollLeft-n.left-this.$padding)/this.characterWidth,i=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),s=Math.round(r);return{row:i,column:s,side:r-s>0?1:-1}},this.screenToTextCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=Math.round((e+this.scrollLeft-n.left-this.$padding)/this.characterWidth),i=(t+this.scrollTop-n.top)/this.lineHeight;return this.session.screenToDocumentPosition(i,Math.max(r,0))},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+Math.round(r.column*this.characterWidth),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition||(this.$composition={keepTextAreaAtCursor:this.$keepTextAreaAtCursor,cssText:this.textarea.style.cssText}),this.$keepTextAreaAtCursor=!0,i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor()},this.setCompositionText=function(e){this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;i.removeCssClass(this.textarea,"ace_composition"),this.$keepTextAreaAtCursor=this.$composition.keepTextAreaAtCursor,this.textarea.style.cssText=this.$composition.cssText,this.$composition=null},this.setTheme=function(e,t){function o(r){if(n.$themeId!=e)return t&&t();if(!r.cssClass)return;i.importCssString(r.cssText,r.cssClass,n.container.ownerDocument),n.theme&&i.removeCssClass(n.container,n.theme.cssClass);var s="padding"in r?r.padding:"padding"in(n.theme||{})?4:n.$padding;n.$padding&&s!=n.$padding&&n.setPadding(s),n.$theme=r.cssClass,n.theme=r,i.addCssClass(n.container,r.cssClass),i.setCssClass(n.container,"ace_dark",r.isDark),n.$size&&(n.$size.width=0,n.$updateSizeAsync()),n._dispatchEvent("themeLoaded",{theme:r}),t&&t()}var n=this;this.$themeId=e,n._dispatchEvent("themeChange",{theme:e});if(!e||typeof e=="string"){var r=e||this.$options.theme.initialValue;s.loadModule(["theme",r],o)}else o(e)},this.getTheme=function(){return this.$themeId},this.setStyle=function(e,t){i.setCssClass(this.container,e,t!==!1)},this.unsetStyle=function(e){i.removeCssClass(this.container,e)},this.setCursorStyle=function(e){this.scroller.style.cursor!=e&&(this.scroller.style.cursor=e)},this.setMouseCursor=function(e){this.scroller.style.cursor=e},this.destroy=function(){this.$textLayer.destroy(),this.$cursorLayer.destroy()}}).call(g.prototype),s.defineOptions(g.prototype,"renderer",{animatedScroll:{initialValue:!1},showInvisibles:{set:function(e){this.$textLayer.setShowInvisibles(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!1},showPrintMargin:{set:function(){this.$updatePrintMargin()},initialValue:!0},printMarginColumn:{set:function(){this.$updatePrintMargin()},initialValue:80},printMargin:{set:function(e){typeof e=="number"&&(this.$printMarginColumn=e),this.$showPrintMargin=!!e,this.$updatePrintMargin()},get:function(){return this.$showPrintMargin&&this.$printMarginColumn}},showGutter:{set:function(e){this.$gutter.style.display=e?"block":"none",this.$loop.schedule(this.CHANGE_FULL),this.onGutterResize()},initialValue:!0},fadeFoldWidgets:{set:function(e){i.setCssClass(this.$gutter,"ace_fade-fold-widgets",e)},initialValue:!1},showFoldWidgets:{set:function(e){this.$gutterLayer.setShowFoldWidgets(e)},initialValue:!0},showLineNumbers:{set:function(e){this.$gutterLayer.setShowLineNumbers(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},displayIndentGuides:{set:function(e){this.$textLayer.setDisplayIndentGuides(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!0},highlightGutterLine:{set:function(e){if(!this.$gutterLineHighlight){this.$gutterLineHighlight=i.createElement("div"),this.$gutterLineHighlight.className="ace_gutter-active-line",this.$gutter.appendChild(this.$gutterLineHighlight);return}this.$gutterLineHighlight.style.display=e?"":"none",this.$cursorLayer.$pixelPos&&this.$updateGutterLineHighlight()},initialValue:!1,value:!0},hScrollBarAlwaysVisible:{set:function(e){(!this.$hScrollBarAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},vScrollBarAlwaysVisible:{set:function(e){(!this.$vScrollBarAlwaysVisible||!this.$vScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},fontSize:{set:function(e){typeof e=="number"&&(e+="px"),this.container.style.fontSize=e,this.updateFontSize()},initialValue:12},fontFamily:{set:function(e){this.container.style.fontFamily=e,this.updateFontSize()}},maxLines:{set:function(e){this.updateFull()}},minLines:{set:function(e){this.updateFull()}},maxPixelHeight:{set:function(e){this.updateFull()},initialValue:0},scrollPastEnd:{set:function(e){e=+e||0;if(this.$scrollPastEnd==e)return;this.$scrollPastEnd=e,this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:0,handlesSet:!0},fixedWidthGutter:{set:function(e){this.$gutterLayer.$fixedWidth=!!e,this.$loop.schedule(this.CHANGE_GUTTER)}},theme:{set:function(e){this.setTheme(e)},get:function(){return this.$themeId||this.theme},initialValue:"./theme/textmate",handlesSet:!0}}),t.VirtualRenderer=g}),ace.define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/net","ace/lib/event_emitter","ace/config"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/net"),s=e("../lib/event_emitter").EventEmitter,o=e("../config"),u=function(t,n,r,i){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.onMessage=this.onMessage.bind(this),e.nameToUrl&&!e.toUrl&&(e.toUrl=e.nameToUrl);if(o.get("packaged")||!e.toUrl)i=i||o.moduleUrl(n,"worker");else{var s=this.$normalizePath;i=i||s(e.toUrl("ace/worker/worker.js",null,"_"));var u={};t.forEach(function(t){u[t]=s(e.toUrl(t,null,"_").replace(/(\.js)?(\?.*)?$/,""))})}try{this.$worker=new Worker(i)}catch(a){if(!(a instanceof window.DOMException))throw a;var f=this.$workerBlob(i),l=window.URL||window.webkitURL,c=l.createObjectURL(f);this.$worker=new Worker(c),l.revokeObjectURL(c)}this.$worker.postMessage({init:!0,tlns:u,module:n,classname:r}),this.callbackId=1,this.callbacks={},this.$worker.onmessage=this.onMessage};(function(){r.implement(this,s),this.onMessage=function(e){var t=e.data;switch(t.type){case"event":this._signal(t.name,{data:t.data});break;case"call":var n=this.callbacks[t.id];n&&(n(t.data),delete this.callbacks[t.id]);break;case"error":this.reportError(t.data);break;case"log":window.console&&console.log&&console.log.apply(console,t.data)}},this.reportError=function(e){window.console&&console.error&&console.error(e)},this.$normalizePath=function(e){return i.qualifyURL(e)},this.terminate=function(){this._signal("terminate",{}),this.deltaQueue=null,this.$worker.terminate(),this.$worker=null,this.$doc&&this.$doc.off("change",this.changeListener),this.$doc=null},this.send=function(e,t){this.$worker.postMessage({command:e,args:t})},this.call=function(e,t,n){if(n){var r=this.callbackId++;this.callbacks[r]=n,t.push(r)}this.send(e,t)},this.emit=function(e,t){try{this.$worker.postMessage({event:e,data:{data:t.data}})}catch(n){console.error(n.stack)}},this.attachToDocument=function(e){this.$doc&&this.terminate(),this.$doc=e,this.call("setValue",[e.getValue()]),e.on("change",this.changeListener)},this.changeListener=function(e){this.deltaQueue||(this.deltaQueue=[],setTimeout(this.$sendDeltaQueue,0)),e.action=="insert"?this.deltaQueue.push(e.start,e.lines):this.deltaQueue.push(e.start,e.end)},this.$sendDeltaQueue=function(){var e=this.deltaQueue;if(!e)return;this.deltaQueue=null,e.length>50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})},this.$workerBlob=function(e){var t="importScripts('"+i.qualifyURL(e)+"');";try{return new Blob([t],{type:"application/javascript"})}catch(n){var r=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder,s=new r;return s.append(t),s.getBlob("application/javascript")}}}).call(u.prototype);var a=function(e,t,n){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.callbackId=1,this.callbacks={},this.messageBuffer=[];var r=null,i=!1,u=Object.create(s),a=this;this.$worker={},this.$worker.terminate=function(){},this.$worker.postMessage=function(e){a.messageBuffer.push(e),r&&(i?setTimeout(f):f())},this.setEmitSync=function(e){i=e};var f=function(){var e=a.messageBuffer.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};u.postMessage=function(e){a.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},o.loadModule(["worker",t],function(e){r=new e[n](u);while(a.messageBuffer.length)f()})};a.prototype=u.prototype,t.UIWorkerClient=a,t.WorkerClient=u}),ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var i=this.pos;i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);r.$insertRight=!0,r.detach(),e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e=this.pos.column&&t.start.column<=this.pos.column+this.length+1,s=t.start.column-this.pos.column;this.updateAnchors(e),i&&(this.length+=n);if(i&&!this.session.$fromUndo)if(e.action==="insert")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.insertMergedLines(a,e.lines)}else if(e.action==="remove")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.remove(new r(a.row,a.column,a.row,a.column-n))}this.$updating=!1,this.updateMarkers()},this.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},this.updateMarkers=function(){if(this.$updating)return;var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new r(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)},this.onCursorChange=function(e){if(this.$updating||!this.session)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},this.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},this.cancel=function(){if(this.$undoStackDepth===-1)return;var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length?this.$onRemoveRange(e):this.ranges[0]&&this.fromOrientedRange(this.ranges[0])},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal("removeRange",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var n=this.getRange(),r=this.isBackwards(),s=n.start.row,o=n.end.row;if(s==o){if(r)var u=n.end,a=n.start;else var u=n.start,a=n.end;this.addRange(i.fromPoints(a,a)),this.addRange(i.fromPoints(u,u));return}var f=[],l=this.getLineRange(s,!0);l.start.column=n.start.column,f.push(l);for(var c=s+1;c1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.selectionLead),s=this.session.documentToScreenPosition(this.selectionAnchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column0)d--;if(d>0){var m=0;while(r[m].isEmpty())m++}for(var g=d;g>=m;g--)r[g].isEmpty()&&r.splice(g,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges();var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;r0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.removeFullLines(u,f);p=this.$reAlignText(p,l),this.session.insert({row:u,column:0},p.join("\n")+"\n"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),io?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(" ",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o," ")+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),st[3].length&&(o=t[3].length),t):[e]}).map(t?f:n?r?l:f:c)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off("addRange",this.$onAddRange),n.multiSelect.off("removeRange",this.$onRemoveRange),n.multiSelect.off("multiSelect",this.$onMultiSelect),n.multiSelect.off("singleSelect",this.$onSingleSelect),n.multiSelect.lead.off("change",this.$checkMultiselectChange),n.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e("./config").defineOptions(d.prototype,"editor",{enableMultiselect:{set:function(e){m(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",o)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",o))},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),ace.define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../../range").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?"start":t=="markbeginend"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++tf){var h=e.getLine(l).length;return new r(f,u,l,h)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=e.getFoldWidget(u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),ace.define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),ace.define("ace/line_widgets",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e){this.session=e,this.session.widgetManager=this,this.session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on("change",this.updateOnChange),this.session.on("changeFold",this.updateOnFold),this.session.on("changeEditor",this.$onChangeEditor)}var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./range").Range;(function(){this.getRowLength=function(e){var t;return this.lineWidgets?t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0:t=0,!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.$getWidgetScreenLength=function(){var e=0;return this.lineWidgets.forEach(function(t){t&&t.rowCount&&!t.hidden&&(e+=t.rowCount)}),e},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach();if(this.editor==e)return;this.detach(),this.editor=e,e&&(e.widgetManager=this,e.renderer.on("beforeRender",this.measureWidgets),e.renderer.on("afterRender",this.renderWidgets))},this.detach=function(e){var t=this.editor;if(!t)return;this.editor=null,t.widgetManager=null,t.renderer.off("beforeRender",this.measureWidgets),t.renderer.off("afterRender",this.renderWidgets);var n=this.session.lineWidgets;n&&n.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})},this.updateOnFold=function(e,t){var n=t.lineWidgets;if(!n||!e.action)return;var r=e.data,i=r.start.row,s=r.end.row,o=e.action=="add";for(var u=i+1;u0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;if(u.hidden){u.el.style.top=-100-(u.pixelHeight||0)+"px";continue}u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+"px";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+"px",u.fullWidth&&u.screenWidth&&(u.el.style.minWidth=n.width+2*n.padding+"px"),u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+"px":u.el.style.right=""}}}).call(o.prototype),t.LineWidgets=o}),ace.define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[s]);if(o>0)r=s+1;else{if(!(o<0))return s;i=s-1}}return-(r+1)}function u(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.length)return;var i=o(r,{row:t,column:-1},s.comparePoints);i<0&&(i=-i-1),i>=r.length?i=n>0?0:r.length-1:i===0&&n<0&&(i=r.length-1);var u=r[i];if(!u||!n)return;if(u.row===t){do u=r[i+=n];while(u&&u.row===t);if(!u)return r.slice()}var a=[];t=u.row;do a[n<0?"unshift":"push"](u),u=r[i+=n];while(u&&u.row==t);return a.length&&a}var r=e("../line_widgets").LineWidgets,i=e("../lib/dom"),s=e("../range").Range;t.showErrorMarker=function(e,t){var n=e.session;n.widgetManager||(n.widgetManager=new r(n),n.widgetManager.attach(e));var s=e.getCursorPosition(),o=s.row,a=n.widgetManager.getWidgetsAtRow(o).filter(function(e){return e.type=="errorMarker"})[0];a?a.destroy():o-=t;var f=u(n,o,t),l;if(f){var c=f[0];s.column=(c.pos&&typeof c.column!="number"?c.pos.sc:c.column)||0,s.row=c.row,l=e.renderer.$gutterLayer.$annotations[s.row]}else{if(a)return;l={text:["Looks good!"],className:"ace_ok"}}e.session.unfold(s.row),e.selection.moveToPosition(s);var h={row:s.row,fixedWidth:!0,coverGutter:!0,el:i.createElement("div"),type:"errorMarker"},p=h.el.appendChild(i.createElement("div")),d=h.el.appendChild(i.createElement("div"));d.className="error_widget_arrow "+l.className;var v=e.renderer.$cursorLayer.getPixelPosition(s).left;d.style.left=v+e.renderer.gutterWidth-5+"px",h.el.className="error_widget_wrapper",p.className="error_widget "+l.className,p.innerHTML=l.text.join("
"),p.appendChild(i.createElement("div"));var m=function(e,t,n){if(t===0&&(n==="esc"||n==="return"))return h.destroy(),{command:"null"}};h.destroy=function(){if(e.$mouseHandler.isMousePressed)return;e.keyBinding.removeKeyboardHandler(m),n.widgetManager.removeLineWidget(h),e.off("changeSelection",h.destroy),e.off("changeSession",h.destroy),e.off("mouseup",h.destroy),e.off("change",h.destroy)},e.keyBinding.addKeyboardHandler(m),e.on("changeSelection",h.destroy),e.on("changeSession",h.destroy),e.on("mouseup",h.destroy),e.on("change",h.destroy),e.session.widgetManager.addLineWidget(h),h.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:h.el.offsetHeight})},i.importCssString(" .error_widget_wrapper { background: inherit; color: inherit; border:none } .error_widget { border-top: solid 2px; border-bottom: solid 2px; margin: 5px 0; padding: 10px 40px; white-space: pre-wrap; } .error_widget.ace_error, .error_widget_arrow.ace_error{ border-color: #ff5a5a } .error_widget.ace_warning, .error_widget_arrow.ace_warning{ border-color: #F1D817 } .error_widget.ace_info, .error_widget_arrow.ace_info{ border-color: #5a5a5a } .error_widget.ace_ok, .error_widget_arrow.ace_ok{ border-color: #5aaa5a } .error_widget_arrow { position: absolute; border: solid 5px; border-top-color: transparent!important; border-right-color: transparent!important; border-left-color: transparent!important; top: -5px; }","")}),ace.define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"],function(e,t,n){"use strict";e("./lib/fixoldbrowsers");var r=e("./lib/dom"),i=e("./lib/event"),s=e("./editor").Editor,o=e("./edit_session").EditSession,u=e("./undomanager").UndoManager,a=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.require=e,t.edit=function(e){if(typeof e=="string"){var n=e;e=document.getElementById(n);if(!e)throw new Error("ace.edit can't find div #"+n)}if(e&&e.env&&e.env.editor instanceof s)return e.env.editor;var o="";if(e&&/input|textarea/i.test(e.tagName)){var u=e;o=u.value,e=r.createElement("pre"),u.parentNode.replaceChild(e,u)}else e&&(o=r.getInnerText(e),e.innerHTML="");var f=t.createEditSession(o),l=new s(new a(e));l.setSession(f);var c={document:f,editor:l,onResize:l.resize.bind(l,null)};return u&&(c.textarea=u),i.addListener(window,"resize",c.onResize),l.on("destroy",function(){i.removeListener(window,"resize",c.onResize),c.editor.container.env=null}),l.container.env=l.env=c,l},t.createEditSession=function(e,t){var n=new o(e,t);return n.setUndoManager(new u),n},t.EditSession=o,t.UndoManager=u,t.version="1.2.3"}); + (function() { + ace.require(["ace/ace"], function(a) { + a && a.config.init(true); + if (!window.ace) + window.ace = a; + for (var key in a) if (a.hasOwnProperty(key)) + window.ace[key] = a[key]; + }); + })(); diff --git a/src/Umbraco.Web.UI.Client/lib/ace/mode-css.js b/src/Umbraco.Web.UI.Client/lib/ace/mode-css.js new file mode 100644 index 0000000000..02e4cc380b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/ace/mode-css.js @@ -0,0 +1 @@ +ace.define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},h=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},p=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return h(a,l,"{","}");if(p.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(p.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(p.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var d=u.substring(s.column,s.column+1);if(d=="}"){var v=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(v!==null&&p.isAutoInsertedClosing(s,u,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var m="";p.isMaybeInsertedClosing(s,u)&&(m=o.stringRepeat("}",f.maybeInsertedBrackets),p.clearMaybeInsertedClosing());var d=u.substring(s.column,s.column+1);if(d==="}"){var g=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!g)return null;var y=this.$getIndent(r.getLine(g.row))}else{if(!m){p.clearMaybeInsertedClosing();return}var y=this.$getIndent(u)}var b=y+r.getTabString();return{text:"\n"+b+"\n"+y+m,selection:[1,b.length,1,b.length]}}p.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"(",")");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"[","]");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return h(o,u,s,s);if(!u){var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column),p=f.substring(a.column,a.column+1),d=r.getTokenAt(a.row,a.column),v=r.getTokenAt(a.row,a.column+1);if(l=="\\"&&d&&/escape/.test(d.type))return null;var m=d&&/string|escape/.test(d.type),g=!v||/string|escape/.test(v.type),y;if(p==s)y=m!==g;else{if(m&&!g)return null;if(m&&g)return null;var b=r.$mode.tokenRe;b.lastIndex=0;var w=b.test(l);b.lastIndex=0;var E=b.test(l);if(w||E)return null;if(p&&!/[\s;,.})\]\\]/.test(p))return null;y=!0}return{text:y?s+s:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};p.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},p.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},p.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},p.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},p.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},p.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},p.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},p.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(p,i),t.CstyleBehaviour=p}),ace.define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),ace.define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}) diff --git a/src/Umbraco.Web.UI.Client/lib/ace/mode-javascript.js b/src/Umbraco.Web.UI.Client/lib/ace/mode-javascript.js new file mode 100644 index 0000000000..20afa82e76 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/ace/mode-javascript.js @@ -0,0 +1 @@ +ace.define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),ace.define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?\:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||!e.noJSX)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},h=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},p=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return h(a,l,"{","}");if(p.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(p.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(p.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var d=u.substring(s.column,s.column+1);if(d=="}"){var v=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(v!==null&&p.isAutoInsertedClosing(s,u,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var m="";p.isMaybeInsertedClosing(s,u)&&(m=o.stringRepeat("}",f.maybeInsertedBrackets),p.clearMaybeInsertedClosing());var d=u.substring(s.column,s.column+1);if(d==="}"){var g=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!g)return null;var y=this.$getIndent(r.getLine(g.row))}else{if(!m){p.clearMaybeInsertedClosing();return}var y=this.$getIndent(u)}var b=y+r.getTabString();return{text:"\n"+b+"\n"+y+m,selection:[1,b.length,1,b.length]}}p.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"(",")");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"[","]");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return h(o,u,s,s);if(!u){var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column),p=f.substring(a.column,a.column+1),d=r.getTokenAt(a.row,a.column),v=r.getTokenAt(a.row,a.column+1);if(l=="\\"&&d&&/escape/.test(d.type))return null;var m=d&&/string|escape/.test(d.type),g=!v||/string|escape/.test(v.type),y;if(p==s)y=m!==g;else{if(m&&!g)return null;if(m&&g)return null;var b=r.$mode.tokenRe;b.lastIndex=0;var w=b.test(l);b.lastIndex=0;var E=b.test(l);if(w||E)return null;if(p&&!/[\s;,.})\]\\]/.test(p))return null;y=!0}return{text:y?s+s:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};p.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},p.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},p.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},p.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},p.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},p.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},p.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},p.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(p,i),t.CstyleBehaviour=p}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),ace.define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("../worker/worker_client").WorkerClient,f=e("./behaviour/cstyle").CstyleBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.foldingRules=new l};r.inherits(c,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*\:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new a(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(c.prototype),t.Mode=c}) diff --git a/src/Umbraco.Web.UI.Client/lib/ace/mode-razor.js b/src/Umbraco.Web.UI.Client/lib/ace/mode-razor.js new file mode 100644 index 0000000000..bb23872e84 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/ace/mode-razor.js @@ -0,0 +1 @@ +ace.define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),ace.define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?\:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||!e.noJSX)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},h=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},p=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return h(a,l,"{","}");if(p.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(p.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(p.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var d=u.substring(s.column,s.column+1);if(d=="}"){var v=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(v!==null&&p.isAutoInsertedClosing(s,u,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var m="";p.isMaybeInsertedClosing(s,u)&&(m=o.stringRepeat("}",f.maybeInsertedBrackets),p.clearMaybeInsertedClosing());var d=u.substring(s.column,s.column+1);if(d==="}"){var g=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!g)return null;var y=this.$getIndent(r.getLine(g.row))}else{if(!m){p.clearMaybeInsertedClosing();return}var y=this.$getIndent(u)}var b=y+r.getTabString();return{text:"\n"+b+"\n"+y+m,selection:[1,b.length,1,b.length]}}p.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"(",")");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"[","]");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return h(o,u,s,s);if(!u){var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column),p=f.substring(a.column,a.column+1),d=r.getTokenAt(a.row,a.column),v=r.getTokenAt(a.row,a.column+1);if(l=="\\"&&d&&/escape/.test(d.type))return null;var m=d&&/string|escape/.test(d.type),g=!v||/string|escape/.test(v.type),y;if(p==s)y=m!==g;else{if(m&&!g)return null;if(m&&g)return null;var b=r.$mode.tokenRe;b.lastIndex=0;var w=b.test(l);b.lastIndex=0;var E=b.test(l);if(w||E)return null;if(p&&!/[\s;,.})\]\\]/.test(p))return null;y=!0}return{text:y?s+s:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};p.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},p.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},p.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},p.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},p.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},p.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},p.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},p.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(p,i),t.CstyleBehaviour=p}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),ace.define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("../worker/worker_client").WorkerClient,f=e("./behaviour/cstyle").CstyleBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.foldingRules=new l};r.inherits(c,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*\:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new a(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(c.prototype),t.Mode=c}),ace.define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),ace.define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),ace.define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),ace.define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}),ace.define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),ace.define("ace/mode/html_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./css_highlight_rules").CssHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=i.createMap({a:"anchor",button:"form",form:"form",img:"image",input:"form",label:"form",option:"form",script:"script",select:"form",textarea:"form",style:"style",table:"table",tbody:"table",td:"table",tfoot:"table",th:"table",tr:"table"}),f=function(){u.call(this),this.addRules({attributes:[{include:"tag_whitespace"},{token:"entity.other.attribute-name.xml",regex:"[-_a-zA-Z0-9:.]+"},{token:"keyword.operator.attribute-equals.xml",regex:"=",push:[{include:"tag_whitespace"},{token:"string.unquoted.attribute-value.html",regex:"[^<>='\"`\\s]+",next:"pop"},{token:"empty",regex:"",next:"pop"}]},{include:"attribute_value"}],tag:[{token:function(e,t){var n=a[t];return["meta.tag.punctuation."+(e=="<"?"":"end-")+"tag-open.xml","meta.tag"+(n?"."+n:"")+".tag-name.xml"]},regex:"(",next:"start"}]}),this.embedTagRules(s,"css-","style"),this.embedTagRules((new o({noJSX:!0})).getRules(),"js-","script"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),ace.define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getCursorPosition(),a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name"))f=a.stepBackward();var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value==="-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column-1}function l(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"tag-name"))i=n.stepBackward();if(i)return i.value}function c(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"attribute-name"))i=n.stepBackward();if(i)return i.value}var r=e("../token_iterator").TokenIterator,i=["accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","inert","itemid","itemprop","itemref","itemscope","itemtype","lang","spellcheck","style","tabindex","title","translate"],s=["onabort","onblur","oncancel","oncanplay","oncanplaythrough","onchange","onclick","onclose","oncontextmenu","oncuechange","ondblclick","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onpause","onplay","onplaying","onprogress","onratechange","onreset","onscroll","onseeked","onseeking","onselect","onshow","onstalled","onsubmit","onsuspend","ontimeupdate","onvolumechange","onwaiting"],o=i.concat(s),u={html:{manifest:1},head:{},title:{},base:{href:1,target:1},link:{href:1,hreflang:1,rel:{stylesheet:1,icon:1},media:{all:1,screen:1,print:1},type:{"text/css":1,"image/png":1,"image/jpeg":1,"image/gif":1},sizes:1},meta:{"http-equiv":{"content-type":1},name:{description:1,keywords:1},content:{"text/html; charset=UTF-8":1},charset:1},style:{type:1,media:{all:1,screen:1,print:1},scoped:1},script:{charset:1,type:{"text/javascript":1},src:1,defer:1,async:1},noscript:{href:1},body:{onafterprint:1,onbeforeprint:1,onbeforeunload:1,onhashchange:1,onmessage:1,onoffline:1,onpopstate:1,onredo:1,onresize:1,onstorage:1,onundo:1,onunload:1},section:{},nav:{},article:{pubdate:1},aside:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},header:{},footer:{},address:{},main:{},p:{},hr:{},pre:{},blockquote:{cite:1},ol:{start:1,reversed:1},ul:{},li:{value:1},dl:{},dt:{},dd:{},figure:{},figcaption:{},div:{},a:{href:1,target:{_blank:1,top:1},ping:1,rel:{nofollow:1,alternate:1,author:1,bookmark:1,help:1,license:1,next:1,noreferrer:1,prefetch:1,prev:1,search:1,tag:1},media:1,hreflang:1,type:1},em:{},strong:{},small:{},s:{},cite:{},q:{cite:1},dfn:{},abbr:{},data:{},time:{datetime:1},code:{},"var":{},samp:{},kbd:{},sub:{},sup:{},i:{},b:{},u:{},mark:{},ruby:{},rt:{},rp:{},bdi:{},bdo:{},span:{},br:{},wbr:{},ins:{cite:1,datetime:1},del:{cite:1,datetime:1},img:{alt:1,src:1,height:1,width:1,usemap:1,ismap:1},iframe:{name:1,src:1,height:1,width:1,sandbox:{"allow-same-origin":1,"allow-top-navigation":1,"allow-forms":1,"allow-scripts":1},seamless:{seamless:1}},embed:{src:1,height:1,width:1,type:1},object:{param:1,data:1,type:1,height:1,width:1,usemap:1,name:1,form:1,classid:1},param:{name:1,value:1},video:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},width:1,height:1,poster:1,muted:{muted:1},preload:{auto:1,metadata:1,none:1}},audio:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},muted:{muted:1},preload:{auto:1,metadata:1,none:1}},source:{src:1,type:1,media:1},track:{kind:1,src:1,srclang:1,label:1,"default":1},canvas:{width:1,height:1},map:{name:1},area:{shape:1,coords:1,href:1,hreflang:1,alt:1,target:1,media:1,rel:1,ping:1,type:1},svg:{},math:{},table:{summary:1},caption:{},colgroup:{span:1},col:{span:1},tbody:{},thead:{},tfoot:{},tr:{},td:{headers:1,rowspan:1,colspan:1},th:{headers:1,rowspan:1,colspan:1,scope:1},form:{"accept-charset":1,action:1,autocomplete:1,enctype:{"multipart/form-data":1,"application/x-www-form-urlencoded":1},method:{get:1,post:1},name:1,novalidate:1,target:{_blank:1,top:1}},fieldset:{disabled:1,form:1,name:1},legend:{},label:{form:1,"for":1},input:{type:{text:1,password:1,hidden:1,checkbox:1,submit:1,radio:1,file:1,button:1,reset:1,image:31,color:1,date:1,datetime:1,"datetime-local":1,email:1,month:1,number:1,range:1,search:1,tel:1,time:1,url:1,week:1},accept:1,alt:1,autocomplete:{on:1,off:1},autofocus:{autofocus:1},checked:{checked:1},disabled:{disabled:1},form:1,formaction:1,formenctype:{"application/x-www-form-urlencoded":1,"multipart/form-data":1,"text/plain":1},formmethod:{get:1,post:1},formnovalidate:{formnovalidate:1},formtarget:{_blank:1,_self:1,_parent:1,_top:1},height:1,list:1,max:1,maxlength:1,min:1,multiple:{multiple:1},name:1,pattern:1,placeholder:1,readonly:{readonly:1},required:{required:1},size:1,src:1,step:1,width:1,files:1,value:1},button:{autofocus:1,disabled:{disabled:1},form:1,formaction:1,formenctype:1,formmethod:1,formnovalidate:1,formtarget:1,name:1,value:1,type:{button:1,submit:1}},select:{autofocus:1,disabled:1,form:1,multiple:{multiple:1},name:1,size:1,readonly:{readonly:1}},datalist:{},optgroup:{disabled:1,label:1},option:{disabled:1,selected:1,label:1,value:1},textarea:{autofocus:{autofocus:1},disabled:{disabled:1},form:1,maxlength:1,name:1,placeholder:1,readonly:{readonly:1},required:{required:1},rows:1,cols:1,wrap:{on:1,off:1,hard:1,soft:1}},keygen:{autofocus:1,challenge:{challenge:1},disabled:{disabled:1},form:1,keytype:{rsa:1,dsa:1,ec:1},name:1},output:{"for":1,form:1,name:1},progress:{value:1,max:1},meter:{value:1,min:1,max:1,low:1,high:1,optimum:1},details:{open:1},summary:{},command:{type:1,label:1,icon:1,disabled:1,checked:1,radiogroup:1,command:1},menu:{type:1,label:1},dialog:{open:1}},a=Object.keys(u),h=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(f(i,"tag-name")||f(i,"tag-open")||f(i,"end-tag-open"))return this.getTagCompletions(e,t,n,r);if(f(i,"tag-whitespace")||f(i,"attribute-name"))return this.getAttributeCompletions(e,t,n,r);if(f(i,"attribute-value"))return this.getAttributeValueCompletions(e,t,n,r);var s=t.getLine(n.row).substr(0,n.column);return/&[A-z]*$/i.test(s)?this.getHTMLEntityCompletions(e,t,n,r):[]},this.getTagCompletions=function(e,t,n,r){return a.map(function(e){return{value:e,meta:"tag",score:Number.MAX_VALUE}})},this.getAttributeCompletions=function(e,t,n,r){var i=l(t,n);if(!i)return[];var s=o;return i in u&&(s=s.concat(Object.keys(u[i]))),s.map(function(e){return{caption:e,snippet:e+'="$0"',meta:"attribute",score:Number.MAX_VALUE}})},this.getAttributeValueCompletions=function(e,t,n,r){var i=l(t,n),s=c(t,n);if(!i)return[];var o=[];return i in u&&s in u[i]&&typeof u[i][s]=="object"&&(o=Object.keys(u[i][s])),o.map(function(e){return{caption:e,snippet:e,meta:"attribute value",score:Number.MAX_VALUE}})},this.getHTMLEntityCompletions=function(e,t,n,r){var i=["Aacute;","aacute;","Acirc;","acirc;","acute;","AElig;","aelig;","Agrave;","agrave;","alefsym;","Alpha;","alpha;","amp;","and;","ang;","Aring;","aring;","asymp;","Atilde;","atilde;","Auml;","auml;","bdquo;","Beta;","beta;","brvbar;","bull;","cap;","Ccedil;","ccedil;","cedil;","cent;","Chi;","chi;","circ;","clubs;","cong;","copy;","crarr;","cup;","curren;","Dagger;","dagger;","dArr;","darr;","deg;","Delta;","delta;","diams;","divide;","Eacute;","eacute;","Ecirc;","ecirc;","Egrave;","egrave;","empty;","emsp;","ensp;","Epsilon;","epsilon;","equiv;","Eta;","eta;","ETH;","eth;","Euml;","euml;","euro;","exist;","fnof;","forall;","frac12;","frac14;","frac34;","frasl;","Gamma;","gamma;","ge;","gt;","hArr;","harr;","hearts;","hellip;","Iacute;","iacute;","Icirc;","icirc;","iexcl;","Igrave;","igrave;","image;","infin;","int;","Iota;","iota;","iquest;","isin;","Iuml;","iuml;","Kappa;","kappa;","Lambda;","lambda;","lang;","laquo;","lArr;","larr;","lceil;","ldquo;","le;","lfloor;","lowast;","loz;","lrm;","lsaquo;","lsquo;","lt;","macr;","mdash;","micro;","middot;","minus;","Mu;","mu;","nabla;","nbsp;","ndash;","ne;","ni;","not;","notin;","nsub;","Ntilde;","ntilde;","Nu;","nu;","Oacute;","oacute;","Ocirc;","ocirc;","OElig;","oelig;","Ograve;","ograve;","oline;","Omega;","omega;","Omicron;","omicron;","oplus;","or;","ordf;","ordm;","Oslash;","oslash;","Otilde;","otilde;","otimes;","Ouml;","ouml;","para;","part;","permil;","perp;","Phi;","phi;","Pi;","pi;","piv;","plusmn;","pound;","Prime;","prime;","prod;","prop;","Psi;","psi;","quot;","radic;","rang;","raquo;","rArr;","rarr;","rceil;","rdquo;","real;","reg;","rfloor;","Rho;","rho;","rlm;","rsaquo;","rsquo;","sbquo;","Scaron;","scaron;","sdot;","sect;","shy;","Sigma;","sigma;","sigmaf;","sim;","spades;","sub;","sube;","sum;","sup;","sup1;","sup2;","sup3;","supe;","szlig;","Tau;","tau;","there4;","Theta;","theta;","thetasym;","thinsp;","THORN;","thorn;","tilde;","times;","trade;","Uacute;","uacute;","uArr;","uarr;","Ucirc;","ucirc;","Ugrave;","ugrave;","uml;","upsih;","Upsilon;","upsilon;","Uuml;","uuml;","weierp;","Xi;","xi;","Yacute;","yacute;","yen;","Yuml;","yuml;","Zeta;","zeta;","zwj;","zwnj;"];return i.map(function(e){return{caption:e,snippet:e,meta:"html entity",score:Number.MAX_VALUE}})}}).call(h.prototype),t.HtmlCompletions=h}),ace.define("ace/mode/html",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text","ace/mode/javascript","ace/mode/css","ace/mode/html_highlight_rules","ace/mode/behaviour/xml","ace/mode/folding/html","ace/mode/html_completions","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text").Mode,o=e("./javascript").Mode,u=e("./css").Mode,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./behaviour/xml").XmlBehaviour,l=e("./folding/html").FoldMode,c=e("./html_completions").HtmlCompletions,h=e("../worker/worker_client").WorkerClient,p=["area","base","br","col","embed","hr","img","input","keygen","link","meta","menuitem","param","source","track","wbr"],d=["li","dt","dd","p","rt","rp","optgroup","option","colgroup","td","th"],v=function(e){this.fragmentContext=e&&e.fragmentContext,this.HighlightRules=a,this.$behaviour=new f,this.$completer=new c,this.createModeDelegates({"js-":o,"css-":u}),this.foldingRules=new l(this.voidElements,i.arrayToMap(d))};r.inherits(v,s),function(){this.blockComment={start:""},this.voidElements=i.arrayToMap(p),this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){if(this.constructor!=v)return;var t=new h(["ace"],"ace/mode/html_worker","Worker");return t.attachToDocument(e.getDocument()),this.fragmentContext&&t.call("setOptions",[{context:this.fragmentContext}]),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/html"}.call(v.prototype),t.Mode=v}),ace.define("ace/mode/csharp_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o=function(){var e=this.createKeywordMapper({"variable.language":"this",keyword:"abstract|event|new|struct|as|explicit|null|switch|base|extern|object|this|bool|false|operator|throw|break|finally|out|true|byte|fixed|override|try|case|float|params|typeof|catch|for|private|uint|char|foreach|protected|ulong|checked|goto|public|unchecked|class|if|readonly|unsafe|const|implicit|ref|ushort|continue|in|return|using|decimal|int|sbyte|virtual|default|interface|sealed|volatile|delegate|internal|short|void|do|is|sizeof|while|double|lock|stackalloc|else|long|static|enum|namespace|string|var|dynamic","constant.language":"null|true|false"},"identifier");this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},i.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string",regex:/'(?:.|\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n]))'/},{token:"string",start:'"',end:'"|$',next:[{token:"constant.language.escape",regex:/\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n])/},{token:"invalid",regex:/\\./}]},{token:"string",start:'@"',end:'"',next:[{token:"constant.language.escape",regex:'""'}]},{token:"string",start:/\$"/,end:'"|$',next:[{token:"constant.language.escape",regex:/\\(:?$)|{{/},{token:"constant.language.escape",regex:/\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n])/},{token:"invalid",regex:/\\./}]},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:e,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"keyword",regex:"^\\s*#(if|else|elif|endif|define|undef|warning|error|line|region|endregion|pragma)"},{token:"punctuation.operator",regex:"\\?|\\:|\\,|\\;|\\."},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}]},this.embedRules(i,"doc-",[i.getEndRule("start")]),this.normalizeRules()};r.inherits(o,s),t.CSharpHighlightRules=o}),ace.define("ace/mode/razor_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/doc_comment_highlight_rules","ace/mode/html_highlight_rules","ace/mode/csharp_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./doc_comment_highlight_rules").DocCommentHighlightRules,o=e("./html_highlight_rules").HtmlHighlightRules,u=e("./csharp_highlight_rules").CSharpHighlightRules,a="razor-block-",f=function(){u.call(this);var e=function(e,t){return typeof t=="function"?t(e):t},t="in-braces";this.$rules.start.unshift({regex:"[\\[({]",onMatch:function(e,n,r){var i=/razor-[^\-]+-/.exec(n)[0];return r.unshift(e),r.unshift(i+t),this.next=i+t,"paren.lparen"}});var n={"{":"}","[":"]","(":")"};this.$rules[t]=i.deepCopy(this.$rules.start),this.$rules[t].unshift({regex:"[\\])}]",onMatch:function(t,r,i){var s=i[1];return n[s]!==t?"invalid.illegal":(i.shift(),i.shift(),this.next=e(t,i[0])||"start","paren.rparen")}})};r.inherits(f,u);var l=function(){o.call(this);var e={regex:"@[({]|@functions{",onMatch:function(e,t,n){return n.unshift(e),n.unshift("razor-block-start"),this.next="razor-block-start","punctuation.block.razor"}},t={"@{":"}","@(":")","@functions{":"}"},n={regex:"[})]",onMatch:function(e,n,r){var i=r[1];return t[i]!==e?"invalid.illegal":(r.shift(),r.shift(),this.next=r.shift()||"start","punctuation.block.razor")}},r={regex:"@(?![{(])",onMatch:function(e,t,n){return n.unshift("razor-short-start"),this.next="razor-short-start","punctuation.short.razor"}},i={token:"",regex:"(?=[^A-Za-z_\\.()\\[\\]])",next:"pop"},s={regex:"@(?=if)",onMatch:function(e,t,n){return n.unshift(function(e){return e!=="}"?"start":n.shift()||"start"}),this.next="razor-block-start","punctuation.control.razor"}},u=[{token:["meta.directive.razor","text","identifier"],regex:"^(\\s*@model)(\\s+)(.+)$"},e,r];for(var a in this.$rules)this.$rules[a].unshift.apply(this.$rules[a],u);this.embedRules(f,"razor-block-",[n],["start"]),this.embedRules(f,"razor-short-",[i],["start"]),this.normalizeRules()};r.inherits(l,o),t.RazorHighlightRules=l,t.RazorLangHighlightRules=f}),ace.define("ace/mode/razor_completions",["require","exports","module","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../token_iterator").TokenIterator,i=["abstract","as","base","bool","break","byte","case","catch","char","checked","class","const","continue","decimal","default","delegate","do","double","else","enum","event","explicit","extern","false","finally","fixed","float","for","foreach","goto","if","implicit","in","int","interface","internal","is","lock","long","namespace","new","null","object","operator","out","override","params","private","protected","public","readonly","ref","return","sbyte","sealed","short","sizeof","stackalloc","static","string","struct","switch","this","throw","true","try","typeof","uint","ulong","unchecked","unsafe","ushort","using","var","virtual","void","volatile","while"],s=["Html","Model","Url","Layout"],o=function(){};(function(){this.getCompletions=function(e,t,n,r){if(e.lastIndexOf("razor-short-start")==-1&&e.lastIndexOf("razor-block-start")==-1)return[];var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e.lastIndexOf("razor-short-start")!=-1)return this.getShortStartCompletions(e,t,n,r);if(e.lastIndexOf("razor-block-start")!=-1)return this.getKeywordCompletions(e,t,n,r)},this.getShortStartCompletions=function(e,t,n,r){return s.map(function(e){return{value:e,meta:"keyword",score:Number.MAX_VALUE}})},this.getKeywordCompletions=function(e,t,n,r){return s.concat(i).map(function(e){return{value:e,meta:"keyword",score:Number.MAX_VALUE}})}}).call(o.prototype),t.RazorCompletions=o}),ace.define("ace/mode/razor",["require","exports","module","ace/lib/oop","ace/mode/html","ace/mode/razor_highlight_rules","ace/mode/razor_completions","ace/mode/html_completions"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./html").Mode,s=e("./razor_highlight_rules").RazorHighlightRules,o=e("./razor_completions").RazorCompletions,u=e("./html_completions").HtmlCompletions,a=function(){i.call(this),this.$highlightRules=new s,this.$completer=new o,this.$htmlCompleter=new u};r.inherits(a,i),function(){this.getCompletions=function(e,t,n,r){var i=this.$completer.getCompletions(e,t,n,r),s=this.$htmlCompleter.getCompletions(e,t,n,r);return i.concat(s)},this.createWorker=function(e){return null},this.$id="ace/mode/razor"}.call(a.prototype),t.Mode=a}) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js new file mode 100644 index 0000000000..331354491a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js @@ -0,0 +1,341 @@ +(function() { + 'use strict'; + + function AceEditorDirective(umbAceEditorConfig) { + + if (angular.isUndefined(window.ace)) { + throw new Error('ui-ace need ace to work... (o rly?)'); + } + + /** + * Sets editor options such as the wrapping mode or the syntax checker. + * + * The supported options are: + * + *
    + *
  • showGutter
  • + *
  • useWrapMode
  • + *
  • onLoad
  • + *
  • theme
  • + *
  • mode
  • + *
+ * + * @param acee + * @param session ACE editor session + * @param {object} opts Options to be set + */ + var setOptions = function(acee, session, opts) { + + // sets the ace worker path, if running from concatenated + // or minified source + if (angular.isDefined(opts.workerPath)) { + var config = window.ace.require('ace/config'); + config.set('workerPath', opts.workerPath); + } + // ace requires loading + if (angular.isDefined(opts.require)) { + opts.require.forEach(function(n) { + window.ace.require(n); + }); + } + // Boolean options + if (angular.isDefined(opts.showGutter)) { + acee.renderer.setShowGutter(opts.showGutter); + } + if (angular.isDefined(opts.useWrapMode)) { + session.setUseWrapMode(opts.useWrapMode); + } + if (angular.isDefined(opts.showInvisibles)) { + acee.renderer.setShowInvisibles(opts.showInvisibles); + } + if (angular.isDefined(opts.showIndentGuides)) { + acee.renderer.setDisplayIndentGuides(opts.showIndentGuides); + } + if (angular.isDefined(opts.useSoftTabs)) { + session.setUseSoftTabs(opts.useSoftTabs); + } + if (angular.isDefined(opts.showPrintMargin)) { + acee.setShowPrintMargin(opts.showPrintMargin); + } + + // commands + if (angular.isDefined(opts.disableSearch) && opts.disableSearch) { + acee.commands.addCommands([{ + name: 'unfind', + bindKey: { + win: 'Ctrl-F', + mac: 'Command-F' + }, + exec: function() { + return false; + }, + readOnly: true + }]); + } + + // Basic options + if (angular.isString(opts.theme)) { + acee.setTheme('ace/theme/' + opts.theme); + } + if (angular.isString(opts.mode)) { + session.setMode('ace/mode/' + opts.mode); + } + // Advanced options + if (angular.isDefined(opts.firstLineNumber)) { + if (angular.isNumber(opts.firstLineNumber)) { + session.setOption('firstLineNumber', opts.firstLineNumber); + } else if (angular.isFunction(opts.firstLineNumber)) { + session.setOption('firstLineNumber', opts.firstLineNumber()); + } + } + + // advanced options + var key, obj; + if (angular.isDefined(opts.advanced)) { + for (key in opts.advanced) { + // create a javascript object with the key and value + obj = { + name: key, + value: opts.advanced[key] + }; + // try to assign the option to the ace editor + acee.setOption(obj.name, obj.value); + } + } + + // advanced options for the renderer + if (angular.isDefined(opts.rendererOptions)) { + for (key in opts.rendererOptions) { + // create a javascript object with the key and value + obj = { + name: key, + value: opts.rendererOptions[key] + }; + // try to assign the option to the ace editor + acee.renderer.setOption(obj.name, obj.value); + } + } + + // onLoad callbacks + angular.forEach(opts.callbacks, function(cb) { + if (angular.isFunction(cb)) { + cb(acee); + } + }); + }; + + function link(scope, el, attr, ngModel) { + + /** + * Corresponds the umbAceEditorConfig ACE configuration. + * @type object + */ + var options = umbAceEditorConfig.ace || {}; + + /** + * umbAceEditorConfig merged with user options via json in attribute or data binding + * @type object + */ + var opts = angular.extend({}, options, scope.$eval(attr.umbAceEditor)); + + /** + * ACE editor + * @type object + */ + var acee = window.ace.edit(el[0]); + + /** + * ACE editor session. + * @type object + * @see [EditSession]{@link http://ace.c9.io/#nav=api&api=edit_session} + */ + var session = acee.getSession(); + + /** + * Reference to a change listener created by the listener factory. + * @function + * @see listenerFactory.onChange + */ + var onChangeListener; + + /** + * Reference to a blur listener created by the listener factory. + * @function + * @see listenerFactory.onBlur + */ + var onBlurListener; + + /** + * Calls a callback by checking its existing. The argument list + * is variable and thus this function is relying on the arguments + * object. + * @throws {Error} If the callback isn't a function + */ + var executeUserCallback = function() { + + /** + * The callback function grabbed from the array-like arguments + * object. The first argument should always be the callback. + * + * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments} + * @type {*} + */ + var callback = arguments[0]; + + /** + * Arguments to be passed to the callback. These are taken + * from the array-like arguments object. The first argument + * is stripped because that should be the callback function. + * + * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments} + * @type {Array} + */ + var args = Array.prototype.slice.call(arguments, 1); + + if (angular.isDefined(callback)) { + scope.$evalAsync(function() { + if (angular.isFunction(callback)) { + callback(args); + } else { + throw new Error('ui-ace use a function as callback.'); + } + }); + } + }; + + + + /** + * Listener factory. Until now only change listeners can be created. + * @type object + */ + var listenerFactory = { + /** + * Creates a change listener which propagates the change event + * and the editor session to the callback from the user option + * onChange. It might be exchanged during runtime, if this + * happens the old listener will be unbound. + * + * @param callback callback function defined in the user options + * @see onChangeListener + */ + onChange: function(callback) { + return function(e) { + var newValue = session.getValue(); + + if (ngModel && newValue !== ngModel.$viewValue && + // HACK make sure to only trigger the apply outside of the + // digest loop 'cause ACE is actually using this callback + // for any text transformation ! + !scope.$$phase && !scope.$root.$$phase) { + scope.$evalAsync(function() { + ngModel.$setViewValue(newValue); + }); + } + + executeUserCallback(callback, e, acee); + }; + }, + /** + * Creates a blur listener which propagates the editor session + * to the callback from the user option onBlur. It might be + * exchanged during runtime, if this happens the old listener + * will be unbound. + * + * @param callback callback function defined in the user options + * @see onBlurListener + */ + onBlur: function(callback) { + return function() { + executeUserCallback(callback, acee); + }; + } + }; + + attr.$observe('readonly', function(value) { + acee.setReadOnly(!!value || value === ''); + }); + + // Value Blind + if (ngModel) { + ngModel.$formatters.push(function(value) { + if (angular.isUndefined(value) || value === null) { + return ''; + } else if (angular.isObject(value) || angular.isArray(value)) { + throw new Error('ui-ace cannot use an object or an array as a model'); + } + return value; + }); + + ngModel.$render = function() { + session.setValue(ngModel.$viewValue); + }; + } + + // Listen for option updates + var updateOptions = function(current, previous) { + if (current === previous) { + return; + } + opts = angular.extend({}, options, scope.$eval(attr.umbAceEditor)); + + opts.callbacks = [opts.onLoad]; + if (opts.onLoad !== options.onLoad) { + // also call the global onLoad handler + opts.callbacks.unshift(options.onLoad); + } + + // EVENTS + + // unbind old change listener + session.removeListener('change', onChangeListener); + + // bind new change listener + onChangeListener = listenerFactory.onChange(opts.onChange); + session.on('change', onChangeListener); + + // unbind old blur listener + //session.removeListener('blur', onBlurListener); + acee.removeListener('blur', onBlurListener); + + // bind new blur listener + onBlurListener = listenerFactory.onBlur(opts.onBlur); + acee.on('blur', onBlurListener); + + setOptions(acee, session, opts); + }; + + scope.$watch(attr.umbAceEditor, updateOptions, /* deep watch */ true); + + // set the options here, even if we try to watch later, if this + // line is missing things go wrong (and the tests will also fail) + updateOptions(options); + + el.on('$destroy', function() { + acee.session.$stopWorker(); + acee.destroy(); + }); + + scope.$watch(function() { + return [el[0].offsetWidth, el[0].offsetHeight]; + }, function() { + acee.resize(); + acee.renderer.updateFull(); + }, true); + + } + + var directive = { + restrict: 'EA', + require: '?ngModel', + link: link + }; + + return directive; + } + + angular.module('umbraco.directives') + .constant('umbAceEditorConfig', {}) + .directive('umbAceEditor', AceEditorDirective); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js new file mode 100644 index 0000000000..b44ce8f20c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -0,0 +1,92 @@ +(function () { + "use strict"; + + function TemplatesEditController($scope) { + + var vm = this; + + vm.page = {}; + vm.page.loading = false; + + vm.mode = "css"; + + vm.aceOption = { + mode: vm.mode.toLowerCase(), + onLoad: function(ace) { + + console.log(ace); + + // HACK to have the ace instance in the scope... + /* + $scope.modeChanged = function() { + _ace.getSession().setMode("ace/mode/" + $scope.mode.toLowerCase()); + }; + */ + + } + }; + + vm.aceModel = ';; Scheme code in here.\n' + + '(define (double x)\n\t(* x x))\n\n\n' + + '\n' + + '\n\t\n\t\n\t\n\n\n\n' + + '// Javascript code in here.\n' + + 'function foo(msg) {\n\tvar r = Math.random();\n\treturn "" + r + " : " + msg;\n}'; + + vm.openPageFieldOverlay = openPageFieldOverlay; + vm.openDictionaryItemOverlay = openDictionaryItemOverlay; + vm.openQueryBuilderOverlay = openQueryBuilderOverlay; + + function init() { + + + } + + function openPageFieldOverlay() { + vm.pageFieldOverlay = { + view: "mediapicker", + show: true, + submit: function(model) { + + }, + close: function(model) { + vm.pageFieldOverlay.show = false; + vm.pageFieldOverlay = null; + } + }; + } + + function openDictionaryItemOverlay() { + vm.dictionaryItemOverlay = { + view: "mediapicker", + show: true, + submit: function(model) { + + }, + close: function(model) { + vm.dictionaryItemOverlay.show = false; + vm.dictionaryItemOverlay = null; + } + }; + } + + function openQueryBuilderOverlay() { + vm.queryBuilderOverlay = { + view: "mediapicker", + show: true, + submit: function(model) { + + }, + close: function(model) { + vm.queryBuilderOverlay.show = false; + vm.queryBuilderOverlay = null; + } + }; + } + + init(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.Templates.EditController", TemplatesEditController); +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html new file mode 100644 index 0000000000..c6244a8035 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -0,0 +1,94 @@ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ + + + + + + + + + + + +
+ +
+ + + + + + + + + + + +
diff --git a/src/Umbraco.Web/UI/JavaScript/JsInitialize.js b/src/Umbraco.Web/UI/JavaScript/JsInitialize.js index 0314c65fae..afd12f27a9 100644 --- a/src/Umbraco.Web/UI/JavaScript/JsInitialize.js +++ b/src/Umbraco.Web/UI/JavaScript/JsInitialize.js @@ -16,6 +16,11 @@ 'lib/ng-file-upload/ng-file-upload.min.js', 'lib/angular-local-storage/angular-local-storage.min.js', + 'lib/ace/ace-src-min-noconflict.js', + 'lib/ace/mode-javascript.js', + 'lib/ace/mode-razor.js', + 'lib/ace/mode-css.js', + 'lib/bootstrap/js/bootstrap.2.3.2.min.js', 'lib/bootstrap-tabdrop/bootstrap-tabdrop.js', 'lib/umbraco/Extensions.js', From ebd2bf149c2544ee3b1f4ffb8761fb2a237efb24 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Sun, 12 Jun 2016 18:48:46 +0200 Subject: [PATCH 002/599] Basic editor + macro dialog implemented --- src/Umbraco.Web.UI.Client/bower.json | 3 +- .../lib/ace-razor-mode/theme/razor_chrome.css | 161 ++++++++++++++++++ .../lib/ace/ace-src-min-noconflict.js | 10 -- src/Umbraco.Web.UI.Client/lib/ace/mode-css.js | 1 - .../lib/ace/mode-javascript.js | 1 - .../lib/ace/mode-razor.js | 1 - .../components/editor/umb-editor-header.html | 2 +- .../src/views/templates/edit.controller.js | 103 ++++++++--- .../src/views/templates/edit.html | 80 ++++----- src/Umbraco.Web/UI/JavaScript/JsInitialize.js | 6 +- src/Umbraco.Web/Umbraco.Web.csproj | 3 + 11 files changed, 286 insertions(+), 85 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/lib/ace-razor-mode/theme/razor_chrome.css delete mode 100644 src/Umbraco.Web.UI.Client/lib/ace/ace-src-min-noconflict.js delete mode 100644 src/Umbraco.Web.UI.Client/lib/ace/mode-css.js delete mode 100644 src/Umbraco.Web.UI.Client/lib/ace/mode-javascript.js delete mode 100644 src/Umbraco.Web.UI.Client/lib/ace/mode-razor.js diff --git a/src/Umbraco.Web.UI.Client/bower.json b/src/Umbraco.Web.UI.Client/bower.json index d1397b5d4e..dbd1b36d65 100644 --- a/src/Umbraco.Web.UI.Client/bower.json +++ b/src/Umbraco.Web.UI.Client/bower.json @@ -27,6 +27,7 @@ "tinymce": "~4.1.10", "codemirror": "~5.3.0", "angular-local-storage": "~0.2.3", - "moment": "~2.10.3" + "moment": "~2.10.3", + "ace-builds": "^1.2.3" } } diff --git a/src/Umbraco.Web.UI.Client/lib/ace-razor-mode/theme/razor_chrome.css b/src/Umbraco.Web.UI.Client/lib/ace-razor-mode/theme/razor_chrome.css new file mode 100644 index 0000000000..7f401520d5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/lib/ace-razor-mode/theme/razor_chrome.css @@ -0,0 +1,161 @@ +.ace-chrome .ace_gutter { + background: white !important; + color: #ccc !important; + overflow : hidden; +} + +.ace-chrome .ace_print-margin { + +} + +.ace-chrome { + background-color: #FFFFFF; + color: black; +} + +.ace-chrome .ace_cursor { + color: black; +} + +.ace-chrome .ace_invisible { + color: rgb(191, 191, 191); +} + +.ace-chrome .ace_constant.ace_buildin { + color: rgb(88, 72, 246); +} + +.ace-chrome .ace_constant.ace_language { + color: rgb(88, 92, 246); +} + +.ace-chrome .ace_constant.ace_library { + color: rgb(6, 150, 14); +} + +.ace-chrome .ace_invalid { + background-color: rgb(153, 0, 0); + color: white; +} + +.ace_punctuation.ace_short.ace_razor{ + background: yellow; +} + +.ace-chrome .ace_fold { +} + +.ace-chrome .ace_support.ace_function { + color: rgb(60, 76, 114); +} + +.ace-chrome .ace_support.ace_constant { + color: rgb(6, 150, 14); +} + +.ace-chrome .ace_support.ace_type, +.ace-chrome .ace_support.ace_class +.ace-chrome .ace_support.ace_other { + color: rgb(109, 121, 222); +} + +.ace-chrome .ace_variable.ace_parameter { + font-style:italic; + color:#FD971F; +} +.ace-chrome .ace_keyword.ace_operator { + color: rgb(104, 118, 135); +} + +.ace-chrome .ace_comment { + color: #236e24; +} + +.ace-chrome .ace_comment.ace_doc { + color: #236e24; +} + +.ace-chrome .ace_comment.ace_doc.ace_tag { + color: #236e24; +} + +.ace-chrome .ace_constant.ace_numeric { + color: rgb(0, 0, 205); +} + +.ace-chrome .ace_variable { + color: rgb(49, 132, 149); +} + +.ace-chrome .ace_xml-pe { + color: rgb(104, 104, 91); +} + +.ace-chrome .ace_entity.ace_name.ace_function { + color: #0000A2; +} + + +.ace-chrome .ace_heading { + color: rgb(12, 7, 255); +} + +.ace-chrome .ace_list { + color:rgb(185, 6, 144); +} + +.ace-chrome .ace_marker-layer .ace_selection { + background: rgb(181, 213, 255); +} + +.ace-chrome .ace_marker-layer .ace_step { + background: rgb(252, 255, 0); +} + +.ace-chrome .ace_marker-layer .ace_stack { + background: rgb(164, 229, 101); +} + +.ace-chrome .ace_marker-layer .ace_bracket { + margin: -1px 0 0 -1px; + border: 1px solid rgb(192, 192, 192); +} + +.ace-chrome .ace_marker-layer .ace_active-line { + background: rgba(0, 0, 0, 0.07) !important; +} + +.ace-chrome .ace_gutter-active-line { + background: rgba(0, 0, 0, 0.07) !important; +} + +.ace-chrome .ace_marker-layer .ace_selected-word { + background: rgb(250, 250, 255); + border: 1px solid rgb(200, 200, 250); +} + +.ace-chrome .ace_storage, +.ace-chrome .ace_keyword, +.ace-chrome .ace_meta.ace_tag { + color: rgb(147, 15, 128); +} + +.ace-chrome .ace_string.ace_regex { + color: rgb(255, 0, 0) +} + +.ace-chrome .ace_string { + color: #1A1AA6; +} + +.ace-chrome .ace_entity.ace_other.ace_attribute-name { + color: #994409; +} + +.ace-chrome .ace_indent-guide { + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y; +} + +.ace-chrome .ace_razor { + background: yellow; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/ace/ace-src-min-noconflict.js b/src/Umbraco.Web.UI.Client/lib/ace/ace-src-min-noconflict.js deleted file mode 100644 index b338717bfd..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/ace/ace-src-min-noconflict.js +++ /dev/null @@ -1,10 +0,0 @@ -(function(){function o(n){var i=e;n&&(e[n]||(e[n]={}),i=e[n]);if(!i.define||!i.define.packaged)t.original=i.define,i.define=t,i.define.packaged=!0;if(!i.require||!i.require.packaged)r.original=i.require,i.require=r,i.require.packaged=!0}var ACE_NAMESPACE = "ace",e=function(){return this}();!e&&typeof window!="undefined"&&(e=window);if(!ACE_NAMESPACE&&typeof requirejs!="undefined")return;var t=function(e,n,r){if(typeof e!="string"){t.original?t.original.apply(this,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=n),t.modules[e]||(t.payloads[e]=r,t.modules[e]=null)};t.modules={},t.payloads={};var n=function(e,t,n){if(typeof t=="string"){var i=s(e,t);if(i!=undefined)return n&&n(),i}else if(Object.prototype.toString.call(t)==="[object Array]"){var o=[];for(var u=0,a=t.length;u1&&u(t,"")>-1&&(a=RegExp(this.source,r.replace.call(o(this),"g","")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;et.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),ace.define("ace/lib/es5-shim",["require","exports","module"],function(e,t,n){function r(){}function w(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}function H(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}function B(e){var t=typeof e;return e===null||t==="undefined"||t==="boolean"||t==="number"||t==="string"}function j(e){var t,n,r;if(B(e))return e;n=e.valueOf;if(typeof n=="function"){t=n.call(e);if(B(t))return t}r=e.toString;if(typeof r=="function"){t=r.call(e);if(B(t))return t}throw new TypeError}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError("Function.prototype.bind called on incompatible "+n);var i=u.call(arguments,1),s=function(){if(this instanceof s){var e=n.apply(this,i.concat(u.call(arguments)));return Object(e)===e?e:this}return n.apply(t,i.concat(u.call(arguments)))};return n.prototype&&(r.prototype=n.prototype,s.prototype=new r,r.prototype=null),s});var i=Function.prototype.call,s=Array.prototype,o=Object.prototype,u=s.slice,a=i.bind(o.toString),f=i.bind(o.hasOwnProperty),l,c,h,p,d;if(d=f(o,"__defineGetter__"))l=i.bind(o.__defineGetter__),c=i.bind(o.__defineSetter__),h=i.bind(o.__lookupGetter__),p=i.bind(o.__lookupSetter__);if([1,2].splice(0).length!=2)if(!function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t=[],n;t.splice.apply(t,e(20)),t.splice.apply(t,e(26)),n=t.length,t.splice(5,0,"XXX"),n+1==t.length;if(n+1==t.length)return!0}())Array.prototype.splice=function(e,t){var n=this.length;e>0?e>n&&(e=n):e==void 0?e=0:e<0&&(e=Math.max(n+e,0)),e+ta)for(h=l;h--;)this[f+h]=this[a+h];if(s&&e===c)this.length=c,this.push.apply(this,i);else{this.length=c+s;for(h=0;h>>0;if(a(t)!="[object Function]")throw new TypeError;while(++s>>0,s=Array(i),o=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var u=0;u>>0,s=[],o,u=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var f=0;f>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var s=0,o;if(arguments.length>=2)o=arguments[1];else do{if(s in r){o=r[s++];break}if(++s>=i)throw new TypeError("reduce of empty array with no initial value")}while(!0);for(;s>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var s,o=i-1;if(arguments.length>=2)s=arguments[1];else do{if(o in r){s=r[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}while(!0);do o in this&&(s=t.call(void 0,s,r[o],o,n));while(o--);return s});if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1)Array.prototype.indexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=H(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,H(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1};Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:o)});if(!Object.getOwnPropertyDescriptor){var y="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(y+t);if(!f(t,n))return;var r,i,s;r={enumerable:!0,configurable:!0};if(d){var u=t.__proto__;t.__proto__=o;var i=h(t,n),s=p(t,n);t.__proto__=u;if(i||s)return i&&(r.get=i),s&&(r.set=s),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var b;Object.prototype.__proto__===null?b=function(){return{__proto__:null}}:b=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=b();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var E=w({}),S=typeof document=="undefined"||w(document.createElement("div"));if(!E||!S)var x=Object.defineProperty}if(!Object.defineProperty||x){var T="Property description must be an object: ",N="Object.defineProperty called on non-object: ",C="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(N+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(T+r);if(x)try{return x.call(Object,t,n,r)}catch(i){}if(f(r,"value"))if(d&&(h(t,n)||p(t,n))){var s=t.__proto__;t.__proto__=o,delete t[n],t[n]=r.value,t.__proto__=s}else t[n]=r.value;else{if(!d)throw new TypeError(C);f(r,"get")&&l(t,n,r.get),f(r,"set")&&c(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)f(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(f(t,n))n+="?";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n=0?parseFloat((i.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((i.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=(window.Controllers||window.controllers)&&window.navigator.product==="Gecko",t.isOldGecko=t.isGecko&&parseInt((i.match(/rv\:(\d+)/)||[])[1],10)<4,t.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(i.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(i.split(" Chrome/")[1])||undefined,t.isAIR=i.indexOf("AdobeAIR")>=0,t.isIPad=i.indexOf("iPad")>=0,t.isTouchPad=i.indexOf("TouchPad")>=0,t.isChromeOS=i.indexOf(" CrOS ")>=0}),ace.define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function a(e,t,n){var a=u(t);if(!i.isMac&&s){s.OSKey&&(a|=8);if(s.altGr){if((3&a)==3)return;s.altGr=0}if(n===18||n===17){var f="location"in t?t.location:t.keyLocation;if(n===17&&f===1)s[n]==1&&(o=t.timeStamp);else if(n===18&&a===3&&f===2){var l=t.timeStamp-o;l<50&&(s.altGr=!0)}}}n in r.MODIFIER_KEYS&&(n=-1),a&8&&n>=91&&n<=93&&(n=-1);if(!a&&n===13){var f="location"in t?t.location:t.keyLocation;if(f===3){e(t,a,-n);if(t.defaultPrevented)return}}if(i.isChromeOS&&a&8){e(t,a,n);if(t.defaultPrevented)return;a&=-9}return!!a||n in r.FUNCTION_KEYS||n in r.PRINTABLE_KEYS?e(t,a,n):!1}function f(){s=Object.create(null),s.count=0,s.lastT=0}var r=e("./keys"),i=e("./useragent"),s=null,o=0;t.addListener=function(e,t,n){if(e.addEventListener)return e.addEventListener(t,n,!1);if(e.attachEvent){var r=function(){n.call(e,window.event)};n._wrapper=r,e.attachEvent("on"+t,r)}},t.removeListener=function(e,t,n){if(e.removeEventListener)return e.removeEventListener(t,n,!1);e.detachEvent&&e.detachEvent("on"+t,n._wrapper||n)},t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},t.preventDefault=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1},t.getButton=function(e){return e.type=="dblclick"?0:e.type=="contextmenu"||i.isMac&&e.ctrlKey&&!e.altKey&&!e.shiftKey?2:e.preventDefault?e.button:{1:0,2:2,4:1}[e.button]},t.capture=function(e,n,r){function i(e){n&&n(e),r&&r(e),t.removeListener(document,"mousemove",n,!0),t.removeListener(document,"mouseup",i,!0),t.removeListener(document,"dragstart",i,!0)}return t.addListener(document,"mousemove",n,!0),t.addListener(document,"mouseup",i,!0),t.addListener(document,"dragstart",i,!0),i},t.addTouchMoveListener=function(e,n){if("ontouchmove"in e){var r,i;t.addListener(e,"touchstart",function(e){var t=e.changedTouches[0];r=t.clientX,i=t.clientY}),t.addListener(e,"touchmove",function(e){var t=1,s=e.changedTouches[0];e.wheelX=-(s.clientX-r)/t,e.wheelY=-(s.clientY-i)/t,r=s.clientX,i=s.clientY,n(e)})}},t.addMouseWheelListener=function(e,n){"onmousewheel"in e?t.addListener(e,"mousewheel",function(e){var t=8;e.wheelDeltaX!==undefined?(e.wheelX=-e.wheelDeltaX/t,e.wheelY=-e.wheelDeltaY/t):(e.wheelX=0,e.wheelY=-e.wheelDelta/t),n(e)}):"onwheel"in e?t.addListener(e,"wheel",function(e){var t=.35;switch(e.deltaMode){case e.DOM_DELTA_PIXEL:e.wheelX=e.deltaX*t||0,e.wheelY=e.deltaY*t||0;break;case e.DOM_DELTA_LINE:case e.DOM_DELTA_PAGE:e.wheelX=(e.deltaX||0)*5,e.wheelY=(e.deltaY||0)*5}n(e)}):t.addListener(e,"DOMMouseScroll",function(e){e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=(e.detail||0)*5,e.wheelY=0):(e.wheelX=0,e.wheelY=(e.detail||0)*5),n(e)})},t.addMultiMouseDownListener=function(e,n,r,s){function c(e){t.getButton(e)!==0?o=0:e.detail>1?(o++,o>4&&(o=1)):o=1;if(i.isIE){var c=Math.abs(e.clientX-u)>5||Math.abs(e.clientY-a)>5;if(!f||c)o=1;f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),o==1&&(u=e.clientX,a=e.clientY)}e._clicks=o,r[s]("mousedown",e);if(o>4)o=0;else if(o>1)return r[s](l[o],e)}function h(e){o=2,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),r[s]("mousedown",e),r[s](l[o],e)}var o=0,u,a,f,l={2:"dblclick",3:"tripleclick",4:"quadclick"};Array.isArray(e)||(e=[e]),e.forEach(function(e){t.addListener(e,"mousedown",c),i.isOldIE&&t.addListener(e,"dblclick",h)})};var u=!i.isMac||!i.isOpera||"KeyboardEvent"in window?function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)}:function(e){return 0|(e.metaKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.ctrlKey?8:0)};t.getModifierString=function(e){return r.KEY_MODS[u(e)]},t.addCommandKeyListener=function(e,n){var r=t.addListener;if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var o=null;r(e,"keydown",function(e){o=e.keyCode}),r(e,"keypress",function(e){return a(n,e,o)})}else{var u=null;r(e,"keydown",function(e){var t=e.keyCode;s[t]=(s[t]||0)+1,t==91||t==92?s.OSKey=!0:s.OSKey&&e.timeStamp-s.lastT>200&&s.count==1&&f(),s[t]==1&&s.count++,s.lastT=e.timeStamp;var r=a(n,e,t);return u=e.defaultPrevented,r}),r(e,"keypress",function(e){u&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),u=null)}),r(e,"keyup",function(e){var t=e.keyCode;s[t]?s.count=Math.max(s.count-1,0):f();if(t==91||t==92)s.OSKey=!1;s[t]=null}),s||(f(),r(window,"focus",f))}};if(typeof window=="object"&&window.postMessage&&!i.isOldIE){var l=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+l;t.addListener(n,"message",function i(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,"message",i),e())}),n.postMessage(r,"*")}}t.nextFrame=typeof window=="object"&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame),t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),ace.define("ace/lib/lang",["require","exports","module"],function(e,t,n){"use strict";t.last=function(e){return e[e.length-1]},t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){var n="";while(t>0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n1),e.preventDefault()},this.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;n.$blockScrolling++,this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.renderer.scroller.setCapture&&n.renderer.scroller.setCapture(),n.setStyle("ace_selecting"),this.setState("select"),n.$blockScrolling--},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);t.$blockScrolling++;if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=f(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.$blockScrolling--,t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);n.$blockScrolling++;if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=f(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.$blockScrolling--,n.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=a(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>o||t-this.mousedownEvent.time>this.$focusTimout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState("select")):(i=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=i,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},this.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()},this.onTouchMove=function(e){var t=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()}}).call(u.prototype),t.DefaultHandlers=u}),ace.define("ace/tooltip",["require","exports","module","ace/lib/oop","ace/lib/dom"],function(e,t,n){"use strict";function s(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}var r=e("./lib/oop"),i=e("./lib/dom");(function(){this.$init=function(){return this.$element=i.createElement("div"),this.$element.className="ace_tooltip",this.$element.style.display="none",this.$parentNode.appendChild(this.$element),this.$element},this.getElement=function(){return this.$element||this.$init()},this.setText=function(e){i.setInnerText(this.getElement(),e)},this.setHtml=function(e){this.getElement().innerHTML=e},this.setPosition=function(e,t){this.getElement().style.left=e+"px",this.getElement().style.top=t+"px"},this.setClassName=function(e){i.addCssClass(this.getElement(),e)},this.show=function(e,t,n){e!=null&&this.setText(e),t!=null&&n!=null&&this.setPosition(t,n),this.isOpen||(this.getElement().style.display="block",this.isOpen=!0)},this.hide=function(){this.isOpen&&(this.getElement().style.display="none",this.isOpen=!1)},this.getHeight=function(){return this.getElement().offsetHeight},this.getWidth=function(){return this.getElement().offsetWidth}}).call(s.prototype),t.Tooltip=s}),ace.define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event","ace/tooltip"],function(e,t,n){"use strict";function u(e){function l(){var r=u.getDocumentPosition().row,s=n.$annotations[r];if(!s)return c();var o=t.session.getLength();if(r==o){var a=t.renderer.pixelToScreenCoordinates(0,u.y).row,l=u.$pos;if(a>t.session.documentToScreenRow(l.row,l.column))return c()}if(f==s)return;f=s.text.join("
"),i.setHtml(f),i.show(),t.on("mousewheel",c);if(e.$tooltipFollowsMouse)h(u);else{var p=u.domEvent.target,d=p.getBoundingClientRect(),v=i.getElement().style;v.left=d.right+"px",v.top=d.bottom+"px"}}function c(){o&&(o=clearTimeout(o)),f&&(i.hide(),f=null,t.removeEventListener("mousewheel",c))}function h(e){i.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,i=new a(t.container);e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i=="foldWidgets")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState("selectByLines"),e.captureMouse(r),r.preventDefault()});var o,u,f;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(r.hasCssClass(n,"ace_fold-widget"))return c();f&&e.$tooltipFollowsMouse&&h(t),u=t;if(o)return;o=setTimeout(function(){o=null,u&&!e.isMousePressed?l():c()},50)}),s.addListener(t.renderer.$gutter,"mouseout",function(e){u=null;if(!f||o)return;o=setTimeout(function(){o=null,c()},50)}),t.on("changeSession",c)}function a(e){o.call(this,e)}var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/event"),o=e("../tooltip").Tooltip;i.inherits(a,o),function(){this.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),o.prototype.setPosition.call(this,e,t)}}.call(a.prototype),t.GutterHandler=u}),ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}return this.$inSelection},this.getButton=function(){return r.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=i.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(s.prototype)}),ace.define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||e.column!=n.column;if(!S||i||s)t.$blockScrolling+=1,t.moveCursorToPosition(e),t.$blockScrolling-=1,S=r,x={x:p,y:d};else{var o=l(x.x,x.y,p,d);o>a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.$blockScrolling+=1,t.selection.fromOrientedRange(m),t.$blockScrolling-=1,t.isFocused()&&!w&&t.renderer.$cursorLayer.setBlinking(!t.getReadOnly()),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("img");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",s.isOpera&&(n.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.addEventListener("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",s.isOpera&&(t.container.appendChild(n),n.scrollTop=0),i.setDragImage&&i.setDragImage(n,0,0),s.isOpera&&t.container.removeChild(n),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.renderer.$cursorLayer.setBlinking(!0)}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e)),i.addListener(c,"dragend",this.onDragEnd.bind(e)),i.addListener(c,"dragenter",this.onDragEnter.bind(e)),i.addListener(c,"dragover",this.onDragOver.bind(e)),i.addListener(c,"dragleave",this.onDragLeave.bind(e)),i.addListener(c,"drop",this.onDrop.bind(e));var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.renderer.$cursorLayer.setBlinking(!this.editor.getReadOnly()),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var n=s.isWin?"default":"move";e.renderer.setCursorStyle(n),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),ace.define("ace/lib/net",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./dom");t.get=function(e,t){var n=new XMLHttpRequest;n.open("GET",e,!0),n.onreadystatechange=function(){n.readyState===4&&t(n.responseText)},n.send(null)},t.loadScript=function(e,t){var n=r.getDocumentHead(),i=document.createElement("script");i.src=e,n.appendChild(i),i.onload=i.onreadystatechange=function(e,n){if(n||!i.readyState||i.readyState=="loaded"||i.readyState=="complete")i=i.onload=i.onreadystatechange=null,n||t()}},t.qualifyURL=function(e){var t=document.createElement("a");return t.href=e,t.href}}),ace.define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;o1&&(i=n[n.length-2]);var o=a[t+"Path"];return o==null?o=a.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return a.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(n,r){var i,o;Array.isArray(n)&&(o=n[0],n=n[1]);try{i=e(n)}catch(u){}if(i&&!t.$loading[n])return r&&r(i);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var a=function(){e([n],function(e){t._emit("load.module",{name:n,module:e});var r=t.$loading[n];t.$loading[n]=null,r.forEach(function(t){t&&t(e)})})};if(!t.get("packaged"))return a();s.loadScript(t.moduleUrl(n,o),a)},t.init=f}),ace.define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event","ace/mouse/dragdrop_handler","ace/config"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("./default_handlers").DefaultHandlers,o=e("./default_gutter_handler").GutterHandler,u=e("./mouse_event").MouseEvent,a=e("./dragdrop_handler").DragdropHandler,f=e("../config"),l=function(e){var t=this;this.editor=e,new s(this),new o(this),new a(this);var n=function(t){var n=!document.hasFocus||!document.hasFocus()||!e.isFocused()&&document.activeElement==(e.textInput&&e.textInput.getElement());n&&window.focus(),e.focus()},u=e.renderer.getMouseEventTarget();r.addListener(u,"click",this.onMouseEvent.bind(this,"click")),r.addListener(u,"mousemove",this.onMouseMove.bind(this,"mousemove")),r.addMultiMouseDownListener([u,e.renderer.scrollBarV&&e.renderer.scrollBarV.inner,e.renderer.scrollBarH&&e.renderer.scrollBarH.inner,e.textInput&&e.textInput.getElement()].filter(Boolean),[400,300,250],this,"onMouseEvent"),r.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,"mousewheel")),r.addTouchMoveListener(e.container,this.onTouchMove.bind(this,"touchmove"));var f=e.renderer.$gutter;r.addListener(f,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),r.addListener(f,"click",this.onMouseEvent.bind(this,"gutterclick")),r.addListener(f,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),r.addListener(f,"mousemove",this.onMouseEvent.bind(this,"guttermousemove")),r.addListener(u,"mousedown",n),r.addListener(f,"mousedown",n),i.isIE&&e.renderer.scrollBarV&&(r.addListener(e.renderer.scrollBarV.element,"mousedown",n),r.addListener(e.renderer.scrollBarH.element,"mousedown",n)),e.on("mousemove",function(n){if(t.state||t.$dragDelay||!t.$dragEnabled)return;var r=e.renderer.screenToTextCoordinates(n.x,n.y),i=e.session.selection.getRange(),s=e.renderer;!i.isEmpty()&&i.insideStart(r.row,r.column)?s.setCursorStyle("default"):s.setCursorStyle("")})};(function(){this.onMouseEvent=function(e,t){this.editor._emit(e,new u(t,this.editor))},this.onMouseMove=function(e,t){var n=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!n||!n.length)return;this.editor._emit(e,new u(t,this.editor))},this.onMouseWheel=function(e,t){var n=new u(t,this.editor);n.speed=this.$scrollSpeed*2,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.onTouchMove=function(e,t){var n=new u(t,this.editor);n.speed=1,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){this.x=e.x,this.y=e.y,this.isMousePressed=!0;var n=this.editor.renderer;n.$keepTextAreaAtCursor&&(n.$keepTextAreaAtCursor=null);var s=this,o=function(e){if(!e)return;if(i.isWebKit&&!e.which&&s.releaseMouse)return s.releaseMouse();s.x=e.clientX,s.y=e.clientY,t&&t(e),s.mouseEvent=new u(e,s.editor),s.$mouseMoved=!0},a=function(e){clearInterval(l),f(),s[s.state+"End"]&&s[s.state+"End"](e),s.state="",n.$keepTextAreaAtCursor==null&&(n.$keepTextAreaAtCursor=!0,n.$moveTextAreaToCursor()),s.isMousePressed=!1,s.$onCaptureMouseMove=s.releaseMouse=null,e&&s.onMouseEvent("mouseup",e)},f=function(){s[s.state]&&s[s.state](),s.$mouseMoved=!1};if(i.isOldIE&&e.domEvent.type=="dblclick")return setTimeout(function(){a(e)});s.$onCaptureMouseMove=o,s.releaseMouse=r.capture(this.editor.container,o,a);var l=setInterval(f,20)},this.releaseMouse=null,this.cancelContextMenu=function(){var e=function(t){if(t&&t.domEvent&&t.domEvent.type!="contextmenu")return;this.editor.off("nativecontextmenu",e),t&&t.domEvent&&r.stopEvent(t.domEvent)}.bind(this);setTimeout(e,10),this.editor.on("nativecontextmenu",e)}}).call(l.prototype),f.defineOptions(l.prototype,"mouseHandler",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:i.isMac?150:0},dragEnabled:{initialValue:!0},focusTimout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=l}),ace.define("ace/mouse/fold_handler",["require","exports","module"],function(e,t,n){"use strict";function r(e){e.on("click",function(t){var n=t.getDocumentPosition(),r=e.session,i=r.getFoldAt(n.row,n.column,1);i&&(t.getAccelKey()?r.removeFold(i):r.expandFold(i),t.stop())}),e.on("gutterclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session;i.foldWidgets&&i.foldWidgets[r]&&e.session.onFoldWidgetClick(r,t),e.isFocused()||e.focus(),t.stop()}}),e.on("gutterdblclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session,s=i.getParentFoldRangeData(r,!0),o=s.range||s.firstRange;if(o){r=o.start.row;var u=i.getFoldAt(r,i.getLine(r).length,1);u?i.removeFold(u):(i.addFold("...",o),e.renderer.scrollCursorIntoView({row:o.start.row,column:0}))}t.stop()}})}t.FoldHandler=r}),ace.define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event"],function(e,t,n){"use strict";var r=e("../lib/keys"),i=e("../lib/event"),s=function(e){this.$editor=e,this.$data={editor:e},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0)},this.setKeyboardHandler=function(e){var t=this.$handlers;if(t[t.length-1]==e)return;while(t[t.length-1]&&t[t.length-1]!=this.$defaultHandler)this.removeKeyboardHandler(t[t.length-1]);this.addKeyboardHandler(e,1)},this.addKeyboardHandler=function(e,t){if(!e)return;typeof e=="function"&&!e.handleKeyboard&&(e.handleKeyboard=e);var n=this.$handlers.indexOf(e);n!=-1&&this.$handlers.splice(n,1),t==undefined?this.$handlers.push(e):this.$handlers.splice(t,0,e),n==-1&&e.attach&&e.attach(this.$editor)},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return t==-1?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.getStatusText=function(){var e=this.$data,t=e.editor;return this.$handlers.map(function(n){return n.getStatusText&&n.getStatusText(t,e)||""}).filter(Boolean).join(" ")},this.$callKeyboardHandlers=function(e,t,n,r){var s,o=!1,u=this.$editor.commands;for(var a=this.$handlers.length;a--;){s=this.$handlers[a].handleKeyboard(this.$data,e,t,n,r);if(!s||!s.command)continue;s.command=="null"?o=!0:o=u.exec(s.command,this.$editor,s.args,r),o&&r&&e!=-1&&s.passEvent!=1&&s.command.passEvent!=1&&i.stopEvent(r);if(o)break}return!o&&e==-1&&(s={command:"insertstring"},o=u.exec("insertstring",this.$editor,t)),o&&this.$editor._signal("keyboardActivity",s),o},this.onCommandKey=function(e,t,n){var i=r.keyCodeToString(n);this.$callKeyboardHandlers(t,i,n,e)},this.onTextInput=function(e){this.$callKeyboardHandlers(-1,e)}}).call(s.prototype),t.KeyBinding=s}),ace.define("ace/range",["require","exports","module"],function(e,t,n){"use strict";var r=function(e,t){return e.row-t.row||e.column-t.column},i=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.rowt)var r={row:t+1,column:0};else if(this.start.rowt.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.isEmpty()?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){var e=this.doc.getLength()-1;this.setSelectionAnchor(0,0),this.moveCursorTo(e,this.doc.getLine(e).length)},this.setRange=this.setSelectionRange=function(e,t){t?(this.setSelectionAnchor(e.end.row,e.end.column),this.selectTo(e.start.row,e.start.column)):(this.setSelectionAnchor(e.start.row,e.start.column),this.selectTo(e.end.row,e.end.column)),this.getRange().isEmpty()&&(this.$isEmpty=!0),this.$desiredColumn=null},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(e.column-n,e.column).split(" ").length-1==n?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var s=this.session.getFoldAt(e,t,1);if(s){this.moveCursorTo(s.end.row,s.end.column);return}if(i=this.session.nonTokenRe.exec(r))t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t);if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e0&&this.moveCursorWordLeft();return}if(o=this.session.tokenRe.exec(s))t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t,n=0,r,i=/\s/,s=this.session.tokenRe;s.lastIndex=0;if(t=this.session.tokenRe.exec(e))n=this.session.tokenRe.lastIndex;else{while((r=e[n])&&i.test(r))n++;if(n<1){s.lastIndex=0;while((r=e[n])&&!s.test(r)){s.lastIndex=0,n++;if(i.test(r)){if(n>2){n--;break}while((r=e[n])&&i.test(r))n++;if(n>2)break}}}}return s.lastIndex=0,n},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column);t===0&&(this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var r=this.session.screenToDocumentPosition(n.row+e,n.column);e!==0&&t===0&&r.row===this.lead.row&&r.column===this.lead.column&&this.session.lineWidgets&&this.session.lineWidgets[r.row]&&(r.row>0||e>0)&&r.row++,this.moveCursorTo(r.row,r.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0,this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e(this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e[t].isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),ace.define("ace/tokenizer",["require","exports","module","ace/config"],function(e,t,n){"use strict";var r=e("./config"),i=2e3,s=function(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:"text"},o="g",u=[];for(var a=0;a1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push("$")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}};(function(){this.$setMaxTokenCount=function(e){i=e|0},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;il){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;yi){c>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});while(l1&&n[0]!==r&&n.unshift("#tmp",r),{tokens:f,state:n.length?n:r}},this.reportError=r.reportError}).call(s.prototype),t.Tokenizer=s}),ace.define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/lang"),i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}};(function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n},this.getCurrentTokenPosition=function(){return{row:this.$row,column:this.getCurrentTokenColumn()}}}).call(r.prototype),t.TokenIterator=r}),ace.define("ace/mode/text",["require","exports","module","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour","ace/unicode","ace/lib/lang","ace/token_iterator","ace/range"],function(e,t,n){"use strict";var r=e("../tokenizer").Tokenizer,i=e("./text_highlight_rules").TextHighlightRules,s=e("./behaviour").Behaviour,o=e("../unicode"),u=e("../lib/lang"),a=e("../token_iterator").TokenIterator,f=e("../range").Range,l=function(){this.HighlightRules=i,this.$behaviour=new s};(function(){this.tokenRe=new RegExp("^["+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]+","g"),this.nonTokenRe=new RegExp("^(?:[^"+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]|\\s])+","g"),this.getTokenizer=function(){return this.$tokenizer||(this.$highlightRules=this.$highlightRules||new this.HighlightRules,this.$tokenizer=new r(this.$highlightRules.getRules())),this.$tokenizer},this.lineCommentStart="",this.blockComment="",this.toggleCommentLines=function(e,t,n,r){function w(e){for(var t=n;t<=r;t++)e(i.getLine(t),t)}var i=t.doc,s=!0,o=!0,a=Infinity,f=t.getTabSize(),l=!1;if(!this.lineCommentStart){if(!this.blockComment)return!1;var c=this.blockComment.start,h=this.blockComment.end,p=new RegExp("^(\\s*)(?:"+u.escapeRegExp(c)+")"),d=new RegExp("(?:"+u.escapeRegExp(h)+")\\s*$"),v=function(e,t){if(g(e,t))return;if(!s||/\S/.test(e))i.insertInLine({row:t,column:e.length},h),i.insertInLine({row:t,column:a},c)},m=function(e,t){var n;(n=e.match(d))&&i.removeInLine(t,e.length-n[0].length,e.length),(n=e.match(p))&&i.removeInLine(t,n[1].length,n[0].length)},g=function(e,n){if(p.test(e))return!0;var r=t.getTokens(n);for(var i=0;i2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(ne.length&&(E=e.length)}),a==Infinity&&(a=E,s=!1,o=!1),l&&a%f!=0&&(a=Math.floor(a/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new a(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,l=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new f(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new a(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new f(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);l.start.row==c&&(l.start.column+=h),l.end.row==c&&(l.end.column+=h),t.selection.fromOrientedRange(l)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)e[t]&&(this.$embeds.push(t),this.$modes[t]=new e[t]);var n=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(var t=0;t=0&&t.row=0&&t.column<=e[t.row].length}function s(e,t){t.action!="insert"&&t.action!="remove"&&r(t,"delta.action must be 'insert' or 'remove'"),t.lines instanceof Array||r(t,"delta.lines must be an Array"),(!t.start||!t.end)&&r(t,"delta.start/end must be an present");var n=t.start;i(e,t.start)||r(t,"delta.start must be contained in document");var s=t.end;t.action=="remove"&&!i(e,s)&&r(t,"delta.end must contained in document for 'remove' actions");var o=s.row-n.row,u=s.column-(o==0?n.column:0);(o!=t.lines.length-1||t.lines[o].length!=u)&&r(t,"delta.range must match delta lines")}t.applyDelta=function(e,t,n){var r=t.start.row,i=t.start.column,s=e[r]||"";switch(t.action){case"insert":var o=t.lines;if(o.length===1)e[r]=s.substring(0,i)+t.lines[0]+s.substring(i);else{var u=[r,1].concat(t.lines);e.splice.apply(e,u),e[r]=s.substring(0,i)+e[r],e[r+t.lines.length-1]+=s.substring(i)}break;case"remove":var a=t.end.column,f=t.end.row;r===f?e[r]=s.substring(0,i)+s.substring(a):e.splice(r,f-r+1,s.substring(0,i)+e[f].substring(a))}}}),ace.define("ace/anchor",["require","exports","module","ace/lib/oop","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=t.Anchor=function(e,t,n){this.$onChange=this.onChange.bind(this),this.attach(e),typeof n=="undefined"?this.setPosition(t.row,t.column):this.setPosition(t,n)};(function(){function e(e,t,n){var r=n?e.column<=t.column:e.columnthis.row)return;var n=t(e,{row:this.row,column:this.column},this.$insertRight);this.setPosition(n.row,n.column,!0)},this.setPosition=function(e,t,n){var r;n?r={row:e,column:t}:r=this.$clipPositionToDocument(e,t);if(this.row==r.row&&this.column==r.column)return;var i={row:this.row,column:this.column};this.row=r.row,this.column=r.column,this._signal("change",{old:i,value:r})},this.detach=function(){this.document.removeEventListener("change",this.$onChange)},this.attach=function(e){this.document=e||this.document,this.document.on("change",this.$onChange)},this.$clipPositionToDocument=function(e,t){var n={};return e>=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/apply_delta","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./apply_delta").applyDelta,s=e("./lib/event_emitter").EventEmitter,o=e("./range").Range,u=e("./anchor").Anchor,a=function(e){this.$lines=[""],e.length===0?this.$lines=[""]:Array.isArray(e)?this.insertMergedLines({row:0,column:0},e):this.insert({row:0,column:0},e)};(function(){r.implement(this,s),this.setValue=function(e){var t=this.getLength()-1;this.remove(new o(0,0,t,this.getLine(t).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new u(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){return this.getLinesForRange(e).join(this.getNewLineCharacter())},this.getLinesForRange=function(e){var t;if(e.start.row===e.end.row)t=[this.getLine(e.start.row).substring(e.start.column,e.end.column)];else{t=this.getLines(e.start.row,e.end.row),t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column))}return t},this.insertLines=function(e,t){return console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."),this.insertFullLines(e,t)},this.removeLines=function(e,t){return console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."),this.removeFullLines(e,t)},this.insertNewLine=function(e){return console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."),this.insertMergedLines(e,["",""])},this.insert=function(e,t){return this.getLength()<=1&&this.$detectNewLine(t),this.insertMergedLines(e,this.$split(t))},this.insertInLine=function(e,t){var n=this.clippedPos(e.row,e.column),r=this.pos(e.row,e.column+t.length);return this.applyDelta({start:n,end:r,action:"insert",lines:[t]},!0),this.clonePos(r)},this.clippedPos=function(e,t){var n=this.getLength();e===undefined?e=n:e<0?e=0:e>=n&&(e=n-1,t=undefined);var r=this.getLine(e);return t==undefined&&(t=r.length),t=Math.min(Math.max(t,0),r.length),{row:e,column:t}},this.clonePos=function(e){return{row:e.row,column:e.column}},this.pos=function(e,t){return{row:e,column:t}},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):(e.row=Math.max(0,e.row),e.column=Math.min(Math.max(e.column,0),this.getLine(e.row).length)),e},this.insertFullLines=function(e,t){e=Math.min(Math.max(e,0),this.getLength());var n=0;e0,r=t=0&&this.applyDelta({start:this.pos(e,this.getLine(e).length),end:this.pos(e+1,0),action:"remove",lines:["",""]})},this.replace=function(e,t){e instanceof o||(e=o.fromPoints(e.start,e.end));if(t.length===0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);var n;return t?n=this.insert(e.start,t):n=e.start,n},this.applyDeltas=function(e){for(var t=0;t=0;t--)this.revertDelta(e[t])},this.applyDelta=function(e,t){var n=e.action=="insert";if(n?e.lines.length<=1&&!e.lines[0]:!o.comparePoints(e.start,e.end))return;n&&e.lines.length>2e4&&this.$splitAndapplyLargeDelta(e,2e4),i(this.$lines,e,t),this._signal("change",e)},this.$splitAndapplyLargeDelta=function(e,t){var n=e.lines,r=n.length,i=e.start.row,s=e.start.column,o=0,u=0;do{o=u,u+=t-1;var a=n.slice(o,u);if(u>r){e.lines=a,e.start.row=i+o,e.start.column=s;break}a.push(""),this.applyDelta({start:this.pos(i+o,s),end:this.pos(i+u,s=0),action:e.action,lines:a},!0)}while(!0)},this.revertDelta=function(e){this.applyDelta({start:this.clonePos(e.start),end:this.clonePos(e.end),action:e.action=="insert"?"remove":"insert",lines:e.lines.slice()})},this.indexToPosition=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length;for(var i=t||0,s=n.length;i20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,s<=r&&n.fireUpdateEvent(s,r)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal("update",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.lines[t]=null;else if(e.action=="remove")this.lines.splice(t,n+1,null),this.states.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.lines.splice.apply(this.lines,r),this.states.splice.apply(this.states,r)}this.currentLine=Math.min(t,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens}}).call(s.prototype),t.BackgroundTokenizer=s}),ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:"");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i)}}}).call(o.prototype),t.SearchHighlight=o}),ace.define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.rowthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},this.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.apply(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.rowr)break;l.start.row==r&&l.start.column>=t.column&&(l.start.column!=t.column||!this.$insertRight)&&(l.start.column+=o,l.start.row+=s);if(l.end.row==r&&l.end.column>=t.column){if(l.end.column==t.column&&this.$insertRight)continue;l.end.column==t.column&&o>0&&al.start.column&&l.end.column==u[a+1].start.column&&(l.end.column-=o),l.end.column+=o,l.end.row+=s}}if(s!=0&&a=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i=t){u=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column;if(u0&&(this.removeFolds(p),p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?(n=new r(0,0,this.getLength(),0),t=!0):typeof e=="number"?n=new r(e,0,e,this.getLine(e).length):"row"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRangeList(n);if(t)this.removeFolds(i);else{var s=i;while(s.length)this.expandFolds(s),s=this.getFoldsInRangeList(n)}if(i.length)return i},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(t=e){i=s.end.row;try{var o=this.addFold("...",s);o&&(o.collapseChildren=n)}catch(u){}}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error("invalid fold style: "+e+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==e)return;this.$foldStyle=e,e=="manual"&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)},this.$setFolding=function(e){if(this.$foldMode==e)return;this.$foldMode=e,this.off("change",this.$updateFoldWidgets),this.off("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets),this._signal("changeAnnotation");if(!e||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.$tokenizerUpdateFoldWidgets=this.tokenizerUpdateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets),this.on("tokenizerUpdate",this.$tokenizerUpdateFoldWidgets)},this.getParentFoldRangeData=function(e,t){var n=this.foldWidgets;if(!n||t&&n[e])return{};var r=e-1,i;while(r>=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n==="end"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s){t.children||t.all?this.removeFold(s):this.expandFold(s);return}var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range)){this.removeFold(s);return}}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,f,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold("...",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold("...",n)}},this.updateFoldWidgets=function(e){var t=e.start.row,n=e.end.row-t;if(n===0)this.foldWidgets[t]=null;else if(e.action=="remove")this.foldWidgets.splice(t,n+1,null);else{var r=Array(n+1);r.unshift(t,1),this.foldWidgets.splice.apply(this.foldWidgets,r)}},this.tokenizerUpdateFoldWidgets=function(e){var t=e.data;t.first!=t.last&&this.foldWidgets.length>t.first&&this.foldWidgets.splice(t.first,this.foldWidgets.length)}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator;t.Folding=u}),ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:start|begin)\b/,"(?:start|begin|end)")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}r.implement(this,o),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.$deltasFold.length&&(t.$deltas.push({group:"fold",deltas:t.$deltasFold}),t.$deltasFold=[]),t.$deltasDoc.length&&(t.$deltas.push({group:"doc",deltas:t.$deltasDoc}),t.$deltasDoc=[]),t.$deltas.length>0&&e.execute({action:"aceupdate",args:[t.$deltas,t],merge:t.mergeUndoDeltas}),t.mergeUndoDeltas=!1,t.$deltas=[]},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):" "},this.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption("tabSize",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption("overwrite",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(oe&&(e=t.screenWidth)}),this.lineWidgetWidth=e},this.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;ao){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.removeFullLines=function(e,t){return this.doc.removeFullLines(e,t)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=e.length-1;r!=-1;r--){var i=e[r];i.group=="doc"?(this.doc.revertDeltas(i.deltas),n=this.$getUndoSelection(i.deltas,!0,n)):i.deltas.forEach(function(e){this.addFolds(e.folds)},this)}return this.$fromUndo=!1,n&&this.$undoSelect&&!t&&this.selection.setSelectionRange(n),n},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=0;re.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,l=s.start,o=l.row-a.row,u=l.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.doc.insertInLine({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new f(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new f(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeFullLines(e,t);return this.doc.insertFullLines(e+i,u),o.length&&this.addFolds(o),i},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this.$useWrapMode&&this._signal("changeWrapMode")},this.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n=e.action,r=e.start,i=e.end,s=r.row,o=i.row,u=o-s,a=null;this.$updating=!0;if(u!=0)if(n==="remove"){this[t?"$wrapData":"$rowLengthCache"].splice(s,u);var f=this.$foldData;a=this.getFoldsInRange(e),this.removeFolds(a);var l=this.getFoldLine(i.row),c=0;if(l){l.addRemoveChars(i.row,i.column,r.column-i.column),l.shiftRow(-u);var h=this.getFoldLine(s);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c=i.row&&l.shiftRow(-u)}o=s}else{var p=Array(u);p.unshift(s,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(s),c=0;if(l){var v=l.range.compareInside(r.row,r.column);v==0?(l=l.split(r.row,r.column),l&&(l.shiftRow(u),l.addRemoveChars(o,0,i.column-r.column))):v==-1&&(l.addRemoveChars(s,0,i.column-r.column),l.shiftRow(u)),c=f.indexOf(l)+1}for(c;c=s&&l.shiftRow(u)}}else{u=Math.abs(e.start.column-e.end.column),n==="remove"&&(a=this.getFoldsInRange(e),this.removeFolds(a),u=-u);var l=this.getFoldLine(s);l&&l.addRemoveChars(s,r.column,u)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(s,o):this.$updateRowLengthCache(s,o),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var r=this.doc.getAllLines(),i=this.getTabSize(),s=this.$wrapData,o=this.$wrapLimit,a,f,l=e;t=Math.min(t,r.length-1);while(l<=t)f=this.getFoldLine(l,f),f?(a=[],f.walk(function(e,t,i,s){var o;if(e!=null){o=this.$getDisplayTokens(e,a.length),o[0]=n;for(var f=1;fr-b){var w=a+r-b;if(e[w-1]>=p&&e[w]>=p){y(w);continue}if(e[w]==n||e[w]==u){for(w;w!=a-1;w--)if(e[w]==n)break;if(w>a){y(w);continue}w=a+r;for(w;w>2)),a-1);while(w>E&&e[w]E&&e[w]E&&e[w]==l)w--}else while(w>E&&e[w]E){y(++w);continue}w=a+r,e[w]==t&&w--,y(w-b)}return s},this.$getDisplayTokens=function(n,r){var i=[],s;r=r||0;for(var o=0;o39&&u<48||u>57&&u<64?i.push(l):u>=4352&&m(u)?i.push(e,t):i.push(e)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i=4352&&m(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.lineWidgets=null,this.getRowLength=function(e){if(this.lineWidgets)var t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0;else t=0;return!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getRowWrapIndent=function(e){if(this.$useWrapMode){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE),n=this.$wrapData[t.row];return n.length&&n[0]=0)var o=a[f],r=this.$docRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getLength()-1,p=this.getNextFoldLine(r),d=p?p.start.row:Infinity;while(o<=e){u=this.getRowLength(r);if(o+u>e||r>=h)break;o+=u,r++,r>d&&(r=p.end.row+1,p=this.getNextFoldLine(r,p),d=p?p.start.row:Infinity),c&&(this.$docRowCache.push(r),this.$screenRowCache.push(o))}if(p&&p.start.row<=r)n=this.getFoldDisplayLine(p),r=p.start.row;else{if(o+u<=e||r>h)return{row:h,column:this.getLine(h).length};n=this.getLine(r),p=null}var v=0;if(this.$useWrapMode){var m=this.$wrapData[r];if(m){var g=Math.floor(e-o);s=m[g],g>0&&m.length&&(v=m.indent,i=m[g-1]||m[m.length-1],n=n.substring(i))}}return i+=this.$getStringScreenWidth(n,t-v)[1],this.$useWrapMode&&i>=s&&(i=s-1),p?p.idxToPosition(i):{row:r,column:i}},this.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);var v=0;if(this.$useWrapMode){var m=this.$wrapData[i];if(m){var g=0;while(d.length>=m[g])r++,g++;d=d.substring(m[g-1]||0,d.length),v=g>0?m.indent:0}}return{row:r,column:v+this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;ro&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},this.$setFontMetrics=function(e){if(!this.$enableVarChar)return;this.$getStringScreenWidth=function(t,n,r){if(n===0)return[0,0];n||(n=Infinity),r=r||0;var i,s;for(s=0;sn)break}return[r,s]}},this.destroy=function(){this.bgTokenizer&&(this.bgTokenizer.setDocument(null),this.bgTokenizer=null),this.$stopWorker()}}).call(p.prototype),e("./edit_session/folding").Folding.call(p.prototype),e("./edit_session/bracket_match").BracketMatch.call(p.prototype),s.defineOptions(p.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;this.$wrap=e;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}},get:function(){return this.getUseWrapMode()?this.$wrap==-1?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=e=="auto"?this.$mode.type!="text":e!="text",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$modified=!0,this.$resetRowCache(0),this.$updateWrapData(0,this.getLength()-1)))},initialValue:"auto"},indentedSoftWrap:{initialValue:!0},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){if(isNaN(e)||this.$tabSize===e)return;this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize")},initialValue:4,handlesSet:!0},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId}}}),t.EditSession=p}),ace.define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){this.$options={}};(function(){this.set=function(e){return i.mixin(this.$options,e),this},this.getOptions=function(){return r.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$options,n=this.$matchIterator(e,t);if(!n)return!1;var r=null;return n.forEach(function(e,n,i){if(!e.start){var o=e.offset+(i||0);r=new s(n,o,n,o+e.length);if(!e.length&&t.start&&t.start.start&&t.skipCurrent!=0&&r.isEqual(t.start))return r=null,!1}else r=e;return!0}),r},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;hv)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g=0;gE&&o[h].end.row==n.end.row)h--;o=o.slice(g,h+1);for(g=0,h=o.length;g=0;u--)if(i(o[u],t,s))return!0};else var u=function(e,t,s){var o=r.getMatchOffsets(e,n);for(var u=0;u=o;r--)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=u,o=s.row;r>=o;r--)if(n(e.getLine(r),r))return}:function(n){var r=s.row,i=e.getLine(r).substr(s.column);if(n(i,r,s.column))return;for(r+=1;r<=u;r++)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=o,u=s.row;r<=u;r++)if(n(e.getLine(r),r))return};return{forEach:a}}}).call(o.prototype),t.Search=o}),ace.define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function o(e,t){this.platform=t||(i.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function u(e,t){o.call(this,e,t),this.$singleCommand=!1}var r=e("../lib/keys"),i=e("../lib/useragent"),s=r.KEY_MODS;u.prototype=o.prototype,function(){function e(e){return typeof e=="object"&&e.bindKey&&e.bindKey.position||0}this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var n=e&&(typeof e=="string"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},this.bindKey=function(e,t,n){typeof e=="object"&&e&&(n==undefined&&(n=e.position),e=e[this.platform]);if(!e)return;if(typeof t=="function")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split("|").forEach(function(e){var r="";if(e.indexOf(" ")!=-1){var i=e.split(/\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=s[t.hashId]+t.key;r+=(r?" ":"")+n,this._addCommandToBinding(r,"chainKeys")},this),r+=" "}var o=this.parseKeys(e),u=s[o.hashId]+o.key;this._addCommandToBinding(r+u,t,n)},this)},this._addCommandToBinding=function(t,n,r){var i=this.commandKeyBinding,s;if(!n)delete i[t];else if(!i[t]||this.$singleCommand)i[t]=n;else{Array.isArray(i[t])?(s=i[t].indexOf(n))!=-1&&i[t].splice(s,1):i[t]=[i[t]],typeof r!="number"&&(r||n.isDefault?r=-100:r=e(n));var o=i[t];for(s=0;sr)break}o.splice(s,0,n)}},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n});if(typeof n!="object")return;n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=s[t]+n;return this.commandKeyBinding[r]},this.handleKeyboard=function(e,t,n,r){if(r<0)return;var i=s[t]+n,o=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=" "+i,o=this.commandKeyBinding[e.$keyChain]||o);if(o)if(o=="chainKeys"||o[o.length-1]=="chainKeys")return e.$keyChain=e.$keyChain||i,{command:"null"};if(e.$keyChain)if(!!t&&t!=4||n.length!=1){if(t==-1||r>0)e.$keyChain=""}else e.$keyChain=e.$keyChain.slice(0,-i.length-1);return{command:o}},this.getStatusText=function(e,t){return t.$keyChain||""}}.call(o.prototype),t.HashHandler=o,t.MultiHashHandler=u}),ace.define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../keyboard/hash_handler").MultiHashHandler,s=e("../lib/event_emitter").EventEmitter,o=function(e,t){i.call(this,t,e),this.byName=this.commands,this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit("exec",i),this._signal("afterExec",i),i.returnValue===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),ace.define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,n){"use strict";function o(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",bindKey:o("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",bindKey:o("Alt-E","Ctrl-E"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",bindKey:o("Alt-Shift-E","Ctrl-Shift-E"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",bindKey:o("Ctrl-L","Command-L"),exec:function(e){var t=parseInt(prompt("Enter line number:"),10);isNaN(t)||e.gotoLine(t)},readOnly:!0},{name:"fold",bindKey:o("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:o("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",bindKey:o("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",bindKey:o("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"foldall",bindKey:o(null,"Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",bindKey:o("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",bindKey:o("Ctrl-Shift-Home","Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",bindKey:o("Shift-Up","Shift-Up"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golineup",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttoend",bindKey:o("Ctrl-Shift-End","Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",bindKey:o("Shift-Down","Shift-Down"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",bindKey:o("Alt-Shift-Left","Command-Shift-Left"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",bindKey:o("Shift-Left","Shift-Left"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",bindKey:o("Alt-Shift-Right","Command-Shift-Right"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",bindKey:o("Ctrl-P","Ctrl-P"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"selecttomatching",bindKey:o("Ctrl-Shift-P","Ctrl-Shift-P"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"expandToMatching",bindKey:o("Ctrl-Shift-M","Ctrl-Shift-M"),exec:function(e){e.jumpToMatching(!0,!0)},multiSelectAction:"forEach",scrollIntoView:"animate",readOnly:!0},{name:"passKeysToBrowser",bindKey:o(null,null),exec:function(){},passEvent:!0,readOnly:!0},{name:"copy",exec:function(e){},readOnly:!0},{name:"cut",exec:function(e){var t=e.getSelectionRange();e._emit("cut",t),e.selection.isEmpty()||(e.session.remove(t),e.clearSelection())},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"paste",exec:function(e,t){e.$handlePaste(t)},scrollIntoView:"cursor"},{name:"removeline",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"modifyNumberDown",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",bindKey:o("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",bindKey:o("Alt-Delete","Ctrl-K"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",bindKey:o("Ctrl-T","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"expandtoline",bindKey:o("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"joinlines",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\n\s*/," ").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=" "+c),f+=c}i.row+10?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;o0&&this.$blockScrolling--;var n=t&&t.scrollIntoView;if(n){switch(n){case"center-animate":n="animate";case"center":this.renderer.scrollCursorIntoView(null,.5);break;case"animate":case"cursor":this.renderer.scrollCursorIntoView();break;case"selectionPart":var r=this.selection.getRange(),i=this.renderer.layerConfig;(r.start.row>=i.lastRow||r.end.row<=i.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}n=="animate"&&this.renderer.animateScrolling(this.curOp.scrollTop)}this.prevOp=this.curOp,this.curOp=null}},this.$mergeableCommands=["backspace","del","insertstring"],this.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},this.setKeyboardHandler=function(e,t){if(e&&typeof e=="string"){this.$keybindingId=e;var n=this;g.loadModule(["keybinding",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;this.curOp&&this.endOperation(),this.curOp={};var t=this.session;if(t){this.session.off("change",this.$onDocumentChange),this.session.off("changeMode",this.$onChangeMode),this.session.off("tokenizerUpdate",this.$onTokenizerUpdate),this.session.off("changeTabSize",this.$onChangeTabSize),this.session.off("changeWrapLimit",this.$onChangeWrapLimit),this.session.off("changeWrapMode",this.$onChangeWrapMode),this.session.off("changeFold",this.$onChangeFold),this.session.off("changeFrontMarker",this.$onChangeFrontMarker),this.session.off("changeBackMarker",this.$onChangeBackMarker),this.session.off("changeBreakpoint",this.$onChangeBreakpoint),this.session.off("changeAnnotation",this.$onChangeAnnotation),this.session.off("changeOverwrite",this.$onCursorChange),this.session.off("changeScrollTop",this.$onScrollTopChange),this.session.off("changeScrollLeft",this.$onScrollLeftChange);var n=this.session.getSelection();n.off("changeCursor",this.$onCursorChange),n.off("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.on("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.on("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.on("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.on("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.on("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.on("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.on("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.on("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.on("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.on("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.on("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.on("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.on("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.on("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.on("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.on("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.$blockScrolling+=1,this.onCursorChange(),this.$blockScrolling-=1,this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),this.curOp=null,t&&t._signal("changeEditor",{oldEditor:this}),e&&e._signal("changeEditor",{editor:this})},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e,t){this.renderer.setTheme(e,t)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption("fontSize")||i.computedStyle(this.container,"fontSize")},this.setFontSize=function(e){this.setOption("fontSize",e)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=t.findMatchingBracket(e.getCursorPosition());if(n)var r=new p(n.row,n.column,n.row,n.column+1);else if(t.$mode.getMatching)var r=t.$mode.getMatching(e.session);r&&(t.$bracketHighlight=t.addMarker(r,"ace_bracket","text"))},50)},this.$highlightTags=function(){if(this.$highlightTagPending)return;var e=this;this.$highlightTagPending=!0,setTimeout(function(){e.$highlightTagPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=e.getCursorPosition(),r=new y(e.session,n.row,n.column),i=r.getCurrentToken();if(!i||!/\b(?:tag-open|tag-name)/.test(i.type)){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}if(i.type.indexOf("tag-open")!=-1){i=r.stepForward();if(!i)return}var s=i.value,o=0,u=r.stepBackward();if(u.value=="<"){do u=i,i=r.stepForward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="=0)}else{do i=u,u=r.stepBackward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new p(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column-1,r=t.end.column+1,i=e.getLine(t.start.row),s=i.length,o=i.substring(Math.max(n,0),Math.min(r,s));if(n>=0&&/^[\w\d]/.test(o)||r<=s&&/[\w\d]$/.test(o))return;o=i.substring(t.start.column,t.end.column);if(!/^[\w\d]+$/.test(o))return;var u=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:o});return u},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText();return this._signal("copy",e),e},this.onCopy=function(){this.commands.exec("copy",this)},this.onCut=function(){this.commands.exec("cut",this)},this.onPaste=function(e,t){var n={text:e,event:t};this.commands.exec("paste",this,n)},this.$handlePaste=function(e){typeof e=="string"&&(e={text:e}),this._signal("paste",e);var t=e.text;if(!this.inMultiSelectMode||this.inVirtualSelectionMode)this.insert(t);else{var n=t.split(/\r\n|\r|\n/),r=this.selection.rangeList.ranges;if(n.length>r.length||n.length<2||!n[1])return this.commands.exec("insertstring",this,t);for(var i=r.length;i--;){var s=r[i];s.isEmpty()||this.session.remove(s),this.session.insert(s.start,n[i])}}},this.execCommand=function(e,t){return this.commands.exec(e,this,t)},this.insert=function(e,t){var n=this.session,r=n.getMode(),i=this.getCursorPosition();if(this.getBehavioursEnabled()&&!t){var s=r.transformAction(n.getState(i.row),"insertion",this,n,e);s&&(e!==s.text&&(this.session.mergeUndoDeltas=!1,this.$mergeNextCommand=!1),e=s.text)}e==" "&&(e=this.session.getTabString());if(!this.selection.isEmpty()){var o=this.getSelectionRange();i=this.session.remove(o),this.clearSelection()}else if(this.session.getOverwrite()){var o=new p.fromPoints(i,i);o.end.column+=e.length,this.session.remove(o)}if(e=="\n"||e=="\r\n"){var u=n.getLine(i.row);if(i.column>u.search(/\S|$/)){var a=u.substr(i.column).search(/\S|$/);n.doc.removeInLine(i.row,i.column,i.column+a)}}this.clearSelection();var f=i.column,l=n.getState(i.row),u=n.getLine(i.row),c=r.checkOutdent(l,u,e),h=n.insert(i,e);s&&s.selection&&(s.selection.length==2?this.selection.setSelectionRange(new p(i.row,f+s.selection[0],i.row,f+s.selection[1])):this.selection.setSelectionRange(new p(i.row+s.selection[0],s.selection[1],i.row+s.selection[2],s.selection[3])));if(n.getDocument().isNewLine(e)){var d=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},d)}c&&r.autoOutdent(l,n,i.row)},this.onTextInput=function(e){this.keyBinding.onTextInput(e)},this.onCommandKey=function(e,t,n){this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},this.getScrollSpeed=function(){return this.getOption("scrollSpeed")},this.setDragDelay=function(e){this.setOption("dragDelay",e)},this.getDragDelay=function(){return this.getOption("dragDelay")},this.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},this.getSelectionStyle=function(){return this.getOption("selectionStyle")},this.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},this.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption("readOnly",e)},this.getReadOnly=function(){return this.getOption("readOnly")},this.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},this.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},this.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},this.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},this.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},this.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;tt.toLowerCase()?1:0});var r=new p(0,0,0,0);for(var i=e.first;i<=e.last;i++){var s=t.getLine(i);r.start.row=i,r.end.row=i,r.end.column=s.length,t.replace(r,n[i-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},this.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new p(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&np+1)break;p=d.last}l--,u=this.session.$moveLines(h,p,t?0:e),t&&e==-1&&(c=l+1);while(c<=l)o[c].moveBy(u,0),c++;t||(u=0),a+=u}i.fromOrientedRange(i.ranges[0]),i.rangeList.attach(this.session),this.inVirtualSelectionMode=!1}},this.$getSelectedRows=function(e){return e=(e||this.getSelectionRange()).collapseRows(),{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},this.onCompositionStart=function(e){this.renderer.showComposition(this.getCursorPosition())},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);this.$blockScrolling++,t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection()),this.$blockScrolling--;var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new y(this.session,n.row,n.column),i=r.getCurrentToken(),s=i||r.stepForward();if(!s)return;var o,u=!1,a={},f=n.column-s.start,l,c={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(s.value.match(/[{}()\[\]]/g))for(;f=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),this.$blockScrolling-=1,r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.$blockScrolling+=1,this.session.unfold(e),this.selection.setSelectionRange(e),this.$blockScrolling-=1;var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},this.undo=function(){this.$blockScrolling++,this.session.getUndoManager().undo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.$blockScrolling++,this.session.getUndoManager().redo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy()},this.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.topwindow.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.off("changeSelection",s),this.renderer.off("afterRender",u),this.renderer.off("beforeRender",o)}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!="wide",i.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e))}}).call(b.prototype),g.defineOptions(b.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.$resetCursorStyle()},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},keyboardHandler:{set:function(e){this.setKeyboardHandler(e)},get:function(){return this.keybindingId},handlesSet:!0},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",showLineNumbers:"renderer",showGutter:"renderer",displayIndentGuides:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",tabSize:"session",wrap:"session",indentedSoftWrap:"session",foldStyle:"session",mode:"session"}),t.Editor=b}),ace.define("ace/undomanager",["require","exports","module"],function(e,t,n){"use strict";var r=function(){this.reset()};(function(){function e(e){return{action:e.action,start:e.start,end:e.end,lines:e.lines.length==1?null:e.lines,text:e.lines.length==1?e.lines[0]:null}}function t(e){return{action:e.action,start:e.start,end:e.end,lines:e.lines||[e.text]}}function n(e,t){var n=new Array(e.length);for(var r=0;r0},this.hasRedo=function(){return this.$redoStack.length>0},this.markClean=function(){this.dirtyCounter=0},this.isClean=function(){return this.dirtyCounter===0},this.$serializeDeltas=function(t){return n(t,e)},this.$deserializeDeltas=function(e){return n(e,t)}}).call(r.prototype),t.UndoManager=r}),ace.define("ace/layer/gutter",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/lang"),o=e("../lib/event_emitter").EventEmitter,u=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_gutter-layer",e.appendChild(this.element),this.setShowFoldWidgets(this.$showFoldWidgets),this.gutterWidth=0,this.$annotations=[],this.$updateAnnotations=this.$updateAnnotations.bind(this),this.$cells=[]};(function(){i.implement(this,o),this.setSession=function(e){this.session&&this.session.removeEventListener("change",this.$updateAnnotations),this.session=e,e&&e.on("change",this.$updateAnnotations)},this.addGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.addGutterDecoration"),this.session.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.removeGutterDecoration"),this.session.removeGutterDecoration(e,t)},this.setAnnotations=function(e){this.$annotations=[];for(var t=0;to&&(v=s.end.row+1,s=t.getNextFoldLine(v,s),o=s?s.start.row:Infinity);if(v>i){while(this.$cells.length>d+1)p=this.$cells.pop(),this.element.removeChild(p.element);break}p=this.$cells[++d],p||(p={element:null,textNode:null,foldWidget:null},p.element=r.createElement("div"),p.textNode=document.createTextNode(""),p.element.appendChild(p.textNode),this.element.appendChild(p.element),this.$cells[d]=p);var m="ace_gutter-cell ";a[v]&&(m+=a[v]),f[v]&&(m+=f[v]),this.$annotations[v]&&(m+=this.$annotations[v].className),p.element.className!=m&&(p.element.className=m);var g=t.getRowLength(v)*e.lineHeight+"px";g!=p.element.style.height&&(p.element.style.height=g);if(u){var y=u[v];y==null&&(y=u[v]=t.getFoldWidget(v))}if(y){p.foldWidget||(p.foldWidget=r.createElement("span"),p.element.appendChild(p.foldWidget));var m="ace_fold-widget ace_"+y;y=="start"&&v==o&&vn.right-t.right)return"foldWidgets"}}).call(u.prototype),t.Gutter=u}),ace.define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../range").Range,i=e("../lib/dom"),s=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){function e(e,t,n,r){return(e?1:0)|(t?2:0)|(n?4:0)|(r?8:0)}this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.update=function(e){var e=e||this.config;if(!e)return;this.config=e;var t=[];for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,i,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type=="text"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+" ace_start"+" ace_br15",e)}this.element.innerHTML=t.join("")},this.$getTop=function(e,t){return(e-t.firstRowScreen)*t.lineHeight},this.drawTextMarker=function(t,n,i,s,o){var u=this.session,a=n.start.row,f=n.end.row,l=a,c=0,h=0,p=u.getScreenLastRowColumn(l),d=new r(l,n.start.column,l,h);for(;l<=f;l++)d.start.row=d.end.row=l,d.start.column=l==a?n.start.column:u.getRowWrapIndent(l),d.end.column=p,c=h,h=p,p=l+1p,l==f),s,l==f?0:1,o)},this.drawMultiLineMarker=function(e,t,n,r,i){var s=this.$padding,o=r.lineHeight,u=this.$getTop(t.start.row,r),a=s+t.start.column*r.characterWidth;i=i||"",e.push("
"),u=this.$getTop(t.end.row,r);var f=t.end.column*r.characterWidth;e.push("
"),o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<=0)return;u=this.$getTop(t.start.row+1,r);var l=(t.start.column?1:0)|(t.end.column?0:8);e.push("
")},this.drawSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;e.push("
")},this.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),e.push("
")},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;e.push("
")}}).call(s.prototype),t.Marker=s}),ace.define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this)};(function(){r.implement(this,u),this.EOF_CHAR="\u00b6",this.EOL_CHAR_LF="\u00ac",this.EOL_CHAR_CRLF="\u00a4",this.EOL_CHAR=this.EOL_CHAR_LF,this.TAB_CHAR="\u2014",this.SPACE_CHAR="\u00b7",this.$padding=0,this.$updateEolChar=function(){var e=this.session.doc.getNewLineCharacter()=="\n"?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=e)return this.EOL_CHAR=e,!0},this.setPadding=function(e){this.$padding=e,this.element.style.padding="0 "+e+"px"},this.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},this.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},this.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},this.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},this.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},this.setSession=function(e){this.session=e,e&&this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;n"+s.stringRepeat(this.TAB_CHAR,n)+""):t.push(s.stringRepeat(" ",n));if(this.displayIndentGuides){this.$indentGuideRe=/\s\S| \t|\t |\s$/;var r="ace_indent-guide",i="",o="";if(this.showInvisibles){r+=" ace_invisible",i=" ace_invisible_space",o=" ace_invisible_tab";var u=s.stringRepeat(this.SPACE_CHAR,this.tabSize),a=s.stringRepeat(this.TAB_CHAR,this.tabSize)}else var u=s.stringRepeat(" ",this.tabSize),a=u;this.$tabStrings[" "]=""+u+"",this.$tabStrings[" "]=""+a+""}},this.updateLines=function(e,t,n){(this.config.lastRow!=e.lastRow||this.config.firstRow!=e.firstRow)&&this.scrollLines(e),this.config=e;var r=Math.max(t,e.firstRow),i=Math.min(n,e.lastRow),s=this.element.childNodes,o=0;for(var u=e.firstRow;uf&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),f=a?a.start.row:Infinity);if(u>i)break;var l=s[o++];if(l){var c=[];this.$renderLine(c,u,!this.$useLineGroups(),u==f?a:!1),l.style.height=e.lineHeight*this.session.getRowLength(u)+"px",l.innerHTML=c.join("")}u++}},this.scrollLines=function(e){var t=this.config;this.config=e;if(!t||t.lastRow0;r--)n.removeChild(n.firstChild);if(t.lastRow>e.lastRow)for(var r=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);r>0;r--)n.removeChild(n.lastChild);if(e.firstRowt.lastRow){var i=this.$renderLinesFragment(e,t.lastRow+1,e.lastRow);n.appendChild(i)}},this.$renderLinesFragment=function(e,t,n){var r=this.element.ownerDocument.createDocumentFragment(),s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=i.createElement("div"),f=[];this.$renderLine(f,s,!1,s==u?o:!1),a.innerHTML=f.join("");if(this.$useLineGroups())a.className="ace_line_group",r.appendChild(a),a.style.height=e.lineHeight*this.session.getRowLength(s)+"px";else while(a.firstChild)r.appendChild(a.firstChild);s++}return r},this.update=function(e){this.config=e;var t=[],n=e.firstRow,r=e.lastRow,i=n,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>r)break;this.$useLineGroups()&&t.push("
"),this.$renderLine(t,i,!1,i==o?s:!1),this.$useLineGroups()&&t.push("
"),i++}this.element.innerHTML=t.join("")},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var i=this,o=/\t|&|<|>|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\u3000\uFEFF\uFFF9-\uFFFC])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]/g,u=function(e,n,r,o,u){if(n)return i.showInvisibles?""+s.stringRepeat(i.SPACE_CHAR,e.length)+"":e;if(e=="&")return"&";if(e=="<")return"<";if(e==">")return">";if(e==" "){var a=i.session.getScreenTabSize(t+o);return t+=a-1,i.$tabStrings[a]}if(e=="\u3000"){var f=i.showInvisibles?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",l=i.showInvisibles?i.SPACE_CHAR:"";return t+=1,""+l+""}return r?""+i.SPACE_CHAR+"":(t+=1,""+e+"")},a=r.replace(o,u);if(!this.$textToken[n.type]){var f="ace_"+n.type.replace(/\./g," ace_"),l="";n.type=="fold"&&(l=" style='width:"+n.value.length*this.config.characterWidth+"px;' "),e.push("",a,"")}else e.push(a);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);return r<=0||r>=n?t:t[0]==" "?(r-=r%this.tabSize,e.push(s.stringRepeat(this.$tabStrings[" "],r/this.tabSize)),t.substr(r)):t[0]==" "?(e.push(s.stringRepeat(this.$tabStrings[" "],r)),t.substr(r)):t},this.$renderWrappedLine=function(e,t,n,r){var i=0,o=0,u=n[0],a=0;for(var f=0;f=u)a=this.$renderToken(e,a,l,c.substring(0,u-i)),c=c.substring(u-i),i=u,r||e.push("","
"),e.push(s.stringRepeat("\u00a0",n.indent)),o++,a=0,u=n[o]||Number.MAX_VALUE;c.length!=0&&(i+=c.length,a=this.$renderToken(e,a,l,c))}}},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;s");if(i.length){var s=this.session.getRowSplitData(t);s&&s.length?this.$renderWrappedLine(e,i,s,n):this.$renderSimpleLine(e,i)}this.showInvisibles&&(r&&(t=r.end.row),e.push("",t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,"")),n||e.push("
")},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.lengthn-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(sn?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(a.prototype),t.Text=a}),ace.define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i,s=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),i===undefined&&(i=!("opacity"in this.element.style)),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=(i?this.$updateVisibility:this.$updateOpacity).bind(this)};(function(){this.$updateVisibility=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.visibility=e?"":"hidden"},this.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.opacity=e?"":"0"},this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&!i&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.$updateCursors=this.$updateOpacity.bind(this),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking"),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible)return;this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+n.column*this.config.characterWidth,i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.update=function(e){this.config=e;var t=this.session.$selectionMarkers,n=0,r=0;if(t===undefined||t.length===0)t=[{cursor:null}];for(var n=0,i=t.length;ne.height+e.offset||s.top<0)&&n>1)continue;var o=(this.cursors[r++]||this.addCursor()).style;this.drawCursor?this.drawCursor(o,s,e,t[n],this.session):(o.left=s.left+"px",o.top=s.top+"px",o.width=e.characterWidth+"px",o.height=e.lineHeight+"px")}while(this.cursors.length>r)this.removeCursor();var u=this.session.getOverwrite();this.$setOverwrite(u),this.$pixelPos=s,this.restartTimer()},this.drawCursor=null,this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(s.prototype),t.Cursor=s}),ace.define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/event_emitter").EventEmitter,u=function(e){this.element=i.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+this.classSuffix,this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,s.addListener(this.element,"scroll",this.onScroll.bind(this)),s.addListener(this.element,"mousedown",s.preventDefault)};(function(){r.implement(this,o),this.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e}}).call(u.prototype);var a=function(e,t){u.call(this,e),this.scrollTop=0,t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+"px"};r.inherits(a,u),function(){this.classSuffix="-v",this.onScroll=function(){this.skipEvent||(this.scrollTop=this.element.scrollTop,this._emit("scroll",{data:this.scrollTop})),this.skipEvent=!1},this.getWidth=function(){return this.isVisible?this.width:0},this.setHeight=function(e){this.element.style.height=e+"px"},this.setInnerHeight=function(e){this.inner.style.height=e+"px"},this.setScrollHeight=function(e){this.inner.style.height=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=this.element.scrollTop=e)}}.call(a.prototype);var f=function(e,t){u.call(this,e),this.scrollLeft=0,this.height=t.$scrollbarWidth,this.inner.style.height=this.element.style.height=(this.height||15)+5+"px"};r.inherits(f,u),function(){this.classSuffix="-h",this.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},this.getHeight=function(){return this.isVisible?this.height:0},this.setWidth=function(e){this.element.style.width=e+"px"},this.setInnerWidth=function(e){this.inner.style.width=e+"px"},this.setScrollWidth=function(e){this.inner.style.width=e+"px"},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}.call(f.prototype),t.ScrollBar=a,t.ScrollBarV=a,t.ScrollBarH=f,t.VScrollBar=a,t.HScrollBar=f}),ace.define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){"use strict";var r=e("./lib/event"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.window=t||window};(function(){this.schedule=function(e){this.changes=this.changes|e;if(!this.pending&&this.changes){this.pending=!0;var t=this;r.nextFrame(function(){t.pending=!1;var e;while(e=t.changes)t.changes=0,t.onRender(e)},this.window)}}}).call(i.prototype),t.RenderLoop=i}),ace.define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=0,f=t.FontMetrics=function(e){this.el=i.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),a||this.$testFractionalRect(),this.$measureNode.innerHTML=s.stringRepeat("X",a),this.$characterSize={width:0,height:0},this.checkForSizeChanges()};(function(){r.implement(this,u),this.$characterSize={width:0,height:0},this.$testFractionalRect=function(){var e=i.createElement("div");this.$setMeasureNodeStyles(e.style),e.style.width="0.2px",document.documentElement.appendChild(e);var t=e.getBoundingClientRect().width;t>0&&t<1?a=50:a=100,e.parentNode.removeChild(e)},this.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="0px",e.visibility="hidden",e.position="absolute",e.whiteSpace="pre",o.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(){var e=this.$measureSizes();if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=setInterval(function(){e.checkForSizeChanges()},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&(clearInterval(this.$pollSizeChangesTimer),this.$pollSizeChangesTimer=0)},this.$measureSizes=function(){if(a===50){var e=null;try{e=this.$measureNode.getBoundingClientRect()}catch(t){e={width:0,height:0}}var n={height:e.height,width:e.width/a}}else var n={height:this.$measureNode.clientHeight,width:this.$measureNode.clientWidth/a};return n.width===0||n.height===0?null:n},this.$measureCharWidth=function(e){this.$main.innerHTML=s.stringRepeat(e,a);var t=this.$main.getBoundingClientRect();return t.width/a},this.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(t=this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)}}).call(f.prototype)}),ace.define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/lib/useragent","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./config"),o=e("./lib/useragent"),u=e("./layer/gutter").Gutter,a=e("./layer/marker").Marker,f=e("./layer/text").Text,l=e("./layer/cursor").Cursor,c=e("./scrollbar").HScrollBar,h=e("./scrollbar").VScrollBar,p=e("./renderloop").RenderLoop,d=e("./layer/font_metrics").FontMetrics,v=e("./lib/event_emitter").EventEmitter,m='.ace_editor {position: relative;overflow: hidden;font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'source-code-pro\', monospace;direction: ltr;}.ace_scroller {position: absolute;overflow: hidden;top: 0;bottom: 0;background-color: inherit;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;cursor: text;}.ace_content {position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;min-width: 100%;}.ace_dragging .ace_scroller:before{position: absolute;top: 0;left: 0;right: 0;bottom: 0;content: \'\';background: rgba(250, 250, 250, 0.01);z-index: 1000;}.ace_dragging.ace_dark .ace_scroller:before{background: rgba(0, 0, 0, 0.01);}.ace_selecting, .ace_selecting * {cursor: text !important;}.ace_gutter {position: absolute;overflow : hidden;width: auto;top: 0;bottom: 0;left: 0;cursor: default;z-index: 4;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");}.ace_scrollbar {position: absolute;right: 0;bottom: 0;z-index: 6;}.ace_scrollbar-inner {position: absolute;cursor: text;left: 0;top: 0;}.ace_scrollbar-v{overflow-x: hidden;overflow-y: scroll;top: 0;}.ace_scrollbar-h {overflow-x: scroll;overflow-y: hidden;left: 0;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;font: inherit;padding: 0 1px;margin: 0 -1px;text-indent: -1em;-ms-user-select: text;-moz-user-select: text;-webkit-user-select: text;user-select: text;white-space: pre!important;}.ace_text-input.ace_composition {background: inherit;color: inherit;z-index: 1000;opacity: 1;text-indent: 0;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;word-wrap: normal;white-space: pre;height: 100%;width: 100%;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;}.ace_text-layer {font: inherit !important;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;border-left: 2px solid;transform: translatez(0);}.ace_slim-cursors .ace_cursor {border-left-width: 1px;}.ace_overwrite-cursors .ace_cursor {border-left-width: 0;border-bottom: 1px solid;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_smooth-blinking .ace_cursor {-webkit-transition: opacity 0.18s;transition: opacity 0.18s;}.ace_editor.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;}.ace_line .ace_fold {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");}.ace_tooltip {background-color: #FFF;background-image: -webkit-linear-gradient(top, transparent, rgba(0, 0, 0, 0.1));background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;max-width: 100%;padding: 3px 4px;position: fixed;z-index: 999999;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;cursor: default;white-space: pre;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;pointer-events: none;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block; }.ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");}.ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}.ace_dark .ace_fold-widget {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");}.ace_dark .ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {-webkit-transition: opacity 0.4s ease 0.05s;transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {-webkit-transition: opacity 0.05s ease 0.05s;transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}.ace_br1 {border-top-left-radius : 3px;}.ace_br2 {border-top-right-radius : 3px;}.ace_br3 {border-top-left-radius : 3px; border-top-right-radius: 3px;}.ace_br4 {border-bottom-right-radius: 3px;}.ace_br5 {border-top-left-radius : 3px; border-bottom-right-radius: 3px;}.ace_br6 {border-top-right-radius : 3px; border-bottom-right-radius: 3px;}.ace_br7 {border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px;}.ace_br8 {border-bottom-left-radius : 3px;}.ace_br9 {border-top-left-radius : 3px; border-bottom-left-radius: 3px;}.ace_br10{border-top-right-radius : 3px; border-bottom-left-radius: 3px;}.ace_br11{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br12{border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br13{border-top-left-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br14{border-top-right-radius : 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}.ace_br15{border-top-left-radius : 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px;}';i.importCssString(m,"ace_editor.css");var g=function(e,t){var n=this;this.container=e||i.createElement("div"),this.$keepTextAreaAtCursor=!o.isOldIE,i.addCssClass(this.container,"ace_editor"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new u(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new a(this.content);var r=this.$textLayer=new f(this.content);this.canvas=r.element,this.$markerFront=new a(this.content),this.$cursorLayer=new l(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new h(this.container,this),this.scrollBarH=new c(this.container,this),this.scrollBarV.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new d(this.container),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.addEventListener("changeCharacterSize",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$loop=new p(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),s.resetOptions(this),s._emit("renderer",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,v),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin()},this.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode)},this.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},this.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar()},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.$changes=0,this.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},this.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0),this.scrollBarV.scrollLeft=this.scrollBarV.scrollTop=null},this.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var i=0,s=this.$size,o={width:s.width,height:s.height,scrollerHeight:s.scrollerHeight,scrollerWidth:s.scrollerWidth};r&&(e||s.height!=r)&&(s.height=r,i|=this.CHANGE_SIZE,s.scrollerHeight=s.height,this.$horizScroll&&(s.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",i|=this.CHANGE_SCROLL);if(n&&(e||s.width!=n)){i|=this.CHANGE_SIZE,s.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,this.scrollBarH.element.style.left=this.scroller.style.left=t+"px",s.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()),this.scrollBarH.element.style.right=this.scroller.style.right=this.scrollBarV.getWidth()+"px",this.scroller.style.bottom=this.scrollBarH.getHeight()+"px";if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)i|=this.CHANGE_FULL}return s.$dirty=!n||!r,i&&this._signal("resize",o),i},this.onGutterResize=function(){var e=this.$showGutter?this.$gutter.offsetWidth:0;e!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,e,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):(this.$computeLayerConfig(),this.$loop.schedule(this.CHANGE_MARKER))},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption("showInvisibles",e)},this.getShowInvisibles=function(){return this.getOption("showInvisibles")},this.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},this.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},this.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},this.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},this.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},this.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},this.getShowGutter=function(){return this.getOption("showGutter")},this.setShowGutter=function(e){return this.setOption("showGutter",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.$updateGutterLineHighlight=function(){var e=this.$cursorLayer.$pixelPos,t=this.layerConfig.lineHeight;if(this.session.getUseWrapMode()){var n=this.session.selection.getCursor();n.column=0,e=this.$cursorLayer.getPixelPosition(n,!0),t*=this.session.getRowLength(n.row)}this.$gutterLineHighlight.style.top=e.top-this.layerConfig.offset+"px",this.$gutterLineHighlight.style.height=t+"px"},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=this.characterWidth*this.$printMarginColumn+this.$padding+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.scroller},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){if(!this.$keepTextAreaAtCursor)return;var e=this.layerConfig,t=this.$cursorLayer.$pixelPos.top,n=this.$cursorLayer.$pixelPos.left;t-=e.offset;var r=this.textarea.style,i=this.lineHeight;if(t<0||t>e.height-i){r.top=r.left="0";return}var s=this.characterWidth;if(this.$composition){var o=this.textarea.value.replace(/^\x01+/,"");s*=this.session.$getStringScreenWidth(o)[0]+2,i+=2}n-=this.scrollLeft,n>this.$size.scrollerWidth-s&&(n=this.$size.scrollerWidth-s),n+=this.gutterWidth,r.height=i+"px",r.width=s+"px",r.left=Math.min(n,this.$size.scrollerWidth-s)+"px",r.top=Math.min(t,this.$size.height-i)+"px"},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=this.layerConfig,t=e.lastRow,n=this.session.documentToScreenRow(t,0)*e.lineHeight;return n-this.session.getScrollTop()>e.height-e.lineHeight?t-1:t},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},this.getVScrollBarAlwaysVisible=function(){return this.$vScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},this.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$frozen=!1,this.freeze=function(){this.$frozen=!0},this.unfreeze=function(){this.$frozen=!1},this.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender");var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-this.layerConfig.firstRow)*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),this.$gutterLayer.element.style.marginTop=-n.offset+"px",this.content.style.marginTop=-n.offset+"px",this.content.style.width=n.width+2*this.$padding+"px",this.content.style.height=n.minHeight+"px"}e&this.CHANGE_H_SCROLL&&(this.content.style.marginLeft=-this.scrollLeft+"px",this.scroller.className=this.scrollLeft<=0?"ace_scroller":"ace_scroller ace_scroll-left");if(e&this.CHANGE_FULL){this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this._signal("afterRender");return}if(e&this.CHANGE_SCROLL){e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this.$moveTextAreaToCursor(),this._signal("afterRender");return}e&this.CHANGE_TEXT?(this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n):(e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER)&&this.$showGutter&&this.$gutterLayer.update(n),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal("afterRender")},this.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.min(t,Math.max((this.$minLines||1)*this.lineHeight,e))+this.scrollMargin.v+(this.$extraHeight||0);this.$horizScroll&&(n+=this.scrollBarH.getHeight()),this.$maxPixelHeight&&n>this.$maxPixelHeight&&(n=this.$maxPixelHeight);var r=e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||r!=this.$vScroll){r!=this.$vScroll&&(this.$vScroll=r,this.scrollBarV.setVisible(r));var i=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,i,n),this.desiredHeight=n,this._signal("autosize")}},this.$computeLayerConfig=function(){var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.$getLongestLine(),o=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-s-2*this.$padding<0),u=this.$horizScroll!==o;u&&(this.$horizScroll=o,this.scrollBarH.setVisible(o));var a=this.$vScroll;this.$maxLines&&this.lineHeight>1&&this.$autosize();var f=this.scrollTop%this.lineHeight,l=t.scrollerHeight+this.lineHeight,c=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=c;var h=this.scrollMargin;this.session.setScrollTop(Math.max(-h.top,Math.min(this.scrollTop,i-t.scrollerHeight+h.bottom))),this.session.setScrollLeft(Math.max(-h.left,Math.min(this.scrollLeft,s+2*this.$padding-t.scrollerWidth+h.right)));var p=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+c<0||this.scrollTop>h.top),d=a!==p;d&&(this.$vScroll=p,this.scrollBarV.setVisible(p));var v=Math.ceil(l/this.lineHeight)-1,m=Math.max(0,Math.round((this.scrollTop-f)/this.lineHeight)),g=m+v,y,b,w=this.lineHeight;m=e.screenToDocumentRow(m,0);var E=e.getFoldLine(m);E&&(m=E.start.row),y=e.documentToScreenRow(m,0),b=e.getRowLength(m)*w,g=Math.min(e.screenToDocumentRow(g,0),e.getLength()-1),l=t.scrollerHeight+e.getRowLength(g)*w+b,f=this.scrollTop-y*w;var S=0;this.layerConfig.width!=s&&(S=this.CHANGE_H_SCROLL);if(u||d)S=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),d&&(s=this.$getLongestLine());return this.layerConfig={width:s,padding:this.$padding,firstRow:m,firstRowScreen:y,lastRow:g,lineHeight:w,characterWidth:this.characterWidth,minHeight:l,maxHeight:i,offset:f,gutterOffset:Math.max(0,Math.ceil((f+t.height-t.scrollerHeight)/w)),height:this.$size.scrollerHeight},S},this.$updateLines=function(){var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(ts?(t&&a+o>s+this.lineHeight&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-ui?(i=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},this.pixelToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=(e+this.scrollLeft-n.left-this.$padding)/this.characterWidth,i=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),s=Math.round(r);return{row:i,column:s,side:r-s>0?1:-1}},this.screenToTextCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=Math.round((e+this.scrollLeft-n.left-this.$padding)/this.characterWidth),i=(t+this.scrollTop-n.top)/this.lineHeight;return this.session.screenToDocumentPosition(i,Math.max(r,0))},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+Math.round(r.column*this.characterWidth),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition||(this.$composition={keepTextAreaAtCursor:this.$keepTextAreaAtCursor,cssText:this.textarea.style.cssText}),this.$keepTextAreaAtCursor=!0,i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor()},this.setCompositionText=function(e){this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;i.removeCssClass(this.textarea,"ace_composition"),this.$keepTextAreaAtCursor=this.$composition.keepTextAreaAtCursor,this.textarea.style.cssText=this.$composition.cssText,this.$composition=null},this.setTheme=function(e,t){function o(r){if(n.$themeId!=e)return t&&t();if(!r.cssClass)return;i.importCssString(r.cssText,r.cssClass,n.container.ownerDocument),n.theme&&i.removeCssClass(n.container,n.theme.cssClass);var s="padding"in r?r.padding:"padding"in(n.theme||{})?4:n.$padding;n.$padding&&s!=n.$padding&&n.setPadding(s),n.$theme=r.cssClass,n.theme=r,i.addCssClass(n.container,r.cssClass),i.setCssClass(n.container,"ace_dark",r.isDark),n.$size&&(n.$size.width=0,n.$updateSizeAsync()),n._dispatchEvent("themeLoaded",{theme:r}),t&&t()}var n=this;this.$themeId=e,n._dispatchEvent("themeChange",{theme:e});if(!e||typeof e=="string"){var r=e||this.$options.theme.initialValue;s.loadModule(["theme",r],o)}else o(e)},this.getTheme=function(){return this.$themeId},this.setStyle=function(e,t){i.setCssClass(this.container,e,t!==!1)},this.unsetStyle=function(e){i.removeCssClass(this.container,e)},this.setCursorStyle=function(e){this.scroller.style.cursor!=e&&(this.scroller.style.cursor=e)},this.setMouseCursor=function(e){this.scroller.style.cursor=e},this.destroy=function(){this.$textLayer.destroy(),this.$cursorLayer.destroy()}}).call(g.prototype),s.defineOptions(g.prototype,"renderer",{animatedScroll:{initialValue:!1},showInvisibles:{set:function(e){this.$textLayer.setShowInvisibles(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!1},showPrintMargin:{set:function(){this.$updatePrintMargin()},initialValue:!0},printMarginColumn:{set:function(){this.$updatePrintMargin()},initialValue:80},printMargin:{set:function(e){typeof e=="number"&&(this.$printMarginColumn=e),this.$showPrintMargin=!!e,this.$updatePrintMargin()},get:function(){return this.$showPrintMargin&&this.$printMarginColumn}},showGutter:{set:function(e){this.$gutter.style.display=e?"block":"none",this.$loop.schedule(this.CHANGE_FULL),this.onGutterResize()},initialValue:!0},fadeFoldWidgets:{set:function(e){i.setCssClass(this.$gutter,"ace_fade-fold-widgets",e)},initialValue:!1},showFoldWidgets:{set:function(e){this.$gutterLayer.setShowFoldWidgets(e)},initialValue:!0},showLineNumbers:{set:function(e){this.$gutterLayer.setShowLineNumbers(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},displayIndentGuides:{set:function(e){this.$textLayer.setDisplayIndentGuides(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!0},highlightGutterLine:{set:function(e){if(!this.$gutterLineHighlight){this.$gutterLineHighlight=i.createElement("div"),this.$gutterLineHighlight.className="ace_gutter-active-line",this.$gutter.appendChild(this.$gutterLineHighlight);return}this.$gutterLineHighlight.style.display=e?"":"none",this.$cursorLayer.$pixelPos&&this.$updateGutterLineHighlight()},initialValue:!1,value:!0},hScrollBarAlwaysVisible:{set:function(e){(!this.$hScrollBarAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},vScrollBarAlwaysVisible:{set:function(e){(!this.$vScrollBarAlwaysVisible||!this.$vScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},fontSize:{set:function(e){typeof e=="number"&&(e+="px"),this.container.style.fontSize=e,this.updateFontSize()},initialValue:12},fontFamily:{set:function(e){this.container.style.fontFamily=e,this.updateFontSize()}},maxLines:{set:function(e){this.updateFull()}},minLines:{set:function(e){this.updateFull()}},maxPixelHeight:{set:function(e){this.updateFull()},initialValue:0},scrollPastEnd:{set:function(e){e=+e||0;if(this.$scrollPastEnd==e)return;this.$scrollPastEnd=e,this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:0,handlesSet:!0},fixedWidthGutter:{set:function(e){this.$gutterLayer.$fixedWidth=!!e,this.$loop.schedule(this.CHANGE_GUTTER)}},theme:{set:function(e){this.setTheme(e)},get:function(){return this.$themeId||this.theme},initialValue:"./theme/textmate",handlesSet:!0}}),t.VirtualRenderer=g}),ace.define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/net","ace/lib/event_emitter","ace/config"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/net"),s=e("../lib/event_emitter").EventEmitter,o=e("../config"),u=function(t,n,r,i){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.onMessage=this.onMessage.bind(this),e.nameToUrl&&!e.toUrl&&(e.toUrl=e.nameToUrl);if(o.get("packaged")||!e.toUrl)i=i||o.moduleUrl(n,"worker");else{var s=this.$normalizePath;i=i||s(e.toUrl("ace/worker/worker.js",null,"_"));var u={};t.forEach(function(t){u[t]=s(e.toUrl(t,null,"_").replace(/(\.js)?(\?.*)?$/,""))})}try{this.$worker=new Worker(i)}catch(a){if(!(a instanceof window.DOMException))throw a;var f=this.$workerBlob(i),l=window.URL||window.webkitURL,c=l.createObjectURL(f);this.$worker=new Worker(c),l.revokeObjectURL(c)}this.$worker.postMessage({init:!0,tlns:u,module:n,classname:r}),this.callbackId=1,this.callbacks={},this.$worker.onmessage=this.onMessage};(function(){r.implement(this,s),this.onMessage=function(e){var t=e.data;switch(t.type){case"event":this._signal(t.name,{data:t.data});break;case"call":var n=this.callbacks[t.id];n&&(n(t.data),delete this.callbacks[t.id]);break;case"error":this.reportError(t.data);break;case"log":window.console&&console.log&&console.log.apply(console,t.data)}},this.reportError=function(e){window.console&&console.error&&console.error(e)},this.$normalizePath=function(e){return i.qualifyURL(e)},this.terminate=function(){this._signal("terminate",{}),this.deltaQueue=null,this.$worker.terminate(),this.$worker=null,this.$doc&&this.$doc.off("change",this.changeListener),this.$doc=null},this.send=function(e,t){this.$worker.postMessage({command:e,args:t})},this.call=function(e,t,n){if(n){var r=this.callbackId++;this.callbacks[r]=n,t.push(r)}this.send(e,t)},this.emit=function(e,t){try{this.$worker.postMessage({event:e,data:{data:t.data}})}catch(n){console.error(n.stack)}},this.attachToDocument=function(e){this.$doc&&this.terminate(),this.$doc=e,this.call("setValue",[e.getValue()]),e.on("change",this.changeListener)},this.changeListener=function(e){this.deltaQueue||(this.deltaQueue=[],setTimeout(this.$sendDeltaQueue,0)),e.action=="insert"?this.deltaQueue.push(e.start,e.lines):this.deltaQueue.push(e.start,e.end)},this.$sendDeltaQueue=function(){var e=this.deltaQueue;if(!e)return;this.deltaQueue=null,e.length>50&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})},this.$workerBlob=function(e){var t="importScripts('"+i.qualifyURL(e)+"');";try{return new Blob([t],{type:"application/javascript"})}catch(n){var r=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder,s=new r;return s.append(t),s.getBlob("application/javascript")}}}).call(u.prototype);var a=function(e,t,n){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.callbackId=1,this.callbacks={},this.messageBuffer=[];var r=null,i=!1,u=Object.create(s),a=this;this.$worker={},this.$worker.terminate=function(){},this.$worker.postMessage=function(e){a.messageBuffer.push(e),r&&(i?setTimeout(f):f())},this.setEmitSync=function(e){i=e};var f=function(){var e=a.messageBuffer.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};u.postMessage=function(e){a.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},o.loadModule(["worker",t],function(e){r=new e[n](u);while(a.messageBuffer.length)f()})};a.prototype=u.prototype,t.UIWorkerClient=a,t.WorkerClient=u}),ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(this.$pos.row,this.$pos.column);var i=this.pos;i.$insertRight=!0,i.detach(),i.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);r.$insertRight=!0,r.detach(),e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1)})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e=this.pos.column&&t.start.column<=this.pos.column+this.length+1,s=t.start.column-this.pos.column;this.updateAnchors(e),i&&(this.length+=n);if(i&&!this.session.$fromUndo)if(e.action==="insert")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.insertMergedLines(a,e.lines)}else if(e.action==="remove")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};this.doc.remove(new r(a.row,a.column,a.row,a.column-n))}this.$updating=!1,this.updateMarkers()},this.updateAnchors=function(e){this.pos.onChange(e);for(var t=this.others.length;t--;)this.others[t].onChange(e);this.updateMarkers()},this.updateMarkers=function(){if(this.$updating)return;var e=this,t=this.session,n=function(n,i){t.removeMarker(n.markerId),n.markerId=t.addMarker(new r(n.row,n.column,n.row,n.column+e.length),i,null,!1)};n(this.pos,this.mainClass);for(var i=this.others.length;i--;)n(this.others[i],this.othersClass)},this.onCursorChange=function(e){if(this.$updating||!this.session)return;var t=this.session.selection.getCursor();t.row===this.pos.row&&t.column>=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},this.detach=function(){this.session.removeMarker(this.pos&&this.pos.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.session.setUndoSelect(!0),this.session=null},this.cancel=function(){if(this.$undoStackDepth===-1)return;var e=this.session.getUndoManager(),t=(e.$undoStack||e.$undostack).length-this.$undoStackDepth;for(var n=0;n1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length?this.$onRemoveRange(e):this.ranges[0]&&this.fromOrientedRange(this.ranges[0])},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal("removeRange",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var n=this.getRange(),r=this.isBackwards(),s=n.start.row,o=n.end.row;if(s==o){if(r)var u=n.end,a=n.start;else var u=n.start,a=n.end;this.addRange(i.fromPoints(a,a)),this.addRange(i.fromPoints(u,u));return}var f=[],l=this.getLineRange(s,!0);l.start.column=n.start.column,f.push(l);for(var c=s+1;c1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.selectionLead),s=this.session.documentToScreenPosition(this.selectionAnchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column0)d--;if(d>0){var m=0;while(r[m].isEmpty())m++}for(var g=d;g>=m;g--)r[g].isEmpty()&&r.splice(g,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges();var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;r0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.removeFullLines(u,f);p=this.$reAlignText(p,l),this.session.insert({row:u,column:0},p.join("\n")+"\n"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),io?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(" ",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o," ")+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),st[3].length&&(o=t[3].length),t):[e]}).map(t?f:n?r?l:f:c)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off("addRange",this.$onAddRange),n.multiSelect.off("removeRange",this.$onRemoveRange),n.multiSelect.off("multiSelect",this.$onMultiSelect),n.multiSelect.off("singleSelect",this.$onSingleSelect),n.multiSelect.lead.off("change",this.$checkMultiselectChange),n.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e("./config").defineOptions(d.prototype,"editor",{enableMultiselect:{set:function(e){m(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",o)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",o))},value:!0},enableBlockSelect:{set:function(e){this.$blockSelectEnabled=e},value:!0}})}),ace.define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../../range").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?"start":t=="markbeginend"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++tf){var h=e.getLine(l).length;return new r(f,u,l,h)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=e.getFoldWidget(u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),ace.define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),ace.define("ace/line_widgets",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e){this.session=e,this.session.widgetManager=this,this.session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on("change",this.updateOnChange),this.session.on("changeFold",this.updateOnFold),this.session.on("changeEditor",this.$onChangeEditor)}var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./range").Range;(function(){this.getRowLength=function(e){var t;return this.lineWidgets?t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0:t=0,!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.$getWidgetScreenLength=function(){var e=0;return this.lineWidgets.forEach(function(t){t&&t.rowCount&&!t.hidden&&(e+=t.rowCount)}),e},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach();if(this.editor==e)return;this.detach(),this.editor=e,e&&(e.widgetManager=this,e.renderer.on("beforeRender",this.measureWidgets),e.renderer.on("afterRender",this.renderWidgets))},this.detach=function(e){var t=this.editor;if(!t)return;this.editor=null,t.widgetManager=null,t.renderer.off("beforeRender",this.measureWidgets),t.renderer.off("afterRender",this.renderWidgets);var n=this.session.lineWidgets;n&&n.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})},this.updateOnFold=function(e,t){var n=t.lineWidgets;if(!n||!e.action)return;var r=e.data,i=r.start.row,s=r.end.row,o=e.action=="add";for(var u=i+1;u0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;if(u.hidden){u.el.style.top=-100-(u.pixelHeight||0)+"px";continue}u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+"px";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+"px",u.fullWidth&&u.screenWidth&&(u.el.style.minWidth=n.width+2*n.padding+"px"),u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+"px":u.el.style.right=""}}}).call(o.prototype),t.LineWidgets=o}),ace.define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[s]);if(o>0)r=s+1;else{if(!(o<0))return s;i=s-1}}return-(r+1)}function u(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.length)return;var i=o(r,{row:t,column:-1},s.comparePoints);i<0&&(i=-i-1),i>=r.length?i=n>0?0:r.length-1:i===0&&n<0&&(i=r.length-1);var u=r[i];if(!u||!n)return;if(u.row===t){do u=r[i+=n];while(u&&u.row===t);if(!u)return r.slice()}var a=[];t=u.row;do a[n<0?"unshift":"push"](u),u=r[i+=n];while(u&&u.row==t);return a.length&&a}var r=e("../line_widgets").LineWidgets,i=e("../lib/dom"),s=e("../range").Range;t.showErrorMarker=function(e,t){var n=e.session;n.widgetManager||(n.widgetManager=new r(n),n.widgetManager.attach(e));var s=e.getCursorPosition(),o=s.row,a=n.widgetManager.getWidgetsAtRow(o).filter(function(e){return e.type=="errorMarker"})[0];a?a.destroy():o-=t;var f=u(n,o,t),l;if(f){var c=f[0];s.column=(c.pos&&typeof c.column!="number"?c.pos.sc:c.column)||0,s.row=c.row,l=e.renderer.$gutterLayer.$annotations[s.row]}else{if(a)return;l={text:["Looks good!"],className:"ace_ok"}}e.session.unfold(s.row),e.selection.moveToPosition(s);var h={row:s.row,fixedWidth:!0,coverGutter:!0,el:i.createElement("div"),type:"errorMarker"},p=h.el.appendChild(i.createElement("div")),d=h.el.appendChild(i.createElement("div"));d.className="error_widget_arrow "+l.className;var v=e.renderer.$cursorLayer.getPixelPosition(s).left;d.style.left=v+e.renderer.gutterWidth-5+"px",h.el.className="error_widget_wrapper",p.className="error_widget "+l.className,p.innerHTML=l.text.join("
"),p.appendChild(i.createElement("div"));var m=function(e,t,n){if(t===0&&(n==="esc"||n==="return"))return h.destroy(),{command:"null"}};h.destroy=function(){if(e.$mouseHandler.isMousePressed)return;e.keyBinding.removeKeyboardHandler(m),n.widgetManager.removeLineWidget(h),e.off("changeSelection",h.destroy),e.off("changeSession",h.destroy),e.off("mouseup",h.destroy),e.off("change",h.destroy)},e.keyBinding.addKeyboardHandler(m),e.on("changeSelection",h.destroy),e.on("changeSession",h.destroy),e.on("mouseup",h.destroy),e.on("change",h.destroy),e.session.widgetManager.addLineWidget(h),h.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:h.el.offsetHeight})},i.importCssString(" .error_widget_wrapper { background: inherit; color: inherit; border:none } .error_widget { border-top: solid 2px; border-bottom: solid 2px; margin: 5px 0; padding: 10px 40px; white-space: pre-wrap; } .error_widget.ace_error, .error_widget_arrow.ace_error{ border-color: #ff5a5a } .error_widget.ace_warning, .error_widget_arrow.ace_warning{ border-color: #F1D817 } .error_widget.ace_info, .error_widget_arrow.ace_info{ border-color: #5a5a5a } .error_widget.ace_ok, .error_widget_arrow.ace_ok{ border-color: #5aaa5a } .error_widget_arrow { position: absolute; border: solid 5px; border-top-color: transparent!important; border-right-color: transparent!important; border-left-color: transparent!important; top: -5px; }","")}),ace.define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"],function(e,t,n){"use strict";e("./lib/fixoldbrowsers");var r=e("./lib/dom"),i=e("./lib/event"),s=e("./editor").Editor,o=e("./edit_session").EditSession,u=e("./undomanager").UndoManager,a=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.require=e,t.edit=function(e){if(typeof e=="string"){var n=e;e=document.getElementById(n);if(!e)throw new Error("ace.edit can't find div #"+n)}if(e&&e.env&&e.env.editor instanceof s)return e.env.editor;var o="";if(e&&/input|textarea/i.test(e.tagName)){var u=e;o=u.value,e=r.createElement("pre"),u.parentNode.replaceChild(e,u)}else e&&(o=r.getInnerText(e),e.innerHTML="");var f=t.createEditSession(o),l=new s(new a(e));l.setSession(f);var c={document:f,editor:l,onResize:l.resize.bind(l,null)};return u&&(c.textarea=u),i.addListener(window,"resize",c.onResize),l.on("destroy",function(){i.removeListener(window,"resize",c.onResize),c.editor.container.env=null}),l.container.env=l.env=c,l},t.createEditSession=function(e,t){var n=new o(e,t);return n.setUndoManager(new u),n},t.EditSession=o,t.UndoManager=u,t.version="1.2.3"}); - (function() { - ace.require(["ace/ace"], function(a) { - a && a.config.init(true); - if (!window.ace) - window.ace = a; - for (var key in a) if (a.hasOwnProperty(key)) - window.ace[key] = a[key]; - }); - })(); diff --git a/src/Umbraco.Web.UI.Client/lib/ace/mode-css.js b/src/Umbraco.Web.UI.Client/lib/ace/mode-css.js deleted file mode 100644 index 02e4cc380b..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/ace/mode-css.js +++ /dev/null @@ -1 +0,0 @@ -ace.define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},h=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},p=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return h(a,l,"{","}");if(p.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(p.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(p.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var d=u.substring(s.column,s.column+1);if(d=="}"){var v=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(v!==null&&p.isAutoInsertedClosing(s,u,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var m="";p.isMaybeInsertedClosing(s,u)&&(m=o.stringRepeat("}",f.maybeInsertedBrackets),p.clearMaybeInsertedClosing());var d=u.substring(s.column,s.column+1);if(d==="}"){var g=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!g)return null;var y=this.$getIndent(r.getLine(g.row))}else{if(!m){p.clearMaybeInsertedClosing();return}var y=this.$getIndent(u)}var b=y+r.getTabString();return{text:"\n"+b+"\n"+y+m,selection:[1,b.length,1,b.length]}}p.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"(",")");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"[","]");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return h(o,u,s,s);if(!u){var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column),p=f.substring(a.column,a.column+1),d=r.getTokenAt(a.row,a.column),v=r.getTokenAt(a.row,a.column+1);if(l=="\\"&&d&&/escape/.test(d.type))return null;var m=d&&/string|escape/.test(d.type),g=!v||/string|escape/.test(v.type),y;if(p==s)y=m!==g;else{if(m&&!g)return null;if(m&&g)return null;var b=r.$mode.tokenRe;b.lastIndex=0;var w=b.test(l);b.lastIndex=0;var E=b.test(l);if(w||E)return null;if(p&&!/[\s;,.})\]\\]/.test(p))return null;y=!0}return{text:y?s+s:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};p.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},p.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},p.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},p.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},p.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},p.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},p.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},p.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(p,i),t.CstyleBehaviour=p}),ace.define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),ace.define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}) diff --git a/src/Umbraco.Web.UI.Client/lib/ace/mode-javascript.js b/src/Umbraco.Web.UI.Client/lib/ace/mode-javascript.js deleted file mode 100644 index 20afa82e76..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/ace/mode-javascript.js +++ /dev/null @@ -1 +0,0 @@ -ace.define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),ace.define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?\:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||!e.noJSX)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},h=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},p=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return h(a,l,"{","}");if(p.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(p.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(p.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var d=u.substring(s.column,s.column+1);if(d=="}"){var v=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(v!==null&&p.isAutoInsertedClosing(s,u,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var m="";p.isMaybeInsertedClosing(s,u)&&(m=o.stringRepeat("}",f.maybeInsertedBrackets),p.clearMaybeInsertedClosing());var d=u.substring(s.column,s.column+1);if(d==="}"){var g=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!g)return null;var y=this.$getIndent(r.getLine(g.row))}else{if(!m){p.clearMaybeInsertedClosing();return}var y=this.$getIndent(u)}var b=y+r.getTabString();return{text:"\n"+b+"\n"+y+m,selection:[1,b.length,1,b.length]}}p.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"(",")");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"[","]");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return h(o,u,s,s);if(!u){var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column),p=f.substring(a.column,a.column+1),d=r.getTokenAt(a.row,a.column),v=r.getTokenAt(a.row,a.column+1);if(l=="\\"&&d&&/escape/.test(d.type))return null;var m=d&&/string|escape/.test(d.type),g=!v||/string|escape/.test(v.type),y;if(p==s)y=m!==g;else{if(m&&!g)return null;if(m&&g)return null;var b=r.$mode.tokenRe;b.lastIndex=0;var w=b.test(l);b.lastIndex=0;var E=b.test(l);if(w||E)return null;if(p&&!/[\s;,.})\]\\]/.test(p))return null;y=!0}return{text:y?s+s:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};p.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},p.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},p.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},p.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},p.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},p.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},p.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},p.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(p,i),t.CstyleBehaviour=p}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),ace.define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("../worker/worker_client").WorkerClient,f=e("./behaviour/cstyle").CstyleBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.foldingRules=new l};r.inherits(c,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*\:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new a(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(c.prototype),t.Mode=c}) diff --git a/src/Umbraco.Web.UI.Client/lib/ace/mode-razor.js b/src/Umbraco.Web.UI.Client/lib/ace/mode-razor.js deleted file mode 100644 index bb23872e84..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/ace/mode-razor.js +++ /dev/null @@ -1 +0,0 @@ -ace.define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),ace.define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";function a(){var e=o.replace("\\d","\\d\\-"),t={onMatch:function(e,t,n){var r=e.charAt(1)=="/"?2:1;if(r==1)t!=this.nextState?n.unshift(this.next,this.nextState,0):n.unshift(this.next),n[2]++;else if(r==2&&t==this.nextState){n[1]--;if(!n[1]||n[1]<0)n.shift(),n.shift()}return[{type:"meta.tag.punctuation."+(r==1?"":"end-")+"tag-open.xml",value:e.slice(0,r)},{type:"meta.tag.tag-name.xml",value:e.substr(r)}]},regex:"",onMatch:function(e,t,n){return t==n[0]&&n.shift(),e.length==2&&(n[0]==this.nextState&&n[1]--,(!n[1]||n[1]<0)&&n.splice(0,2)),this.next=n[0]||"start",[{type:this.token,value:e}]},nextState:"jsx"},n,f("jsxAttributes"),{token:"entity.other.attribute-name.xml",regex:e},{token:"keyword.operator.attribute-equals.xml",regex:"="},{token:"text.tag-whitespace.xml",regex:"\\s+"},{token:"string.attribute-value.xml",regex:"'",stateName:"jsx_attr_q",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',stateName:"jsx_attr_qq",push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"reference"},{defaultToken:"string.attribute-value.xml"}]},t],this.$rules.reference=[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}]}function f(e){return[{token:"comment",regex:/\/\*/,next:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]},{token:"comment",regex:"\\/\\/",next:[i.getTagRule(),{token:"comment",regex:"$|^",next:e||"pop"},{defaultToken:"comment",caseInsensitive:!0}]}]}var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*",u=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|async|await|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|u{[0-9a-fA-F]{1,6}}|[0-2][0-7]{0,2}|3[0-7][0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[i.getStartRule("doc-start"),f("no_regex"),{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/},{token:"constant.numeric",regex:/[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+o+")(\\.)(prototype)(\\.)("+o+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+o+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+o+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:o},{token:"punctuation.operator",regex:/[.](?![.])/,next:"property"},{token:"keyword.operator",regex:/--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?\:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],property:[{token:"text",regex:"\\s+"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+o+")(\\.)("+o+")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",next:"function_arguments"},{token:"punctuation.operator",regex:/[.](?![.])/},{token:"support.function",regex:/(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:"support.function.dom",regex:/(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:"support.constant",regex:/(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:"identifier",regex:o},{regex:"",token:"empty",next:"no_regex"}],start:[i.getStartRule("doc-start"),f("start"),{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:o},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],qqstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:r},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]};if(!e||!e.noES6)this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)n.unshift("start",t);else if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1||this.next.indexOf("jsx")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:r},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),(!e||!e.noJSX)&&a.call(this);this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(u,s),t.JavaScriptHighlightRules=u}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},h=function(e,t,n,r){var i=e.end.row-e.start.row;return{text:n+t+r,selection:[0,e.start.column+1,i,e.end.column+(i?0:1)]}},p=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return h(a,l,"{","}");if(p.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(p.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(p.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var d=u.substring(s.column,s.column+1);if(d=="}"){var v=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(v!==null&&p.isAutoInsertedClosing(s,u,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var m="";p.isMaybeInsertedClosing(s,u)&&(m=o.stringRepeat("}",f.maybeInsertedBrackets),p.clearMaybeInsertedClosing());var d=u.substring(s.column,s.column+1);if(d==="}"){var g=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!g)return null;var y=this.$getIndent(r.getLine(g.row))}else{if(!m){p.clearMaybeInsertedClosing();return}var y=this.$getIndent(u)}var b=y+r.getTabString();return{text:"\n"+b+"\n"+y+m,selection:[1,b.length,1,b.length]}}p.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"(",")");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return h(s,o,"[","]");if(p.isSaneInsertion(n,r))return p.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&p.isAutoInsertedClosing(u,a,i))return p.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return h(o,u,s,s);if(!u){var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column),p=f.substring(a.column,a.column+1),d=r.getTokenAt(a.row,a.column),v=r.getTokenAt(a.row,a.column+1);if(l=="\\"&&d&&/escape/.test(d.type))return null;var m=d&&/string|escape/.test(d.type),g=!v||/string|escape/.test(v.type),y;if(p==s)y=m!==g;else{if(m&&!g)return null;if(m&&g)return null;var b=r.$mode.tokenRe;b.lastIndex=0;var w=b.test(l);b.lastIndex=0;var E=b.test(l);if(w||E)return null;if(p&&!/[\s;,.})\]\\]/.test(p))return null;y=!0}return{text:y?s+s:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};p.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},p.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},p.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},p.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},p.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},p.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},p.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},p.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(p,i),t.CstyleBehaviour=p}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.singleLineBlockCommentRe=/^\s*(\/\*).*\*\/\s*$/,this.tripleStarBlockCommentRe=/^\s*(\/\*\*\*).*\*\/\s*$/,this.startRegionRe=/^\s*(\/\*|\/\/)#?region\b/,this._getFoldWidgetBase=this.getFoldWidget,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);if(this.singleLineBlockCommentRe.test(r)&&!this.startRegionRe.test(r)&&!this.tripleStarBlockCommentRe.test(r))return"";var i=this._getFoldWidgetBase(e,t,n);return!i&&this.startRegionRe.test(r)?"start":i},this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n);if(this.startRegionRe.test(i))return this.getCommentRegionBlock(e,i,n);var s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)},this.getCommentRegionBlock=function(e,t,n){var r=t.search(/\s*$/),s=e.getLength(),o=n,u=/^\s*(?:\/\*|\/\/|--)#?(end)?region\b/,a=1;while(++no)return new i(o,r,l,t.length)}}.call(o.prototype)}),ace.define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("../worker/worker_client").WorkerClient,f=e("./behaviour/cstyle").CstyleBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.foldingRules=new l};r.inherits(c,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*\:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new a(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(c.prototype),t.Mode=c}),ace.define("ace/mode/css_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text_highlight_rules").TextHighlightRules,o=t.supportType="align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-shadow|box-sizing|caption-side|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|nav-down|nav-index|nav-left|nav-right|nav-up|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index",u=t.supportFunction="rgb|rgba|url|attr|counter|counters",a=t.supportConstant="absolute|after-edge|after|all-scroll|all|alphabetic|always|antialiased|armenian|auto|avoid-column|avoid-page|avoid|balance|baseline|before-edge|before|below|bidi-override|block-line-height|block|bold|bolder|border-box|both|bottom|box|break-all|break-word|capitalize|caps-height|caption|center|central|char|circle|cjk-ideographic|clone|close-quote|col-resize|collapse|column|consider-shifts|contain|content-box|cover|crosshair|cubic-bezier|dashed|decimal-leading-zero|decimal|default|disabled|disc|disregard-shifts|distribute-all-lines|distribute-letter|distribute-space|distribute|dotted|double|e-resize|ease-in|ease-in-out|ease-out|ease|ellipsis|end|exclude-ruby|fill|fixed|georgian|glyphs|grid-height|groove|hand|hanging|hebrew|help|hidden|hiragana-iroha|hiragana|horizontal|icon|ideograph-alpha|ideograph-numeric|ideograph-parenthesis|ideograph-space|ideographic|inactive|include-ruby|inherit|initial|inline-block|inline-box|inline-line-height|inline-table|inline|inset|inside|inter-ideograph|inter-word|invert|italic|justify|katakana-iroha|katakana|keep-all|last|left|lighter|line-edge|line-through|line|linear|list-item|local|loose|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|lr-tb|ltr|mathematical|max-height|max-size|medium|menu|message-box|middle|move|n-resize|ne-resize|newspaper|no-change|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|nw-resize|oblique|open-quote|outset|outside|overline|padding-box|page|pointer|pre-line|pre-wrap|pre|preserve-3d|progress|relative|repeat-x|repeat-y|repeat|replaced|reset-size|ridge|right|round|row-resize|rtl|s-resize|scroll|se-resize|separate|slice|small-caps|small-caption|solid|space|square|start|static|status-bar|step-end|step-start|steps|stretch|strict|sub|super|sw-resize|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|tb-rl|text-after-edge|text-before-edge|text-bottom|text-size|text-top|text|thick|thin|transparent|underline|upper-alpha|upper-latin|upper-roman|uppercase|use-script|vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|z-index|zero",f=t.supportConstantColor="aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow",l=t.supportConstantFonts="arial|century|comic|courier|cursive|fantasy|garamond|georgia|helvetica|impact|lucida|symbol|system|tahoma|times|trebuchet|utopia|verdana|webdings|sans-serif|serif|monospace",c=t.numRe="\\-?(?:(?:[0-9]+)|(?:[0-9]*\\.[0-9]+))",h=t.pseudoElements="(\\:+)\\b(after|before|first-letter|first-line|moz-selection|selection)\\b",p=t.pseudoClasses="(:)\\b(active|checked|disabled|empty|enabled|first-child|first-of-type|focus|hover|indeterminate|invalid|last-child|last-of-type|link|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|only-child|only-of-type|required|root|target|valid|visited)\\b",d=function(){var e=this.createKeywordMapper({"support.function":u,"support.constant":a,"support.type":o,"support.constant.color":f,"support.constant.fonts":l},"text",!0);this.$rules={start:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"@.*?{",push:"media"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],media:[{token:"comment",regex:"\\/\\*",push:"comment"},{token:"paren.lparen",regex:"\\{",push:"ruleset"},{token:"string",regex:"\\}",next:"pop"},{token:"keyword",regex:"#[a-z0-9-_]+"},{token:"variable",regex:"\\.[a-z0-9-_]+"},{token:"string",regex:":[a-z0-9-_]+"},{token:"constant",regex:"[a-z0-9-_]+"},{caseInsensitive:!0}],comment:[{token:"comment",regex:"\\*\\/",next:"pop"},{defaultToken:"comment"}],ruleset:[{token:"paren.rparen",regex:"\\}",next:"pop"},{token:"comment",regex:"\\/\\*",push:"comment"},{token:"string",regex:'["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'},{token:"string",regex:"['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']"},{token:["constant.numeric","keyword"],regex:"("+c+")(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vw|%)"},{token:"constant.numeric",regex:c},{token:"constant.numeric",regex:"#[a-f0-9]{6}"},{token:"constant.numeric",regex:"#[a-f0-9]{3}"},{token:["punctuation","entity.other.attribute-name.pseudo-element.css"],regex:h},{token:["punctuation","entity.other.attribute-name.pseudo-class.css"],regex:p},{token:["support.function","string","support.function"],regex:"(url\\()(.*)(\\))"},{token:e,regex:"\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*"},{caseInsensitive:!0}]},this.normalizeRules()};r.inherits(d,s),t.CssHighlightRules=d}),ace.define("ace/mode/css_completions",["require","exports","module"],function(e,t,n){"use strict";var r={background:{"#$0":1},"background-color":{"#$0":1,transparent:1,fixed:1},"background-image":{"url('/$0')":1},"background-repeat":{repeat:1,"repeat-x":1,"repeat-y":1,"no-repeat":1,inherit:1},"background-position":{bottom:2,center:2,left:2,right:2,top:2,inherit:2},"background-attachment":{scroll:1,fixed:1},"background-size":{cover:1,contain:1},"background-clip":{"border-box":1,"padding-box":1,"content-box":1},"background-origin":{"border-box":1,"padding-box":1,"content-box":1},border:{"solid $0":1,"dashed $0":1,"dotted $0":1,"#$0":1},"border-color":{"#$0":1},"border-style":{solid:2,dashed:2,dotted:2,"double":2,groove:2,hidden:2,inherit:2,inset:2,none:2,outset:2,ridged:2},"border-collapse":{collapse:1,separate:1},bottom:{px:1,em:1,"%":1},clear:{left:1,right:1,both:1,none:1},color:{"#$0":1,"rgb(#$00,0,0)":1},cursor:{"default":1,pointer:1,move:1,text:1,wait:1,help:1,progress:1,"n-resize":1,"ne-resize":1,"e-resize":1,"se-resize":1,"s-resize":1,"sw-resize":1,"w-resize":1,"nw-resize":1},display:{none:1,block:1,inline:1,"inline-block":1,"table-cell":1},"empty-cells":{show:1,hide:1},"float":{left:1,right:1,none:1},"font-family":{Arial:2,"Comic Sans MS":2,Consolas:2,"Courier New":2,Courier:2,Georgia:2,Monospace:2,"Sans-Serif":2,"Segoe UI":2,Tahoma:2,"Times New Roman":2,"Trebuchet MS":2,Verdana:1},"font-size":{px:1,em:1,"%":1},"font-weight":{bold:1,normal:1},"font-style":{italic:1,normal:1},"font-variant":{normal:1,"small-caps":1},height:{px:1,em:1,"%":1},left:{px:1,em:1,"%":1},"letter-spacing":{normal:1},"line-height":{normal:1},"list-style-type":{none:1,disc:1,circle:1,square:1,decimal:1,"decimal-leading-zero":1,"lower-roman":1,"upper-roman":1,"lower-greek":1,"lower-latin":1,"upper-latin":1,georgian:1,"lower-alpha":1,"upper-alpha":1},margin:{px:1,em:1,"%":1},"margin-right":{px:1,em:1,"%":1},"margin-left":{px:1,em:1,"%":1},"margin-top":{px:1,em:1,"%":1},"margin-bottom":{px:1,em:1,"%":1},"max-height":{px:1,em:1,"%":1},"max-width":{px:1,em:1,"%":1},"min-height":{px:1,em:1,"%":1},"min-width":{px:1,em:1,"%":1},overflow:{hidden:1,visible:1,auto:1,scroll:1},"overflow-x":{hidden:1,visible:1,auto:1,scroll:1},"overflow-y":{hidden:1,visible:1,auto:1,scroll:1},padding:{px:1,em:1,"%":1},"padding-top":{px:1,em:1,"%":1},"padding-right":{px:1,em:1,"%":1},"padding-bottom":{px:1,em:1,"%":1},"padding-left":{px:1,em:1,"%":1},"page-break-after":{auto:1,always:1,avoid:1,left:1,right:1},"page-break-before":{auto:1,always:1,avoid:1,left:1,right:1},position:{absolute:1,relative:1,fixed:1,"static":1},right:{px:1,em:1,"%":1},"table-layout":{fixed:1,auto:1},"text-decoration":{none:1,underline:1,"line-through":1,blink:1},"text-align":{left:1,right:1,center:1,justify:1},"text-transform":{capitalize:1,uppercase:1,lowercase:1,none:1},top:{px:1,em:1,"%":1},"vertical-align":{top:1,bottom:1},visibility:{hidden:1,visible:1},"white-space":{nowrap:1,normal:1,pre:1,"pre-line":1,"pre-wrap":1},width:{px:1,em:1,"%":1},"word-spacing":{normal:1},filter:{"alpha(opacity=$0100)":1},"text-shadow":{"$02px 2px 2px #777":1},"text-overflow":{"ellipsis-word":1,clip:1,ellipsis:1},"-moz-border-radius":1,"-moz-border-radius-topright":1,"-moz-border-radius-bottomright":1,"-moz-border-radius-topleft":1,"-moz-border-radius-bottomleft":1,"-webkit-border-radius":1,"-webkit-border-top-right-radius":1,"-webkit-border-top-left-radius":1,"-webkit-border-bottom-right-radius":1,"-webkit-border-bottom-left-radius":1,"-moz-box-shadow":1,"-webkit-box-shadow":1,transform:{"rotate($00deg)":1,"skew($00deg)":1},"-moz-transform":{"rotate($00deg)":1,"skew($00deg)":1},"-webkit-transform":{"rotate($00deg)":1,"skew($00deg)":1}},i=function(){};(function(){this.completionsDefined=!1,this.defineCompletions=function(){if(document){var e=document.createElement("c").style;for(var t in e){if(typeof e[t]!="string")continue;var n=t.replace(/[A-Z]/g,function(e){return"-"+e.toLowerCase()});r.hasOwnProperty(n)||(r[n]=1)}}this.completionsDefined=!0},this.getCompletions=function(e,t,n,r){this.completionsDefined||this.defineCompletions();var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e==="ruleset"){var s=t.getLine(n.row).substr(0,n.column);return/:[^;]+$/.test(s)?(/([\w\-]+):[^:]*$/.test(s),this.getPropertyValueCompletions(e,t,n,r)):this.getPropertyCompletions(e,t,n,r)}return[]},this.getPropertyCompletions=function(e,t,n,i){var s=Object.keys(r);return s.map(function(e){return{caption:e,snippet:e+": $0",meta:"property",score:Number.MAX_VALUE}})},this.getPropertyValueCompletions=function(e,t,n,i){var s=t.getLine(n.row).substr(0,n.column),o=(/([\w\-]+):[^:]*$/.exec(s)||{})[1];if(!o)return[];var u=[];return o in r&&typeof r[o]=="object"&&(u=Object.keys(r[o])),u.map(function(e){return{caption:e,snippet:e,meta:"property value",score:Number.MAX_VALUE}})}}).call(i.prototype),t.CssCompletions=i}),ace.define("ace/mode/behaviour/css",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/mode/behaviour/cstyle","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("./cstyle").CstyleBehaviour,o=e("../../token_iterator").TokenIterator,u=function(){this.inherit(s),this.add("colon","insertion",function(e,t,n,r,i){if(i===":"){var s=n.getCursorPosition(),u=new o(r,s.row,s.column),a=u.getCurrentToken();a&&a.value.match(/\s+/)&&(a=u.stepBackward());if(a&&a.type==="support.type"){var f=r.doc.getLine(s.row),l=f.substring(s.column,s.column+1);if(l===":")return{text:"",selection:[1,1]};if(!f.substring(s.column).match(/^\s*;/))return{text:":;",selection:[1,1]}}}}),this.add("colon","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s===":"){var u=n.getCursorPosition(),a=new o(r,u.row,u.column),f=a.getCurrentToken();f&&f.value.match(/\s+/)&&(f=a.stepBackward());if(f&&f.type==="support.type"){var l=r.doc.getLine(i.start.row),c=l.substring(i.end.column,i.end.column+1);if(c===";")return i.end.column++,i}}}),this.add("semicolon","insertion",function(e,t,n,r,i){if(i===";"){var s=n.getCursorPosition(),o=r.doc.getLine(s.row),u=o.substring(s.column,s.column+1);if(u===";")return{text:"",selection:[1,1]}}})};r.inherits(u,s),t.CssBehaviour=u}),ace.define("ace/mode/css",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/css_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/css_completions","ace/mode/behaviour/css","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./css_highlight_rules").CssHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../worker/worker_client").WorkerClient,a=e("./css_completions").CssCompletions,f=e("./behaviour/css").CssBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.$completer=new a,this.foldingRules=new l};r.inherits(c,i),function(){this.foldingRules="cStyle",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e).tokens;if(i.length&&i[i.length-1].type=="comment")return r;var s=t.match(/^.*\{\s*$/);return s&&(r+=n),r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){var t=new u(["ace"],"ace/mode/css_worker","Worker");return t.attachToDocument(e.getDocument()),t.on("annotate",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/css"}.call(c.prototype),t.Mode=c}),ace.define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(e){var t="[_:a-zA-Z\u00c0-\uffff][-_:.a-zA-Z0-9\u00c0-\uffff]*";this.$rules={start:[{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\[",next:"cdata"},{token:["punctuation.xml-decl.xml","keyword.xml-decl.xml"],regex:"(<\\?)(xml)(?=[\\s])",next:"xml_decl",caseInsensitive:!0},{token:["punctuation.instruction.xml","keyword.instruction.xml"],regex:"(<\\?)("+t+")",next:"processing_instruction"},{token:"comment.xml",regex:"<\\!--",next:"comment"},{token:["xml-pe.doctype.xml","xml-pe.doctype.xml"],regex:"(<\\!)(DOCTYPE)(?=[\\s])",next:"doctype",caseInsensitive:!0},{include:"tag"},{token:"text.end-tag-open.xml",regex:"",next:"start"}],processing_instruction:[{token:"punctuation.instruction.xml",regex:"\\?>",next:"start"},{defaultToken:"instruction.xml"}],doctype:[{include:"whitespace"},{include:"string"},{token:"xml-pe.doctype.xml",regex:">",next:"start"},{token:"xml-pe.xml",regex:"[-_a-zA-Z0-9:]+"},{token:"punctuation.int-subset",regex:"\\[",push:"int_subset"}],int_subset:[{token:"text.xml",regex:"\\s+"},{token:"punctuation.int-subset.xml",regex:"]",next:"pop"},{token:["punctuation.markup-decl.xml","keyword.markup-decl.xml"],regex:"(<\\!)("+t+")",push:[{token:"text",regex:"\\s+"},{token:"punctuation.markup-decl.xml",regex:">",next:"pop"},{include:"string"}]}],cdata:[{token:"string.cdata.xml",regex:"\\]\\]>",next:"start"},{token:"text.xml",regex:"\\s+"},{token:"text.xml",regex:"(?:[^\\]]|\\](?!\\]>))+"}],comment:[{token:"comment.xml",regex:"-->",next:"start"},{defaultToken:"comment.xml"}],reference:[{token:"constant.language.escape.reference.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],attr_reference:[{token:"constant.language.escape.reference.attribute-value.xml",regex:"(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"}],tag:[{token:["meta.tag.punctuation.tag-open.xml","meta.tag.punctuation.end-tag-open.xml","meta.tag.tag-name.xml"],regex:"(?:(<)|(",next:"start"}]}],tag_whitespace:[{token:"text.tag-whitespace.xml",regex:"\\s+"}],whitespace:[{token:"text.whitespace.xml",regex:"\\s+"}],string:[{token:"string.xml",regex:"'",push:[{token:"string.xml",regex:"'",next:"pop"},{defaultToken:"string.xml"}]},{token:"string.xml",regex:'"',push:[{token:"string.xml",regex:'"',next:"pop"},{defaultToken:"string.xml"}]}],attributes:[{token:"entity.other.attribute-name.xml",regex:"(?:"+t+":)?"+t+""},{token:"keyword.operator.attribute-equals.xml",regex:"="},{include:"tag_whitespace"},{include:"attribute_value"}],attribute_value:[{token:"string.attribute-value.xml",regex:"'",push:[{token:"string.attribute-value.xml",regex:"'",next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]},{token:"string.attribute-value.xml",regex:'"',push:[{token:"string.attribute-value.xml",regex:'"',next:"pop"},{include:"attr_reference"},{defaultToken:"string.attribute-value.xml"}]}]},this.constructor===s&&this.normalizeRules()};(function(){this.embedTagRules=function(e,t,n){this.$rules.tag.unshift({token:["meta.tag.punctuation.tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(<)("+n+"(?=\\s|>|$))",next:[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:t+"start"}]}),this.$rules[n+"-end"]=[{include:"attributes"},{token:"meta.tag.punctuation.tag-close.xml",regex:"/?>",next:"start",onMatch:function(e,t,n){return n.splice(0),this.token}}],this.embedRules(e,t,[{token:["meta.tag.punctuation.end-tag-open.xml","meta.tag."+n+".tag-name.xml"],regex:"(|$))",next:n+"-end"},{token:"string.cdata.xml",regex:"<\\!\\[CDATA\\["},{token:"string.cdata.xml",regex:"\\]\\]>"}])}}).call(i.prototype),r.inherits(s,i),t.XmlHighlightRules=s}),ace.define("ace/mode/html_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/css_highlight_rules","ace/mode/javascript_highlight_rules","ace/mode/xml_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./css_highlight_rules").CssHighlightRules,o=e("./javascript_highlight_rules").JavaScriptHighlightRules,u=e("./xml_highlight_rules").XmlHighlightRules,a=i.createMap({a:"anchor",button:"form",form:"form",img:"image",input:"form",label:"form",option:"form",script:"script",select:"form",textarea:"form",style:"style",table:"table",tbody:"table",td:"table",tfoot:"table",th:"table",tr:"table"}),f=function(){u.call(this),this.addRules({attributes:[{include:"tag_whitespace"},{token:"entity.other.attribute-name.xml",regex:"[-_a-zA-Z0-9:.]+"},{token:"keyword.operator.attribute-equals.xml",regex:"=",push:[{include:"tag_whitespace"},{token:"string.unquoted.attribute-value.html",regex:"[^<>='\"`\\s]+",next:"pop"},{token:"empty",regex:"",next:"pop"}]},{include:"attribute_value"}],tag:[{token:function(e,t){var n=a[t];return["meta.tag.punctuation."+(e=="<"?"":"end-")+"tag-open.xml","meta.tag"+(n?"."+n:"")+".tag-name.xml"]},regex:"(",next:"start"}]}),this.embedTagRules(s,"css-","style"),this.embedTagRules((new o({noJSX:!0})).getRules(),"js-","script"),this.constructor===f&&this.normalizeRules()};r.inherits(f,u),t.HtmlHighlightRules=f}),ace.define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";function u(e,t){return e.type.lastIndexOf(t+".xml")>-1}var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),a=function(){this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){var o=i,a=r.doc.getTextRange(n.getSelectionRange());if(a!==""&&a!=="'"&&a!='"'&&n.getWrapBehavioursEnabled())return{text:o+a+o,selection:!1};var f=n.getCursorPosition(),l=r.doc.getLine(f.row),c=l.substring(f.column,f.column+1),h=new s(r,f.row,f.column),p=h.getCurrentToken();if(c==o&&(u(p,"attribute-value")||u(p,"string")))return{text:"",selection:[1,1]};p||(p=h.stepBackward());if(!p)return;while(u(p,"tag-whitespace")||u(p,"whitespace"))p=h.stepBackward();var d=!c||c.match(/\s/);if(u(p,"attribute-equals")&&(d||c==">")||u(p,"decl-attribute-equals")&&(d||c=="?"))return{text:o+o,selection:[1,1]}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}}),this.add("autoclosing","insertion",function(e,t,n,r,i){if(i==">"){var o=n.getCursorPosition(),a=new s(r,o.row,o.column),f=a.getCurrentToken()||a.stepBackward();if(!f||!(u(f,"tag-name")||u(f,"tag-whitespace")||u(f,"attribute-name")||u(f,"attribute-equals")||u(f,"attribute-value")))return;if(u(f,"reference.attribute-value"))return;if(u(f,"attribute-value")){var l=f.value.charAt(0);if(l=='"'||l=="'"){var c=f.value.charAt(f.value.length-1),h=a.getCurrentTokenColumn()+f.value.length;if(h>o.column||h==o.column&&l!=c)return}}while(!u(f,"tag-name"))f=a.stepBackward();var p=a.getCurrentTokenRow(),d=a.getCurrentTokenColumn();if(u(a.stepBackward(),"end-tag-open"))return;var v=f.value;p==o.row&&(v=v.substring(0,o.column-d));if(this.voidElements.hasOwnProperty(v.toLowerCase()))return;return{text:">",selection:[1,1]}}}),this.add("autoindent","insertion",function(e,t,n,r,i){if(i=="\n"){var o=n.getCursorPosition(),u=r.getLine(o.row),a=new s(r,o.row,o.column),f=a.getCurrentToken();if(f&&f.type.indexOf("tag-close")!==-1){if(f.value=="/>")return;while(f&&f.type.indexOf("tag-name")===-1)f=a.stepBackward();if(!f)return;var l=f.value,c=a.getCurrentTokenRow();f=a.stepBackward();if(!f||f.type.indexOf("end-tag")!==-1)return;if(this.voidElements&&!this.voidElements[l]){var h=r.getTokenAt(o.row,o.column+1),u=r.getLine(c),p=this.$getIndent(u),d=p+r.getTabString();return h&&h.value==="-1}var r=e("../../lib/oop"),i=e("../../lib/lang"),s=e("../../range").Range,o=e("./fold_mode").FoldMode,u=e("../../token_iterator").TokenIterator,a=t.FoldMode=function(e,t){o.call(this),this.voidElements=e||{},this.optionalEndTags=r.mixin({},this.voidElements),t&&r.mixin(this.optionalEndTags,t)};r.inherits(a,o);var f=function(){this.tagName="",this.closing=!1,this.selfClosing=!1,this.start={row:0,column:0},this.end={row:0,column:0}};(function(){this.getFoldWidget=function(e,t,n){var r=this._getFirstTagInLine(e,n);return r?r.closing||!r.tagName&&r.selfClosing?t=="markbeginend"?"end":"":!r.tagName||r.selfClosing||this.voidElements.hasOwnProperty(r.tagName.toLowerCase())?"":this._findEndTagInLine(e,n,r.tagName,r.end.column)?"":"start":""},this._getFirstTagInLine=function(e,t){var n=e.getTokens(t),r=new f;for(var i=0;i";break}}return r}if(l(s,"tag-close"))return r.selfClosing=s.value=="/>",r;r.start.column+=s.value.length}return null},this._findEndTagInLine=function(e,t,n,r){var i=e.getTokens(t),s=0;for(var o=0;o",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length,e.stepForward(),n;while(t=e.stepForward());return null},this._readTagBackward=function(e){var t=e.getCurrentToken();if(!t)return null;var n=new f;do{if(l(t,"tag-open"))return n.closing=l(t,"end-tag-open"),n.start.row=e.getCurrentTokenRow(),n.start.column=e.getCurrentTokenColumn(),e.stepBackward(),n;l(t,"tag-name")?n.tagName=t.value:l(t,"tag-close")&&(n.selfClosing=t.value=="/>",n.end.row=e.getCurrentTokenRow(),n.end.column=e.getCurrentTokenColumn()+t.value.length)}while(t=e.stepBackward());return null},this._pop=function(e,t){while(e.length){var n=e[e.length-1];if(!t||n.tagName==t.tagName)return e.pop();if(this.optionalEndTags.hasOwnProperty(n.tagName)){e.pop();continue}return null}},this.getFoldWidgetRange=function(e,t,n){var r=this._getFirstTagInLine(e,n);if(!r)return null;var i=r.closing||r.selfClosing,o=[],a;if(!i){var f=new u(e,n,r.start.column),l={row:n,column:r.start.column+r.tagName.length+2};r.start.row==r.end.row&&(l.column=r.end.column);while(a=this._readTagForward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(a.closing){this._pop(o,a);if(o.length==0)return s.fromPoints(l,a.start)}else o.push(a)}}else{var f=new u(e,n,r.end.column),c={row:n,column:r.start.column};while(a=this._readTagBackward(f)){if(a.selfClosing){if(!o.length)return a.start.column+=a.tagName.length+2,a.end.column-=2,s.fromPoints(a.start,a.end);continue}if(!a.closing){this._pop(o,a);if(o.length==0)return a.start.column+=a.tagName.length+2,a.start.row==a.end.row&&a.start.column-1}function l(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"tag-name"))i=n.stepBackward();if(i)return i.value}function c(e,t){var n=new r(e,t.row,t.column),i=n.getCurrentToken();while(i&&!f(i,"attribute-name"))i=n.stepBackward();if(i)return i.value}var r=e("../token_iterator").TokenIterator,i=["accesskey","class","contenteditable","contextmenu","dir","draggable","dropzone","hidden","id","inert","itemid","itemprop","itemref","itemscope","itemtype","lang","spellcheck","style","tabindex","title","translate"],s=["onabort","onblur","oncancel","oncanplay","oncanplaythrough","onchange","onclick","onclose","oncontextmenu","oncuechange","ondblclick","ondrag","ondragend","ondragenter","ondragleave","ondragover","ondragstart","ondrop","ondurationchange","onemptied","onended","onerror","onfocus","oninput","oninvalid","onkeydown","onkeypress","onkeyup","onload","onloadeddata","onloadedmetadata","onloadstart","onmousedown","onmousemove","onmouseout","onmouseover","onmouseup","onmousewheel","onpause","onplay","onplaying","onprogress","onratechange","onreset","onscroll","onseeked","onseeking","onselect","onshow","onstalled","onsubmit","onsuspend","ontimeupdate","onvolumechange","onwaiting"],o=i.concat(s),u={html:{manifest:1},head:{},title:{},base:{href:1,target:1},link:{href:1,hreflang:1,rel:{stylesheet:1,icon:1},media:{all:1,screen:1,print:1},type:{"text/css":1,"image/png":1,"image/jpeg":1,"image/gif":1},sizes:1},meta:{"http-equiv":{"content-type":1},name:{description:1,keywords:1},content:{"text/html; charset=UTF-8":1},charset:1},style:{type:1,media:{all:1,screen:1,print:1},scoped:1},script:{charset:1,type:{"text/javascript":1},src:1,defer:1,async:1},noscript:{href:1},body:{onafterprint:1,onbeforeprint:1,onbeforeunload:1,onhashchange:1,onmessage:1,onoffline:1,onpopstate:1,onredo:1,onresize:1,onstorage:1,onundo:1,onunload:1},section:{},nav:{},article:{pubdate:1},aside:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},header:{},footer:{},address:{},main:{},p:{},hr:{},pre:{},blockquote:{cite:1},ol:{start:1,reversed:1},ul:{},li:{value:1},dl:{},dt:{},dd:{},figure:{},figcaption:{},div:{},a:{href:1,target:{_blank:1,top:1},ping:1,rel:{nofollow:1,alternate:1,author:1,bookmark:1,help:1,license:1,next:1,noreferrer:1,prefetch:1,prev:1,search:1,tag:1},media:1,hreflang:1,type:1},em:{},strong:{},small:{},s:{},cite:{},q:{cite:1},dfn:{},abbr:{},data:{},time:{datetime:1},code:{},"var":{},samp:{},kbd:{},sub:{},sup:{},i:{},b:{},u:{},mark:{},ruby:{},rt:{},rp:{},bdi:{},bdo:{},span:{},br:{},wbr:{},ins:{cite:1,datetime:1},del:{cite:1,datetime:1},img:{alt:1,src:1,height:1,width:1,usemap:1,ismap:1},iframe:{name:1,src:1,height:1,width:1,sandbox:{"allow-same-origin":1,"allow-top-navigation":1,"allow-forms":1,"allow-scripts":1},seamless:{seamless:1}},embed:{src:1,height:1,width:1,type:1},object:{param:1,data:1,type:1,height:1,width:1,usemap:1,name:1,form:1,classid:1},param:{name:1,value:1},video:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},width:1,height:1,poster:1,muted:{muted:1},preload:{auto:1,metadata:1,none:1}},audio:{src:1,autobuffer:1,autoplay:{autoplay:1},loop:{loop:1},controls:{controls:1},muted:{muted:1},preload:{auto:1,metadata:1,none:1}},source:{src:1,type:1,media:1},track:{kind:1,src:1,srclang:1,label:1,"default":1},canvas:{width:1,height:1},map:{name:1},area:{shape:1,coords:1,href:1,hreflang:1,alt:1,target:1,media:1,rel:1,ping:1,type:1},svg:{},math:{},table:{summary:1},caption:{},colgroup:{span:1},col:{span:1},tbody:{},thead:{},tfoot:{},tr:{},td:{headers:1,rowspan:1,colspan:1},th:{headers:1,rowspan:1,colspan:1,scope:1},form:{"accept-charset":1,action:1,autocomplete:1,enctype:{"multipart/form-data":1,"application/x-www-form-urlencoded":1},method:{get:1,post:1},name:1,novalidate:1,target:{_blank:1,top:1}},fieldset:{disabled:1,form:1,name:1},legend:{},label:{form:1,"for":1},input:{type:{text:1,password:1,hidden:1,checkbox:1,submit:1,radio:1,file:1,button:1,reset:1,image:31,color:1,date:1,datetime:1,"datetime-local":1,email:1,month:1,number:1,range:1,search:1,tel:1,time:1,url:1,week:1},accept:1,alt:1,autocomplete:{on:1,off:1},autofocus:{autofocus:1},checked:{checked:1},disabled:{disabled:1},form:1,formaction:1,formenctype:{"application/x-www-form-urlencoded":1,"multipart/form-data":1,"text/plain":1},formmethod:{get:1,post:1},formnovalidate:{formnovalidate:1},formtarget:{_blank:1,_self:1,_parent:1,_top:1},height:1,list:1,max:1,maxlength:1,min:1,multiple:{multiple:1},name:1,pattern:1,placeholder:1,readonly:{readonly:1},required:{required:1},size:1,src:1,step:1,width:1,files:1,value:1},button:{autofocus:1,disabled:{disabled:1},form:1,formaction:1,formenctype:1,formmethod:1,formnovalidate:1,formtarget:1,name:1,value:1,type:{button:1,submit:1}},select:{autofocus:1,disabled:1,form:1,multiple:{multiple:1},name:1,size:1,readonly:{readonly:1}},datalist:{},optgroup:{disabled:1,label:1},option:{disabled:1,selected:1,label:1,value:1},textarea:{autofocus:{autofocus:1},disabled:{disabled:1},form:1,maxlength:1,name:1,placeholder:1,readonly:{readonly:1},required:{required:1},rows:1,cols:1,wrap:{on:1,off:1,hard:1,soft:1}},keygen:{autofocus:1,challenge:{challenge:1},disabled:{disabled:1},form:1,keytype:{rsa:1,dsa:1,ec:1},name:1},output:{"for":1,form:1,name:1},progress:{value:1,max:1},meter:{value:1,min:1,max:1,low:1,high:1,optimum:1},details:{open:1},summary:{},command:{type:1,label:1,icon:1,disabled:1,checked:1,radiogroup:1,command:1},menu:{type:1,label:1},dialog:{open:1}},a=Object.keys(u),h=function(){};(function(){this.getCompletions=function(e,t,n,r){var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(f(i,"tag-name")||f(i,"tag-open")||f(i,"end-tag-open"))return this.getTagCompletions(e,t,n,r);if(f(i,"tag-whitespace")||f(i,"attribute-name"))return this.getAttributeCompletions(e,t,n,r);if(f(i,"attribute-value"))return this.getAttributeValueCompletions(e,t,n,r);var s=t.getLine(n.row).substr(0,n.column);return/&[A-z]*$/i.test(s)?this.getHTMLEntityCompletions(e,t,n,r):[]},this.getTagCompletions=function(e,t,n,r){return a.map(function(e){return{value:e,meta:"tag",score:Number.MAX_VALUE}})},this.getAttributeCompletions=function(e,t,n,r){var i=l(t,n);if(!i)return[];var s=o;return i in u&&(s=s.concat(Object.keys(u[i]))),s.map(function(e){return{caption:e,snippet:e+'="$0"',meta:"attribute",score:Number.MAX_VALUE}})},this.getAttributeValueCompletions=function(e,t,n,r){var i=l(t,n),s=c(t,n);if(!i)return[];var o=[];return i in u&&s in u[i]&&typeof u[i][s]=="object"&&(o=Object.keys(u[i][s])),o.map(function(e){return{caption:e,snippet:e,meta:"attribute value",score:Number.MAX_VALUE}})},this.getHTMLEntityCompletions=function(e,t,n,r){var i=["Aacute;","aacute;","Acirc;","acirc;","acute;","AElig;","aelig;","Agrave;","agrave;","alefsym;","Alpha;","alpha;","amp;","and;","ang;","Aring;","aring;","asymp;","Atilde;","atilde;","Auml;","auml;","bdquo;","Beta;","beta;","brvbar;","bull;","cap;","Ccedil;","ccedil;","cedil;","cent;","Chi;","chi;","circ;","clubs;","cong;","copy;","crarr;","cup;","curren;","Dagger;","dagger;","dArr;","darr;","deg;","Delta;","delta;","diams;","divide;","Eacute;","eacute;","Ecirc;","ecirc;","Egrave;","egrave;","empty;","emsp;","ensp;","Epsilon;","epsilon;","equiv;","Eta;","eta;","ETH;","eth;","Euml;","euml;","euro;","exist;","fnof;","forall;","frac12;","frac14;","frac34;","frasl;","Gamma;","gamma;","ge;","gt;","hArr;","harr;","hearts;","hellip;","Iacute;","iacute;","Icirc;","icirc;","iexcl;","Igrave;","igrave;","image;","infin;","int;","Iota;","iota;","iquest;","isin;","Iuml;","iuml;","Kappa;","kappa;","Lambda;","lambda;","lang;","laquo;","lArr;","larr;","lceil;","ldquo;","le;","lfloor;","lowast;","loz;","lrm;","lsaquo;","lsquo;","lt;","macr;","mdash;","micro;","middot;","minus;","Mu;","mu;","nabla;","nbsp;","ndash;","ne;","ni;","not;","notin;","nsub;","Ntilde;","ntilde;","Nu;","nu;","Oacute;","oacute;","Ocirc;","ocirc;","OElig;","oelig;","Ograve;","ograve;","oline;","Omega;","omega;","Omicron;","omicron;","oplus;","or;","ordf;","ordm;","Oslash;","oslash;","Otilde;","otilde;","otimes;","Ouml;","ouml;","para;","part;","permil;","perp;","Phi;","phi;","Pi;","pi;","piv;","plusmn;","pound;","Prime;","prime;","prod;","prop;","Psi;","psi;","quot;","radic;","rang;","raquo;","rArr;","rarr;","rceil;","rdquo;","real;","reg;","rfloor;","Rho;","rho;","rlm;","rsaquo;","rsquo;","sbquo;","Scaron;","scaron;","sdot;","sect;","shy;","Sigma;","sigma;","sigmaf;","sim;","spades;","sub;","sube;","sum;","sup;","sup1;","sup2;","sup3;","supe;","szlig;","Tau;","tau;","there4;","Theta;","theta;","thetasym;","thinsp;","THORN;","thorn;","tilde;","times;","trade;","Uacute;","uacute;","uArr;","uarr;","Ucirc;","ucirc;","Ugrave;","ugrave;","uml;","upsih;","Upsilon;","upsilon;","Uuml;","uuml;","weierp;","Xi;","xi;","Yacute;","yacute;","yen;","Yuml;","yuml;","Zeta;","zeta;","zwj;","zwnj;"];return i.map(function(e){return{caption:e,snippet:e,meta:"html entity",score:Number.MAX_VALUE}})}}).call(h.prototype),t.HtmlCompletions=h}),ace.define("ace/mode/html",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text","ace/mode/javascript","ace/mode/css","ace/mode/html_highlight_rules","ace/mode/behaviour/xml","ace/mode/folding/html","ace/mode/html_completions","ace/worker/worker_client"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./text").Mode,o=e("./javascript").Mode,u=e("./css").Mode,a=e("./html_highlight_rules").HtmlHighlightRules,f=e("./behaviour/xml").XmlBehaviour,l=e("./folding/html").FoldMode,c=e("./html_completions").HtmlCompletions,h=e("../worker/worker_client").WorkerClient,p=["area","base","br","col","embed","hr","img","input","keygen","link","meta","menuitem","param","source","track","wbr"],d=["li","dt","dd","p","rt","rp","optgroup","option","colgroup","td","th"],v=function(e){this.fragmentContext=e&&e.fragmentContext,this.HighlightRules=a,this.$behaviour=new f,this.$completer=new c,this.createModeDelegates({"js-":o,"css-":u}),this.foldingRules=new l(this.voidElements,i.arrayToMap(d))};r.inherits(v,s),function(){this.blockComment={start:""},this.voidElements=i.arrayToMap(p),this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.getCompletions=function(e,t,n,r){return this.$completer.getCompletions(e,t,n,r)},this.createWorker=function(e){if(this.constructor!=v)return;var t=new h(["ace"],"ace/mode/html_worker","Worker");return t.attachToDocument(e.getDocument()),this.fragmentContext&&t.call("setOptions",[{context:this.fragmentContext}]),t.on("error",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/html"}.call(v.prototype),t.Mode=v}),ace.define("ace/mode/csharp_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o=function(){var e=this.createKeywordMapper({"variable.language":"this",keyword:"abstract|event|new|struct|as|explicit|null|switch|base|extern|object|this|bool|false|operator|throw|break|finally|out|true|byte|fixed|override|try|case|float|params|typeof|catch|for|private|uint|char|foreach|protected|ulong|checked|goto|public|unchecked|class|if|readonly|unsafe|const|implicit|ref|ushort|continue|in|return|using|decimal|int|sbyte|virtual|default|interface|sealed|volatile|delegate|internal|short|void|do|is|sizeof|while|double|lock|stackalloc|else|long|static|enum|namespace|string|var|dynamic","constant.language":"null|true|false"},"identifier");this.$rules={start:[{token:"comment",regex:"\\/\\/.*$"},i.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment"},{token:"string",regex:/'(?:.|\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n]))'/},{token:"string",start:'"',end:'"|$',next:[{token:"constant.language.escape",regex:/\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n])/},{token:"invalid",regex:/\\./}]},{token:"string",start:'@"',end:'"',next:[{token:"constant.language.escape",regex:'""'}]},{token:"string",start:/\$"/,end:'"|$',next:[{token:"constant.language.escape",regex:/\\(:?$)|{{/},{token:"constant.language.escape",regex:/\\(:?u[\da-fA-F]+|x[\da-fA-F]+|[tbrf'"n])/},{token:"invalid",regex:/\\./}]},{token:"constant.numeric",regex:"0[xX][0-9a-fA-F]+\\b"},{token:"constant.numeric",regex:"[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b"},{token:"constant.language.boolean",regex:"(?:true|false)\\b"},{token:e,regex:"[a-zA-Z_$][a-zA-Z0-9_$]*\\b"},{token:"keyword.operator",regex:"!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\?\\:|\\*=|%=|\\+=|\\-=|&=|\\^=|\\b(?:in|instanceof|new|delete|typeof|void)"},{token:"keyword",regex:"^\\s*#(if|else|elif|endif|define|undef|warning|error|line|region|endregion|pragma)"},{token:"punctuation.operator",regex:"\\?|\\:|\\,|\\;|\\."},{token:"paren.lparen",regex:"[[({]"},{token:"paren.rparen",regex:"[\\])}]"},{token:"text",regex:"\\s+"}],comment:[{token:"comment",regex:".*?\\*\\/",next:"start"},{token:"comment",regex:".+"}]},this.embedRules(i,"doc-",[i.getEndRule("start")]),this.normalizeRules()};r.inherits(o,s),t.CSharpHighlightRules=o}),ace.define("ace/mode/razor_highlight_rules",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/doc_comment_highlight_rules","ace/mode/html_highlight_rules","ace/mode/csharp_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/lang"),s=e("./doc_comment_highlight_rules").DocCommentHighlightRules,o=e("./html_highlight_rules").HtmlHighlightRules,u=e("./csharp_highlight_rules").CSharpHighlightRules,a="razor-block-",f=function(){u.call(this);var e=function(e,t){return typeof t=="function"?t(e):t},t="in-braces";this.$rules.start.unshift({regex:"[\\[({]",onMatch:function(e,n,r){var i=/razor-[^\-]+-/.exec(n)[0];return r.unshift(e),r.unshift(i+t),this.next=i+t,"paren.lparen"}});var n={"{":"}","[":"]","(":")"};this.$rules[t]=i.deepCopy(this.$rules.start),this.$rules[t].unshift({regex:"[\\])}]",onMatch:function(t,r,i){var s=i[1];return n[s]!==t?"invalid.illegal":(i.shift(),i.shift(),this.next=e(t,i[0])||"start","paren.rparen")}})};r.inherits(f,u);var l=function(){o.call(this);var e={regex:"@[({]|@functions{",onMatch:function(e,t,n){return n.unshift(e),n.unshift("razor-block-start"),this.next="razor-block-start","punctuation.block.razor"}},t={"@{":"}","@(":")","@functions{":"}"},n={regex:"[})]",onMatch:function(e,n,r){var i=r[1];return t[i]!==e?"invalid.illegal":(r.shift(),r.shift(),this.next=r.shift()||"start","punctuation.block.razor")}},r={regex:"@(?![{(])",onMatch:function(e,t,n){return n.unshift("razor-short-start"),this.next="razor-short-start","punctuation.short.razor"}},i={token:"",regex:"(?=[^A-Za-z_\\.()\\[\\]])",next:"pop"},s={regex:"@(?=if)",onMatch:function(e,t,n){return n.unshift(function(e){return e!=="}"?"start":n.shift()||"start"}),this.next="razor-block-start","punctuation.control.razor"}},u=[{token:["meta.directive.razor","text","identifier"],regex:"^(\\s*@model)(\\s+)(.+)$"},e,r];for(var a in this.$rules)this.$rules[a].unshift.apply(this.$rules[a],u);this.embedRules(f,"razor-block-",[n],["start"]),this.embedRules(f,"razor-short-",[i],["start"]),this.normalizeRules()};r.inherits(l,o),t.RazorHighlightRules=l,t.RazorLangHighlightRules=f}),ace.define("ace/mode/razor_completions",["require","exports","module","ace/token_iterator"],function(e,t,n){"use strict";var r=e("../token_iterator").TokenIterator,i=["abstract","as","base","bool","break","byte","case","catch","char","checked","class","const","continue","decimal","default","delegate","do","double","else","enum","event","explicit","extern","false","finally","fixed","float","for","foreach","goto","if","implicit","in","int","interface","internal","is","lock","long","namespace","new","null","object","operator","out","override","params","private","protected","public","readonly","ref","return","sbyte","sealed","short","sizeof","stackalloc","static","string","struct","switch","this","throw","true","try","typeof","uint","ulong","unchecked","unsafe","ushort","using","var","virtual","void","volatile","while"],s=["Html","Model","Url","Layout"],o=function(){};(function(){this.getCompletions=function(e,t,n,r){if(e.lastIndexOf("razor-short-start")==-1&&e.lastIndexOf("razor-block-start")==-1)return[];var i=t.getTokenAt(n.row,n.column);if(!i)return[];if(e.lastIndexOf("razor-short-start")!=-1)return this.getShortStartCompletions(e,t,n,r);if(e.lastIndexOf("razor-block-start")!=-1)return this.getKeywordCompletions(e,t,n,r)},this.getShortStartCompletions=function(e,t,n,r){return s.map(function(e){return{value:e,meta:"keyword",score:Number.MAX_VALUE}})},this.getKeywordCompletions=function(e,t,n,r){return s.concat(i).map(function(e){return{value:e,meta:"keyword",score:Number.MAX_VALUE}})}}).call(o.prototype),t.RazorCompletions=o}),ace.define("ace/mode/razor",["require","exports","module","ace/lib/oop","ace/mode/html","ace/mode/razor_highlight_rules","ace/mode/razor_completions","ace/mode/html_completions"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./html").Mode,s=e("./razor_highlight_rules").RazorHighlightRules,o=e("./razor_completions").RazorCompletions,u=e("./html_completions").HtmlCompletions,a=function(){i.call(this),this.$highlightRules=new s,this.$completer=new o,this.$htmlCompleter=new u};r.inherits(a,i),function(){this.getCompletions=function(e,t,n,r){var i=this.$completer.getCompletions(e,t,n,r),s=this.$htmlCompleter.getCompletions(e,t,n,r);return i.concat(s)},this.createWorker=function(e){return null},this.$id="ace/mode/razor"}.call(a.prototype),t.Mode=a}) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html index fd112a018a..d610c1bc5b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html @@ -2,7 +2,7 @@
-
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index b44ce8f20c..28244b1a7c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -1,47 +1,96 @@ (function () { "use strict"; - function TemplatesEditController($scope) { + function TemplatesEditController($scope, $routeParams, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService) { var vm = this; - vm.page = {}; - vm.page.loading = false; + vm.page.loading = true; + + //menu + vm.page.menu = {}; + vm.page.menu.currentSection = appState.getSectionState("currentSection"); + vm.page.menu.currentNode = null; - vm.mode = "css"; - - vm.aceOption = { - mode: vm.mode.toLowerCase(), - onLoad: function(ace) { - - console.log(ace); - - // HACK to have the ace instance in the scope... - /* - $scope.modeChanged = function() { - _ace.getSession().setMode("ace/mode/" + $scope.mode.toLowerCase()); - }; - */ - - } + vm.insert = function(str){ + vm.editor.insert(str); + vm.editor.focus(); }; - vm.aceModel = ';; Scheme code in here.\n' + - '(define (double x)\n\t(* x x))\n\n\n' + - '\n' + - '\n\t\n\t\n\t\n\n\n\n' + - '// Javascript code in here.\n' + - 'function foo(msg) {\n\tvar r = Math.random();\n\treturn "" + r + " : " + msg;\n}'; + vm.save = function(){ + vm.page.saveButtonState = "busy"; + vm.template.content = vm.editor.getValue(); + + templateResource.save(vm.template).then(function(saved){ + notificationsService.success("Template saved") + vm.page.saveButtonState = "success"; + vm.template = saved; + + //sync state + editorState.set(vm.template); + navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + + + }, function(err){ + notificationsService.error("Template save failed") + vm.page.saveButtonState = "error"; + }); + }; + + vm.init = function(){ + + + //we need to load this somewhere, for now its here. + assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css"); + templateResource.getById($routeParams.id).then(function(template){ + + vm.page.loading = false; + vm.template = template; + + //sync state + editorState.set(vm.template); + navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + + vm.aceOption = { + mode: "razor", + theme: "chrome", + + onLoad: function(_editor) { + vm.editor = _editor; + } + }; + }); + + }; + vm.openPageFieldOverlay = openPageFieldOverlay; vm.openDictionaryItemOverlay = openDictionaryItemOverlay; vm.openQueryBuilderOverlay = openQueryBuilderOverlay; + vm.openMacroOverlay = openMacroOverlay; - function init() { + function openMacroOverlay() { + + vm.macroPickerOverlay = { + view: "macropicker", + dialogData: {}, + show: true, + submit: function(model) { + var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); + vm.insert(macroObject.syntax); + vm.macroPickerOverlay.show = false; + vm.macroPickerOverlay = null; + } + }; } + function openPageFieldOverlay() { vm.pageFieldOverlay = { view: "mediapicker", @@ -84,7 +133,7 @@ }; } - init(); + vm.init(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index c6244a8035..eeab721902 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -2,7 +2,11 @@ -
+ + @@ -10,65 +14,63 @@ name="vm.template.name" alias="vm.template.alias" hideDescription="true" - hide-icon="true"> + description="vm.template.virtualPath" + menu="vm.page.menu" + hide-icon="false"> - +
+ + + + + + +
- - - - - - - - - - - - - -
+
+ + +
+
+ ng-model="vm.template.content">
+
- + + + + - - - - +
-
+ + + @@ -339,9 +340,11 @@ + + From 2368ea7cf84557a8f572b92569b41f9a70072d65 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Sun, 12 Jun 2016 18:49:05 +0200 Subject: [PATCH 003/599] Serverside template services --- .../src/common/resources/template.resource.js | 172 ++++++++++++++++++ .../Editors/BackOfficeController.cs | 4 + src/Umbraco.Web/Editors/TemplateController.cs | 110 +++++++++++ .../Models/ContentEditing/TemplateDisplay.cs | 34 ++++ .../Models/Mapping/TemplateModelMapping.cs | 29 +++ 5 files changed, 349 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js create mode 100644 src/Umbraco.Web/Editors/TemplateController.cs create mode 100644 src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs create mode 100644 src/Umbraco.Web/Models/Mapping/TemplateModelMapping.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js new file mode 100644 index 0000000000..c90bb1766d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js @@ -0,0 +1,172 @@ +/** + * @ngdoc service + * @name umbraco.resources.templateResource + * @description Loads in data for templates + **/ +function templateResource($q, $http, umbDataFormatter, umbRequestHelper) { + + return { + + /** + * @ngdoc method + * @name umbraco.resources.templateResource#getById + * @methodOf umbraco.resources.templateResource + * + * @description + * Gets a template item with a given id + * + * ##usage + *
+         * templateResource.getById(1234)
+         *    .then(function(template) {
+         *        alert('its here!');
+         *    });
+         * 
+ * + * @param {Int} id id of template to retrieve + * @returns {Promise} resourcePromise object. + * + */ + getById: function (id) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "templateApiBaseUrl", + "GetById", + [{ id: id }])), + "Failed to retrieve data for template id " + id); + }, + + /** + * @ngdoc method + * @name umbraco.resources.templateResource#getByName + * @methodOf umbraco.resources.templateResource + * + * @description + * Gets a template item with a given name + * + * ##usage + *
+         * templateResource.getByName("upload")
+         *    .then(function(datatype) {
+         *        alert('its here!');
+         *    });
+         * 
+ * + * @param {String} name Name of template to retrieve + * @returns {Promise} resourcePromise object. + * + */ + getByAlias: function (alias) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "templateApiBaseUrl", + "GetByAlias", + [{ alias: alias }])), + "Failed to retrieve data for template with alias: " + alias); + }, + + getAll: function () { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "templateApiBaseUrl", + "GetAll")), + "Failed to retrieve data"); + }, + + + /** + * @ngdoc method + * @name umbraco.resources.contentResource#getScaffold + * @methodOf umbraco.resources.contentResource + * + * @description + * Returns a scaffold of an empty template item + * + * The scaffold is used to build editors for templates that has not yet been populated with data. + * + * ##usage + *
+         * templateResource.getScaffold()
+         *    .then(function(scaffold) {
+         *        var myType = scaffold;
+         *        myType.name = "My new template";
+         *
+         *        templateResource.save(myType, myType.preValues, true)
+         *            .then(function(type){
+         *                alert("Retrieved, updated and saved again");
+         *            });
+         *    });
+         * 
+ * + * @returns {Promise} resourcePromise object containing the template scaffold. + * + */ + getScaffold: function () { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "templateApiBaseUrl", + "GetEmpty")), + "Failed to retrieve data for empty template"); + }, + + /** + * @ngdoc method + * @name umbraco.resources.templateResource#deleteById + * @methodOf umbraco.resources.templateResource + * + * @description + * Deletes a template with a given id + * + * ##usage + *
+         * templateResource.deleteById(1234)
+         *    .then(function() {
+         *        alert('its gone!');
+         *    });
+         * 
+ * + * @param {Int} id id of content item to delete + * @returns {Promise} resourcePromise object. + * + */ + deleteById: function(id) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "templateApiBaseUrl", + "DeleteById", + [{ id: id }])), + "Failed to delete item " + id); + }, + + + /** + * @ngdoc method + * @name umbraco.resources.templateResource#save + * @methodOf umbraco.resources.templateResource + * + * @description + * Saves or update a template + * + * @param {Object} template object to create/update + * @returns {Promise} resourcePromise object. + * + */ + save: function (template) { + + return umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("templateApiBaseUrl", "PostSave"), template), + "Failed to save data for template id " + template.id); + } + }; +} + +angular.module("umbraco.resources").factory("templateResource", templateResource); diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 95e1f7803e..968a047fba 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -327,6 +327,10 @@ namespace Umbraco.Web.Editors "tagApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( controller => controller.GetAllTags(null)) }, + { + "templateApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetById(0)) + }, { "memberTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl( controller => controller.GetNodes("-1", null)) diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs new file mode 100644 index 0000000000..27b7c67e11 --- /dev/null +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http; +using AutoMapper; +using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi.Filters; +using Constants = Umbraco.Core.Constants; + +namespace Umbraco.Web.Editors +{ + [PluginController("UmbracoApi")] + [UmbracoTreeAuthorize(Constants.Trees.Templates)] + public class TemplateController : UmbracoAuthorizedJsonController + { + /// + /// Gets data type by alias + /// + /// + /// + public TemplateDisplay GetByAlias(string alias) + { + var template = Services.FileService.GetTemplate(alias); + return template == null ? null : Mapper.Map(template); + } + + /// + /// Get all templates + /// + /// + public IEnumerable GetAll() + { + return Services.FileService.GetTemplates().Select(Mapper.Map); + } + + /// + /// Gets the content json for the content id + /// + /// + /// + public TemplateDisplay GetById(int id) + { + var template = Services.FileService.GetTemplate(id); + + if (template == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + return Mapper.Map(template); + } + + /// + /// Deletes a template wth a given ID + /// + /// + /// + [HttpDelete] + [HttpPost] + public HttpResponseMessage DeleteById(int id) + { + var template = Services.FileService.GetTemplate(id); + if (template == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + Services.FileService.DeleteTemplate(template.Alias); + return Request.CreateResponse(HttpStatusCode.OK); + } + + public TemplateDisplay GetEmpty() + { + var dt = new Template("", ""); + return Mapper.Map((ITemplate)dt); + } + + /// + /// Saves the data type + /// + /// + /// + public TemplateDisplay PostSave(TemplateDisplay display) + { + if(display.Id > 0) + { + var template = Services.FileService.GetTemplate(display.Id); + Mapper.Map(display, template); + Services.FileService.SaveTemplate(template); + return display; + } + else + { + //create + ITemplate master = null; + if (string.IsNullOrEmpty(display.MasterTemplateAlias) == false) + master = Services.FileService.GetTemplate(display.MasterTemplateAlias); + + var template = Services.FileService.CreateTemplateWithIdentity(display.Name, display.Content, master); + Mapper.Map(template, display); + return display; + } + } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs new file mode 100644 index 0000000000..dacccb8ceb --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "template", Namespace = "")] + public class TemplateDisplay + { + [DataMember(Name = "id")] + public int Id { get; set; } + + [DataMember(Name = "name")] + public string Name { get; set; } + + [DataMember(Name = "alias")] + public string Alias { get; set; } + + [DataMember(Name = "content")] + public string Content { get; set; } + + [DataMember(Name = "path")] + public string Path { get; set; } + + [DataMember(Name = "virtualPath")] + public string VirtualPath { get; set; } + + [DataMember(Name = "masterTemplateAlias")] + public string MasterTemplateAlias { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/Mapping/TemplateModelMapping.cs b/src/Umbraco.Web/Models/Mapping/TemplateModelMapping.cs new file mode 100644 index 0000000000..c4f96b7def --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/TemplateModelMapping.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Mapping; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + internal class TemplateModelMapper : MapperConfiguration + { + public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) + { + config.CreateMap(); + + config.CreateMap() + .ForMember(x => x.Key, exp => exp.Ignore()) + .ForMember(x => x.Path, exp => exp.Ignore()) + .ForMember(x => x.CreateDate, exp => exp.Ignore()) + .ForMember(x => x.UpdateDate, exp => exp.Ignore()) + .ForMember(x => x.VirtualPath, exp => exp.Ignore()) + .ForMember(x => x.Path, exp => exp.Ignore()); + } + } +} From 8a66943f4a3786b86429cc737f010e8bcc8956b6 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Sun, 12 Jun 2016 18:49:19 +0200 Subject: [PATCH 004/599] Makes template tree load the new angular editor --- src/Umbraco.Web/Trees/TemplatesTreeController.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Trees/TemplatesTreeController.cs b/src/Umbraco.Web/Trees/TemplatesTreeController.cs index 61cff8f862..b99a9ebff3 100644 --- a/src/Umbraco.Web/Trees/TemplatesTreeController.cs +++ b/src/Umbraco.Web/Trees/TemplatesTreeController.cs @@ -52,8 +52,7 @@ namespace Umbraco.Web.Trees queryStrings, template.Name, template.IsMasterTemplate ? "icon-newspaper" : "icon-newspaper-alt", - template.IsMasterTemplate, - GetEditorPath(template, queryStrings)))); + template.IsMasterTemplate))); return nodes; } From a3614fee0dce1e1157bad7eb03d7feff70dd9af9 Mon Sep 17 00:00:00 2001 From: hemraker Date: Mon, 13 Jun 2016 13:08:33 +0200 Subject: [PATCH 005/599] Added ace to gruntFile.js --- src/Umbraco.Web.UI.Client/gruntFile.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/gruntFile.js b/src/Umbraco.Web.UI.Client/gruntFile.js index d5b785c54c..5d92cdbd8e 100644 --- a/src/Umbraco.Web.UI.Client/gruntFile.js +++ b/src/Umbraco.Web.UI.Client/gruntFile.js @@ -394,7 +394,7 @@ module.exports = function (grunt) { tutorials: { src: [], title: '' - } + } }, eslint:{ @@ -519,7 +519,10 @@ module.exports = function (grunt) { 'addon/selection/*', 'addon/dialog/*' ] - } + }, + 'ace-builds': { + files: ['src-min-noconflict/**'] + } } } }, From db2dafd40c19242be871b8d0f2de71c929dcd48b Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 13 Jun 2016 15:19:30 +0200 Subject: [PATCH 006/599] make it possible to lock a description in the editor header --- .../components/editor/umbeditorheader.directive.js | 1 + src/Umbraco.Web.UI.Client/src/less/panel.less | 6 ++++++ .../src/views/components/editor/umb-editor-header.html | 4 +++- src/Umbraco.Web.UI.Client/src/views/templates/edit.html | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js index cc57eb6e74..4d9222fa45 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js @@ -254,6 +254,7 @@ Use this directive to construct a header inside the main editor window. hideAlias: "@", description: "=", hideDescription: "@", + descriptionLocked: "@", navigation: "=" }, link: link diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index a1776bf12d..09cc6792c0 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -480,6 +480,12 @@ input.umb-panel-header-description { } } +.umb-panel-header-locked-description { + font-size: 12px; + margin-left: 2px; + margin-top: 3px; +} + .umb-editor-drawer-content { display: flex; align-items: center; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html index d610c1bc5b..72a28d8de3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html @@ -43,9 +43,11 @@ class="umb-panel-header-description" localize="placeholder" placeholder="@placeholders_enterDescription" - ng-if="!hideDescription" + ng-if="!hideDescription && !descriptionLocked" ng-model="$parent.description" /> +
{{ description }}
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index eeab721902..9edecde33c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -15,6 +15,7 @@ alias="vm.template.alias" hideDescription="true" description="vm.template.virtualPath" + description-locked="true" menu="vm.page.menu" hide-icon="false"> From e18a668e131599218c83a1256e100f7d316983ce Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 13 Jun 2016 15:22:13 +0200 Subject: [PATCH 007/599] add toolbar --- .../src/views/templates/edit.html | 60 ++++++++++++++----- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index 9edecde33c..c73c8f20a6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -22,27 +22,59 @@ -
- - - - - - -
+ -
- - -
- + + + + + + + + + + + + +
- +
From b2ea92940ef5a8d9de9a0e558c9bcd4357a1200c Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Mon, 13 Jun 2016 15:48:47 +0200 Subject: [PATCH 008/599] Moves querybuilder to a overlay --- .../querybuilder/querybuilder.controller.js | 101 +++++++++++++ .../overlays/querybuilder/querybuilder.html | 136 ++++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js new file mode 100644 index 0000000000..1680c4f9d1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js @@ -0,0 +1,101 @@ +angular.module("umbraco").controller('Umbraco.Overlays.QueryBuilderController', + function($scope, $http, dialogService){ + + $http.get("backoffice/UmbracoApi/TemplateQuery/GetAllowedProperties").then(function(response) { + $scope.properties = response.data; + }); + + $http.get("backoffice/UmbracoApi/TemplateQuery/GetContentTypes").then(function (response) { + $scope.contentTypes = response.data; + }); + + $http.get("backoffice/UmbracoApi/TemplateQuery/GetFilterConditions").then(function (response) { + $scope.conditions = response.data; + }); + + + $scope.query = { + contentType: { + name: "Everything" + }, + source:{ + name: "My website" + }, + filters:[ + { + property:undefined, + operator: undefined + } + ], + sort:{ + property:{ + alias: "", + name: "", + }, + direction: "ascending" + } + }; + + + + $scope.chooseSource = function(query){ + dialogService.contentPicker({ + callback: function (data) { + + if (data.id > 0) { + query.source = { id: data.id, name: data.name }; + } else { + query.source.name = "My website"; + delete query.source.id; + } + } + }); + }; + + var throttledFunc = _.throttle(function() { + + $http.post("backoffice/UmbracoApi/TemplateQuery/PostTemplateQuery", $scope.query).then(function (response) { + $scope.model.result = response.data; + }); + + }, 200); + + $scope.$watch("query", function(value) { + throttledFunc(); + }, true); + + $scope.getPropertyOperators = function (property) { + + var conditions = _.filter($scope.conditions, function(condition) { + var index = condition.appliesTo.indexOf(property.type); + return index >= 0; + }); + return conditions; + }; + + + $scope.addFilter = function(query){ + query.filters.push({}); + }; + + $scope.trashFilter = function (query) { + query.filters.splice(query,1); + }; + + $scope.changeSortOrder = function(query){ + if(query.sort.direction === "ascending"){ + query.sort.direction = "descending"; + }else{ + query.sort.direction = "ascending"; + } + }; + + $scope.setSortProperty = function(query, property){ + query.sort.property = property; + if(property.type === "datetime"){ + query.sort.direction = "descending"; + }else{ + query.sort.direction = "ascending"; + } + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html new file mode 100644 index 0000000000..d18cb3a412 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html @@ -0,0 +1,136 @@ +
+ + + +
+ + + + +

Returns {{model.result.resultCount}} items in {{model.result.executionTime}} miliseconds

+ + + +
+{{model.result.queryExpression}}
+	    
+ +
+
\ No newline at end of file From f17aadccda1fc5f5f6de29d994d982fc892201ac Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Mon, 13 Jun 2016 15:49:00 +0200 Subject: [PATCH 009/599] adds setLayout to the editor --- .../src/views/templates/edit.controller.js | 55 +++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 28244b1a7c..a08303e14d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -68,11 +68,41 @@ }; + + vm.setLayout = function(path){ + + var templateCode = vm.editor.getValue(); + var newValue = path; + var layoutDefRegex = new RegExp("(@{[\\s\\S]*?Layout\\s*?=\\s*?)(\"[^\"]*?\"|null)(;[\\s\\S]*?})", "gi"); + + if (newValue != undefined && newValue != "") { + if (layoutDefRegex.test(templateCode)) { + // Declaration exists, so just update it + templateCode = templateCode.replace(layoutDefRegex, "$1\"" + newValue + "\"$3"); + } else { + // Declaration doesn't exist, so prepend to start of doc + //TODO: Maybe insert at the cursor position, rather than just at the top of the doc? + templateCode = "@{\n\tLayout = \"" + newValue + "\";\n}\n" + templateCode; + } + } else { + if (layoutDefRegex.test(templateCode)) { + // Declaration exists, so just update it + templateCode = templateCode.replace(layoutDefRegex, "$1null$3"); + } + } + + vm.editor.setValue(templateCode); + vm.editor.clearSelection(); + vm.editor.navigateFileStart(); + }; + + vm.openPageFieldOverlay = openPageFieldOverlay; vm.openDictionaryItemOverlay = openDictionaryItemOverlay; vm.openQueryBuilderOverlay = openQueryBuilderOverlay; vm.openMacroOverlay = openMacroOverlay; + function openMacroOverlay() { vm.macroPickerOverlay = { @@ -87,7 +117,6 @@ vm.macroPickerOverlay = null; } }; - } @@ -105,13 +134,17 @@ }; } + function openDictionaryItemOverlay() { vm.dictionaryItemOverlay = { - view: "mediapicker", + view: "treepicker", + dialogOptions: {section: "settings", treeAlias: "dictionary"}, show: true, - submit: function(model) { + submit: function(model) { + console.log(model); }, + close: function(model) { vm.dictionaryItemOverlay.show = false; vm.dictionaryItemOverlay = null; @@ -119,13 +152,27 @@ }; } + function openQueryBuilderOverlay() { vm.queryBuilderOverlay = { - view: "mediapicker", + view: "querybuilder", show: true, + title: "Query for content", + submit: function(model) { + var code = "\n@{\n" + "\tvar selection = " + model.result.queryExpression + ";\n}\n"; + code += "
    \n" + + "\t@foreach(var item in selection){\n" + + "\t\t
  • \n" + + "\t\t\t@item.Name\n" + + "\t\t
  • \n" + + "\t}\n" + + "
\n\n"; + + vm.insert(code); }, + close: function(model) { vm.queryBuilderOverlay.show = false; vm.queryBuilderOverlay = null; From 746072a65dfba345f89d90ca52bd0ebfef957891 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Mon, 13 Jun 2016 15:49:57 +0200 Subject: [PATCH 010/599] sample use of insert layout --- src/Umbraco.Web.UI.Client/src/views/templates/edit.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index eeab721902..21bb3009ea 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -24,14 +24,14 @@ From 44cde95b47eb877da8ea51a058dc9961d3529d1a Mon Sep 17 00:00:00 2001 From: Lars-Erik Aabech Date: Mon, 13 Jun 2016 16:00:33 +0200 Subject: [PATCH 011/599] organize controller added with list of templates for master selection --- .../dialogs/template/organize.controller.js | 22 +++++++++++++++++++ .../common/dialogs/template/organize.html | 21 ++++++++++++++++++ .../src/views/templates/edit.controller.js | 16 ++++++++++++++ .../src/views/templates/edit.html | 9 ++++++++ 4 files changed, 68 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.html diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.controller.js new file mode 100644 index 0000000000..f1ba776714 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.controller.js @@ -0,0 +1,22 @@ +(function() { + "use strict"; + + function OrganizeController(scope, umbRequestHelper, http) { + var allTemplatesUrl = umbRequestHelper.getApiUrl("templateApiBaseUrl", "GetAll"); + + http.get(allTemplatesUrl) + .then(function(result) { + scope.masterPages = result.data; + }); + } + + angular.module("umbraco") + .controller("Umbraco.Dialogs.Template.OrganizeController", + [ + "$scope", + "umbRequestHelper", + "$http", + OrganizeController + ]); + +}()); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.html new file mode 100644 index 0000000000..ad9f1b8d0e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.html @@ -0,0 +1,21 @@ +
+
+ + +
+ +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 28244b1a7c..ea2eb2167d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -72,6 +72,7 @@ vm.openDictionaryItemOverlay = openDictionaryItemOverlay; vm.openQueryBuilderOverlay = openQueryBuilderOverlay; vm.openMacroOverlay = openMacroOverlay; + vm.openOrganizeOverlay = openOrganizeOverlay; function openMacroOverlay() { @@ -133,6 +134,21 @@ }; } + function openOrganizeOverlay() { + vm.organizeOverlay = { + view: "/umbraco/views/common/dialogs/template/organize.html", + show: true, + template: vm.template, + submit: function(model) { + vm.setLayout(model); + }, + close: function(model) { + vm.organizeOverlay.show = false; + vm.organizeOverlay = null; + } + } + } + vm.init(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index eeab721902..0a9e2c5be4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -28,6 +28,8 @@ + +
@@ -93,4 +95,11 @@ view="vm.queryBuilderOverlay.view"> + + +
From 646b3c6efb5cf0e12370cfde1c16bed1a1ab552f Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Mon, 13 Jun 2016 17:22:11 +0200 Subject: [PATCH 012/599] Insert dictionary item dialog Includes a way to override the tree picker as it currently thinks everything is an entity, which it isn't --- .../treepicker/treepicker.controller.js | 12 ++++++++---- .../src/views/templates/edit.controller.js | 17 +++++++++++++++-- .../src/views/templates/edit.html | 7 +++---- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js index e74bfd20a4..21687aafd3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js @@ -206,10 +206,14 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", //This is a tree node, so we don't have an entity to pass in, it will need to be looked up //from the server in this method. - select(args.node.name, args.node.id); - - //toggle checked state - args.node.selected = args.node.selected === true ? false : true; + if($scope.model.select){ + $scope.model.select(args.node) + }else{ + select(args.node.name, args.node.id); + //toggle checked state + args.node.selected = args.node.selected === true ? false : true; + } + } } diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index a08303e14d..8bdb755b01 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -138,11 +138,24 @@ function openDictionaryItemOverlay() { vm.dictionaryItemOverlay = { view: "treepicker", - dialogOptions: {section: "settings", treeAlias: "dictionary"}, + section: "settings", + treeAlias: "dictionary", + entityType: "dictionary", + multiPicker: false, + show: true, submit: function(model) { - console.log(model); + + }, + + select: function(node){ + //crappy hack due to dictionary items not in umbracoNode table + var code = "@Umbraco.GetDictionaryValue(\"" + node.name + "\")"; + vm.insert(code); + + vm.dictionaryItemOverlay.show = false; + vm.dictionaryItemOverlay = null; }, close: function(model) { diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index c73c8f20a6..d856c4efb0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -41,10 +41,9 @@
@@ -54,7 +53,7 @@ button-style="link" label="Query Builder" icon="icon-wand" - action="vm.openDictionaryItemOverlay()"> + action="vm.openQueryBuilderOverlay()"> Date: Mon, 13 Jun 2016 17:27:57 +0200 Subject: [PATCH 013/599] Organize dialog can now insert sections, rendersections, renderbody, and select master. Cursor stays where it was before editor was blurred. --- .../dialogs/template/organize.controller.js | 22 ---- .../common/dialogs/template/organize.html | 21 ---- .../overlays/organize/organize.controller.js | 40 ++++++ .../common/overlays/organize/organize.html | 51 ++++++++ .../src/views/templates/edit.controller.js | 116 +++++++++++------- .../src/views/templates/edit.html | 3 +- 6 files changed, 167 insertions(+), 86 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.controller.js delete mode 100644 src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.html diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.controller.js deleted file mode 100644 index f1ba776714..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.controller.js +++ /dev/null @@ -1,22 +0,0 @@ -(function() { - "use strict"; - - function OrganizeController(scope, umbRequestHelper, http) { - var allTemplatesUrl = umbRequestHelper.getApiUrl("templateApiBaseUrl", "GetAll"); - - http.get(allTemplatesUrl) - .then(function(result) { - scope.masterPages = result.data; - }); - } - - angular.module("umbraco") - .controller("Umbraco.Dialogs.Template.OrganizeController", - [ - "$scope", - "umbRequestHelper", - "$http", - OrganizeController - ]); - -}()); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.html deleted file mode 100644 index ad9f1b8d0e..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/template/organize.html +++ /dev/null @@ -1,21 +0,0 @@ -
-
- - -
- -
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.controller.js new file mode 100644 index 0000000000..136399c172 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.controller.js @@ -0,0 +1,40 @@ +(function() { + "use strict"; + + function OrganizeController(scope, umbRequestHelper, http) { + var allTemplatesUrl = umbRequestHelper.getApiUrl("templateApiBaseUrl", "GetAll"); + + scope.model.addRenderBody = false; + scope.model.mandatoryRenderSection = false; + scope.masterPage = {}; + + http.get(allTemplatesUrl) + .then(function(result) { + scope.masterPages = result.data; + scope.masterPages.splice(0, + 0, + { + alias: null, + name: "None" + }); + scope.model.masterPage = $.grep(scope.masterPages, + function(mp) { + return mp.alias === scope.model.template.masterPageAlias; + })[0]; + }); + + scope.selectMasterPage = function(masterPage) { + scope.model.masterPage = masterPage; + } + } + + angular.module("umbraco") + .controller("Umbraco.Dialogs.Template.OrganizeController", + [ + "$scope", + "umbRequestHelper", + "$http", + OrganizeController + ]); + +}()); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.html new file mode 100644 index 0000000000..57df7e9556 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.html @@ -0,0 +1,51 @@ +
+ +
+

Master page

+
+ +
+
+ + + +
+
+
+

Child page

+
+ + +
+ +
+
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 5a67426e0d..efaa9983e8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -6,24 +6,27 @@ var vm = this; vm.page = {}; vm.page.loading = true; - + //menu vm.page.menu = {}; vm.page.menu.currentSection = appState.getSectionState("currentSection"); vm.page.menu.currentNode = null; - vm.insert = function(str){ + vm.insert = function (str) { + vm.editor.moveCursorToPosition(vm.currentPosition); vm.editor.insert(str); vm.editor.focus(); }; - vm.save = function(){ + vm.currentPosition = { col: 0, row: 0 }; + + vm.save = function () { vm.page.saveButtonState = "busy"; vm.template.content = vm.editor.getValue(); - templateResource.save(vm.template).then(function(saved){ - notificationsService.success("Template saved") + templateResource.save(vm.template).then(function (saved) { + notificationsService.success("Template saved"); vm.page.saveButtonState = "success"; vm.template = saved; @@ -34,42 +37,49 @@ }); - }, function(err){ - notificationsService.error("Template save failed") + }, function (err) { + notificationsService.error("Template save failed"); vm.page.saveButtonState = "error"; }); }; - vm.init = function(){ + function persistCurrentLocation() { + vm.currentPosition = vm.editor.getCursorPosition(); + } + + vm.init = function () { //we need to load this somewhere, for now its here. assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css"); - templateResource.getById($routeParams.id).then(function(template){ - - vm.page.loading = false; - vm.template = template; + templateResource.getById($routeParams.id).then(function (template) { - //sync state - editorState.set(vm.template); - navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { - vm.page.menu.currentNode = syncArgs.node; - }); + vm.page.loading = false; + vm.template = template; - vm.aceOption = { - mode: "razor", - theme: "chrome", + //sync state + editorState.set(vm.template); + navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); - onLoad: function(_editor) { - vm.editor = _editor; + vm.aceOption = { + mode: "razor", + theme: "chrome", + + onLoad: function (_editor) { + vm.editor = _editor; + + vm.editor.on("blur", persistCurrentLocation); + vm.editor.on("focus", persistCurrentLocation); } }; }); }; - - - vm.setLayout = function(path){ + + + vm.setLayout = function (path) { var templateCode = vm.editor.getValue(); var newValue = path; @@ -105,12 +115,12 @@ function openMacroOverlay() { - + vm.macroPickerOverlay = { view: "macropicker", dialogData: {}, show: true, - submit: function(model) { + submit: function (model) { var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); vm.insert(macroObject.syntax); @@ -120,15 +130,15 @@ }; } - + function openPageFieldOverlay() { vm.pageFieldOverlay = { view: "mediapicker", show: true, - submit: function(model) { + submit: function (model) { }, - close: function(model) { + close: function (model) { vm.pageFieldOverlay.show = false; vm.pageFieldOverlay = null; } @@ -139,28 +149,27 @@ function openDictionaryItemOverlay() { vm.dictionaryItemOverlay = { view: "treepicker", - dialogOptions: {section: "settings", treeAlias: "dictionary"}, + dialogOptions: { section: "settings", treeAlias: "dictionary" }, show: true, - submit: function(model) { + submit: function (model) { console.log(model); }, - close: function(model) { + close: function (model) { vm.dictionaryItemOverlay.show = false; vm.dictionaryItemOverlay = null; } }; } - function openQueryBuilderOverlay() { vm.queryBuilderOverlay = { view: "querybuilder", show: true, title: "Query for content", - - submit: function(model) { + + submit: function (model) { var code = "\n@{\n" + "\tvar selection = " + model.result.queryExpression + ";\n}\n"; code += "
    \n" + @@ -174,7 +183,7 @@ vm.insert(code); }, - close: function(model) { + close: function (model) { vm.queryBuilderOverlay.show = false; vm.queryBuilderOverlay = null; } @@ -183,13 +192,38 @@ function openOrganizeOverlay() { vm.organizeOverlay = { - view: "/umbraco/views/common/dialogs/template/organize.html", + view: "organize", show: true, template: vm.template, - submit: function(model) { - vm.setLayout(model); + submit: function (model) { + if (model.masterPage && model.masterPage.alias) { + vm.template.masterPageAlias = model.masterPage.alias; + vm.setLayout(model.masterPage.alias + ".cshtml"); + } else { + vm.template.masterPageAlias = null; + vm.setLayout(null); + } + + if (model.addRenderBody) { + vm.insert("@RenderBody()"); + } + + if (model.addRenderSection) { + vm.insert("@RenderSection(\"" + + model.renderSectionName + + "\", " + + model.mandatoryRenderSection + + ")"); + } + + if (model.addSection) { + vm.insert("@section " + model.sectionName + "\r\n{\r\n\r\n}\r\n"); + } + + vm.organizeOverlay.show = false; + vm.organizeOverlay = null; }, - close: function(model) { + close: function (model) { vm.organizeOverlay.show = false; vm.organizeOverlay = null; } diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index 1a8e93f1eb..137ea8a354 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -45,7 +45,6 @@
  • Value
  • Dictionary
  • Partial
  • -
  • Organize
@@ -63,7 +62,7 @@ button-style="link" label="Organise" icon="icon-layout" - action="vm.openQueryBuilderOverlay()"> + action="vm.openOrganizeOverlay()">
From 1bbcf47d07aac7118461c2983d4eb8b44118c6fb Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 13 Jun 2016 18:48:26 +0200 Subject: [PATCH 014/599] add "insert overlay" with insert dictionary item and insert macro --- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../less/components/umb-insert-code-box.less | 28 +++++++ .../overlays/insert/insert.controller.js | 76 +++++++++++++++++++ .../views/common/overlays/insert/insert.html | 41 ++++++++++ .../src/views/templates/edit.controller.js | 41 +++++++++- .../src/views/templates/edit.html | 9 ++- 6 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/umb-insert-code-box.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 9d01cae4b9..bd4bd1861d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -108,6 +108,7 @@ @import "components/umb-empty-state.less"; @import "components/umb-property-editor.less"; @import "components/umb-iconpicker.less"; +@import "components/umb-insert-code-box.less"; @import "components/buttons/umb-button.less"; @import "components/buttons/umb-button-group.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-insert-code-box.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-insert-code-box.less new file mode 100644 index 0000000000..7bb84dbb41 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-insert-code-box.less @@ -0,0 +1,28 @@ +.umb-insert-code-boxes { + display: flex; + flex-direction: column; +} + +.umb-insert-code-box { + border: 2px solid @grayLighter; + padding: 15px 20px; + margin-bottom: 10px; + border-radius: 3px; +} + +.umb-insert-code-box:hover, +.umb-insert-code-box.-selected { + border-color: @blue; + cursor: pointer; +} + +.umb-insert-code-box__title { + font-size: 15px; + margin-bottom: 5px; + font-weight: bold; + color: @black; +} + +.umb-insert-code-box__description { + font-size: 11px; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js new file mode 100644 index 0000000000..f2e00fa76c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js @@ -0,0 +1,76 @@ +(function () { + "use strict"; + + function InsertOverlayController($scope) { + + var vm = this; + + if(!$scope.model.title) { + $scope.model.title = "Insert"; + } + + if(!$scope.model.subtitle) { + $scope.model.subtitle = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; + } + + vm.openMacroPicker = openMacroPicker; + vm.openDictionaryItemOverlay = openDictionaryItemOverlay; + + + function openMacroPicker() { + + vm.macroPickerOverlay = { + view: "macropicker", + show: true, + dialogData: {}, + submit: function(model) { + + $scope.model.insert = { + "type": "macro", + "macroParams": model.macroParams, + "selectedMacro": model.selectedMacro + }; + + $scope.model.submit($scope.model); + + vm.macroPickerOverlay.show = false; + vm.macroPickerOverlay = null; + + } + }; + + } + + function openDictionaryItemOverlay() { + + vm.dictionaryItemOverlay = { + view: "treepicker", + section: "settings", + treeAlias: "dictionary", + entityType: "dictionary", + multiPicker: false, + show: true, + select: function(node){ + + $scope.model.insert = { + "type": "dictionary", + "node": node + }; + + $scope.model.submit($scope.model); + + vm.dictionaryItemOverlay.show = false; + vm.dictionaryItemOverlay = null; + }, + + close: function(model) { + vm.dictionaryItemOverlay.show = false; + vm.dictionaryItemOverlay = null; + } + }; + } + + } + + angular.module("umbraco").controller("Umbraco.Overlays.InsertOverlay", InsertOverlayController); +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html new file mode 100644 index 0000000000..fdc8368197 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html @@ -0,0 +1,41 @@ +
+ +
+ +
+
Macro
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque gravida hendrerit. Nunc lacinia ipsum sed urna volutpat finibus.
+
+ +
+
Value
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque gravida hendrerit. Nunc lacinia ipsum sed urna volutpat finibus.
+
+ +
+
Dictionary
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque gravida hendrerit. Nunc lacinia ipsum sed urna volutpat finibus.
+
+ +
+
Partial
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque gravida hendrerit. Nunc lacinia ipsum sed urna volutpat finibus.
+
+ +
+ + + + + + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 8bdb755b01..e27ba652ee 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -23,7 +23,7 @@ vm.template.content = vm.editor.getValue(); templateResource.save(vm.template).then(function(saved){ - notificationsService.success("Template saved") + notificationsService.success("Template saved"); vm.page.saveButtonState = "success"; vm.template = saved; @@ -35,7 +35,7 @@ }, function(err){ - notificationsService.error("Template save failed") + notificationsService.error("Template save failed"); vm.page.saveButtonState = "error"; }); }; @@ -75,7 +75,7 @@ var newValue = path; var layoutDefRegex = new RegExp("(@{[\\s\\S]*?Layout\\s*?=\\s*?)(\"[^\"]*?\"|null)(;[\\s\\S]*?})", "gi"); - if (newValue != undefined && newValue != "") { + if (newValue !== undefined && newValue !== "") { if (layoutDefRegex.test(templateCode)) { // Declaration exists, so just update it templateCode = templateCode.replace(layoutDefRegex, "$1\"" + newValue + "\"$3"); @@ -101,6 +101,41 @@ vm.openDictionaryItemOverlay = openDictionaryItemOverlay; vm.openQueryBuilderOverlay = openQueryBuilderOverlay; vm.openMacroOverlay = openMacroOverlay; + vm.openInsertOverlay = openInsertOverlay; + + function openInsertOverlay() { + + vm.insertOverlay = { + view: "insert", + hideSubmitButton: true, + show: true, + submit: function(model) { + + switch(model.insert.type) { + case "macro": + + var macroObject = macroService.collectValueData(model.insert.selectedMacro, model.insert.macroParams, "Mvc"); + vm.insert(macroObject.syntax); + break; + + case "dictionary": + //crappy hack due to dictionary items not in umbracoNode table + var code = "@Umbraco.GetDictionaryValue(\"" + model.insert.node.name + "\")"; + vm.insert(code); + break; + } + + vm.insertOverlay.show = false; + vm.insertOverlay = null; + + }, + close: function(oldModel) { + vm.insertOverlay.show = false; + vm.insertOverlay = null; + } + }; + + } function openMacroOverlay() { diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index d856c4efb0..888ff4c9a5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -33,7 +33,7 @@ button-style="link" label="Insert" icon="icon-add" - action="vm.openDocumentation()"> + action="vm.openInsertOverlay()"> @@ -97,6 +97,13 @@ + + + Date: Mon, 13 Jun 2016 19:02:28 +0200 Subject: [PATCH 015/599] create template tweaks --- .../src/views/templates/edit.controller.js | 49 ++++++++++++------- src/Umbraco.Web/Editors/TemplateController.cs | 12 ++++- .../umbraco/create/templateTasks.cs | 3 +- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index e27ba652ee..47d8177ca8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -45,29 +45,42 @@ //we need to load this somewhere, for now its here. assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css"); - templateResource.getById($routeParams.id).then(function(template){ - - vm.page.loading = false; - vm.template = template; + if($routeParams.create){ - //sync state - editorState.set(vm.template); - navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { - vm.page.menu.currentNode = syncArgs.node; - }); + templateResource.getScaffold().then(function(template){ + vm.ready(template); + }); - vm.aceOption = { - mode: "razor", - theme: "chrome", + }else{ - onLoad: function(_editor) { - vm.editor = _editor; - } - }; - }); + templateResource.getById($routeParams.id).then(function(template){ + vm.ready(template); + }); + + } }; + vm.ready = function(template){ + vm.page.loading = false; + vm.template = template; + + //sync state + editorState.set(vm.template); + navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + + vm.aceOption = { + mode: "razor", + theme: "chrome", + + onLoad: function(_editor) { + vm.editor = _editor; + } + } + }; + vm.setLayout = function(path){ @@ -188,7 +201,7 @@ //crappy hack due to dictionary items not in umbracoNode table var code = "@Umbraco.GetDictionaryValue(\"" + node.name + "\")"; vm.insert(code); - + vm.dictionaryItemOverlay.show = false; vm.dictionaryItemOverlay = null; }, diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs index 27b7c67e11..d45000155c 100644 --- a/src/Umbraco.Web/Editors/TemplateController.cs +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -77,7 +77,17 @@ namespace Umbraco.Web.Editors public TemplateDisplay GetEmpty() { var dt = new Template("", ""); - return Mapper.Map((ITemplate)dt); + var scaffold = Mapper.Map((ITemplate)dt); + scaffold.Path = "-1"; + scaffold.Content = @"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage +@{ + Layout = null; +} + +@* The fun starts here *@ + +"; + return scaffold; } /// diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/templateTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/templateTasks.cs index bbddcd9de8..8c1cab8e78 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/templateTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/templateTasks.cs @@ -13,8 +13,7 @@ using umbraco.cms.businesslogic.member; namespace umbraco { public class templateTasks : LegacyDialogTask - { - + { public override bool PerformSave() { var masterId = ParentID; From 93891a6a71e0fe28871a495457ea7f736b2ef6f2 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Mon, 13 Jun 2016 19:02:53 +0200 Subject: [PATCH 016/599] Make the template tree use the new view for create --- .../Trees/TemplatesTreeController.cs | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web/Trees/TemplatesTreeController.cs b/src/Umbraco.Web/Trees/TemplatesTreeController.cs index b99a9ebff3..04458cbc20 100644 --- a/src/Umbraco.Web/Trees/TemplatesTreeController.cs +++ b/src/Umbraco.Web/Trees/TemplatesTreeController.cs @@ -48,11 +48,13 @@ namespace Umbraco.Web.Trees nodes.AddRange(found.Select(template => CreateTreeNode( template.Id.ToString(CultureInfo.InvariantCulture), //TODO: Fix parent ID stuff for templates - "-1", + "-1", queryStrings, template.Name, template.IsMasterTemplate ? "icon-newspaper" : "icon-newspaper-alt", - template.IsMasterTemplate))); + template.IsMasterTemplate, + GetEditorPath(template, queryStrings) + ))); return nodes; } @@ -66,15 +68,14 @@ namespace Umbraco.Web.Trees protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) { var menu = new MenuItemCollection(); + //Create the normal create action + var item = menu.Items.Add(ui.Text("actions", ActionNew.Instance.Alias)); + item.NavigateToRoute(string.Format("{0}/templates/edit/{1}?create=true", queryStrings.GetValue("application"), id)); + if (id == Constants.System.Root.ToInvariantString()) { - //Create the normal create action - menu.Items.Add(ui.Text("actions", ActionNew.Instance.Alias)) - //Since we haven't implemented anything for templates in angular, this needs to be converted to - //use the legacy format - .ConvertLegacyMenuItem(null, "inittemplates", queryStrings.GetValue("application")); - + //refresh action menu.Items.Add(ui.Text("actions", ActionRefresh.Instance.Alias), true); @@ -85,12 +86,6 @@ namespace Umbraco.Web.Trees if (template == null) return new MenuItemCollection(); var entity = FromTemplate(template); - //Create the create action for creating sub layouts - menu.Items.Add(ui.Text("actions", ActionNew.Instance.Alias)) - //Since we haven't implemented anything for templates in angular, this needs to be converted to - //use the legacy format - .ConvertLegacyMenuItem(entity, "templates", queryStrings.GetValue("application")); - //don't allow delete if it has child layouts if (template.IsMasterTemplate == false) { @@ -131,8 +126,7 @@ namespace Umbraco.Web.Trees return Services.FileService.DetermineTemplateRenderingEngine(template) == RenderingEngine.WebForms ? "/" + queryStrings.GetValue("application") + "/framed/" + Uri.EscapeDataString("settings/editTemplate.aspx?templateID=" + template.Id) - : "/" + queryStrings.GetValue("application") + "/framed/" + - Uri.EscapeDataString("settings/Views/EditView.aspx?treeType=" + Constants.Trees.Templates + "&templateID=" + template.Id); + : null; } } } From fba91b203c4bc3b57ce2e54467257e74ced323be Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Mon, 13 Jun 2016 19:04:22 +0200 Subject: [PATCH 017/599] merge --- src/Umbraco.Web.UI.Client/src/views/templates/edit.html | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index 888ff4c9a5..d856c4efb0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -33,7 +33,7 @@ button-style="link" label="Insert" icon="icon-add" - action="vm.openInsertOverlay()"> + action="vm.openDocumentation()"> @@ -97,13 +97,6 @@ - - - Date: Mon, 13 Jun 2016 19:31:40 +0200 Subject: [PATCH 018/599] Add page field overlay --- .../insertfield/insertfield.controller.js | 176 +++++++++++++++++ .../overlays/insertfield/insertfield.html | 184 ++++++++++++++++++ .../src/views/templates/edit.controller.js | 18 +- .../src/views/templates/edit.html | 10 +- 4 files changed, 376 insertions(+), 12 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.html diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.controller.js new file mode 100644 index 0000000000..a2463434b9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.controller.js @@ -0,0 +1,176 @@ +(function () { + "use strict"; + + function InsertFieldController ($scope, $http, contentTypeResource) { + var vm = this; + + vm.field; + vm.altField; + vm.altText; + vm.insertPreviewTitle = "Modify output"; + vm.insertBefore; + vm.insertAfter; + vm.recursive = false; + vm.properties; + vm.date = false; + vm.dateTime = false; + vm.dateTimeSeparator = ""; + vm.casingUpper = false; + vm.casingLower = false; + vm.encodeHtml = false; + vm.encodeUrl = false; + vm.convertLinebreaks = false; + + vm.showAltField = false; + vm.showAltText = false; + + vm.showInsertPreview = showInsertPreview; + vm.updateInsertPreview = updateInsertPreview; + vm.hideInsertPreview = hideInsertPreview; + vm.updateDate = updateDate; + vm.updateDateTime = updateDateTime; + vm.updateUpper = updateUpper; + vm.updateLower = updateLower; + vm.updateEncodeHtml = updateEncodeHtml; + vm.updateEncodeUrl = updateEncodeUrl; + + contentTypeResource.getAllPropertyTypeAliases().then(function(array) { + vm.properties = array; + }); + + //hide preview by default + function hideInsertPreview () { + $scope.model.itemDetails = null; + } + + //Create preview + function showInsertPreview () { + var previewDetails = {}; + + previewDetails.title = vm.insertPreviewTitle; + + if (!vm.insertBefore && !vm.insertAfter) { + previewDetails.description = $scope.model.umbracoField + } else if (!vm.insertAfter) { + previewDetails.description = vm.insertBefore + ' ' + $scope.model.umbracoField; + } else { + previewDetails.description = vm.insertBefore + ' ' + $scope.model.umbracoField + ' ' + vm.insertAfter; + } + $scope.model.itemDetails = previewDetails; + } + + //Update preview + function updateInsertPreview () { + var previewDetails = $scope.model.itemDetails; + + previewDetails.title = vm.insertPreviewTitle; + + if (!vm.insertBefore && !vm.insertAfter) { + previewDetails.description = ' ' + $scope.model.umbracoField; + } else if (!vm.insertAfter) { + previewDetails.description = vm.insertBefore + ' ' + $scope.model.umbracoField; + } else { + previewDetails.description = vm.insertBefore + ' ' + $scope.model.umbracoField + ' ' + vm.insertAfter; + } + + $scope.model.itemDetails = previewDetails; + } + + // date formatting + function updateDate () { + if (vm.date) { + vm.date = false; + return; + }else { + vm.date = true; + if (vm.dateTime) { + vm.dateTime = false; + } + } + } + + function updateDateTime () { + if (vm.dateTime) { + vm.dateTime = false + } else { + vm.dateTime = true; + if (vm.date) { + vm.date = false; + } + } + } + + // casing + function updateUpper() { + if (vm.casingUpper) { + vm.casingUpper = false; + return; + }else { + vm.casingUpper = true; + if (vm.casingLower) { + vm.casingLower = false; + } + } + } + + function updateLower() { + if (vm.casingLower) { + vm.casingLower = false; + return; + }else { + vm.casingLower = true; + if (vm.casingUpper) { + vm.casingUpper = false; + } + } + } + + // encoding + function updateEncodeHtml() { + if (vm.encodeHtml) { + vm.encodeHtml = false; + return; + }else { + vm.encodeHtml = true; + if (vm.encodeUrl) { + vm.encodeUrl = false; + } + } + } + + function updateEncodeUrl() { + if (vm.encodeUrl) { + vm.encodeUrl = false; + return; + } else { + vm.encodeUrl = true; + if (vm.encodeHtml) { + vm.encodeHtml = false; + } + } + } + + $scope.updatePageField = function() { + var pageField = (vm.field !== undefined ? '@Umbraco.Field("' + vm.field + '"' : "") + + (vm.altField !== undefined ? ', altFieldAlias:"' + vm.altField + '"': "") + + (vm.altText !== undefined ? ', altText:"' + vm.altText + '"' : "") + + (vm.insertBefore !== undefined ? ', insertBefore:"' + vm.insertBefore + '"' : "") + + (vm.insertAfter !== undefined ? ', insertAfter:"' + vm.insertAfter + '"' : "") + + (vm.recursive !== false ? ', recursive: ' + vm.recursive : "") + + (vm.date !== false ? ', formatAsDate: ' + vm.date : "") + + (vm.dateTime !== false ? ', formatAsDateWithTimeSeparator:"' + vm.dateTimeSeparator + '"' : "") + + (vm.casingUpper !== false ? ', casing: ' + "RenderFieldCaseType.Upper" : "") + + (vm.casingLower !== false ? ', casing: ' + "RenderFieldCaseType.Lower" : "") + + (vm.encodeHtml !== false ? ', encoding: ' + "RenderFieldEncodingType.Html" : "") + + (vm.encodeUrl !== false ? ', encoding: ' + "RenderFieldEncodingType.Url" : "") + + (vm.convertLinebreaks !== false ? ', convertLineBreaks: ' + "true" : "") + + (vm.field ? ')' : ""); + $scope.model.umbracoField = pageField; + return pageField; + }; + + + } + + angular.module("umbraco").controller("Umbraco.Overlays.InsertFieldController", InsertFieldController); +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.html new file mode 100644 index 0000000000..c96484e0c6 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.html @@ -0,0 +1,184 @@ + +
+ +
+
+ + + +
+ +
+
+
+ +
+
+ + +
Add alternative field
+ +
+
+ + +
+ + +
+
+
+ + + + + Add default value + + +
+ +
+ + +
+ + +
+
+ +
+ + +
+
+ +

+ + +
+
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ + Date only + Date and time + +
+
+
+ +
+
+ +
+ + Upper + lower +
+
+
+ +
+
+ +
+ + + HTML + + URL + +
+
+
+ + +
+
+ +

+ + +
+
+
+ +
+
+ +

+ +

{{updatePageField()}}

+
+
+
+ + +
+ + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 28244b1a7c..d59d19762a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -6,7 +6,7 @@ var vm = this; vm.page = {}; vm.page.loading = true; - + //menu vm.page.menu = {}; vm.page.menu.currentSection = appState.getSectionState("currentSection"); @@ -46,7 +46,7 @@ //we need to load this somewhere, for now its here. assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css"); templateResource.getById($routeParams.id).then(function(template){ - + vm.page.loading = false; vm.template = template; @@ -67,14 +67,14 @@ }); }; - + vm.openPageFieldOverlay = openPageFieldOverlay; vm.openDictionaryItemOverlay = openDictionaryItemOverlay; vm.openQueryBuilderOverlay = openQueryBuilderOverlay; vm.openMacroOverlay = openMacroOverlay; function openMacroOverlay() { - + vm.macroPickerOverlay = { view: "macropicker", dialogData: {}, @@ -90,13 +90,17 @@ } - + function openPageFieldOverlay() { vm.pageFieldOverlay = { - view: "mediapicker", + title: "Insert page field", + description: "Insert data in template", + submitButtonLabel: "Insert", + closeButtonlabel: "Cancel", + view: "insertfield", show: true, submit: function(model) { - + vm.insert(model.umbracoField); }, close: function(model) { vm.pageFieldOverlay.show = false; diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index eeab721902..122e615267 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -22,7 +22,7 @@
- + @@ -30,18 +30,18 @@
-
+
-
- +
+
- +
From a29de2f9108c95767b43fcb443c5316d617b71d7 Mon Sep 17 00:00:00 2001 From: hemraker Date: Mon, 13 Jun 2016 19:40:54 +0200 Subject: [PATCH 019/599] Close pageFieldOverlay on submit --- .../src/views/templates/edit.controller.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index e1117e601f..eb164387fd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -56,7 +56,7 @@ templateResource.getScaffold().then(function(template){ vm.ready(template); - }); + }); }else{ @@ -68,7 +68,7 @@ }; - + vm.ready = function(template){ vm.page.loading = false; vm.template = template; @@ -89,7 +89,7 @@ } }; - + vm.setLayout = function(path){ var templateCode = vm.editor.getValue(); @@ -187,6 +187,8 @@ show: true, submit: function(model) { vm.insert(model.umbracoField); + vm.pageFieldOverlay.show = false; + vm.pageFieldOverlay = null; }, close: function (model) { vm.pageFieldOverlay.show = false; @@ -199,7 +201,7 @@ function openDictionaryItemOverlay() { vm.dictionaryItemOverlay = { view: "treepicker", - section: "settings", + section: "settings", treeAlias: "dictionary", entityType: "dictionary", multiPicker: false, From 221438c5fc9ede2d580222fb0815b725e2d09250 Mon Sep 17 00:00:00 2001 From: hemraker Date: Mon, 13 Jun 2016 19:44:05 +0200 Subject: [PATCH 020/599] Fixe merge confusion --- src/Umbraco.Web.UI.Client/src/views/templates/edit.html | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index d856c4efb0..888ff4c9a5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -33,7 +33,7 @@ button-style="link" label="Insert" icon="icon-add" - action="vm.openDocumentation()"> + action="vm.openInsertOverlay()"> @@ -97,6 +97,13 @@ + + + Date: Mon, 13 Jun 2016 19:58:02 +0200 Subject: [PATCH 021/599] Add page field to overview --- .../overlays/insert/insert.controller.js | 28 +++++++++++++++++++ .../views/common/overlays/insert/insert.html | 9 +++++- .../src/views/templates/edit.controller.js | 3 ++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js index f2e00fa76c..c73aa912a4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js @@ -14,6 +14,7 @@ } vm.openMacroPicker = openMacroPicker; + vm.openPageFieldOverlay = openPageFieldOverlay; vm.openDictionaryItemOverlay = openDictionaryItemOverlay; @@ -41,6 +42,33 @@ } + function openPageFieldOverlay() { + vm.pageFieldOverlay = { + title: "Insert page field", + description: "Insert data in template", + submitButtonLabel: "Insert", + closeButtonlabel: "Cancel", + view: "insertfield", + show: true, + submit: function(model) { + + $scope.model.insert = { + "type": "umbracoField", + "umbracoField": model.umbracoField + }; + + $scope.model.submit($scope.model); + + vm.pageFieldOverlay.show = false; + vm.pageFieldOverlay = null; + }, + close: function (model) { + vm.pageFieldOverlay.show = false; + vm.pageFieldOverlay = null; + } + }; + } + function openDictionaryItemOverlay() { vm.dictionaryItemOverlay = { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html index fdc8368197..a09bb1d41b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html @@ -7,7 +7,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque gravida hendrerit. Nunc lacinia ipsum sed urna volutpat finibus.
-
+
Value
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque gravida hendrerit. Nunc lacinia ipsum sed urna volutpat finibus.
@@ -31,6 +31,13 @@ position="right"> + + + Date: Mon, 13 Jun 2016 20:12:17 +0200 Subject: [PATCH 022/599] Adds partial view picker --- .../views/common/overlays/insert/insert.html | 2 +- .../src/views/templates/edit.controller.js | 43 +++++++++++++++++++ .../src/views/templates/edit.html | 19 +++++++- 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html index fdc8368197..a42f3c6d54 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html @@ -17,7 +17,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque gravida hendrerit. Nunc lacinia ipsum sed urna volutpat finibus.
-
+
Partial
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque gravida hendrerit. Nunc lacinia ipsum sed urna volutpat finibus.
diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 0ef5de0311..2d4704f227 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -124,6 +124,7 @@ vm.openMacroOverlay = openMacroOverlay; vm.openInsertOverlay = openInsertOverlay; vm.openOrganizeOverlay = openOrganizeOverlay; + vm.openPartialOverlay = openPartialOverlay; function openInsertOverlay() { @@ -166,6 +167,8 @@ view: "macropicker", dialogData: {}, show: true, + title: "Insert macro", + submit: function (model) { var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); @@ -181,6 +184,7 @@ vm.pageFieldOverlay = { view: "mediapicker", show: true, + submit: function (model) { }, @@ -200,6 +204,7 @@ entityType: "dictionary", multiPicker: false, show: true, + title: "Insert dictionary item", select: function(node){ //crappy hack due to dictionary items not in umbracoNode table @@ -221,6 +226,38 @@ }; } + function openPartialOverlay() { + vm.partialItemOverlay = { + view: "treepicker", + section: "settings", + treeAlias: "partialViews", + entityType: "partialView", + multiPicker: false, + show: true, + title: "Insert Partial view", + + select: function(node){ + //crappy hack due to dictionary items not in umbracoNode table + var code = "@Html.Partial(\"" + node.name + "\")"; + vm.insert(code); + + vm.partialItemOverlay.show = false; + vm.partialItemOverlay = null; + }, + + submit: function (model) { + console.log(model); + vm.partialItemOverlay.show = false; + vm.partialItemOverlay = null; + }, + + close: function (model) { + vm.partialItemOverlay.show = false; + vm.partialItemOverlay = null; + } + }; + } + function openQueryBuilderOverlay() { vm.queryBuilderOverlay = { view: "querybuilder", @@ -248,11 +285,15 @@ }; } + function openOrganizeOverlay() { vm.organizeOverlay = { + view: "organize", show: true, template: vm.template, + title: "Organise template", + submit: function (model) { if (model.masterPage && model.masterPage.alias) { vm.template.masterPageAlias = model.masterPage.alias; @@ -281,10 +322,12 @@ vm.organizeOverlay.show = false; vm.organizeOverlay = null; }, + close: function (model) { vm.organizeOverlay.show = false; vm.organizeOverlay = null; } + } } diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index d856c4efb0..32f653ac54 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -44,6 +44,7 @@
  • Value
  • Dictionary
  • Macro
  • +
  • Partial view
  • @@ -56,12 +57,13 @@ action="vm.openQueryBuilderOverlay()"> + + action="vm.openOrganizeOverlay()"> @@ -125,4 +127,19 @@ view="vm.queryBuilderOverlay.view"> + + + + + + + From 3d95c3a74427414104661b80f6b18826b67de6fa Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Mon, 13 Jun 2016 20:35:00 +0200 Subject: [PATCH 023/599] partial view dialog --- .../src/views/templates/edit.html | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index 32f653ac54..0c9ce31c35 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -33,7 +33,7 @@ button-style="link" label="Insert" icon="icon-add" - action="vm.openDocumentation()"> + action="vm.openInsertOverlay()"> @@ -134,10 +134,17 @@ position="right"> + + + From 0558fbd7ff331a4668b89ffd6c101d4ff35e3a3a Mon Sep 17 00:00:00 2001 From: Lars-Erik Aabech Date: Mon, 13 Jun 2016 20:51:58 +0200 Subject: [PATCH 024/599] Unit test for template editor. Currently testing that masterpage and layout is set. --- .../template.editor.controller.tests.js | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/test/unit/app/templates/template.editor.controller.tests.js diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template.editor.controller.tests.js b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template.editor.controller.tests.js new file mode 100644 index 0000000000..a5657bf842 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template.editor.controller.tests.js @@ -0,0 +1,127 @@ +/// +/// +/// +/// + +(function() { + "use strict"; + + describe("templates editor controller", + function() { + + + var scope, + controllerFactory, + q, + ace, + controller, + nada = function() {}; + + beforeEach(function() { + angular.module('umbraco.filters', []); + angular.module('umbraco.directives', []); + angular.module('umbraco.resources', []); + angular.module('umbraco.services', []); + angular.module('umbraco.packages', []); + angular.module('umbraco.views', []); + angular.module('ngCookies', []); + angular.module('ngSanitize', []); + angular.module('ngMobile', []); + angular.module('tmh.dynamicLocale', []); + angular.module('ngFileUpload', []); + angular.module('LocalStorageModule', []); + }); + + beforeEach(module("umbraco")); + + beforeEach(inject(function($controller, $rootScope, $q) { + + controllerFactory = $controller; + scope = $rootScope.$new(); + q = $q; + + ace = { + on: function(){} + } + + controller = createController(); + scope.$digest(); + controller.aceOption.onLoad(ace); + + })); + + function resolvedPromise(obj) { + return function() { + var def = q.defer(); + def.resolve(obj); + return def.promise; + } + } + + function createController() { + return controllerFactory("Umbraco.Editors.Templates.EditController", + { + $scope: scope, + $routeParams: {}, + templateResource: { + getById: resolvedPromise({}) + }, + assetsService: { + loadCss: function() {} + }, + notificationsService: { + }, + editorState: { + set: function(){} + }, + navigationService: { + syncTree: resolvedPromise({}) + }, + appState: { + getSectionState : function() { return {}; } + }, + macroService: {} + }); + } + + it("has ace editor", + function () { + expect(controller.editor).toBe(ace); + }); + + it("sets masterpage on template", + function () { + controller.setLayout = function() {}; + + controller.openOrganizeOverlay(); + controller.organizeOverlay.submit({ + masterPage: { + alias: "NewMasterPage" + } + }); + expect(controller.template.masterPageAlias).toBe("NewMasterPage"); + }); + + it("changes layout value when masterpage is selected", + function() { + var newTemplate; + ace.clearSelection = nada; + ace.navigateFileStart = nada; + ace.getValue = function () { + return "@{ Layout = null; }"; + } + ace.setValue = function (value) { + newTemplate = value; + } + + controller.openOrganizeOverlay(); + controller.organizeOverlay.submit({ + masterPage: { + alias: "NewMasterPage" + } + }); + expect(newTemplate).toBe("@{ Layout = \"NewMasterPage.cshtml\"; }"); + }); + }); + +}()); From b853b8e89fc18c440ed0b7f9bceb39040d8065f7 Mon Sep 17 00:00:00 2001 From: Lars-Erik Aabech Date: Tue, 14 Jun 2016 01:03:57 +0200 Subject: [PATCH 025/599] Tests pass with karma. Won't run with R# testrunner without uncommenting stub modules, but breaks running with karma. --- ....js => template-editor-controller.spec.js} | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) rename src/Umbraco.Web.UI.Client/test/unit/app/templates/{template.editor.controller.tests.js => template-editor-controller.spec.js} (82%) diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template.editor.controller.tests.js b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js similarity index 82% rename from src/Umbraco.Web.UI.Client/test/unit/app/templates/template.editor.controller.tests.js rename to src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js index a5657bf842..cb70446bb1 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template.editor.controller.tests.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js @@ -17,20 +17,21 @@ controller, nada = function() {}; - beforeEach(function() { - angular.module('umbraco.filters', []); - angular.module('umbraco.directives', []); - angular.module('umbraco.resources', []); - angular.module('umbraco.services', []); - angular.module('umbraco.packages', []); - angular.module('umbraco.views', []); - angular.module('ngCookies', []); - angular.module('ngSanitize', []); - angular.module('ngMobile', []); - angular.module('tmh.dynamicLocale', []); - angular.module('ngFileUpload', []); - angular.module('LocalStorageModule', []); - }); + // UNCOMMENT TO RUN WITH RESHARPERS TESTRUNNER FOR JS + //beforeEach(function() { + // angular.module('umbraco.filters', []); + // angular.module('umbraco.directives', []); + // angular.module('umbraco.resources', []); + // angular.module('umbraco.services', []); + // angular.module('umbraco.packages', []); + // angular.module('umbraco.views', []); + // angular.module('ngCookies', []); + // angular.module('ngSanitize', []); + // angular.module('ngMobile', []); + // angular.module('tmh.dynamicLocale', []); + // angular.module('ngFileUpload', []); + // angular.module('LocalStorageModule', []); + //}); beforeEach(module("umbraco")); From 2688e281a781ee80393c1880bbd4e448458b318e Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Tue, 14 Jun 2016 01:07:41 +0200 Subject: [PATCH 026/599] insert partial view dialog --- .../overlays/insert/insert.controller.js | 30 +++++++++++++++++++ .../views/common/overlays/insert/insert.html | 8 +++++ .../src/views/templates/edit.controller.js | 21 +++++++++++-- .../src/views/templates/edit.html | 7 ----- 4 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js index c73aa912a4..1bdc6b778e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js @@ -98,6 +98,36 @@ }; } + function openPartialOverlay() { + vm.partialItemOverlay = { + view: "treepicker", + section: "settings", + treeAlias: "partialViews", + entityType: "partialView", + multiPicker: false, + show: true, + title: "Insert Partial view", + + select: function(node){ + + $scope.model.insert = { + "type": "partial", + "node": node + }; + + $scope.model.submit($scope.model); + + vm.partialItemOverlay.show = false; + vm.partialItemOverlay = null; + }, + + close: function (model) { + vm.partialItemOverlay.show = false; + vm.partialItemOverlay = null; + } + }; + } + } angular.module("umbraco").controller("Umbraco.Overlays.InsertOverlay", InsertOverlayController); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html index cbeb5731c3..4d8fb4a4bd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html @@ -24,6 +24,7 @@ + + + + diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 3769ce7abb..c60b456a4e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -18,7 +18,6 @@ vm.editor.focus(); }; - vm.currentPosition = { col: 0, row: 0 }; vm.save = function () { vm.page.saveButtonState = "busy"; @@ -85,6 +84,14 @@ onLoad: function(_editor) { vm.editor = _editor; + + //initial cursor placement + vm.editor.navigateFileEnd(); + persistCurrentLocation(); + + //change on blur, focus + vm.editor.on("blur", persistCurrentLocation); + vm.editor.on("focus", persistCurrentLocation); } } }; @@ -146,6 +153,13 @@ var code = "@Umbraco.GetDictionaryValue(\"" + model.insert.node.name + "\")"; vm.insert(code); break; + + case "partial": + //crappy hack due to dictionary items not in umbracoNode table + var code = "@Html.Partial(\"" + model.insert.node.name + "\")"; + vm.insert(code); + break; + case "umbracoField": vm.insert(model.insert.umbracoField); break; @@ -197,7 +211,7 @@ vm.pageFieldOverlay.show = false; vm.pageFieldOverlay = null; }, - + close: function (model) { vm.pageFieldOverlay.show = false; vm.pageFieldOverlay = null; @@ -286,6 +300,9 @@ "\n\n"; vm.insert(code); + + vm.queryBuilderOverlay.show = false; + vm.queryBuilderOverlay = null; }, close: function (model) { diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index 2cdc8e6bae..a712f4220d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -141,13 +141,6 @@ position="right"> - - - Date: Tue, 6 Sep 2016 10:53:09 +0200 Subject: [PATCH 027/599] Dutch Languagefile Update This updates the Dutch language file with new/missing translations and fixes some wrong translations used by the grid. --- src/Umbraco.Web.UI/umbraco/config/lang/nl.xml | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml index 2955ce716a..ce89979502 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml @@ -189,38 +189,38 @@ Welkom - Stay - Discard changes - You have unsaved changes - Are you sure you want to navigate away from this page? - you have unsaved changes + Blijf op deze pagina + Negeer wijzigingen + Wijzigingen niet opgeslagen + Weet je zeker dat deze pagina wilt verlaten? - er zijn onopgeslagen wijzigingen Done - Deleted %0% item - Deleted %0% items - Deleted %0% out of %1% item - Deleted %0% out of %1% items + %0% item verwijderd + %0% items verwijderd + Item %0% van de %1% verwijderd + Items %0% van de %1% verwijderd - Published %0% item - Published %0% items - Published %0% out of %1% item - Published %0% out of %1% items + %0% item gepubliceerd + %0% items gepubliceerd + Item %0% van de %1% gepubliceerd + Items %0% van de %1% gepubliceerd - Unpublished %0% item - Unpublished %0% items - Unpublished %0% out of %1% item - Unpublished %0% out of %1% items + %0% item gedepubliceerd + %0% items gedepubliceerd + Item %0% van de %1% gedepubliceerd + Items %0% van de %1% gedepubliceerd - Moved %0% item - Moved %0% items - Moved %0% out of %1% item - Moved %0% out of %1% items + %0% item verplaatst + %0% items verplaatst + item %0% van de %1% verplaatst + items %0% van de %1% verplaatst - Copied %0% item - Copied %0% items - Copied %0% out of %1% item - Copied %0% out of %1% items + %0% item gekopieerd + %0% items gekopieerd + item %0% van de %1% gekopieerd + item %0% van de %1% gekopieerd Naam @@ -431,7 +431,7 @@ Toon pagina bij versturen Formaat Sorteren - Submit + Verstuur Type Type om te zoeken... Omhoog @@ -447,8 +447,8 @@ Breedte Ja Map - Reorder - I am done reordering + Herschikken + Klaar met herschikken Zoekresultaten @@ -846,15 +846,15 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Item toevoegen - Choose a layout - Een rij aan de lay-out toevoegen - teken onderaan en voeg je eerste item toe]]> + Kies de indeling + Kies een indeling voor deze pagina om content toe te kunnen voegen + Plaats een (extra) content blok]]> Drop content - Settings applied - - This content is not allowed here - This content is allowed here + Instellingen toegepast + Deze content is hier niet toegestaan + Deze content is hier toegestaan + Klik om een item te embedden Klik om een afbeelding in te voegen Afbeelding ondertitel... From b40a1fa6132ad4dda8411036fdbf23df851696c0 Mon Sep 17 00:00:00 2001 From: Claus Date: Mon, 12 Sep 2016 12:47:12 +0200 Subject: [PATCH 028/599] U4-8963 Updates for ImageProcessor parameters to support background color --- .../PropertyEditors/ImageCropperTest.cs | 12 +++++++ src/Umbraco.Web/HtmlHelperRenderExtensions.cs | 10 +++--- .../ImageCropperTemplateExtensions.cs | 32 +++++++++++++------ src/Umbraco.Web/UrlHelperRenderExtensions.cs | 18 +++++++---- 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs index 450c69abb4..d3d6bf156d 100644 --- a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs +++ b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs @@ -358,5 +358,17 @@ namespace Umbraco.Tests.PropertyEditors var urlString = mediaPath.GetCropUrl(imageCropperValue: cropperJson, height: 200); Assert.AreEqual(mediaPath + "?anchor=center&mode=crop&height=200", urlString); } + + /// + /// Test to check result when using a background color with padding + /// + [Test] + public void GetCropUrl_BackgroundColorParameter() + { + var cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"" + mediaPath + "\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}"; + + var urlString = mediaPath.GetCropUrl(400, 400, cropperJson, imageCropMode: ImageCropMode.Pad, backgroundColor: "fff"); + Assert.AreEqual(mediaPath + "?mode=pad&width=400&height=400&bgcolor=fff", urlString); + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index 3062613b6b..2802e4fe80 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -228,12 +228,13 @@ namespace Umbraco.Web bool cacheBuster = true, string furtherOptions = null, ImageCropRatioMode? ratioMode = null, - bool upScale = true) + bool upScale = true, + string backgroundColor = null) { return new HtmlString(mediaItem.GetCropUrl(width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode, - upScale)); + upScale, backgroundColor)); } [Obsolete("Use the UrlHelper.GetCropUrl extension instead")] @@ -252,12 +253,13 @@ namespace Umbraco.Web string cacheBusterValue = null, string furtherOptions = null, ImageCropRatioMode? ratioMode = null, - bool upScale = true) + bool upScale = true, + string backgroundColor = null) { return new HtmlString(imageUrl.GetCropUrl(width, height, imageCropperValue, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, - upScale)); + upScale, backgroundColor)); } #endregion diff --git a/src/Umbraco.Web/ImageCropperTemplateExtensions.cs b/src/Umbraco.Web/ImageCropperTemplateExtensions.cs index e518c5e246..a9b0140d20 100644 --- a/src/Umbraco.Web/ImageCropperTemplateExtensions.cs +++ b/src/Umbraco.Web/ImageCropperTemplateExtensions.cs @@ -91,10 +91,13 @@ namespace Umbraco.Web /// /// /// Use a dimension as a ratio - /// + /// /// /// If the image should be upscaled to requested dimensions - /// + /// + /// + /// Changes the background color of the image. Used when adding a background when resizing image formats without an alpha channel. + /// /// /// The . /// @@ -112,7 +115,8 @@ namespace Umbraco.Web bool cacheBuster = true, string furtherOptions = null, ImageCropRatioMode? ratioMode = null, - bool upScale = true) + bool upScale = true, + string backgroundColor = null) { if (mediaItem == null) throw new ArgumentNullException("mediaItem"); @@ -132,7 +136,7 @@ namespace Umbraco.Web mediaItemUrl = stronglyTyped.Src; return GetCropUrl( mediaItemUrl, stronglyTyped, width, height, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, - cacheBusterValue, furtherOptions, ratioMode, upScale); + cacheBusterValue, furtherOptions, ratioMode, upScale, backgroundColor); } //this shouldn't be the case but we'll check @@ -143,14 +147,14 @@ namespace Umbraco.Web mediaItemUrl = stronglyTyped.Src; return GetCropUrl( mediaItemUrl, stronglyTyped, width, height, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, - cacheBusterValue, furtherOptions, ratioMode, upScale); + cacheBusterValue, furtherOptions, ratioMode, upScale, backgroundColor); } //it's a single string mediaItemUrl = cropperValue.ToString(); return GetCropUrl( mediaItemUrl, width, height, mediaItemUrl, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, - cacheBusterValue, furtherOptions, ratioMode, upScale); + cacheBusterValue, furtherOptions, ratioMode, upScale, backgroundColor); } /// @@ -198,6 +202,9 @@ namespace Umbraco.Web /// /// If the image should be upscaled to requested dimensions /// + /// + /// Changes the background color of the image. Used when adding a background when resizing image formats without an alpha channel. + /// /// /// The . /// @@ -215,7 +222,8 @@ namespace Umbraco.Web string cacheBusterValue = null, string furtherOptions = null, ImageCropRatioMode? ratioMode = null, - bool upScale = true) + bool upScale = true, + string backgroundColor = null) { if (string.IsNullOrEmpty(imageUrl)) return string.Empty; @@ -226,7 +234,7 @@ namespace Umbraco.Web } return GetCropUrl( imageUrl, cropDataSet, width, height, cropAlias, quality, imageCropMode, - imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, upScale); + imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, upScale, backgroundColor); } public static string GetCropUrl( @@ -243,7 +251,8 @@ namespace Umbraco.Web string cacheBusterValue = null, string furtherOptions = null, ImageCropRatioMode? ratioMode = null, - bool upScale = true) + bool upScale = true, + string backgroundColor = null) { if (string.IsNullOrEmpty(imageUrl) == false) { @@ -350,6 +359,11 @@ namespace Umbraco.Web imageProcessorUrl.Append("&upscale=false"); } + if (backgroundColor != null) + { + imageProcessorUrl.Append("&bgcolor=" + backgroundColor); + } + if (furtherOptions != null) { imageProcessorUrl.Append(furtherOptions); diff --git a/src/Umbraco.Web/UrlHelperRenderExtensions.cs b/src/Umbraco.Web/UrlHelperRenderExtensions.cs index 629d69ed4a..76a000aded 100644 --- a/src/Umbraco.Web/UrlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/UrlHelperRenderExtensions.cs @@ -114,6 +114,9 @@ namespace Umbraco.Web /// Whether to HTML encode this URL - default is true - w3c standards require html attributes to be html encoded but this can be /// set to false if using the result of this method for CSS. /// + /// + /// Changes the background color of the image. Used when adding a background when resizing image formats without an alpha channel. + /// /// /// The . /// @@ -132,11 +135,12 @@ namespace Umbraco.Web string furtherOptions = null, ImageCropRatioMode? ratioMode = null, bool upScale = true, - bool htmlEncode = true) + bool htmlEncode = true, + string backgroundColor = null) { var url = mediaItem.GetCropUrl(width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode, - upScale); + upScale, backgroundColor); return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); } @@ -190,6 +194,9 @@ namespace Umbraco.Web /// Whether to HTML encode this URL - default is true - w3c standards require html attributes to be html encoded but this can be /// set to false if using the result of this method for CSS. /// + /// + /// Changes the background color of the image. Used when adding a background when resizing image formats without an alpha channel. + /// /// /// The . /// @@ -208,11 +215,12 @@ namespace Umbraco.Web string furtherOptions = null, ImageCropRatioMode? ratioMode = null, bool upScale = true, - bool htmlEncode = true) + bool htmlEncode = true, + string backgroundColor = null) { var url = imageUrl.GetCropUrl(width, height, imageCropperValue, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, - upScale); + upScale, backgroundColor); return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); } @@ -333,7 +341,5 @@ namespace Umbraco.Web { return url.SurfaceAction(action, typeof (T), additionalRouteVals); } - - } } \ No newline at end of file From d34dcb0b271dde92802c7b19e92718d72c773e04 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 29 Sep 2016 13:26:25 +0200 Subject: [PATCH 029/599] Load template editor correct without massive ace deps --- .../directives/components/umbaceeditor.directive.js | 9 ++++++++- .../src/views/templates/edit.controller.js | 5 +++++ src/Umbraco.Web/Editors/TemplateQueryController.cs | 4 ++-- src/Umbraco.Web/UI/JavaScript/JsInitialize.js | 1 - 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js index 331354491a..6e66e75dd6 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js @@ -1,7 +1,7 @@ (function() { 'use strict'; - function AceEditorDirective(umbAceEditorConfig) { + function AceEditorDirective(umbAceEditorConfig, assetsService) { if (angular.isUndefined(window.ace)) { throw new Error('ui-ace need ace to work... (o rly?)'); @@ -32,12 +32,14 @@ var config = window.ace.require('ace/config'); config.set('workerPath', opts.workerPath); } + // ace requires loading if (angular.isDefined(opts.require)) { opts.require.forEach(function(n) { window.ace.require(n); }); } + // Boolean options if (angular.isDefined(opts.showGutter)) { acee.renderer.setShowGutter(opts.showGutter); @@ -126,6 +128,7 @@ function link(scope, el, attr, ngModel) { + /** * Corresponds the umbAceEditorConfig ACE configuration. * @type object @@ -138,11 +141,15 @@ */ var opts = angular.extend({}, options, scope.$eval(attr.umbAceEditor)); + + //load ace libraries here... + /** * ACE editor * @type object */ var acee = window.ace.edit(el[0]); + acee.$blockScrolling = Infinity; /** * ACE editor session. diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index c60b456a4e..195fc87b91 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -81,6 +81,11 @@ vm.aceOption = { mode: "razor", theme: "chrome", + showPrintMargin: false, + + advanced: { + fontSize: '16px' + }, onLoad: function(_editor) { vm.editor = _editor; diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs index 2eaf63f159..a68c3bb9e0 100644 --- a/src/Umbraco.Web/Editors/TemplateQueryController.cs +++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs @@ -65,7 +65,7 @@ namespace Umbraco.Web.Editors var sb = new StringBuilder(); - sb.Append("CurrentPage.Site()"); + sb.Append("Model.Content.Site()"); var timer = new Stopwatch(); @@ -217,7 +217,7 @@ namespace Umbraco.Web.Editors }); - return queryResult; + return queryResult; } private object GetConstraintValue(QueryCondition condition) diff --git a/src/Umbraco.Web/UI/JavaScript/JsInitialize.js b/src/Umbraco.Web/UI/JavaScript/JsInitialize.js index 899c313a7d..44a1ae75c7 100644 --- a/src/Umbraco.Web/UI/JavaScript/JsInitialize.js +++ b/src/Umbraco.Web/UI/JavaScript/JsInitialize.js @@ -17,7 +17,6 @@ 'lib/angular-local-storage/angular-local-storage.min.js', "lib/ace-builds/src-min-noconflict/ace.js", - "lib/ace-builds/src-min-noconflict/mode-razor.js", 'lib/bootstrap/js/bootstrap.2.3.2.min.js', 'lib/bootstrap-tabdrop/bootstrap-tabdrop.js', From 0a49d54a8510ad54afa798f30e38ba783c530c41 Mon Sep 17 00:00:00 2001 From: Tom Pipe Date: Sat, 29 Oct 2016 18:23:25 +0100 Subject: [PATCH 030/599] Fixed issue with member properties sort order --- src/Umbraco.Web/Security/MembershipHelper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Security/MembershipHelper.cs b/src/Umbraco.Web/Security/MembershipHelper.cs index 4f5c14723b..158910a794 100644 --- a/src/Umbraco.Web/Security/MembershipHelper.cs +++ b/src/Umbraco.Web/Security/MembershipHelper.cs @@ -428,7 +428,8 @@ namespace Umbraco.Web.Security var viewProperties = new List(); foreach (var prop in memberType.PropertyTypes - .Where(x => builtIns.Contains(x.Alias) == false && memberType.MemberCanEditProperty(x.Alias))) + .Where(x => builtIns.Contains(x.Alias) == false && memberType.MemberCanEditProperty(x.Alias)) + .OrderBy(p => p.SortOrder)) { var value = string.Empty; if (member != null) From dfbadb155077c26aab0031a4f62112c2a28dc5f2 Mon Sep 17 00:00:00 2001 From: Steve Morgan Date: Thu, 3 Nov 2016 17:03:33 +0000 Subject: [PATCH 031/599] U4-9148 noNodes Splash screen apostrophe char typo fixed (in "Be a part of the community" - we're) --- src/Umbraco.Web.UI/config/splashes/noNodes.aspx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/config/splashes/noNodes.aspx b/src/Umbraco.Web.UI/config/splashes/noNodes.aspx index 92bf10650c..d46ecd6113 100644 --- a/src/Umbraco.Web.UI/config/splashes/noNodes.aspx +++ b/src/Umbraco.Web.UI/config/splashes/noNodes.aspx @@ -44,7 +44,7 @@ From 87814fa43c952ac16062c176aa307d0b6cd382fa Mon Sep 17 00:00:00 2001 From: Alexander Bryukhov Date: Sat, 12 Nov 2016 12:12:41 +0700 Subject: [PATCH 032/599] Update UI lang file for ru-ru All latest 7.5.5 changes --- src/Umbraco.Web.UI/umbraco/config/lang/ru.xml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml index 255bf62d5d..c6f94949d2 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml @@ -27,6 +27,9 @@ Опубликовать Обновить узлы Опубликовать весь сайт + Установить разрешения для страницы '%0%' + Выберите, куда переместить + В структуре документов ниже Восстановить Разрешения Откатить @@ -189,7 +192,7 @@ Роль участника Тип участника Дата не указана - Заголовок страницы + Заголовок ссылки Не является членом групп(ы) Свойства Этот документ опубликован, но скрыт, потому что его родительский документ '%0%' не опубликован @@ -341,9 +344,10 @@ Просмотр элемента кэша Создать папку... Связать с оригиналом + Включая все дочерние Самое дружелюбное сообщество Ссылка на страницу - Открывает документ по ссылке в новом окне или вкладке браузера + Открывать ссылку в новом окне или вкладке браузера Ссылка на медиа-файл Выбрать медиа Выбрать значок From d7aeae3d26233406f3a392d148ccde68503f3bd8 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 14 Nov 2016 11:16:53 +0100 Subject: [PATCH 033/599] bind open partial view overlay function to view model --- .../src/views/common/overlays/insert/insert.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js index 1bdc6b778e..f633e3997f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js @@ -16,7 +16,7 @@ vm.openMacroPicker = openMacroPicker; vm.openPageFieldOverlay = openPageFieldOverlay; vm.openDictionaryItemOverlay = openDictionaryItemOverlay; - + vm.openPartialOverlay = openPartialOverlay; function openMacroPicker() { From 4ae7a32e29c1e69539611a88a466d2274ac57054 Mon Sep 17 00:00:00 2001 From: Ali Sheikh Taheri Date: Mon, 14 Nov 2016 11:54:42 +0000 Subject: [PATCH 034/599] Fix for maximum file size. --- src/Umbraco.Web.UI/config/log4net.Release.config | 3 ++- src/Umbraco.Web.UI/config/log4net.config | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/config/log4net.Release.config b/src/Umbraco.Web.UI/config/log4net.Release.config index 10b8c9eb7a..54a0e23ab2 100644 --- a/src/Umbraco.Web.UI/config/log4net.Release.config +++ b/src/Umbraco.Web.UI/config/log4net.Release.config @@ -10,7 +10,8 @@ - + + diff --git a/src/Umbraco.Web.UI/config/log4net.config b/src/Umbraco.Web.UI/config/log4net.config index eca84962a1..382f886adc 100644 --- a/src/Umbraco.Web.UI/config/log4net.config +++ b/src/Umbraco.Web.UI/config/log4net.config @@ -10,7 +10,8 @@ - + + From 3aaa33ebe97adc585936fbe51fae65bc5840dc4c Mon Sep 17 00:00:00 2001 From: Ali Sheikh Taheri Date: Mon, 14 Nov 2016 11:55:09 +0000 Subject: [PATCH 035/599] added .log file extension for backup logs. --- src/Umbraco.Web.UI/config/log4net.Release.config | 2 +- src/Umbraco.Web.UI/config/log4net.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/config/log4net.Release.config b/src/Umbraco.Web.UI/config/log4net.Release.config index 54a0e23ab2..d77e3f9c0d 100644 --- a/src/Umbraco.Web.UI/config/log4net.Release.config +++ b/src/Umbraco.Web.UI/config/log4net.Release.config @@ -11,7 +11,7 @@ - + diff --git a/src/Umbraco.Web.UI/config/log4net.config b/src/Umbraco.Web.UI/config/log4net.config index 382f886adc..28d454165d 100644 --- a/src/Umbraco.Web.UI/config/log4net.config +++ b/src/Umbraco.Web.UI/config/log4net.config @@ -11,7 +11,7 @@ - + From 19b24b5109db7767ff5b2868f9e2313c1c744f9c Mon Sep 17 00:00:00 2001 From: Ali Sheikh Taheri Date: Mon, 14 Nov 2016 11:56:21 +0000 Subject: [PATCH 036/599] Added maximum number of backups to keep. --- src/Umbraco.Web.UI/config/log4net.Release.config | 9 +++++---- src/Umbraco.Web.UI/config/log4net.config | 13 +++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI/config/log4net.Release.config b/src/Umbraco.Web.UI/config/log4net.Release.config index d77e3f9c0d..e657394df7 100644 --- a/src/Umbraco.Web.UI/config/log4net.Release.config +++ b/src/Umbraco.Web.UI/config/log4net.Release.config @@ -1,18 +1,19 @@ - + - + + @@ -28,6 +29,6 @@ - - + + diff --git a/src/Umbraco.Web.UI/config/log4net.config b/src/Umbraco.Web.UI/config/log4net.config index 28d454165d..0834e2b090 100644 --- a/src/Umbraco.Web.UI/config/log4net.config +++ b/src/Umbraco.Web.UI/config/log4net.config @@ -1,18 +1,19 @@ - + - + - - + + + @@ -28,6 +29,6 @@ - - + + \ No newline at end of file From b3e9617aff634d8421d40ce59064203da379c9c1 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 14 Nov 2016 13:06:45 +0100 Subject: [PATCH 037/599] query builder overlay first refactor: use controllerAs syntax + move css to .less file instead of inline + fix indention --- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../src/less/components/umb-querybuilder.less | 14 ++ .../querybuilder/querybuilder.controller.js | 179 ++++++++------ .../overlays/querybuilder/querybuilder.html | 224 ++++++++---------- 4 files changed, 220 insertions(+), 198 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 861391f230..d6a967733f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -117,6 +117,7 @@ @import "components/umb-lightbox.less"; @import "components/umb-avatar.less"; @import "components/umb-progress-bar.less"; +@import "components/umb-querybuilder.less"; @import "components/buttons/umb-button.less"; @import "components/buttons/umb-button-group.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less new file mode 100644 index 0000000000..2c6a089eeb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less @@ -0,0 +1,14 @@ +.umb-querybuilder .row { + font-size: 12px; + line-height: 12px +} + +.umb-querybuilder .row a.btn { + font-size: 12px; + background: lightyellow +} + +.umb-querybuilder .row > div { + padding: 20px 0; + border-bottom: 1px solid @grayLighter; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js index 1680c4f9d1..b31949598b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js @@ -1,101 +1,122 @@ -angular.module("umbraco").controller('Umbraco.Overlays.QueryBuilderController', - function($scope, $http, dialogService){ - - $http.get("backoffice/UmbracoApi/TemplateQuery/GetAllowedProperties").then(function(response) { - $scope.properties = response.data; +(function () { + "use strict"; + + function QueryBuilderOverlayController($scope, $http, dialogService) { + + var vm = this; + + vm.properties = []; + vm.contentTypes = []; + vm.conditions = []; + + vm.query = { + contentType: { + name: "Everything" + }, + source: { + name: "My website" + }, + filters: [ + { + property: undefined, + operator: undefined + } + ], + sort: { + property: { + alias: "", + name: "", + }, + direction: "ascending" + } + }; + + vm.chooseSource = chooseSource; + vm.getPropertyOperators = getPropertyOperators; + vm.addFilter = addFilter; + vm.trashFilter = trashFilter; + vm.changeSortOrder = changeSortOrder; + vm.setSortProperty = setSortProperty; + + function onInit() { + + $http.get("backoffice/UmbracoApi/TemplateQuery/GetAllowedProperties").then(function (response) { + vm.properties = response.data; }); $http.get("backoffice/UmbracoApi/TemplateQuery/GetContentTypes").then(function (response) { - $scope.contentTypes = response.data; + vm.contentTypes = response.data; }); $http.get("backoffice/UmbracoApi/TemplateQuery/GetFilterConditions").then(function (response) { - $scope.conditions = response.data; + vm.conditions = response.data; }); + } - $scope.query = { - contentType: { - name: "Everything" - }, - source:{ - name: "My website" - }, - filters:[ - { - property:undefined, - operator: undefined - } - ], - sort:{ - property:{ - alias: "", - name: "", - }, - direction: "ascending" - } - }; + function chooseSource(query) { + dialogService.contentPicker({ + callback: function (data) { + if (data.id > 0) { + query.source = { id: data.id, name: data.name }; + } else { + query.source.name = "My website"; + delete query.source.id; + } + } + }); + } + function getPropertyOperators(property) { + var conditions = _.filter(vm.conditions, function (condition) { + var index = condition.appliesTo.indexOf(property.type); + return index >= 0; + }); + return conditions; + } - $scope.chooseSource = function(query){ - dialogService.contentPicker({ - callback: function (data) { + function addFilter(query) { + query.filters.push({}); + } - if (data.id > 0) { - query.source = { id: data.id, name: data.name }; - } else { - query.source.name = "My website"; - delete query.source.id; - } - } - }); - }; + function trashFilter(query) { + query.filters.splice(query, 1); + } - var throttledFunc = _.throttle(function() { + function changeSortOrder(query) { + if (query.sort.direction === "ascending") { + query.sort.direction = "descending"; + } else { + query.sort.direction = "ascending"; + } + } - $http.post("backoffice/UmbracoApi/TemplateQuery/PostTemplateQuery", $scope.query).then(function (response) { - $scope.model.result = response.data; - }); + function setSortProperty(query, property) { + query.sort.property = property; + if (property.type === "datetime") { + query.sort.direction = "descending"; + } else { + query.sort.direction = "ascending"; + } + } - }, 200); + var throttledFunc = _.throttle(function () { - $scope.$watch("query", function(value) { - throttledFunc(); - }, true); + $http.post("backoffice/UmbracoApi/TemplateQuery/PostTemplateQuery", vm.query).then(function (response) { + $scope.model.result = response.data; + }); - $scope.getPropertyOperators = function (property) { + }, 200); - var conditions = _.filter($scope.conditions, function(condition) { - var index = condition.appliesTo.indexOf(property.type); - return index >= 0; - }); - return conditions; - }; + $scope.$watch("vm.query", function (value) { + throttledFunc(); + }, true); - - $scope.addFilter = function(query){ - query.filters.push({}); - }; + onInit(); + + } - $scope.trashFilter = function (query) { - query.filters.splice(query,1); - }; + angular.module("umbraco").controller("Umbraco.Overlays.QueryBuilderController", QueryBuilderOverlayController); - $scope.changeSortOrder = function(query){ - if(query.sort.direction === "ascending"){ - query.sort.direction = "descending"; - }else{ - query.sort.direction = "ascending"; - } - }; - - $scope.setSortProperty = function(query, property){ - query.sort.property = property; - if(property.type === "datetime"){ - query.sort.direction = "descending"; - }else{ - query.sort.direction = "ascending"; - } - }; - }); \ No newline at end of file +})(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html index d18cb3a412..9cf3dc5d55 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html @@ -1,136 +1,122 @@ -
    - - +
    - - + + - -
    - - Order by + - - -

    Returns {{model.result.resultCount}} items in {{model.result.executionTime}} miliseconds

    - - - -
    -{{model.result.queryExpression}}
    -	    
    -
    + + {{vm.query.sort.direction}} + + +
    +
    + +
    Returns {{model.result.resultCount}} items in {{model.result.executionTime}} miliseconds
    + + + +
    +{{model.result.queryExpression}}
    +		
    + +
    \ No newline at end of file From 91b925f95c0c102df8d5e943d10d70a701c3439d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 14 Nov 2016 13:07:26 +0100 Subject: [PATCH 038/599] change white-space on pre elements so line wraps --- src/Umbraco.Web.UI.Client/src/less/hacks.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/hacks.less b/src/Umbraco.Web.UI.Client/src/less/hacks.less index 716cb40256..dc1212f33d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/hacks.less +++ b/src/Umbraco.Web.UI.Client/src/less/hacks.less @@ -197,7 +197,7 @@ pre { font-size: @baseFontSize - 1; // 14px to 13px color: @grayDark; line-height: @baseLineHeight; - white-space: pre; // 1 + white-space: pre-line; // 1 overflow-x: auto; // 1 background-color: #f5f5f5; border: 1px solid #ccc; // fallback for IE7-8 From 94708921182db00411fbd3a6060954e7b306d934 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 14 Nov 2016 13:21:42 +0100 Subject: [PATCH 039/599] query builder overlay: use overlay directive instead of dialogservice to open contentPicker --- .../querybuilder/querybuilder.controller.js | 24 ++++++++++++++----- .../overlays/querybuilder/querybuilder.html | 8 +++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js index b31949598b..48eb1b4c5c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js @@ -55,17 +55,29 @@ } function chooseSource(query) { - dialogService.contentPicker({ - callback: function (data) { + vm.contentPickerOverlay = { + view: "contentpicker", + show: true, + submit: function(model) { - if (data.id > 0) { - query.source = { id: data.id, name: data.name }; + var selectedNodeId = model.selection[0].id; + var selectedNodeName = model.selection[0].name; + + if (selectedNodeId > 0) { + query.source = { id: selectedNodeId, name: selectedNodeName }; } else { query.source.name = "My website"; delete query.source.id; } + + vm.contentPickerOverlay.show = false; + vm.contentPickerOverlay = null; + }, + close: function(oldModel) { + vm.contentPickerOverlay.show = false; + vm.contentPickerOverlay = null; } - }); + }; } function getPropertyOperators(property) { @@ -114,7 +126,7 @@ }, true); onInit(); - + } angular.module("umbraco").controller("Umbraco.Overlays.QueryBuilderController", QueryBuilderOverlayController); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html index 9cf3dc5d55..a6369f8d73 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html @@ -119,4 +119,12 @@ + + + + \ No newline at end of file From 22204a62d4339a0bf018b28e9ce280f0a6f7242d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 14 Nov 2016 19:22:49 +0100 Subject: [PATCH 040/599] move http requests to resource file --- .../resources/templatequery.resource.js | 151 ++++++++++++++++++ .../querybuilder/querybuilder.controller.js | 30 ++-- .../Editors/BackOfficeController.cs | 4 + 3 files changed, 172 insertions(+), 13 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/resources/templatequery.resource.js diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/templatequery.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/templatequery.resource.js new file mode 100644 index 0000000000..a776aef32e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/resources/templatequery.resource.js @@ -0,0 +1,151 @@ +/** + * @ngdoc service + * @name umbraco.resources.templateQueryResource + * @function + * + * @description + * Used by the query builder + */ +(function () { + 'use strict'; + + function templateQueryResource($http, umbRequestHelper) { + + /** + * @ngdoc function + * @name umbraco.resources.templateQueryResource#getAllowedProperties + * @methodOf umbraco.resources.templateQueryResource + * @function + * + * @description + * Called to get allowed properties + * ##usage + *
    +         * templateQueryResource.getAllowedProperties()
    +         *    .then(function(response) {
    +         *
    +         *    });
    +         * 
    + */ + function getAllowedProperties() { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "templateQueryApiBaseUrl", + "GetAllowedProperties")), + 'Failed to retrieve properties'); + } + + /** + * @ngdoc function + * @name umbraco.resources.templateQueryResource#getContentTypes + * @methodOf umbraco.resources.templateQueryResource + * @function + * + * @description + * Called to get content types + * ##usage + *
    +         * templateQueryResource.getContentTypes()
    +         *    .then(function(response) {
    +         *
    +         *    });
    +         * 
    + */ + function getContentTypes() { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "templateQueryApiBaseUrl", + "GetContentTypes")), + 'Failed to retrieve content types'); + } + + /** + * @ngdoc function + * @name umbraco.resources.templateQueryResource#getFilterConditions + * @methodOf umbraco.resources.templateQueryResource + * @function + * + * @description + * Called to the filter conditions + * ##usage + *
    +         * templateQueryResource.getFilterConditions()
    +         *    .then(function(response) {
    +         *
    +         *    });
    +         * 
    + */ + function getFilterConditions() { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "templateQueryApiBaseUrl", + "GetFilterConditions")), + 'Failed to retrieve filter conditions'); + } + + /** + * @ngdoc function + * @name umbraco.resources.templateQueryResource#postTemplateQuery + * @methodOf umbraco.resources.templateQueryResource + * @function + * + * @description + * Called to get content types + * ##usage + *
    +         * var query = {
    +         *     contentType: {
    +         *         name: "Everything"
    +         *      },
    +         *      source: {
    +         *          name: "My website"
    +         *      },
    +         *      filters: [
    +         *          {
    +         *              property: undefined,
    +         *              operator: undefined
    +         *          }
    +         *      ],
    +         *      sort: {
    +         *          property: {
    +         *              alias: "",
    +         *              name: "",
    +         *          },
    +         *          direction: "ascending"
    +         *      }
    +         *  };
    +         * 
    +         * templateQueryResource.postTemplateQuery(query)
    +         *    .then(function(response) {
    +         *
    +         *    });
    +         * 
    + * @param {object} query Query to build result + */ + function postTemplateQuery(query) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "templateQueryApiBaseUrl", + "PostTemplateQuery", + query)), + 'Failed to retrieve result'); + } + + var resource = { + getAllowedProperties: getAllowedProperties, + getContentTypes: getContentTypes, + getFilterConditions: getFilterConditions, + postTemplateQuery: postTemplateQuery + }; + + return resource; + + } + + angular.module('umbraco.resources').factory('templateQueryResource', templateQueryResource); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js index 48eb1b4c5c..c4149f0e14 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function QueryBuilderOverlayController($scope, $http, dialogService) { + function QueryBuilderOverlayController($scope, templateQueryResource) { var vm = this; @@ -40,17 +40,20 @@ function onInit() { - $http.get("backoffice/UmbracoApi/TemplateQuery/GetAllowedProperties").then(function (response) { - vm.properties = response.data; - }); + templateQueryResource.getAllowedProperties() + .then(function (properties) { + vm.properties = properties; + }); - $http.get("backoffice/UmbracoApi/TemplateQuery/GetContentTypes").then(function (response) { - vm.contentTypes = response.data; - }); + templateQueryResource.getContentTypes() + .then(function (contentTypes) { + vm.contentTypes = contentTypes; + }); - $http.get("backoffice/UmbracoApi/TemplateQuery/GetFilterConditions").then(function (response) { - vm.conditions = response.data; - }); + templateQueryResource.getFilterConditions() + .then(function (conditions) { + vm.conditions = conditions; + }); } @@ -115,9 +118,10 @@ var throttledFunc = _.throttle(function () { - $http.post("backoffice/UmbracoApi/TemplateQuery/PostTemplateQuery", vm.query).then(function (response) { - $scope.model.result = response.data; - }); + templateQueryResource.postTemplateQuery(vm.query) + .then(function (response) { + $scope.model.result = response; + }); }, 200); diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 0f6be5d37f..c092811a08 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -363,6 +363,10 @@ namespace Umbraco.Web.Editors { "healthCheckBaseUrl", Url.GetUmbracoApiServiceBaseUrl( controller => controller.GetAllHealthChecks()) + }, + { + "templateQueryApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.PostTemplateQuery(null)) } } }, From addad5b7dc7d2f01c955f02bd2f8c7184c561375 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 14 Nov 2016 19:57:55 +0100 Subject: [PATCH 041/599] fix http request --- .../src/common/resources/templatequery.resource.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/templatequery.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/templatequery.resource.js index a776aef32e..04569fccff 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/templatequery.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/templatequery.resource.js @@ -130,9 +130,9 @@ $http.post( umbRequestHelper.getApiUrl( "templateQueryApiBaseUrl", - "PostTemplateQuery", - query)), - 'Failed to retrieve result'); + "PostTemplateQuery"), + query), + 'Failed to retrieve query'); } var resource = { From 1f3d305a55ab1704124d8a91544f100a46a4735a Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 14 Nov 2016 20:20:03 +0100 Subject: [PATCH 042/599] remove $scope watch --- .../querybuilder/querybuilder.controller.js | 37 ++++++++++++++++--- .../overlays/querybuilder/querybuilder.html | 8 ++-- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js index c4149f0e14..bc5bc10e13 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js @@ -37,6 +37,10 @@ vm.trashFilter = trashFilter; vm.changeSortOrder = changeSortOrder; vm.setSortProperty = setSortProperty; + vm.setContentType = setContentType; + vm.setFilterProperty = setFilterProperty; + vm.setFilterTerm = setFilterTerm; + vm.changeConstraintValue = changeConstraintValue; function onInit() { @@ -54,6 +58,8 @@ .then(function (conditions) { vm.conditions = conditions; }); + + throttledFunc(); } @@ -61,6 +67,7 @@ vm.contentPickerOverlay = { view: "contentpicker", show: true, + submitButtonLabel: "Insert", submit: function(model) { var selectedNodeId = model.selection[0].id; @@ -73,6 +80,8 @@ delete query.source.id; } + throttledFunc(); + vm.contentPickerOverlay.show = false; vm.contentPickerOverlay = null; }, @@ -105,6 +114,7 @@ } else { query.sort.direction = "ascending"; } + throttledFunc(); } function setSortProperty(query, property) { @@ -114,20 +124,37 @@ } else { query.sort.direction = "ascending"; } + throttledFunc(); + } + + function setContentType(contentType) { + vm.query.contentType = contentType; + throttledFunc(); + } + + function setFilterProperty(filter, property) { + filter.property = property; + throttledFunc(); + } + + function setFilterTerm(filter, term) { + filter.term = term; + throttledFunc(); + } + + function changeConstraintValue() { + throttledFunc(); } var throttledFunc = _.throttle(function () { - + templateQueryResource.postTemplateQuery(vm.query) .then(function (response) { $scope.model.result = response; }); }, 200); - - $scope.$watch("vm.query", function (value) { - throttledFunc(); - }, true); + onInit(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html index a6369f8d73..40fb909390 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html @@ -15,7 +15,7 @@ @@ -44,7 +44,7 @@ \n\n"; - vm.insert(code); + insert(code); vm.queryBuilderOverlay.show = false; vm.queryBuilderOverlay = null; @@ -316,51 +288,138 @@ } - function openOrganizeOverlay() { - vm.organizeOverlay = { + function openSectionsOverlay() { - view: "organize", + vm.sectionsOverlay = { + view: "templatesections", show: true, - template: vm.template, - title: "Organise template", + isMasterTemplate: true, + submit: function(model) { - submit: function (model) { - if (model.masterPage && model.masterPage.alias) { - vm.template.masterPageAlias = model.masterPage.alias; - vm.setLayout(model.masterPage.alias + ".cshtml"); - } else { - vm.template.masterPageAlias = null; - vm.setLayout(null); + if (model.insertType === 'renderBody') { + insert("@RenderBody()"); } - if (model.addRenderBody) { - vm.insert("@RenderBody()"); + if (model.insertType === 'renderSection') { + insert("@RenderSection(\"" + model.renderSectionName + "\", " + model.mandatoryRenderSection + ")"); } - if (model.addRenderSection) { - vm.insert("@RenderSection(\"" + - model.renderSectionName + - "\", " + - model.mandatoryRenderSection + - ")"); + if (model.insertType === 'addSection') { + insert("@section " + model.sectionName + "\r\n{\r\n\r\n}\r\n"); } - if (model.addSection) { - vm.insert("@section " + model.sectionName + "\r\n{\r\n\r\n}\r\n"); - } + vm.sectionsOverlay.show = false; + vm.sectionsOverlay = null; - vm.organizeOverlay.show = false; - vm.organizeOverlay = null; }, + close: function(model) { + + vm.sectionsOverlay.show = false; + vm.sectionsOverlay = null; - close: function (model) { - vm.organizeOverlay.show = false; - vm.organizeOverlay = null; } - } } + function openMasterTemplateOverlay() { + + vm.masterTemplateOverlay = { + view: "itempicker", + title: "Choose master template", + availableItems: vm.templates, + show: true, + submit: function(model) { + + var template = model.selectedItem; + + if (template && template.alias) { + vm.template.masterTemplateAlias = template.alias; + setLayout(template.alias + ".cshtml"); + } else { + vm.template.masterTemplateAlias = null; + setLayout(null); + } + + vm.masterTemplateOverlay.show = false; + vm.masterTemplateOverlay = null; + }, + close: function(oldModel) { + vm.masterTemplateOverlay.show = false; + vm.masterTemplateOverlay = null; + } + }; + + } + + function selectMasterTemplate(template) { + + if (template && template.alias) { + vm.template.masterTemplateAlias = template.alias; + setLayout(template.alias + ".cshtml"); + } else { + vm.template.masterTemplateAlias = null; + setLayout(null); + } + + } + + function getMasterTemplateName(masterTemplateAlias, templates) { + + if(masterTemplateAlias) { + + var templateName = ""; + + angular.forEach(templates, function(template){ + if(template.alias === masterTemplateAlias) { + templateName = template.name; + } + }); + + return templateName; + + } else { + return "No template"; + } + + } + + function setLayout(templatePath){ + + var templateCode = vm.editor.getValue(); + var newValue = templatePath; + var layoutDefRegex = new RegExp("(@{[\\s\\S]*?Layout\\s*?=\\s*?)(\"[^\"]*?\"|null)(;[\\s\\S]*?})", "gi"); + + if (newValue !== undefined && newValue !== "") { + if (layoutDefRegex.test(templateCode)) { + // Declaration exists, so just update it + templateCode = templateCode.replace(layoutDefRegex, "$1\"" + newValue + "\"$3"); + } else { + // Declaration doesn't exist, so prepend to start of doc + //TODO: Maybe insert at the cursor position, rather than just at the top of the doc? + templateCode = "@{\n\tLayout = \"" + newValue + "\";\n}\n" + templateCode; + } + } else { + if (layoutDefRegex.test(templateCode)) { + // Declaration exists, so just update it + templateCode = templateCode.replace(layoutDefRegex, "$1null$3"); + } + } + + vm.editor.setValue(templateCode); + vm.editor.clearSelection(); + vm.editor.navigateFileStart(); + } + + function insert(str) { + vm.editor.moveCursorToPosition(vm.currentPosition); + vm.editor.insert(str); + vm.editor.focus(); + } + + function persistCurrentLocation() { + vm.currentPosition = vm.editor.getCursorPosition(); + } + vm.init(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index a712f4220d..67f409f8d3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -27,7 +27,6 @@ + label="Sections" + icon="icon-indent" + action="vm.openSectionsOverlay()"> + + + + + Master template: + {{ vm.getMasterTemplateName(vm.template.masterTemplateAlias, vm.templates) }} + + + +
    @@ -147,6 +155,13 @@ view="vm.partialItemOverlay.view" position="right"> + + +
    From c3fc21a2b3b11fde6ff1854d985fbbea145b8f2d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 16 Nov 2016 12:21:52 +0100 Subject: [PATCH 047/599] remove organise overlay --- .../overlays/organize/organize.controller.js | 40 --------------- .../common/overlays/organize/organize.html | 51 ------------------- 2 files changed, 91 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.controller.js delete mode 100644 src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.html diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.controller.js deleted file mode 100644 index 136399c172..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.controller.js +++ /dev/null @@ -1,40 +0,0 @@ -(function() { - "use strict"; - - function OrganizeController(scope, umbRequestHelper, http) { - var allTemplatesUrl = umbRequestHelper.getApiUrl("templateApiBaseUrl", "GetAll"); - - scope.model.addRenderBody = false; - scope.model.mandatoryRenderSection = false; - scope.masterPage = {}; - - http.get(allTemplatesUrl) - .then(function(result) { - scope.masterPages = result.data; - scope.masterPages.splice(0, - 0, - { - alias: null, - name: "None" - }); - scope.model.masterPage = $.grep(scope.masterPages, - function(mp) { - return mp.alias === scope.model.template.masterPageAlias; - })[0]; - }); - - scope.selectMasterPage = function(masterPage) { - scope.model.masterPage = masterPage; - } - } - - angular.module("umbraco") - .controller("Umbraco.Dialogs.Template.OrganizeController", - [ - "$scope", - "umbRequestHelper", - "$http", - OrganizeController - ]); - -}()); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.html deleted file mode 100644 index 57df7e9556..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/organize/organize.html +++ /dev/null @@ -1,51 +0,0 @@ -
    - -
    -

    Master page

    -
    - -
    -
    - - - -
    -
    -
    -

    Child page

    -
    - - -
    - -
    -
    \ No newline at end of file From 6c5d2c805719e1c68f85092621f3daf966aa823b Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 16 Nov 2016 12:22:03 +0100 Subject: [PATCH 048/599] fix header --- .../src/views/components/editor/umb-editor-header.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html index 4f3b316356..c274892c65 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html @@ -4,7 +4,7 @@
    -
    +
    From 1a80e94bca9c6a07a9174e670cd5735132137433 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 17 Nov 2016 09:31:35 +0100 Subject: [PATCH 049/599] clean up insert field overlay --- .../insertfield/insertfield.controller.js | 269 ++++++-------- .../overlays/insertfield/insertfield.html | 351 +++++++++--------- .../src/views/templates/edit.controller.js | 10 +- 3 files changed, 300 insertions(+), 330 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.controller.js index a2463434b9..06de14963e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.controller.js @@ -1,173 +1,144 @@ (function () { "use strict"; - function InsertFieldController ($scope, $http, contentTypeResource) { - var vm = this; + function InsertFieldController($scope, $http, contentTypeResource) { + + var vm = this; - vm.field; - vm.altField; - vm.altText; - vm.insertPreviewTitle = "Modify output"; - vm.insertBefore; - vm.insertAfter; - vm.recursive = false; - vm.properties; - vm.date = false; - vm.dateTime = false; - vm.dateTimeSeparator = ""; - vm.casingUpper = false; - vm.casingLower = false; - vm.encodeHtml = false; - vm.encodeUrl = false; - vm.convertLinebreaks = false; + vm.field; + vm.altField; + vm.altText; + vm.insertBefore; + vm.insertAfter; + vm.recursive = false; + vm.properties = []; + vm.date = false; + vm.dateTime = false; + vm.dateTimeSeparator = ""; + vm.casingUpper = false; + vm.casingLower = false; + vm.encodeHtml = false; + vm.encodeUrl = false; + vm.convertLinebreaks = false; + vm.removeParagraphTags = false; - vm.showAltField = false; - vm.showAltText = false; + vm.showAltField = false; + vm.showAltText = false; - vm.showInsertPreview = showInsertPreview; - vm.updateInsertPreview = updateInsertPreview; - vm.hideInsertPreview = hideInsertPreview; - vm.updateDate = updateDate; - vm.updateDateTime = updateDateTime; - vm.updateUpper = updateUpper; - vm.updateLower = updateLower; - vm.updateEncodeHtml = updateEncodeHtml; - vm.updateEncodeUrl = updateEncodeUrl; + vm.setDateOption = setDateOption; + vm.setCasingOption = setCasingOption; + vm.setEncodingOption = setEncodingOption; + vm.generateOutputSample = generateOutputSample; - contentTypeResource.getAllPropertyTypeAliases().then(function(array) { - vm.properties = array; - }); + function onInit() { - //hide preview by default - function hideInsertPreview () { - $scope.model.itemDetails = null; - } + // set default title + if(!$scope.model.title) { + $scope.model.title = "Insert value"; + } - //Create preview - function showInsertPreview () { - var previewDetails = {}; + // set default subtitle + if(!$scope.model.subtitle) { + $scope.model.subtitle = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; + } - previewDetails.title = vm.insertPreviewTitle; - - if (!vm.insertBefore && !vm.insertAfter) { - previewDetails.description = $scope.model.umbracoField - } else if (!vm.insertAfter) { - previewDetails.description = vm.insertBefore + ' ' + $scope.model.umbracoField; - } else { - previewDetails.description = vm.insertBefore + ' ' + $scope.model.umbracoField + ' ' + vm.insertAfter; - } - $scope.model.itemDetails = previewDetails; - } - - //Update preview - function updateInsertPreview () { - var previewDetails = $scope.model.itemDetails; - - previewDetails.title = vm.insertPreviewTitle; - - if (!vm.insertBefore && !vm.insertAfter) { - previewDetails.description = ' ' + $scope.model.umbracoField; - } else if (!vm.insertAfter) { - previewDetails.description = vm.insertBefore + ' ' + $scope.model.umbracoField; - } else { - previewDetails.description = vm.insertBefore + ' ' + $scope.model.umbracoField + ' ' + vm.insertAfter; + // Load all fields + contentTypeResource.getAllPropertyTypeAliases().then(function (array) { + vm.properties = array; + }); + } - $scope.model.itemDetails = previewDetails; - } + // date formatting + function setDateOption(option) { + + if (option === 'date') { + if(vm.date) { + vm.date = false; + } else { + vm.date = true; + vm.dateTime = false; + } + } + + if (option === 'dateWithTime') { + if(vm.dateTime) { + vm.dateTime = false; + } else { + vm.date = false; + vm.dateTime = true; + } + } - // date formatting - function updateDate () { - if (vm.date) { - vm.date = false; - return; - }else { - vm.date = true; - if (vm.dateTime) { - vm.dateTime = false; - } } - } - function updateDateTime () { - if (vm.dateTime) { - vm.dateTime = false - } else { - vm.dateTime = true; - if (vm.date) { - vm.date = false; - } + // casing formatting + function setCasingOption(option) { + if (option === 'uppercase') { + if(vm.casingUpper) { + vm.casingUpper = false; + } else { + vm.casingUpper = true; + vm.casingLower = false; + } + } + + if (option === 'lowercase') { + if(vm.casingLower) { + vm.casingLower = false; + } else { + vm.casingUpper = false; + vm.casingLower = true; + } + } } - } - // casing - function updateUpper() { - if (vm.casingUpper) { - vm.casingUpper = false; - return; - }else { - vm.casingUpper = true; - if (vm.casingLower) { - vm.casingLower = false; - } + // encoding formatting + function setEncodingOption(option) { + if (option === 'html') { + if(vm.encodeHtml) { + vm.encodeHtml = false; + } else { + vm.encodeHtml = true; + vm.encodeUrl = false; + } + } + + if (option === 'url') { + if (vm.encodeUrl) { + vm.encodeUrl = false; + } else { + vm.encodeHtml = false; + vm.encodeUrl = true; + } + } } - } - function updateLower() { - if (vm.casingLower) { - vm.casingLower = false; - return; - }else { - vm.casingLower = true; - if (vm.casingUpper) { - vm.casingUpper = false; - } + function generateOutputSample() { + + var pageField = (vm.field !== undefined ? '@Umbraco.Field("' + vm.field + '"' : "") + + (vm.altField !== undefined ? ', altFieldAlias:"' + vm.altField + '"' : "") + + (vm.altText !== undefined ? ', altText:"' + vm.altText + '"' : "") + + (vm.insertBefore !== undefined ? ', insertBefore:"' + vm.insertBefore + '"' : "") + + (vm.insertAfter !== undefined ? ', insertAfter:"' + vm.insertAfter + '"' : "") + + (vm.recursive !== false ? ', recursive: ' + vm.recursive : "") + + (vm.date !== false ? ', formatAsDate: ' + vm.date : "") + + (vm.dateTime !== false ? ', formatAsDateWithTimeSeparator:"' + vm.dateTimeSeparator + '"' : "") + + (vm.casingUpper !== false ? ', casing: ' + "RenderFieldCaseType.Upper" : "") + + (vm.casingLower !== false ? ', casing: ' + "RenderFieldCaseType.Lower" : "") + + (vm.encodeHtml !== false ? ', encoding: ' + "RenderFieldEncodingType.Html" : "") + + (vm.encodeUrl !== false ? ', encoding: ' + "RenderFieldEncodingType.Url" : "") + + (vm.convertLinebreaks !== false ? ', convertLineBreaks: ' + "true" : "") + + (vm.removeParagraphTags !== false ? ', removeParagraphTags: ' + "true": "") + + (vm.field ? ')' : ""); + + $scope.model.umbracoField = pageField; + + return pageField; + } - } - // encoding - function updateEncodeHtml() { - if (vm.encodeHtml) { - vm.encodeHtml = false; - return; - }else { - vm.encodeHtml = true; - if (vm.encodeUrl) { - vm.encodeUrl = false; - } - } - } - - function updateEncodeUrl() { - if (vm.encodeUrl) { - vm.encodeUrl = false; - return; - } else { - vm.encodeUrl = true; - if (vm.encodeHtml) { - vm.encodeHtml = false; - } - } - } - - $scope.updatePageField = function() { - var pageField = (vm.field !== undefined ? '@Umbraco.Field("' + vm.field + '"' : "") - + (vm.altField !== undefined ? ', altFieldAlias:"' + vm.altField + '"': "") - + (vm.altText !== undefined ? ', altText:"' + vm.altText + '"' : "") - + (vm.insertBefore !== undefined ? ', insertBefore:"' + vm.insertBefore + '"' : "") - + (vm.insertAfter !== undefined ? ', insertAfter:"' + vm.insertAfter + '"' : "") - + (vm.recursive !== false ? ', recursive: ' + vm.recursive : "") - + (vm.date !== false ? ', formatAsDate: ' + vm.date : "") - + (vm.dateTime !== false ? ', formatAsDateWithTimeSeparator:"' + vm.dateTimeSeparator + '"' : "") - + (vm.casingUpper !== false ? ', casing: ' + "RenderFieldCaseType.Upper" : "") - + (vm.casingLower !== false ? ', casing: ' + "RenderFieldCaseType.Lower" : "") - + (vm.encodeHtml !== false ? ', encoding: ' + "RenderFieldEncodingType.Html" : "") - + (vm.encodeUrl !== false ? ', encoding: ' + "RenderFieldEncodingType.Url" : "") - + (vm.convertLinebreaks !== false ? ', convertLineBreaks: ' + "true" : "") - + (vm.field ? ')' : ""); - $scope.model.umbracoField = pageField; - return pageField; - }; + onInit(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.html index c96484e0c6..773aebd364 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insertfield/insertfield.html @@ -1,184 +1,187 @@ -
    -
    -
    - - - -
    - -
    -
    -
    - -
    -
    - - - Add alternative field
    - -
    -
    - - -
    - - -
    -
    -
    - - - - - Add default value - - -
    - -
    - - -
    - - -
    -
    - -
    - - -
    -
    - -

    - - -
    -
    -
    - -
    - -
    - - -
    - -
    -
    - -
    - -
    - -
    - - -
    - -
    -
    - -
    - -
    -
    - -
    - - Date only - Date and time - -
    -
    -
    - -
    -
    - -
    - - Upper - lower -
    -
    -
    - -
    -
    - -
    - - - HTML - - URL - -
    -
    -
    - - -
    -
    - -

    - - -
    -
    -
    - +
    -
    - -

    - -

    {{updatePageField()}}

    +
    + +
    + +
    -
    +
    + + +
    -
    + +
    + + Add fallback field +
    +
    + +
    + +
    +
    +
    +
    + + +
    +
    + +
    + +
    +
    +
    +
    + + +
    +
    +
    + + + Yes, make it recursive +
    +
    +
    + +
    Format and encoding
    + + +
    +
    +
    +
    + +
    + Date only + Date and time + +
    +
    +
    + + +
    +
    +
    +
    + +
    + Uppercase + Lowercase +
    +
    +
    + + +
    +
    +
    +
    + +
    + HTML + URL +
    +
    +
    + +
    Modify output
    + + +
    +
    + +
    + +
    +
    +
    + + +
    +
    + +
    + +
    +
    +
    + + +
    +
    +
    +
    + +
    + + Yes, convert line breaks +
    +
    +
    + + +
    +
    +
    +
    + +
    + + Yes, remove paragraph tags +
    +
    +
    + + +
    +
    +
    + +
    {{ vm.generateOutputSample() }}
    +
    +
    +
    + +
    + +
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 7602f89278..0986975a02 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -174,19 +174,15 @@ function openPageFieldOverlay() { vm.pageFieldOverlay = { - title: "Insert page field", - description: "Insert data in template", submitButtonLabel: "Insert", closeButtonlabel: "Cancel", view: "insertfield", show: true, - submit: function (model) { - insert(model.umbracoField); - vm.pageFieldOverlay.show = false; - vm.pageFieldOverlay = null; + insert(model.umbracoField); + vm.pageFieldOverlay.show = false; + vm.pageFieldOverlay = null; }, - close: function (model) { vm.pageFieldOverlay.show = false; vm.pageFieldOverlay = null; From 597a10fc5de7395b82cf789755b4dd67103bf8ea Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 17 Nov 2016 10:33:15 +0100 Subject: [PATCH 050/599] small fixes to overlay buttons + insert order --- .../overlays/insert/insert.controller.js | 2 +- .../views/common/overlays/insert/insert.html | 18 +++++++++--------- .../src/views/templates/edit.controller.js | 19 +++---------------- .../src/views/templates/edit.html | 2 +- 4 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js index 65b34d5432..b6366bca74 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js @@ -22,9 +22,9 @@ vm.macroPickerOverlay = { view: "macropicker", - show: true, title: "Insert macro", dialogData: {}, + show: true, submit: function(model) { $scope.model.insert = { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html index 4d8fb4a4bd..c49c045853 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html @@ -2,23 +2,23 @@
    -
    -
    Macro
    -
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque gravida hendrerit. Nunc lacinia ipsum sed urna volutpat finibus.
    -
    -
    Value
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque gravida hendrerit. Nunc lacinia ipsum sed urna volutpat finibus.
    -
    -
    Dictionary
    +
    +
    Partial view
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque gravida hendrerit. Nunc lacinia ipsum sed urna volutpat finibus.
    -
    -
    Partial
    +
    +
    Macro
    +
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque gravida hendrerit. Nunc lacinia ipsum sed urna volutpat finibus.
    +
    + +
    +
    Dictionary
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris pellentesque gravida hendrerit. Nunc lacinia ipsum sed urna volutpat finibus.
    diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 0986975a02..81f04015ea 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -200,7 +200,6 @@ multiPicker: false, show: true, title: "Insert dictionary item", - select: function(node){ //crappy hack due to dictionary items not in umbracoNode table var code = "@Umbraco.GetDictionaryValue(\"" + node.name + "\")"; @@ -209,11 +208,6 @@ vm.dictionaryItemOverlay.show = false; vm.dictionaryItemOverlay = null; }, - - submit: function (model) { - console.log(model); - }, - close: function (model) { vm.dictionaryItemOverlay.show = false; vm.dictionaryItemOverlay = null; @@ -230,7 +224,6 @@ multiPicker: false, show: true, title: "Insert Partial view", - select: function(node){ //crappy hack due to dictionary items not in umbracoNode table var code = "@Html.Partial(\"" + node.name + "\")"; @@ -239,13 +232,6 @@ vm.partialItemOverlay.show = false; vm.partialItemOverlay = null; }, - - submit: function (model) { - console.log(model); - vm.partialItemOverlay.show = false; - vm.partialItemOverlay = null; - }, - close: function (model) { vm.partialItemOverlay.show = false; vm.partialItemOverlay = null; @@ -288,8 +274,9 @@ vm.sectionsOverlay = { view: "templatesections", - show: true, isMasterTemplate: true, + submitButtonLabel: "Insert", + show: true, submit: function(model) { if (model.insertType === 'renderBody') { @@ -374,7 +361,7 @@ return templateName; } else { - return "No template"; + return "No master"; } } diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index 67f409f8d3..e09a6be4c4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -41,9 +41,9 @@
    From 9de3acfae2c8182b6d813d68aee2f85775086f3f Mon Sep 17 00:00:00 2001 From: Mark320 Date: Thu, 17 Nov 2016 14:22:12 +0000 Subject: [PATCH 051/599] U4-9192 Add jQuery migrate for user controls used in tabs that will improve backwards compatibility --- .../Umbraco/dashboard/UserControlProxy.aspx | 1 + .../Umbraco/dashboard/UserControlProxy.aspx.designer.cs | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/Umbraco.Web.UI/Umbraco/dashboard/UserControlProxy.aspx b/src/Umbraco.Web.UI/Umbraco/dashboard/UserControlProxy.aspx index 59a96e9a37..7728d281c3 100644 --- a/src/Umbraco.Web.UI/Umbraco/dashboard/UserControlProxy.aspx +++ b/src/Umbraco.Web.UI/Umbraco/dashboard/UserControlProxy.aspx @@ -13,6 +13,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco/dashboard/UserControlProxy.aspx.designer.cs b/src/Umbraco.Web.UI/Umbraco/dashboard/UserControlProxy.aspx.designer.cs index 19ac019a8f..628e82a767 100644 --- a/src/Umbraco.Web.UI/Umbraco/dashboard/UserControlProxy.aspx.designer.cs +++ b/src/Umbraco.Web.UI/Umbraco/dashboard/UserControlProxy.aspx.designer.cs @@ -48,6 +48,15 @@ namespace Umbraco.Web.UI.Umbraco.Dashboard { /// protected global::ClientDependency.Core.Controls.JsInclude JsInclude3; + /// + /// JsInclude4 control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::ClientDependency.Core.Controls.JsInclude JsInclude4; + /// /// JsInclude6 control. /// From bc8b8a69b887f7335862f901735364ec431213e6 Mon Sep 17 00:00:00 2001 From: Anders Bjerner Date: Mon, 21 Nov 2016 10:26:08 +0100 Subject: [PATCH 052/599] Typo (sorta) --- src/Umbraco.Web/ImageCropperTemplateExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/ImageCropperTemplateExtensions.cs b/src/Umbraco.Web/ImageCropperTemplateExtensions.cs index e518c5e246..63157e4fee 100644 --- a/src/Umbraco.Web/ImageCropperTemplateExtensions.cs +++ b/src/Umbraco.Web/ImageCropperTemplateExtensions.cs @@ -81,7 +81,7 @@ namespace Umbraco.Web /// Use focal point, to generate an output image using the focal point instead of the predefined crop /// /// - /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters>. + /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters. /// /// /// Add a serialised date of the last edit of the item to ensure client cache refresh when updated From daaa2bf911a449e23735a3016ec90810557d4696 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 29 Nov 2016 15:24:02 +0100 Subject: [PATCH 053/599] replace yellow buttons in query builder with blue --- .../src/less/components/umb-querybuilder.less | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less index 2c6a089eeb..0e6ad29e29 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less @@ -4,8 +4,14 @@ } .umb-querybuilder .row a.btn { - font-size: 12px; - background: lightyellow + padding: 5px 8px; + margin: 0 5px; + font-weight: bold; + background-color: #f3f9ff; + border: 1px solid #bfdff9; + border-radius: 3px; + text-align: center; + display: inline-block; } .umb-querybuilder .row > div { From bd0c1cd9324e92583597d59c98053e144212aecd Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 30 Nov 2016 10:38:04 +0100 Subject: [PATCH 054/599] clean up umb-era-button + add button style to template editor buttons --- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../components/buttons/umb-button-group.less | 22 ++++ .../components/buttons/umb-era-button.less | 104 +++++++++++++++++ .../src/less/components/umb-packages.less | 89 --------------- .../src/less/components/umb-querybuilder.less | 6 + .../src/less/healthcheck.less | 105 ------------------ .../src/views/templates/edit.html | 65 +++++------ 7 files changed, 163 insertions(+), 229 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-era-button.less diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index d6a967733f..c988f9edbb 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -121,6 +121,7 @@ @import "components/buttons/umb-button.less"; @import "components/buttons/umb-button-group.less"; +@import "components/buttons/umb-era-button.less"; @import "components/notifications/umb-notifications.less"; @import "components/umb-file-dropzone.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button-group.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button-group.less index 0b063d094d..119077d52b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button-group.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button-group.less @@ -8,3 +8,25 @@ right: 0; left: auto; } + +// hack for umb-era-button +.umb-button-group { + + display: flex; + + .umb-era-button:first-child { + padding-right: 15px; + border-radius: 3px 0 0 3px; + } + + .umb-era-button.umb-button-group__toggle { + padding-right: 10px; + padding-left: 10px; + border-radius: 0 3px 3px 0; + } + + .umb-era-button.umb-button-group__toggle .caret { + margin: 0; + } + +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-era-button.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-era-button.less new file mode 100644 index 0000000000..8087a601bb --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-era-button.less @@ -0,0 +1,104 @@ +.umb-era-button { + display: flex; + justify-content: center; + align-items: center; + font-size: 14px; + //font-weight: bold; + height: 38px; + line-height: 1; + max-width: 100%; + padding: 0 18px; + color: #202129; + background-color: #edeeee; + text-decoration: none !important; + user-select: none; + white-space: nowrap; + overflow: hidden; + border-radius: 3px; + border: 0 none; + box-sizing: border-box; + cursor: pointer; + transition: background-color 80ms ease, color 80ms ease; +} + + +.umb-era-button:hover, +.umb-era-button:active { + color: #484848; + background-color: #e1e2e2; + outline: none; + text-decoration: none; +} + + +.umb-era-button:focus { + outline: none; +} + +.umb-era-button.-blue { + background: @blue; + color: white; +} + +.umb-era-button.-blue:hover { + background-color: @blueDark; +} + +.umb-era-button.-red { + background: @btnDangerBackground; + color: white; +} + +.umb-era-button.-red:hover { + background-color: darken(@btnDangerBackground, 5%); +} + +.umb-era-button.-link { + padding: 0; + background: transparent; +} + +.umb-era-button.-link:hover { + background-color: transparent; + opacity: .6; +} + +.umb-era-button.-inactive { + cursor: not-allowed; + color: #BBB; + background: #EAE7E7; +} + +.umb-era-button.-inactive:hover { + color: #BBB; + background: #EAE7E7; +} + + +.umb-era-button.-full-width { + display: block; + width: 100%; +} + +.umb-era-button.umb-button--s { + height: 30px; + font-size: 13px; +} + +.umb-era-button.-white { + background-color: @white; + + &:hover { + opacity: .9; + } +} + +.umb-era-button.-text-black { + color: @black; +} + +/* icons */ + +.umb-era-button i { + margin-right: 5px; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less index f14df918c0..a043cd5571 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less @@ -248,95 +248,6 @@ color: black; } -/* umb-buttons-era */ -.umb-era-button { - display: flex; - justify-content: center; - align-items: center; - - font-size: 14px; - font-weight: bold; - - height: 38px; - line-height: 1; - - max-width: 100%; - padding: 0 18px; - - color: #484848; - background-color: #e0e0e0; - - text-decoration: none !important; - user-select: none; - - white-space: nowrap; - overflow: hidden; - - border-radius: 3px; - border: 0 none; - box-sizing: border-box; - - cursor: pointer; - - transition: background-color 80ms ease, color 80ms ease; -} - - -.umb-era-button:hover, -.umb-era-button:active { - color: #484848; - background-color: #d3d3d3; - outline: none; - text-decoration: none; -} - - -.umb-era-button:focus { - outline: none; -} - -.umb-era-button.-blue { - background: @blue; - color: white; -} - -.umb-era-button.-blue:hover { - background-color: @blueDark; -} - -.umb-era-button.-link { - padding: 0; - background: transparent; -} - -.umb-era-button.-link:hover { - background-color: transparent; - opacity: .6; -} - -.umb-era-button.-inactive { - cursor: not-allowed; - color: #BBB; - background: #EAE7E7; -} - -.umb-era-button.-inactive:hover { - color: #BBB; - background: #EAE7E7; -} - - -.umb-era-button.-full-width { - display: block; - width: 100%; -} - -.umb-era-button.umb-button--s { - height: 30px; - font-size: 13px; -} - - /* CATEGORIES */ .umb-packages-categories { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less index 0e6ad29e29..fb6279608e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less @@ -12,6 +12,12 @@ border-radius: 3px; text-align: center; display: inline-block; + +} + +.umb-querybuilder .row a.btn:hover { + background: #e8f1fb; + text-decoration: none; } .umb-querybuilder .row > div { diff --git a/src/Umbraco.Web.UI.Client/src/less/healthcheck.less b/src/Umbraco.Web.UI.Client/src/less/healthcheck.less index 6e82d424bb..d0427d96f7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/healthcheck.less +++ b/src/Umbraco.Web.UI.Client/src/less/healthcheck.less @@ -95,111 +95,6 @@ } -/* umb-buttons-era */ -.umb-era-button { - display: flex; - justify-content: center; - align-items: center; - - font-size: 14px; - font-weight: bold; - - height: 40px; - line-height: 1; - - max-width: 100%; - padding: 0 15px; - - color: #484848; - background-color: #e0e0e0; - - text-decoration: none !important; - user-select: none; - - white-space: nowrap; - overflow: hidden; - - border-radius: 3px; - border: 0 none; - box-sizing: border-box; - - cursor: pointer; - - transition: background-color 80ms ease, color 80ms ease, opacity 80ms ease; -} - - -.umb-era-button:hover, -.umb-era-button:active { - color: #484848; - background-color: #d3d3d3; - outline: none; - text-decoration: none; -} - - -.umb-era-button:focus { - outline: none; -} - -.umb-era-button.-blue { - background: @blue; - color: white; -} - -.umb-era-button.-blue:hover { - background-color: @blueDark; -} - -.umb-era-button.-red { - background: @btnDangerBackground; - color: white; -} - -.umb-era-button.-red:hover { - background-color: darken(@btnDangerBackground, 5%); -} - -.umb-era-button.-link { - padding: 0; - background: transparent; -} - -.umb-era-button.-link:hover { - background-color: transparent; - opacity: .6; -} - -.umb-era-button.-inactive { - cursor: not-allowed; - color: #BBB; - background: #EAE7E7; -} - -.umb-era-button.-inactive:hover { - color: #BBB; - background: #EAE7E7; -} - - -.umb-era-button.-full-width { - display: block; - width: 100%; -} - -.umb-era-button.-white { - background-color: @white; - - &:hover { - opacity: .9; - } -} - -.umb-era-button.-text-black { - color: @black; -} - - /* Spacing for boxes */ .umb-air { flex: 0 0 auto; diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index e09a6be4c4..83f3d3954d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -22,20 +22,26 @@ - +
    - + - - - - - - Master template: - {{ vm.getMasterTemplateName(vm.template.masterTemplateAlias, vm.templates) }} - - - - - +
    Date: Wed, 30 Nov 2016 11:17:21 +0100 Subject: [PATCH 055/599] Fixed simple typo for modelsbuilder --- src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index 173da6cc9b..f68359b0af 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -1142,7 +1142,7 @@ To manage your website, simply open the Umbraco back office and start adding con Building models - this can take abit of time, don't worry + this can take a bit of time, don't worry Models generated Models could not be generated Models generation has failed, see exception in U log From 286907b89e0113d430a3644bf0643d19a3392966 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 30 Nov 2016 12:13:04 +0100 Subject: [PATCH 056/599] fix unit tests --- .../template-editor-controller.spec.js | 79 ++++++++++--------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js index cb70446bb1..55d91c35a6 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js @@ -42,8 +42,14 @@ q = $q; ace = { - on: function(){} - } + on: function(){}, + navigateFileEnd: function() {}, + getCursorPosition: function() {}, + getValue: function() {}, + setValue: function() {}, + clearSelection: function() {}, + navigateFileStart: function() {} + }; controller = createController(); scope.$digest(); @@ -65,7 +71,8 @@ $scope: scope, $routeParams: {}, templateResource: { - getById: resolvedPromise({}) + getById: resolvedPromise({}), + getAll: resolvedPromise({}) }, assetsService: { loadCss: function() {} @@ -85,44 +92,42 @@ }); } - it("has ace editor", - function () { - expect(controller.editor).toBe(ace); - }); + it("has ace editor", function () { + expect(controller.editor).toBe(ace); + }); - it("sets masterpage on template", - function () { - controller.setLayout = function() {}; + it("sets masterpage on template", function () { + controller.setLayout = function() {}; - controller.openOrganizeOverlay(); - controller.organizeOverlay.submit({ - masterPage: { - alias: "NewMasterPage" - } - }); - expect(controller.template.masterPageAlias).toBe("NewMasterPage"); - }); - - it("changes layout value when masterpage is selected", - function() { - var newTemplate; - ace.clearSelection = nada; - ace.navigateFileStart = nada; - ace.getValue = function () { - return "@{ Layout = null; }"; + controller.openMasterTemplateOverlay(); + controller.masterTemplateOverlay.submit({ + selectedItem: { + alias: "NewMasterPage" } - ace.setValue = function (value) { - newTemplate = value; - } - - controller.openOrganizeOverlay(); - controller.organizeOverlay.submit({ - masterPage: { - alias: "NewMasterPage" - } - }); - expect(newTemplate).toBe("@{ Layout = \"NewMasterPage.cshtml\"; }"); }); + expect(controller.template.masterTemplateAlias).toBe("NewMasterPage"); + }); + + it("changes layout value when masterpage is selected", function() { + var newTemplate; + ace.clearSelection = nada; + ace.navigateFileStart = nada; + ace.getValue = function () { + return "@{ Layout = null; }"; + } + ace.setValue = function (value) { + newTemplate = value; + } + + controller.openMasterTemplateOverlay(); + controller.masterTemplateOverlay.submit({ + selectedItem: { + alias: "NewMasterPage" + } + }); + expect(newTemplate).toBe("@{ Layout = \"NewMasterPage.cshtml\"; }"); + }); + }); }()); From 90136c524c071a3d8ac2552ac7e87796e3608566 Mon Sep 17 00:00:00 2001 From: Chris Houston Date: Tue, 6 Dec 2016 11:05:59 +0000 Subject: [PATCH 057/599] Small English grammar error. --- src/Umbraco.Core/CoreBootManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index 4ef08bd02f..e78d2adc5b 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -415,7 +415,7 @@ namespace Umbraco.Core if (currentTry == 5) { - throw new UmbracoStartupFailedException("Umbraco cannot start. A connection string is configured but the Umbraco cannot connect to the database."); + throw new UmbracoStartupFailedException("Umbraco cannot start. A connection string is configured but Umbraco cannot connect to the database."); } } From ba27c7e8e8cf92eb3f2524719ee2dbff47d9ecad Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 6 Dec 2016 15:46:17 +0100 Subject: [PATCH 058/599] U4-9249 - fix member type standard properties --- .../Persistence/Factories/MemberTypeReadOnlyFactory.cs | 9 ++++++++- .../Persistence/Repositories/MemberTypeRepository.cs | 10 ++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs index b4d5d2e566..ba7a47165b 100644 --- a/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MemberTypeReadOnlyFactory.cs @@ -9,9 +9,10 @@ namespace Umbraco.Core.Persistence.Factories { internal class MemberTypeReadOnlyFactory { - public IMemberType BuildEntity(MemberTypeReadOnlyDto dto) + public IMemberType BuildEntity(MemberTypeReadOnlyDto dto, out bool needsSaving) { var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs(); + needsSaving = false; var memberType = new MemberType(dto.ParentId); @@ -47,6 +48,12 @@ namespace Umbraco.Core.Persistence.Factories { if (dto.PropertyTypes.Any(x => x.Alias.Equals(standardPropertyType.Key))) continue; + // beware! + // means that we can return a memberType "from database" that has some property types + // that do *not* come from the database and therefore are incomplete eg have no key, + // no id, no dataTypeDefinitionId - ouch! - better notify caller of the situation + needsSaving = true; + //Add the standard PropertyType to the current list propertyTypes.Add(standardPropertyType.Value); diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs index d06399a85b..7d5defe8c9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs @@ -300,13 +300,19 @@ namespace Umbraco.Core.Persistence.Repositories ///
    /// /// - private static IEnumerable BuildFromDtos(List dtos) + private IEnumerable BuildFromDtos(List dtos) { if (dtos == null || dtos.Any() == false) return Enumerable.Empty(); var factory = new MemberTypeReadOnlyFactory(); - return dtos.Select(factory.BuildEntity); + return dtos.Select(x => + { + bool needsSaving; + var memberType = factory.BuildEntity(x, out needsSaving); + if (needsSaving) PersistUpdatedItem(memberType); + return memberType; + }).ToList(); } /// From 960040e51e707f3676acffbe60e99d04f2edcf70 Mon Sep 17 00:00:00 2001 From: Claus Date: Wed, 7 Dec 2016 12:57:29 +0100 Subject: [PATCH 059/599] U4-9255 Not having access to settings section causes backoffice user to be logged out when browsing media list views --- src/Umbraco.Web/Editors/MediaTypeController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index a423e638b9..0f83ad6d03 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -49,6 +49,7 @@ namespace Umbraco.Web.Editors return Services.ContentTypeService.CountContentTypes(); } + [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] public MediaTypeDisplay GetById(int id) { var ct = Services.ContentTypeService.GetMediaType(id); From 5bc66c5b7fe2bdf311370ece1b998dfff530c5ff Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 7 Dec 2016 14:18:27 +0100 Subject: [PATCH 060/599] Bumps the version --- build/UmbracoVersion.txt | 2 +- src/SolutionInfo.cs | 4 ++-- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index df03fe8381..345fce6961 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,2 +1,2 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) -7.5.5 \ No newline at end of file +7.5.6 \ No newline at end of file diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index b3b5633864..6821a737af 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -11,5 +11,5 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyFileVersion("7.5.5")] -[assembly: AssemblyInformationalVersion("7.5.5")] \ No newline at end of file +[assembly: AssemblyFileVersion("7.5.6")] +[assembly: AssemblyInformationalVersion("7.5.6")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index ccf4aecf13..0b3a17c8d6 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration { public class UmbracoVersion { - private static readonly Version Version = new Version("7.5.5"); + private static readonly Version Version = new Version("7.5.6"); /// /// Gets the current version of Umbraco. diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index c8c90c2ed5..8424c750c7 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -2415,9 +2415,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" True True - 7550 + 7560 / - http://localhost:7550 + http://localhost:7560 False False From b413b7b5af3aa181dca2298c8cbb9b6d0a18af86 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 8 Dec 2016 12:48:44 +1100 Subject: [PATCH 061/599] Lazily creates the static queries instead of eagerly, this fixes an issue of if these services are accessed before Umbraco has booted --- .../Persistence/Repositories/ContentRepository.cs | 8 +------- src/Umbraco.Core/Services/ContentService.cs | 8 +++++++- src/Umbraco.Core/Services/EntityService.cs | 8 +++++++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index d0c226ace2..e9a4652512 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -94,13 +94,7 @@ namespace Umbraco.Core.Persistence.Repositories return ProcessQuery(sql); } - #endregion - - #region Static Queries - - private readonly IQuery _publishedQuery = Query.Builder.Where(x => x.Published == true); - - #endregion + #endregion #region Overrides of PetaPocoRepositoryBase diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 6223e5933b..a4c6d354c1 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -58,7 +58,7 @@ namespace Umbraco.Core.Services #region Static Queries - private readonly IQuery _notTrashedQuery = Query.Builder.Where(x => x.Trashed == false); + private IQuery _notTrashedQuery; #endregion @@ -793,6 +793,12 @@ namespace Umbraco.Core.Services /// internal IEnumerable GetAllPublished() { + //create it once if it is needed (no need for locking here) + if (_notTrashedQuery == null) + { + _notTrashedQuery = Query.Builder.Where(x => x.Trashed == false); + } + using (var repository = RepositoryFactory.CreateContentRepository(UowProvider.GetUnitOfWork())) { return repository.GetByPublishedVersion(_notTrashedQuery); diff --git a/src/Umbraco.Core/Services/EntityService.cs b/src/Umbraco.Core/Services/EntityService.cs index a6b84704fc..b56c1fbed2 100644 --- a/src/Umbraco.Core/Services/EntityService.cs +++ b/src/Umbraco.Core/Services/EntityService.cs @@ -64,7 +64,7 @@ namespace Umbraco.Core.Services #region Static Queries - private readonly IQuery _rootEntityQuery = Query.Builder.Where(x => x.ParentId == -1); + private IQuery _rootEntityQuery; #endregion @@ -392,6 +392,12 @@ namespace Umbraco.Core.Services /// An enumerable list of objects public virtual IEnumerable GetRootEntities(UmbracoObjectTypes umbracoObjectType) { + //create it once if it is needed (no need for locking here) + if (_rootEntityQuery == null) + { + _rootEntityQuery = Query.Builder.Where(x => x.ParentId == -1); + } + var objectTypeId = umbracoObjectType.GetGuid(); using (var repository = RepositoryFactory.CreateEntityRepository(UowProvider.GetUnitOfWork())) { From b9b7c9f96575fff6766b0edc4c01f010609d9fc9 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 13 Dec 2016 10:35:16 +0100 Subject: [PATCH 062/599] More database fixing (sqlCE) --- src/Umbraco.Core/Persistence/DatabaseDebugHelper.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Core/Persistence/DatabaseDebugHelper.cs b/src/Umbraco.Core/Persistence/DatabaseDebugHelper.cs index 06512c2d68..4cb8327f5b 100644 --- a/src/Umbraco.Core/Persistence/DatabaseDebugHelper.cs +++ b/src/Umbraco.Core/Persistence/DatabaseDebugHelper.cs @@ -42,6 +42,8 @@ namespace Umbraco.Core.Persistence { var prof = con as StackExchange.Profiling.Data.ProfiledDbConnection; if (prof != null) con = prof.InnerConnection; + var ceCon = con as System.Data.SqlServerCe.SqlCeConnection; + if (ceCon != null) return null; // "NotSupported: SqlCE"; var dbCon = con as DbConnection; return dbCon == null ? "NotSupported: " + con.GetType() From e97e8caab8a84411f6ab50dcb19c72365566cda3 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 13 Dec 2016 10:57:19 +0100 Subject: [PATCH 063/599] add umbDateTimePicker component --- .../components/umbdatetimepicker.directive.js | 143 ++++++++++++++++++ .../components/umb-date-time-picker.html | 6 + 2 files changed, 149 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/umb-date-time-picker.html diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js new file mode 100644 index 0000000000..2954e1791c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js @@ -0,0 +1,143 @@ +/** +@ngdoc directive +@name umbraco.directives.directive:umbDateTimePicker +@restrict E +@scope + +@description +Use this directive to render a date time picker + +

    Markup example

    +
    +	
    + + + + +
    +
    + +

    Controller example

    +
    +	(function () {
    +		"use strict";
    +
    +		function Controller() {
    +
    +            var vm = this;
    +
    +            vm.date = "";
    +
    +            vm.config = {
    +                pickDate: true,
    +                pickTime: true,
    +                useSeconds: true,
    +                format: "YYYY-MM-DD HH:mm:ss",
    +                icons: {
    +                    time: "icon-time",
    +                    date: "icon-calendar",
    +                    up: "icon-chevron-up",
    +                    down: "icon-chevron-down"
    +                }
    +            };
    +
    +            vm.datePickerChange = datePickerChange;
    +            vm.datePickerError = datePickerError;
    +
    +            function datePickerChange(event) {
    +                // handle change
    +            }
    +
    +            function datePickerError(event) {
    +                // handle error
    +            }
    +
    +        }
    +
    +		angular.module("umbraco").controller("My.Controller", Controller);
    +
    +	})();
    +
    + +@param {object} options (binding): Config object for the date picker. +@param {string} ngModel (binding): Date value. +@param {callback} onChange (callback): Change callback. +@param {callback} onError (callback): Error callback. +**/ + +(function () { + 'use strict'; + + function DateTimePickerDirective(assetsService) { + + function link(scope, element, attrs, ctrl) { + + function onInit() { + // load css file for the date picker + assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css').then(function () { + + }); + + // load the js file for the date picker + assetsService.loadJs('lib/datetimepicker/bootstrap-datetimepicker.js').then(function () { + // init date picker + initDatePicker(); + }); + } + + function onChange(event) { + if (scope.onChange && event.date && event.date.isValid()) { + scope.$apply(function(){ + // Update ngModel + scope.ngModel = event.date.format(scope.options.format); + // callback + scope.onChange({event: event}); + }); + } + } + + function onError(event) { + if (scope.onError) { + scope.$apply(function(){ + // callback + scope.onError({event:event}); + }); + } + } + + function initDatePicker() { + // Open the datepicker and add a changeDate eventlistener + element + .datetimepicker(angular.extend({ useCurrent: true }, scope.options)) + .on("dp.change", onChange) + .on("dp.error", onError); + } + + onInit(); + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-date-time-picker.html', + scope: { + ngModel: "=", + options: "=", + onChange: "&", + onError: "&" + }, + link: link + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbDateTimePicker', DateTimePickerDirective); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-date-time-picker.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-date-time-picker.html new file mode 100644 index 0000000000..aaf8cfe62d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-date-time-picker.html @@ -0,0 +1,6 @@ +
    + + + + +
    \ No newline at end of file From 4863052a084a2bda24ab72161c97785683fcd37a Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 13 Dec 2016 10:58:19 +0100 Subject: [PATCH 064/599] Revert "deploy-11 - implement DeployVersionAttribute" This reverts commit f51a086137fc34a619ed45ac19ea220f01a5626e. --- .../Deploy/DeploySupportAttribute.cs | 16 ---------------- src/Umbraco.Core/Properties/AssemblyInfo.cs | 9 +++------ src/Umbraco.Core/Umbraco.Core.csproj | 1 - 3 files changed, 3 insertions(+), 23 deletions(-) delete mode 100644 src/Umbraco.Core/Deploy/DeploySupportAttribute.cs diff --git a/src/Umbraco.Core/Deploy/DeploySupportAttribute.cs b/src/Umbraco.Core/Deploy/DeploySupportAttribute.cs deleted file mode 100644 index f7f32b52d5..0000000000 --- a/src/Umbraco.Core/Deploy/DeploySupportAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Semver; - -namespace Umbraco.Core.Deploy -{ - [AttributeUsage(AttributeTargets.Assembly)] - public class DeploySupportAttribute : Attribute - { - public DeploySupportAttribute(string version) - { - Version = version; - } - - public SemVersion Version { get; private set; } - } -} diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index 204612b3d9..dbc1ab6c93 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -3,9 +3,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; -using Umbraco.Core.Deploy; -// General Information about an assembly is controlled through the following +// General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Umbraco.Core")] @@ -13,16 +12,14 @@ using Umbraco.Core.Deploy; [assembly: AssemblyConfiguration("")] [assembly: AssemblyProduct("Umbraco CMS")] -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("130a6b5c-50e7-4df3-a0dd-e9e7eb0b7c5c")] -[assembly: DeploySupport("1.0.0-alpha000")] - [assembly: InternalsVisibleTo("umbraco")] [assembly: InternalsVisibleTo("Umbraco.Tests")] [assembly: InternalsVisibleTo("Umbraco.Extensions")] diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 9d27e79e21..11f113ab1e 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -313,7 +313,6 @@ - From 7cbfe01c0bb6b9422eb5c5dd9139c0e268382d1e Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 13 Dec 2016 10:58:24 +0100 Subject: [PATCH 065/599] use umbDateTimePicker component in query builder --- .../src/less/components/umb-querybuilder.less | 4 +++ .../querybuilder/querybuilder.controller.js | 27 +++++++++++++++---- .../overlays/querybuilder/querybuilder.html | 13 +++++++-- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less index fb6279608e..7c983760e1 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less @@ -23,4 +23,8 @@ .umb-querybuilder .row > div { padding: 20px 0; border-bottom: 1px solid @grayLighter; +} + +.umb-querybuilder .datepicker input { + width: 90px; } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js index bc5bc10e13..5166de586e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function QueryBuilderOverlayController($scope, templateQueryResource) { + function QueryBuilderOverlayController($scope, $element, templateQueryResource, assetsService, angularHelper) { var vm = this; @@ -9,6 +9,18 @@ vm.contentTypes = []; vm.conditions = []; + vm.datePickerConfig = { + pickDate: true, + pickTime: false, + format: "YYYY-MM-DD", + icons: { + time: "icon-time", + date: "icon-calendar", + up: "icon-chevron-up", + down: "icon-chevron-down" + } + }; + vm.query = { contentType: { name: "Everything" @@ -41,6 +53,7 @@ vm.setFilterProperty = setFilterProperty; vm.setFilterTerm = setFilterTerm; vm.changeConstraintValue = changeConstraintValue; + vm.datePickerChange = datePickerChange; function onInit() { @@ -58,7 +71,7 @@ .then(function (conditions) { vm.conditions = conditions; }); - + throttledFunc(); } @@ -134,12 +147,10 @@ function setFilterProperty(filter, property) { filter.property = property; - throttledFunc(); } function setFilterTerm(filter, term) { filter.term = term; - throttledFunc(); } function changeConstraintValue() { @@ -154,7 +165,13 @@ }); }, 200); - + + function datePickerChange(event) { + templateQueryResource.postTemplateQuery(vm.query) + .then(function (response) { + $scope.model.result = response; + }); + } onInit(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html index 40fb909390..997855cd3c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html @@ -66,8 +66,17 @@ - - + + + + + + + + From df103580883890faddf5b9ff44604ee1b5ff1ecb Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 13 Dec 2016 11:53:41 +0100 Subject: [PATCH 066/599] Fix weird looking save and publish button group in content section --- .../src/less/components/buttons/umb-button-group.less | 2 +- src/Umbraco.Web.UI.Client/src/views/templates/edit.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button-group.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button-group.less index 119077d52b..4d5fd90fad 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button-group.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button-group.less @@ -10,7 +10,7 @@ } // hack for umb-era-button -.umb-button-group { +.umb-era-button-group { display: flex; diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index 83f3d3954d..d2dd2fdd0e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -32,7 +32,7 @@
    -
    /// The version comment. - public static string CurrentComment { get { return "alpha036"; } } + public static string CurrentComment { get { return "alpha037"; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx From d83d23803a4a9c3d687545e2c7e3970e5132d04b Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 28 Dec 2016 19:11:45 +1100 Subject: [PATCH 111/599] fixes build --- src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs index 7d5113a284..cd08825e2d 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationBase.cs @@ -23,7 +23,7 @@ namespace Umbraco.Core.Persistence.Migrations Logger = logger; } - public IMigrationContext Context { get; private set; } + public IMigrationContext Context { get; internal set; } public abstract void Up(); public abstract void Down(); From b4ca04596ce685cff88e4a68d7f99f4ec76d438a Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 28 Dec 2016 19:21:00 +1100 Subject: [PATCH 112/599] U4-9319 Update to latest ImageProcessor v2.5.0 and ImageProcessor.Web v4.7.0 --- src/Umbraco.Core/Umbraco.Core.csproj | 6 +++--- src/Umbraco.Core/packages.config | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 20 ++++++++++++-------- src/Umbraco.Web.UI/packages.config | 5 +++-- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index e0eb63b265..4d9b35bc98 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -21,7 +21,7 @@ full false bin\Debug\ - DEBUG;TRACE + TRACE;DEBUG prompt 4 false @@ -52,8 +52,8 @@ ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll - - ..\packages\ImageProcessor.2.4.5.0\lib\net45\ImageProcessor.dll + + ..\packages\ImageProcessor.2.5.0.0\lib\net45\ImageProcessor.dll True diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config index 35d78437e7..0e394ae2c9 100644 --- a/src/Umbraco.Core/packages.config +++ b/src/Umbraco.Core/packages.config @@ -2,7 +2,7 @@ - + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 6c46ff0f80..38c333528c 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -41,9 +41,9 @@ true 44319 - - - + enabled + disabled + false ..\ true true @@ -56,7 +56,7 @@ false - DEBUG;TRACE + TRACE;DEBUG true @@ -135,12 +135,12 @@ False ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll - - ..\packages\ImageProcessor.2.4.5.0\lib\net45\ImageProcessor.dll + + ..\packages\ImageProcessor.2.5.0.0\lib\net45\ImageProcessor.dll True - - ..\packages\ImageProcessor.Web.4.6.6.0\lib\net45\ImageProcessor.Web.dll + + ..\packages\ImageProcessor.Web.4.7.0.0\lib\net45\ImageProcessor.Web.dll True @@ -168,6 +168,10 @@ True + + ..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll + True + ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll True diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 008e5e404c..e363460949 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -5,8 +5,8 @@ - - + + @@ -21,6 +21,7 @@ + From 59f39fd535dd70f153c23e086d701287d954a668 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 28 Dec 2016 19:48:35 +1100 Subject: [PATCH 113/599] Ensures we remove the new DLL with the install script like other files --- build/NuSpecs/tools/install.core.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/NuSpecs/tools/install.core.ps1 b/build/NuSpecs/tools/install.core.ps1 index c4f213ba01..e2230e0c32 100644 --- a/build/NuSpecs/tools/install.core.ps1 +++ b/build/NuSpecs/tools/install.core.ps1 @@ -71,6 +71,7 @@ if ($project) { if(Test-Path $umbracoBinFolder\ImageProcessor.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.dll -Force -Confirm:$false } if(Test-Path $umbracoBinFolder\ImageProcessor.Web.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.Web.dll -Force -Confirm:$false } if(Test-Path $umbracoBinFolder\Lucene.Net.dll) { Remove-Item $umbracoBinFolder\Lucene.Net.dll -Force -Confirm:$false } + if(Test-Path $umbracoBinFolder\Microsoft.IO.RecyclableMemoryStream.dll) { Remove-Item $umbracoBinFolder\Microsoft.IO.RecyclableMemoryStream.dll -Force -Confirm:$false } if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll -Force -Confirm:$false } if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll -Force -Confirm:$false } if(Test-Path $umbracoBinFolder\Microsoft.CodeAnalysis.CSharp.dll) { Remove-Item $umbracoBinFolder\Microsoft.CodeAnalysis.CSharp.dll -Force -Confirm:$false } From dea355388568e68b694162115adb89c5cfb7a09b Mon Sep 17 00:00:00 2001 From: mikkelhm Date: Wed, 28 Dec 2016 14:04:40 +0100 Subject: [PATCH 114/599] Change back to using CurrentPage.Site() when queuing with the Query Builder, as Model.Content.Site(), doesnt support the queries the Query Builder creates --- src/Umbraco.Web/Editors/TemplateQueryController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs index a68c3bb9e0..487e5573bf 100644 --- a/src/Umbraco.Web/Editors/TemplateQueryController.cs +++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs @@ -65,7 +65,7 @@ namespace Umbraco.Web.Editors var sb = new StringBuilder(); - sb.Append("Model.Content.Site()"); + sb.Append("CurrentPage.Site()"); var timer = new Stopwatch(); From 19f7e6bd88749f7a6ab38885c878e793eeaf07a4 Mon Sep 17 00:00:00 2001 From: mikkelhm Date: Wed, 28 Dec 2016 14:07:22 +0100 Subject: [PATCH 115/599] When Querying we need to use the Alias to search for, in stead of the name, this is due to names like "Created Date" and "Last Updated Date", who has aliases like "CreateDate", and "UpdateDate" --- src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs index 284518bf1e..8642a65a2e 100644 --- a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs +++ b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs @@ -13,7 +13,7 @@ { private static string MakeBinaryOperation(this QueryCondition condition, string operand, int token) { - return string.Format("{0}{1}@{2}", condition.Property.Name, operand, token); + return string.Format("{0}{1}@{2}", condition.Property.Alias, operand, token); } From 4a547ba1c98355b0d0eb9ff184119e499189eefb Mon Sep 17 00:00:00 2001 From: Marc Goodson Date: Wed, 28 Dec 2016 15:11:10 +0000 Subject: [PATCH 116/599] Add some green to 'stay' option - U4-9323 Closes U4-9323 http://issues.umbraco.org/issue/U4-9323 --- .../src/views/common/notifications/confirmroutechange.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/notifications/confirmroutechange.html b/src/Umbraco.Web.UI.Client/src/views/common/notifications/confirmroutechange.html index 0b2926307c..a772aa0159 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/notifications/confirmroutechange.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/notifications/confirmroutechange.html @@ -3,5 +3,5 @@

    Are you sure you want to navigate away from this page? - you have unsaved changes

    - - \ No newline at end of file + + From 40f7d820a624d403b7ee885c2dd7ed873d86a59f Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 29 Dec 2016 11:36:39 +1100 Subject: [PATCH 117/599] publicizes a few APIs and adds more API docs --- .../Models/Editors/ContentPropertyData.cs | 8 +++++ .../Repositories/VersionableRepositoryBase.cs | 4 +-- .../PropertyEditors/TagValueType.cs | 3 +- src/Umbraco.Core/Services/TagExtractor.cs | 35 +++++++++++++++---- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs b/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs index 14c8640fc9..3798cb08e0 100644 --- a/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs +++ b/src/Umbraco.Core/Models/Editors/ContentPropertyData.cs @@ -16,6 +16,11 @@ namespace Umbraco.Core.Models.Editors /// public class ContentPropertyData { + public ContentPropertyData(object value, PreValueCollection preValues) + : this(value, preValues, new Dictionary()) + { + } + public ContentPropertyData(object value, PreValueCollection preValues, IDictionary additionalData) { Value = value; @@ -28,6 +33,9 @@ namespace Umbraco.Core.Models.Editors ///
    public object Value { get; private set; } + /// + /// The pre-value collection for the content property + /// public PreValueCollection PreValues { get; private set; } /// diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index ded026bbec..163e0810ad 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -561,9 +561,7 @@ WHERE EXISTS( var preVals = new PreValueCollection(asDictionary); - var contentPropData = new ContentPropertyData(property.Value, - preVals, - new Dictionary()); + var contentPropData = new ContentPropertyData(property.Value, preVals); TagExtractor.SetPropertyTags(property, contentPropData, property.Value, tagSupport); } diff --git a/src/Umbraco.Core/PropertyEditors/TagValueType.cs b/src/Umbraco.Core/PropertyEditors/TagValueType.cs index e5f65933ab..e837d1e8bf 100644 --- a/src/Umbraco.Core/PropertyEditors/TagValueType.cs +++ b/src/Umbraco.Core/PropertyEditors/TagValueType.cs @@ -14,7 +14,8 @@ /// The list of tags will be supplied by the property editor's ConvertEditorToDb method result which will need to return an IEnumerable{string} value /// /// - /// if the ConvertEditorToDb doesn't return an IEnumerable{string} then an exception will be thrown. + /// if the ConvertEditorToDb doesn't return an IEnumerable{string} then it will automatically try to be detected as either CSV or JSON and if neither of those match + /// an exception will be thrown. /// CustomTagList } diff --git a/src/Umbraco.Core/Services/TagExtractor.cs b/src/Umbraco.Core/Services/TagExtractor.cs index 0e6d605e41..aa88a9a1aa 100644 --- a/src/Umbraco.Core/Services/TagExtractor.cs +++ b/src/Umbraco.Core/Services/TagExtractor.cs @@ -9,10 +9,15 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Core.Services { /// - /// A simple utility class to extract the tag values from a property/property editor and set them on the content + /// A utility class to extract the tag values from a property/property editor and set them on the content /// - internal class TagExtractor + public class TagExtractor { + /// + /// Will return the for the given + /// + /// + /// public static SupportTagsAttribute GetAttribute(PropertyEditor propEd) { if (propEd == null) return null; @@ -23,9 +28,12 @@ namespace Umbraco.Core.Services /// /// Sets the tag values on the content property based on the property editor's tags attribute /// - /// - /// - /// + /// The content's Property + /// The data that has been submitted to be saved for a content property + /// + /// If the is then this is expected to be a delimited string, + /// otherwise if it is then this is expected to be IEnumerable{string} + /// /// public static void SetPropertyTags(Property property, ContentPropertyData propertyData, object convertedPropertyValue, SupportTagsAttribute attribute) { @@ -51,7 +59,20 @@ namespace Umbraco.Core.Services } } - public static void SetPropertyTags(Property property, object convertedPropertyValue, string delimiter, bool replaceTags, string tagGroup, TagValueType valueType, TagCacheStorageType storageType) + /// + /// Sets the tag values on the content property based on the property editor's tags attribute + /// + /// The content's Property + /// + /// If the is then this is expected to be a delimited string, + /// otherwise if it is then this is expected to be IEnumerable{string} + /// + /// The delimiter for the value if convertedPropertyValue is a string delimited value + /// Whether or not to replace the tags with the new value or append them (true to replace, false to append) + /// The tag group to use when tagging + /// Defines how the tag values will be extracted + /// Defines how to store the tags in cache (CSV or Json) + internal static void SetPropertyTags(Property property, object convertedPropertyValue, string delimiter, bool replaceTags, string tagGroup, TagValueType valueType, TagCacheStorageType storageType) { if (convertedPropertyValue == null) { @@ -65,7 +86,7 @@ namespace Umbraco.Core.Services property.SetTags(storageType, property.Alias, tags, replaceTags, tagGroup); break; case TagValueType.CustomTagList: - //for this to work the object value must be IENumerable + //for this to work the object value must be IEnumerable var stringList = convertedPropertyValue as IEnumerable; if (stringList != null) { From 1f52a0c1868791955658e7a5eeb8a7bf1a2cadd3 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 29 Dec 2016 01:50:43 +0100 Subject: [PATCH 118/599] Changes the querybuilder to use the correct Where syntax --- .../Editors/TemplateQueryController.cs | 37 ++++++++++++------- .../Models/TemplateQuery/QueryCondition.cs | 28 +++++++++++--- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs index a68c3bb9e0..2720847890 100644 --- a/src/Umbraco.Web/Editors/TemplateQueryController.cs +++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs @@ -64,7 +64,8 @@ namespace Umbraco.Web.Editors var queryResult = new QueryResultModel(); var sb = new StringBuilder(); - + var indention = Environment.NewLine + "\t\t\t\t\t\t"; + sb.Append("Model.Content.Site()"); var timer = new Stopwatch(); @@ -129,7 +130,9 @@ namespace Umbraco.Web.Editors sb.Append(".Children"); } + //setup 2 clauses, 1 for returning, 1 for testing var clause = string.Empty; + var tokenizedClause = string.Empty; // WHERE var token = 0; @@ -141,12 +144,13 @@ namespace Umbraco.Web.Editors foreach (var condition in model.Filters) { if(string.IsNullOrEmpty( condition.ConstraintValue)) continue; - - - - var operation = condition.BuildCondition(token); + + //x is passed in as the parameter alias for the linq where statement clause + var operation = condition.BuildCondition("x"); + var tokenizedOperation = condition.BuildTokenizedCondition(token); clause = string.IsNullOrEmpty(clause) ? operation : string.Concat(new[] { clause, " && ", operation }); + tokenizedClause = string.IsNullOrEmpty(tokenizedClause) ? tokenizedOperation : string.Concat(new[] { tokenizedClause, " && ", tokenizedOperation }); token++; } @@ -156,19 +160,21 @@ namespace Umbraco.Web.Editors timer.Start(); - //clause = "Visible && " + clause; - - contents = contents.AsQueryable().Where(clause, model.Filters.Select(this.GetConstraintValue).ToArray()); - // contents = contents.Where(clause, values.ToArray()); + //trial-run the tokenized clause to time the execution + //for review - this uses a tonized query rather then the normal linq query. + contents = contents.AsQueryable().Where(tokenizedClause, model.Filters.Select(this.GetConstraintValue).ToArray()); contents = contents.Where(x => x.IsVisible()); timer.Stop(); - clause = string.Format("\"Visible && {0}\",{1}", clause, - string.Join(",", model.Filters.Select(x => x.Property.Type == "string" ? - string.Format("\"{0}\"", x.ConstraintValue) : x.ConstraintValue).ToArray())); + + //the query to output to the editor + sb.Append(indention); + sb.Append(".Where(x => x.IsVisible())"); + + sb.Append(indention); + sb.AppendFormat(".Where(x => {0})", clause); - sb.AppendFormat(".Where({0})", clause); } else { @@ -178,7 +184,8 @@ namespace Umbraco.Web.Editors timer.Stop(); - sb.Append(".Where(\"Visible\")"); + sb.Append(indention); + sb.Append(".Where(x => x.IsVisible())"); } @@ -192,6 +199,7 @@ namespace Umbraco.Web.Editors var direction = model.Sort.Direction == "ascending" ? string.Empty : " desc"; + sb.Append(indention); sb.AppendFormat(".OrderBy(\"{0}{1}\")", model.Sort.Property.Alias, direction); } @@ -203,6 +211,7 @@ namespace Umbraco.Web.Editors timer.Stop(); + sb.Append(indention); sb.AppendFormat(".Take({0})", model.Take); } } diff --git a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs index 284518bf1e..a1047b8f19 100644 --- a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs +++ b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs @@ -11,16 +11,29 @@ internal static class QueryConditionExtensions { - private static string MakeBinaryOperation(this QueryCondition condition, string operand, int token) + + public static string BuildTokenizedCondition(this QueryCondition condition, int token) { - return string.Format("{0}{1}@{2}", condition.Property.Name, operand, token); + return condition.BuildConditionString(string.Empty, token); } + public static string BuildCondition(this QueryCondition condition, string parameterAlias) + { + return condition.BuildConditionString(parameterAlias + "."); + } - public static string BuildCondition(this QueryCondition condition, int token) + private static string BuildConditionString(this QueryCondition condition, string prefix, int token = -1) { var operand = string.Empty; var value = string.Empty; + var constraintValue = string.Empty; + + //if a token is used, use a token placeholder, otherwise, use the actual value + if(token >= 0){ + constraintValue = string.Format("@{0}", token); + }else { + constraintValue = condition.Property.Type == "string" ? string.Format("\"{0}\"", condition.ConstraintValue) : condition.ConstraintValue; + } switch (condition.Term.Operathor) { @@ -43,17 +56,20 @@ operand = " <= "; break; case Operathor.Contains: - value = string.Format("{0}.Contains(@{1})", condition.Property.Name, token); + value = string.Format("{0}{1}.Contains({2})", prefix, condition.Property.Name, constraintValue); break; case Operathor.NotContains: - value = string.Format("!{0}.Contains(@{1})", condition.Property.Name, token); + value = string.Format("!{0}{1}.Contains({2})", prefix, condition.Property.Name, constraintValue); break; default : operand = " == "; break; } - return string.IsNullOrEmpty(value) ? condition.MakeBinaryOperation(operand, token) : value; + if (string.IsNullOrEmpty(value) == false) + return value; + + return string.Format("{0}{1}{2}{3}", prefix, condition.Property.Name, operand, constraintValue); } } From ffefe417db4e8b603c7861400971db4362a01d1c Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 29 Dec 2016 12:23:30 +1100 Subject: [PATCH 119/599] bumps version --- build/UmbracoVersion.txt | 2 +- src/SolutionInfo.cs | 2 +- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index 63a21fc928..a38b20dea5 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,3 +1,3 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) 7.6.0 -alpha037 \ No newline at end of file +alpha038 \ No newline at end of file diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index a8f9b7d945..ad8b2bfa02 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -12,4 +12,4 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyFileVersion("7.6.0")] -[assembly: AssemblyInformationalVersion("7.6.0-alpha037")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("7.6.0-alpha038")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 5368c3ba6c..d06672172d 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration /// Gets the version comment (like beta or RC). ///
    /// The version comment. - public static string CurrentComment { get { return "alpha037"; } } + public static string CurrentComment { get { return "alpha038"; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx From 436875e8ec08a4e1d6ea7d092d8fb213e099ad96 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 29 Dec 2016 14:02:15 +1100 Subject: [PATCH 120/599] fixes null check with setting master template --- src/Umbraco.Web/Editors/TemplateController.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs index 633e4611e1..e937024250 100644 --- a/src/Umbraco.Web/Editors/TemplateController.cs +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -98,11 +98,13 @@ namespace Umbraco.Web.Editors if (changeMaster) { - ITemplate master = null; if (string.IsNullOrEmpty(display.MasterTemplateAlias) == false) { - master = Services.FileService.GetTemplate(display.MasterTemplateAlias); - template.SetMasterTemplate(master); + var master = Services.FileService.GetTemplate(display.MasterTemplateAlias); + if (master != null) + { + template.SetMasterTemplate(master); + } } } From 61f25edafcf6efc6e6dda2e61b41f598fd437890 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 29 Dec 2016 13:50:11 +0100 Subject: [PATCH 121/599] sync tree --- .../src/views/templates/edit.controller.js | 47 +++++++++++++++++-- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index ae1560bffb..f236244f89 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -1,9 +1,11 @@ (function () { "use strict"; - function TemplatesEditController($scope, $routeParams, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService) { + function TemplatesEditController($scope, $routeParams, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, treeService) { var vm = this; + var oldMasterTemplateAlias = null; + vm.page = {}; vm.page.loading = true; vm.templates = []; @@ -20,16 +22,48 @@ vm.template.content = vm.editor.getValue(); templateResource.save(vm.template).then(function (saved) { - + notificationsService.success("Template saved"); vm.page.saveButtonState = "success"; vm.template = saved; //sync state editorState.set(vm.template); - navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { - vm.page.menu.currentNode = syncArgs.node; - }); + + // sync tree + // if master template alias has changed move the node to it's new location + if(oldMasterTemplateAlias !== vm.template.masterTemplateAlias) { + + // move node to new location in tree + //first we need to remove the node that launched the dialog + treeService.removeNode(vm.page.menu.currentNode); + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + // update stored alias to the new one so the node won't move again unless the alias is changed again + oldMasterTemplateAlias = vm.template.masterTemplateAlias; + + //we need to do a double sync here: first sync to the moved content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was moved!!) + navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "templates", path: activeNodePath, forceReload: false, activate: true }).then(function(syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + } + }); + + } else { + + // normal tree sync + navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + + } }, function (err) { notificationsService.error("Template save failed"); @@ -75,6 +109,9 @@ vm.page.menu.currentNode = syncArgs.node; }); + // save state of master template to use for comparison when syncing the tree on save + oldMasterTemplateAlias = angular.copy(template.masterTemplateAlias); + vm.aceOption = { mode: "razor", theme: "chrome", From 82efa14b7af459f85147a170c29a48e49c43dd55 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 29 Dec 2016 13:50:57 +0100 Subject: [PATCH 122/599] add UI to remove master template --- .../src/views/templates/edit.controller.js | 10 +++++++++ .../src/views/templates/edit.html | 21 +++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index f236244f89..1614e90b89 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -143,6 +143,7 @@ vm.openMasterTemplateOverlay = openMasterTemplateOverlay; vm.selectMasterTemplate = selectMasterTemplate; vm.getMasterTemplateName = getMasterTemplateName; + vm.removeMasterTemplate = removeMasterTemplate; function openInsertOverlay() { @@ -403,6 +404,15 @@ } + function removeMasterTemplate() { + + vm.template.masterTemplateAlias = null; + + // call set layout with no paramters to set layout to null + setLayout(); + + } + function setLayout(templatePath){ var templateCode = vm.editor.getValue(); diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index d2dd2fdd0e..e03f593c90 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -25,10 +25,23 @@
    - - - Master template: {{ vm.getMasterTemplateName(vm.template.masterTemplateAlias, vm.templates) }} - + +
    + + + + + + + +
    +
    From bd448ef05aa135e25b911b8ed43383310ce1fe07 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 30 Dec 2016 11:27:55 +1100 Subject: [PATCH 123/599] U4-9326 Removing a master template from a Template does not update the Template's path correctly --- .../Repositories/TemplateRepository.cs | 7 ++++- .../Repositories/TemplateRepositoryTest.cs | 29 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index 2e93c7c678..620ad10e7d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -219,7 +219,12 @@ namespace Umbraco.Core.Persistence.Repositories { entity.Path = string.Concat(parent.Path, ",", entity.Id); } - + else + { + //this means that the master template has been removed, so we need to reset the template's + //path to be at the root + entity.Path = string.Concat("-1,", entity.Id); + } } //Get TemplateDto from db to get the Primary key of the entity diff --git a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs index 36ae69c733..1a4a7a2338 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs @@ -707,6 +707,35 @@ namespace Umbraco.Tests.Persistence.Repositories } } + [Test] + public void Path_Is_Set_Correctly_On_Update_With_Master_Template_Removal() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var parent = new Template("parent", "parent"); + var child1 = new Template("child1", "child1"); + + child1.MasterTemplateAlias = parent.Alias; + child1.MasterTemplateId = new Lazy(() => parent.Id); + + repository.AddOrUpdate(parent); + repository.AddOrUpdate(child1); + unitOfWork.Commit(); + + //Act + child1.SetMasterTemplate(null); + repository.AddOrUpdate(child1); + unitOfWork.Commit(); + + //Assert + Assert.AreEqual(string.Format("-1,{0}", child1.Id), child1.Path); + + } + } + [TearDown] public override void TearDown() From 3e82a48f4dfb166dcb1b5e5dd99fc6fb840f514e Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 30 Dec 2016 11:38:27 +1100 Subject: [PATCH 124/599] Fixes tree syncing and fixes server side code to reset the master template if it's cleared --- .../views/content/content.move.controller.js | 2 +- .../src/views/templates/edit.controller.js | 22 +++++-------------- src/Umbraco.Web/Editors/TemplateController.cs | 5 +++++ 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js index 75969730a0..c6de405ff6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.move.controller.js @@ -106,7 +106,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController", var activeNode = appState.getTreeState("selectedNode"); //we need to do a double sync here: first sync to the moved content - but don't activate the node, - //then sync to the currenlty edited content (note: this might not be the content that was moved!!) + //then sync to the currently edited content (note: this might not be the content that was moved!!) navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) { if (activeNode) { diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 1614e90b89..a029b0ed12 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -14,8 +14,7 @@ vm.page.menu = {}; vm.page.menu.currentSection = appState.getSectionState("currentSection"); vm.page.menu.currentNode = null; - - + vm.save = function () { vm.page.saveButtonState = "busy"; @@ -35,25 +34,14 @@ if(oldMasterTemplateAlias !== vm.template.masterTemplateAlias) { // move node to new location in tree - //first we need to remove the node that launched the dialog + //first we need to remove the node that we're working on treeService.removeNode(vm.page.menu.currentNode); - - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - + // update stored alias to the new one so the node won't move again unless the alias is changed again oldMasterTemplateAlias = vm.template.masterTemplateAlias; - //we need to do a double sync here: first sync to the moved content - but don't activate the node, - //then sync to the currenlty edited content (note: this might not be the content that was moved!!) - navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true, activate: false }).then(function (args) { - if (activeNode) { - var activeNodePath = treeService.getPath(activeNode).join(); - //sync to this node now - depending on what was copied this might already be synced but might not be - navigationService.syncTree({ tree: "templates", path: activeNodePath, forceReload: false, activate: true }).then(function(syncArgs) { - vm.page.menu.currentNode = syncArgs.node; - }); - } + navigationService.syncTree({ tree: "templates", path: vm.template.path, forceReload: true, activate: true }).then(function (args) { + vm.page.menu.currentNode = args.node; }); } else { diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs index e937024250..eec76ca34e 100644 --- a/src/Umbraco.Web/Editors/TemplateController.cs +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -106,6 +106,11 @@ namespace Umbraco.Web.Editors template.SetMasterTemplate(master); } } + else + { + //remove the master + template.SetMasterTemplate(null); + } } Services.FileService.SaveTemplate(template); From 98430fd5734e718991b04d2d9c02f00a9a9fb156 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 30 Dec 2016 11:42:02 +1100 Subject: [PATCH 125/599] U4-9326 Removing a master template from a Template does not update the Template's path correctly --- .../Repositories/TemplateRepository.cs | 7 ++++- .../Repositories/TemplateRepositoryTest.cs | 29 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index fa780e1bd0..de52ce643b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -241,7 +241,12 @@ namespace Umbraco.Core.Persistence.Repositories { entity.Path = string.Concat(parent.Path, ",", entity.Id); } - + else + { + //this means that the master template has been removed, so we need to reset the template's + //path to be at the root + entity.Path = string.Concat("-1,", entity.Id); + } } //Get TemplateDto from db to get the Primary key of the entity diff --git a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs index 36ae69c733..1a4a7a2338 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs @@ -707,6 +707,35 @@ namespace Umbraco.Tests.Persistence.Repositories } } + [Test] + public void Path_Is_Set_Correctly_On_Update_With_Master_Template_Removal() + { + // Arrange + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + using (var repository = CreateRepository(unitOfWork)) + { + var parent = new Template("parent", "parent"); + var child1 = new Template("child1", "child1"); + + child1.MasterTemplateAlias = parent.Alias; + child1.MasterTemplateId = new Lazy(() => parent.Id); + + repository.AddOrUpdate(parent); + repository.AddOrUpdate(child1); + unitOfWork.Commit(); + + //Act + child1.SetMasterTemplate(null); + repository.AddOrUpdate(child1); + unitOfWork.Commit(); + + //Assert + Assert.AreEqual(string.Format("-1,{0}", child1.Id), child1.Path); + + } + } + [TearDown] public override void TearDown() From 0832220d6131625583446f96fe3368fd4ff431ba Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 30 Dec 2016 12:07:03 +1100 Subject: [PATCH 126/599] publicizes some APIs needed for Deploy --- src/Umbraco.Core/ObjectExtensions.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs index 6ed424c232..fcdc2f52a6 100644 --- a/src/Umbraco.Core/ObjectExtensions.cs +++ b/src/Umbraco.Core/ObjectExtensions.cs @@ -468,19 +468,19 @@ namespace Umbraco.Core /// /// /// - internal static IDictionary ToDictionary(this T o, + public static IDictionary ToDictionary(this T o, params Expression>[] ignoreProperties) { return o.ToDictionary(ignoreProperties.Select(e => o.GetPropertyInfo(e)).Select(propInfo => propInfo.Name).ToArray()); } - /// - /// Turns object into dictionary - /// - /// - /// Properties to ignore - /// - internal static IDictionary ToDictionary(this object o, params string[] ignoreProperties) + /// + /// Turns object into dictionary + /// + /// + /// Properties to ignore + /// + public static IDictionary ToDictionary(this object o, params string[] ignoreProperties) { if (o != null) { From ee925692e5734bb29de02bf49195b640a5fd2fba Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 30 Dec 2016 16:06:40 +1100 Subject: [PATCH 127/599] Bumps version to put on MyGet --- build/UmbracoVersion.txt | 2 +- src/SolutionInfo.cs | 2 +- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index a38b20dea5..c017a7b2f8 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,3 +1,3 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) 7.6.0 -alpha038 \ No newline at end of file +alpha039 \ No newline at end of file diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index ad8b2bfa02..386986164d 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -12,4 +12,4 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyFileVersion("7.6.0")] -[assembly: AssemblyInformationalVersion("7.6.0-alpha038")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("7.6.0-alpha039")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index d06672172d..8d300790f0 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration /// Gets the version comment (like beta or RC). /// /// The version comment. - public static string CurrentComment { get { return "alpha038"; } } + public static string CurrentComment { get { return "alpha039"; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx From dece68022136e64c64b32bda0f4b0124271dfa05 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 30 Dec 2016 11:36:20 +0100 Subject: [PATCH 128/599] =?UTF-8?q?Fixes:=20U4-9328=20UI:=20It=20shouldn?= =?UTF-8?q?=E2=80=99t=20be=20possible=20to=20select=20the=20current=20temp?= =?UTF-8?q?late=20or=20the=20selected=20master=20template=20in=20the=20mas?= =?UTF-8?q?ter=20template=20picker.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/templates/edit.controller.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index a029b0ed12..327da032b8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -332,10 +332,20 @@ function openMasterTemplateOverlay() { + // make collection of available master templates + var availableMasterTemplates = []; + + // filter out the current template and the selected master template + angular.forEach(vm.templates, function(template){ + if(template.alias !== vm.template.alias && template.alias !== vm.template.masterTemplateAlias) { + availableMasterTemplates.push(template); + } + }); + vm.masterTemplateOverlay = { view: "itempicker", title: "Choose master template", - availableItems: vm.templates, + availableItems: availableMasterTemplates, show: true, submit: function(model) { From 5146658e0f177c9c501312750d5e199c411e1e3c Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 2 Jan 2017 13:03:15 +0100 Subject: [PATCH 129/599] fixes: U4-9325 New template editor stays in a $dirty state if you change and then save --- .../src/views/templates/edit.controller.js | 29 ++++++++++++++++++- .../template-editor-controller.spec.js | 10 ++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index a029b0ed12..3d5c72983f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function TemplatesEditController($scope, $routeParams, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, treeService) { + function TemplatesEditController($scope, $routeParams, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, treeService, angularHelper) { var vm = this; var oldMasterTemplateAlias = null; @@ -53,6 +53,10 @@ } + // clear $dirty state on form + setFormState("pristine"); + + }, function (err) { notificationsService.error("Template save failed"); vm.page.saveButtonState = "error"; @@ -426,6 +430,10 @@ vm.editor.setValue(templateCode); vm.editor.clearSelection(); vm.editor.navigateFileStart(); + + // set form state to $dirty + setFormState("dirty"); + } @@ -433,6 +441,9 @@ vm.editor.moveCursorToPosition(vm.currentPosition); vm.editor.insert(str); vm.editor.focus(); + + // set form state to $dirty + setFormState("dirty"); } function wrap(str) { @@ -441,12 +452,28 @@ str = str.replace("{0}", selectedContent); vm.editor.insert(str); vm.editor.focus(); + + // set form state to $dirty + setFormState("dirty"); } function persistCurrentLocation() { vm.currentPosition = vm.editor.getCursorPosition(); } + function setFormState(state) { + + // get the current form + var currentForm = angularHelper.getCurrentForm($scope); + + // set state + if(state === "dirty") { + currentForm.$setDirty(); + } else if(state === "pristine") { + currentForm.$setPristine(); + } + } + vm.init(); } diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js index 55d91c35a6..aae0694876 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js @@ -88,7 +88,15 @@ appState: { getSectionState : function() { return {}; } }, - macroService: {} + macroService: {}, + angularHelper: { + getCurrentForm: function() { + return { + $setDirty: function() {}, + $setPristine: function() {} + } + } + } }); } From 1b8c68af0219bffe6b4aa8237c25e07669c0045b Mon Sep 17 00:00:00 2001 From: Stephan Lonntorp Date: Mon, 2 Jan 2017 14:53:57 +0100 Subject: [PATCH 130/599] Fixes issue U4-9332 U4-9332 Language of DefaultCultureDictionary is cached without the current Culture identifier --- src/Umbraco.Web/Dictionary/UmbracoCultureDictionary.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Dictionary/UmbracoCultureDictionary.cs b/src/Umbraco.Web/Dictionary/UmbracoCultureDictionary.cs index fdb4145160..cb53713e3c 100644 --- a/src/Umbraco.Web/Dictionary/UmbracoCultureDictionary.cs +++ b/src/Umbraco.Web/Dictionary/UmbracoCultureDictionary.cs @@ -137,7 +137,7 @@ namespace Umbraco.Web.Dictionary { //ensure it's stored/retrieved from request cache //NOTE: This is no longer necessary since these are cached at the runtime level, but we can leave it here for now. - return _requestCacheProvider.GetCacheItem(typeof (DefaultCultureDictionary).Name + "Culture", + return _requestCacheProvider.GetCacheItem(typeof (DefaultCultureDictionary).Name + "Culture" + Culture.Name, () => _localizationService.GetLanguageByIsoCode(Culture.Name)); } } From afbceca184ac0ed919d24ef3631153029deb0855 Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Mon, 2 Jan 2017 17:29:37 +0100 Subject: [PATCH 131/599] Makes EXIF data in resized images default --- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 3 + .../config/imageprocessor/cache.config | 10 +++ .../config/imageprocessor/processing.config | 61 +++++++++++++++++++ .../config/imageprocessor/security.config | 24 ++++++++ src/Umbraco.Web.UI/packages.config | 1 + src/Umbraco.Web.UI/web.Template.config | 13 +++- 6 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Web.UI/config/imageprocessor/cache.config create mode 100644 src/Umbraco.Web.UI/config/imageprocessor/processing.config create mode 100644 src/Umbraco.Web.UI/config/imageprocessor/security.config diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 6c46ff0f80..ef27b4ef79 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -651,6 +651,9 @@ BaseRestExtensions.config + + + log4net.config diff --git a/src/Umbraco.Web.UI/config/imageprocessor/cache.config b/src/Umbraco.Web.UI/config/imageprocessor/cache.config new file mode 100644 index 0000000000..a8fd567d6c --- /dev/null +++ b/src/Umbraco.Web.UI/config/imageprocessor/cache.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/imageprocessor/processing.config b/src/Umbraco.Web.UI/config/imageprocessor/processing.config new file mode 100644 index 0000000000..2ce29a542a --- /dev/null +++ b/src/Umbraco.Web.UI/config/imageprocessor/processing.config @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/imageprocessor/security.config b/src/Umbraco.Web.UI/config/imageprocessor/security.config new file mode 100644 index 0000000000..7bce8ee7b5 --- /dev/null +++ b/src/Umbraco.Web.UI/config/imageprocessor/security.config @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 008e5e404c..cc1fb8a78e 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -7,6 +7,7 @@ + diff --git a/src/Umbraco.Web.UI/web.Template.config b/src/Umbraco.Web.UI/web.Template.config index 09df754e74..10c64797f4 100644 --- a/src/Umbraco.Web.UI/web.Template.config +++ b/src/Umbraco.Web.UI/web.Template.config @@ -14,7 +14,13 @@
    - + + +
    +
    +
    + + @@ -434,4 +440,9 @@ + + + + + From 4e2fb8e5dcf50c01dc9634fafa854c022b0a710a Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 3 Jan 2017 10:36:26 +1100 Subject: [PATCH 132/599] Fixes IP nuget references and fixes up several other Nuget assembly references. --- src/SQLCE4Umbraco/SqlCE4Umbraco.csproj | 4 -- src/Umbraco.Core/Umbraco.Core.csproj | 29 +++--------- src/Umbraco.Core/packages.config | 4 +- .../Umbraco.Tests.Benchmarks.csproj | 38 ++++++--------- src/Umbraco.Tests/Umbraco.Tests.csproj | 31 ++----------- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 3 -- src/Umbraco.Web.UI/packages.config | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 46 ++----------------- src/UmbracoExamine/UmbracoExamine.csproj | 3 -- .../umbraco.MacroEngines.csproj | 16 +------ src/umbraco.businesslogic/packages.config | 2 +- .../umbraco.businesslogic.csproj | 12 +---- src/umbraco.cms/umbraco.cms.csproj | 4 -- src/umbraco.controls/umbraco.controls.csproj | 1 - src/umbraco.datalayer/packages.config | 4 +- .../umbraco.datalayer.csproj | 5 +- .../umbraco.editorControls.csproj | 1 - 17 files changed, 38 insertions(+), 167 deletions(-) diff --git a/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj b/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj index 86796e1dd7..0689a7a7d4 100644 --- a/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj +++ b/src/SQLCE4Umbraco/SqlCE4Umbraco.csproj @@ -49,13 +49,9 @@ ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll - False - False ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll - False - False diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4d9b35bc98..ae6c1ba9be 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -39,67 +39,54 @@ ..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.dll - True ..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll - True - False ..\packages\HtmlAgilityPack.1.4.9\lib\Net45\HtmlAgilityPack.dll - + ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll ..\packages\ImageProcessor.2.5.0.0\lib\net45\ImageProcessor.dll - True - False ..\packages\log4net-mediumtrust.2.0.0\lib\log4net.dll - False ..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll - False ..\packages\Microsoft.AspNet.Identity.Owin.2.2.1\lib\net45\Microsoft.AspNet.Identity.Owin.dll - False ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll - + ..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll - True - False ..\packages\Microsoft.Owin.Security.Cookies.3.0.1\lib\net45\Microsoft.Owin.Security.Cookies.dll - False ..\packages\Microsoft.Owin.Security.OAuth.3.0.1\lib\net45\Microsoft.Owin.Security.OAuth.dll - False ..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll - - False + ..\packages\MySql.Data.6.9.8\lib\net45\MySql.Data.dll ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll True - + ..\packages\Owin.1.0\lib\net40\Owin.dll - + ..\packages\semver.1.1.2\lib\net45\Semver.dll @@ -107,11 +94,9 @@ - False ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll - False ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll @@ -1440,9 +1425,7 @@ - - Designer - + diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config index 0e394ae2c9..78bf2b23b8 100644 --- a/src/Umbraco.Core/packages.config +++ b/src/Umbraco.Core/packages.config @@ -3,7 +3,7 @@ - + @@ -15,6 +15,6 @@ - + \ No newline at end of file diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 697b6a10c6..9f6d4e799f 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -39,31 +39,24 @@ ..\packages\BenchmarkDotNet.0.9.9\lib\net45\BenchmarkDotNet.dll - True ..\packages\BenchmarkDotNet.Core.0.9.9\lib\net45\BenchmarkDotNet.Core.dll - True ..\packages\BenchmarkDotNet.Diagnostics.Windows.0.9.9\lib\net45\BenchmarkDotNet.Diagnostics.Windows.dll - True ..\packages\BenchmarkDotNet.Toolchains.Roslyn.0.9.9\lib\net45\BenchmarkDotNet.Toolchains.Roslyn.dll - True ..\packages\Microsoft.CodeAnalysis.Common.1.3.2\lib\net45\Microsoft.CodeAnalysis.dll - True ..\packages\Microsoft.CodeAnalysis.CSharp.1.3.2\lib\net45\Microsoft.CodeAnalysis.CSharp.dll - True ..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.1.0.41\lib\net40\Microsoft.Diagnostics.Tracing.TraceEvent.dll - True @@ -73,20 +66,16 @@ ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.dll - True ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll - True ..\packages\System.Reflection.Metadata.1.2.0\lib\portable-net45+win8\System.Reflection.Metadata.dll - True ..\packages\System.Threading.Tasks.Extensions.4.0.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll - True @@ -106,10 +95,6 @@ - - - - {31785bc3-256c-4613-b2f5-a1b0bdded8c1} @@ -120,7 +105,22 @@ Umbraco.Tests + + + + + + + if not exist "$(TargetDir)x86" md "$(TargetDir)x86" + xcopy /s /y "$(SolutionDir)packages\Microsoft.SqlServer.Compact.4.0.8854.1\NativeBinaries\x86\*.*" "$(TargetDir)x86" + if not exist "$(TargetDir)amd64" md "$(TargetDir)amd64" + xcopy /s /y "$(SolutionDir)packages\Microsoft.SqlServer.Compact.4.0.8854.1\NativeBinaries\amd64\*.*" "$(TargetDir)amd64" + + + xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\amd64\*.* "$(TargetDir)amd64\" /Y /F /E /I /C /D +xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" /Y /F /E /I /C /D + @@ -128,14 +128,6 @@ - - - - - - xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\amd64\*.* "$(TargetDir)amd64\" /Y /F /E /I /C /D -xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" /Y /F /E /I /C /D - - + - + @@ -24,5 +24,5 @@ More information and documentation can be found on GitHub: https://github.com/Sh - + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.config b/src/Umbraco.Web.UI/config/ExamineSettings.config index aacf5e3732..076fb08493 100644 --- a/src/Umbraco.Web.UI/config/ExamineSettings.config +++ b/src/Umbraco.Web.UI/config/ExamineSettings.config @@ -12,18 +12,15 @@ More information and documentation can be found on GitHub: https://github.com/Sh + analyzer="Lucene.Net.Analysis.WhitespaceAnalyzer, Lucene.Net"/> + analyzer="Lucene.Net.Analysis.Standard.StandardAnalyzer, Lucene.Net"/> - + @@ -31,16 +28,13 @@ More information and documentation can be found on GitHub: https://github.com/Sh + analyzer="Lucene.Net.Analysis.WhitespaceAnalyzer, Lucene.Net"/> - + + enableLeadingWildcard="true"/> From 62f21d3ceec7b85ad7bcc1248f3802644a21b37f Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 3 Jan 2017 09:17:05 +0100 Subject: [PATCH 142/599] Update nuspec to install latest version of ImageProcessor --- build/NuSpecs/UmbracoCms.Core.nuspec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 7c1ff15cc8..5ba212640b 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -33,8 +33,8 @@ - - + + @@ -103,4 +103,4 @@ - \ No newline at end of file + From 56d2230251b57fa849524baeab56eed5c4b61c68 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 3 Jan 2017 09:24:23 +0100 Subject: [PATCH 143/599] Add Microsoft.IO.RecyclableMemoryStream to the nuspec, update to the latest version (1.2.1) --- build/NuSpecs/UmbracoCms.Core.nuspec | 3 ++- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 5 +++-- src/Umbraco.Web.UI/packages.config | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 5ba212640b..113de3970c 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -38,7 +38,8 @@ - + + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index c34b2aeb72..e342f6f886 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -166,8 +166,9 @@ True - - ..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll + + ..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.1\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll + True ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index f3914cef7d..4b80afd79d 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -21,7 +21,7 @@ - + From d8b70d79e8f047e8e14426d0e7020b5b1173dfad Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 3 Jan 2017 09:29:22 +0100 Subject: [PATCH 144/599] Version 7.6.0-alpha044 on MyGet --- build/UmbracoVersion.txt | 4 ++-- src/SolutionInfo.cs | 2 +- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index 4dad972e68..096e4d6b5c 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,3 +1,3 @@ -# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) +# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) 7.6.0 -alpha043 \ No newline at end of file +alpha044 diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 8eb9558419..5e904fab70 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -12,4 +12,4 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyFileVersion("7.6.0")] -[assembly: AssemblyInformationalVersion("7.6.0-alpha043")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("7.6.0-alpha044")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 79e4260028..03b1827221 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration /// Gets the version comment (like beta or RC). /// /// The version comment. - public static string CurrentComment { get { return "alpha043"; } } + public static string CurrentComment { get { return "alpha044"; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx From 53bbc5f922e8443356fcd92cd0e2340abde39955 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 3 Jan 2017 19:39:34 +1100 Subject: [PATCH 145/599] U4-9335 UmbracoExamine needs to ignore the useTempStorage option if an IDirectoryFactory config option is supplied by the indexer --- src/UmbracoExamine/BaseUmbracoIndexer.cs | 3 ++- src/UmbracoExamine/UmbracoExamineSearcher.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/UmbracoExamine/BaseUmbracoIndexer.cs b/src/UmbracoExamine/BaseUmbracoIndexer.cs index 26763bd7cd..933e32cfff 100644 --- a/src/UmbracoExamine/BaseUmbracoIndexer.cs +++ b/src/UmbracoExamine/BaseUmbracoIndexer.cs @@ -162,7 +162,8 @@ namespace UmbracoExamine base.Initialize(name, config); - if (config["useTempStorage"] != null) + //detect if a dir factory has been specified, if so then useTempStorage will not be used (deprecated) + if (config["directoryFactory"] == null && config["useTempStorage"] != null) { var fsDir = base.GetLuceneDirectory() as FSDirectory; if (fsDir != null) diff --git a/src/UmbracoExamine/UmbracoExamineSearcher.cs b/src/UmbracoExamine/UmbracoExamineSearcher.cs index acbfefb180..efd0043e0c 100644 --- a/src/UmbracoExamine/UmbracoExamineSearcher.cs +++ b/src/UmbracoExamine/UmbracoExamineSearcher.cs @@ -73,7 +73,8 @@ namespace UmbracoExamine base.Initialize(name, config); - if (config != null && config["useTempStorage"] != null) + //detect if a dir factory has been specified, if so then useTempStorage will not be used (deprecated) + if (config != null && config["directoryFactory"] == null && config["useTempStorage"] != null) { //Use the temp storage directory which will store the index in the local/codegen folder, this is useful // for websites that are running from a remove file server and file IO latency becomes an issue From 84c0682f1619e394c7dacc290b8c432291f1b1c0 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 3 Jan 2017 10:20:22 +0100 Subject: [PATCH 146/599] Not necessary to install Microsoft.IO.RecyclableMemoryStream, the ImageProcessor dependency will do that for us --- build/NuSpecs/UmbracoCms.Core.nuspec | 1 - 1 file changed, 1 deletion(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 113de3970c..44499f6a64 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -39,7 +39,6 @@ - From 051801f337fdf1ad66b7be7a3dd744822a411d24 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Tue, 3 Jan 2017 13:09:22 +0100 Subject: [PATCH 147/599] Couple of copy updates for the render section --- .../templatesections/templatesections.html | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.html index 36af4a2798..1a11d6a46e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.html @@ -2,10 +2,12 @@
    +
    Render child template
    +
    - Renders the entire contents of a child template, by inserting a - @RenderBody() + Renders the contents of a child template, by inserting a + @RenderBody() placeholder.
    @@ -14,9 +16,8 @@
    Render a named section
    - Inserts a @RenderSection placeholder to render a named section of a child template. - This allows you to insert specific the parts of a child template which is - wrapped in a @section{ ... } definition. + Renders a named area of a child template, by insert a @RenderSection(name) placeholder. + This renders an area of a child template which is wrapped in a corresponding @section [name]{ ... } definition.
    @@ -35,7 +36,7 @@ Make section mandatory
    - If mandatory, and the child template does not contain a section with this name, an error will be displayed. + If mandatory, the child template must contain a @section definition, otherwise an error is shown.
    @@ -47,9 +48,9 @@
    Define a named section
    - Marks a part of your template as a named section by wrapping it in - a @section { ... }, which can be inserted in a - specific area of this templates master using @RenderSection. + Defines a part of your template as a named section by wrapping it in + a @section { ... }. This can be rendered in a + specific area of the master of this template, by using @RenderSection.
    From 6a374cbfa6c310514abdf121b6914722b23c3bda Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 3 Jan 2017 15:57:21 +0100 Subject: [PATCH 148/599] Make the NuGet install and upgrade work --- build/NuSpecs/UmbracoCms.Core.nuspec | 1 + build/NuSpecs/UmbracoCms.nuspec | 1 + build/NuSpecs/tools/processing.config.install.xdt | 1 + 3 files changed, 3 insertions(+) create mode 100644 build/NuSpecs/tools/processing.config.install.xdt diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 44499f6a64..5849f2679f 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -35,6 +35,7 @@ + diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index d30a139adc..b56c8e6205 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -43,6 +43,7 @@ + diff --git a/build/NuSpecs/tools/processing.config.install.xdt b/build/NuSpecs/tools/processing.config.install.xdt new file mode 100644 index 0000000000..0bef321533 --- /dev/null +++ b/build/NuSpecs/tools/processing.config.install.xdt @@ -0,0 +1 @@ + \ No newline at end of file From 60f6a60ca9fc8865bc3ec49a5f0d5f5a483c9d66 Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Tue, 3 Jan 2017 16:11:32 +0100 Subject: [PATCH 149/599] Update install post url to https --- src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index 4d8e9c5a4b..fd36d8e4e6 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -81,7 +81,7 @@ namespace Umbraco.Web.Install.InstallSteps { var client = new System.Net.WebClient(); var values = new NameValueCollection { { "name", admin.Name }, { "email", admin.Email} }; - client.UploadValues("http://umbraco.org/base/Ecom/SubmitEmail/installer.aspx", values); + client.UploadValues("https://umbraco.com/base/Ecom/SubmitEmail/installer.aspx", values); } catch { /* fail in silence */ } } From dc62272e6e987dcfb83622b13eaf73c2b42ff42a Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 3 Jan 2017 17:04:38 +0100 Subject: [PATCH 150/599] Revert "Add Microsoft.IO.RecyclableMemoryStream to the nuspec, update to the latest version (1.2.1)" Keep the version the same as what ImageProcessor relies on to avoid confusion and conflicts This reverts commit 56d2230251b57fa849524baeab56eed5c4b61c68. # Conflicts: # build/NuSpecs/UmbracoCms.Core.nuspec --- build/NuSpecs/UmbracoCms.Core.nuspec | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 5 ++--- src/Umbraco.Web.UI/packages.config | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 5849f2679f..03818d13f8 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -39,7 +39,7 @@ - + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index c8fb7ab189..79796e6bb7 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -166,9 +166,8 @@ True - - ..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.1\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll - True + + ..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.0\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index a308c4cb53..2a87d3dce9 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -22,7 +22,7 @@ - + From a92d255e4b0f2b8dc81daf89bdb11064cd3c8336 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 4 Jan 2017 17:10:48 +1100 Subject: [PATCH 151/599] adds notes, obsoletes a few classes that shouldn't have really been there --- src/UmbracoExamine/BaseUmbracoIndexer.cs | 4 ++++ src/UmbracoExamine/LocalStorage/AzureLocalStorageDirectory.cs | 3 +++ .../LocalStorage/CodeGenLocalStorageDirectory.cs | 4 ++++ src/UmbracoExamine/UmbracoExamineSearcher.cs | 4 ++++ 4 files changed, 15 insertions(+) diff --git a/src/UmbracoExamine/BaseUmbracoIndexer.cs b/src/UmbracoExamine/BaseUmbracoIndexer.cs index 933e32cfff..ebb718898b 100644 --- a/src/UmbracoExamine/BaseUmbracoIndexer.cs +++ b/src/UmbracoExamine/BaseUmbracoIndexer.cs @@ -162,6 +162,10 @@ namespace UmbracoExamine base.Initialize(name, config); + //NOTES: useTempStorage is obsolete, tempStorageDirectory is obsolete, both have been superceded by Examine Core's IDirectoryFactory + // tempStorageDirectory never actually got finished in Umbraco Core but accidentally got shipped (it's only enabled on the searcher + // and not the indexer). So this whole block is just legacy + //detect if a dir factory has been specified, if so then useTempStorage will not be used (deprecated) if (config["directoryFactory"] == null && config["useTempStorage"] != null) { diff --git a/src/UmbracoExamine/LocalStorage/AzureLocalStorageDirectory.cs b/src/UmbracoExamine/LocalStorage/AzureLocalStorageDirectory.cs index 6d60d26079..56d1b414c5 100644 --- a/src/UmbracoExamine/LocalStorage/AzureLocalStorageDirectory.cs +++ b/src/UmbracoExamine/LocalStorage/AzureLocalStorageDirectory.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Specialized; +using System.ComponentModel; using System.IO; using System.Web; using Umbraco.Core; @@ -9,6 +10,8 @@ namespace UmbracoExamine.LocalStorage /// /// When running on Azure websites, we can use the local user's storage space /// + [Obsolete("This has been superceded by IDirectoryFactory in Examine Core and should not be used")] + [EditorBrowsable(EditorBrowsableState.Never)] public sealed class AzureLocalStorageDirectory : ILocalStorageDirectory { public DirectoryInfo GetLocalStorageDirectory(NameValueCollection config, string configuredPath) diff --git a/src/UmbracoExamine/LocalStorage/CodeGenLocalStorageDirectory.cs b/src/UmbracoExamine/LocalStorage/CodeGenLocalStorageDirectory.cs index eb7bb9a8b3..4aa92f0e53 100644 --- a/src/UmbracoExamine/LocalStorage/CodeGenLocalStorageDirectory.cs +++ b/src/UmbracoExamine/LocalStorage/CodeGenLocalStorageDirectory.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Specialized; +using System.ComponentModel; using System.IO; using System.Web; @@ -11,6 +13,8 @@ namespace UmbracoExamine.LocalStorage /// This is the default implementation - but it comes with it's own limitations - the CodeGen folder is cleared whenever new /// DLLs are changed in the /bin folder (among other circumstances) which means the index would be re-synced (or rebuilt) there. /// + [Obsolete("This has been superceded by IDirectoryFactory in Examine Core and should not be used")] + [EditorBrowsable(EditorBrowsableState.Never)] public sealed class CodeGenLocalStorageDirectory : ILocalStorageDirectory { public DirectoryInfo GetLocalStorageDirectory(NameValueCollection config, string configuredPath) diff --git a/src/UmbracoExamine/UmbracoExamineSearcher.cs b/src/UmbracoExamine/UmbracoExamineSearcher.cs index efd0043e0c..7a2a80c3fe 100644 --- a/src/UmbracoExamine/UmbracoExamineSearcher.cs +++ b/src/UmbracoExamine/UmbracoExamineSearcher.cs @@ -73,6 +73,10 @@ namespace UmbracoExamine base.Initialize(name, config); + //NOTES: useTempStorage is obsolete, tempStorageDirectory is obsolete, both have been superceded by Examine Core's IDirectoryFactory + // tempStorageDirectory never actually got finished in Umbraco Core but accidentally got shipped (it's only enabled on the searcher + // and not the indexer). So this whole block is just legacy + //detect if a dir factory has been specified, if so then useTempStorage will not be used (deprecated) if (config != null && config["directoryFactory"] == null && config["useTempStorage"] != null) { From 2acde54880e62a3e3c1b16587d511936acefc68b Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 4 Jan 2017 18:11:21 +1100 Subject: [PATCH 152/599] Adds new benchmark and updates the benchmark bootstrapper to be the supported way --- .../LinqCastBenchmarks.cs | 58 +++++++++++++++++++ src/Umbraco.Tests.Benchmarks/Program.cs | 23 +++----- .../Umbraco.Tests.Benchmarks.csproj | 1 + 3 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 src/Umbraco.Tests.Benchmarks/LinqCastBenchmarks.cs diff --git a/src/Umbraco.Tests.Benchmarks/LinqCastBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/LinqCastBenchmarks.cs new file mode 100644 index 0000000000..4118620515 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/LinqCastBenchmarks.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnostics.Windows; + +namespace Umbraco.Tests.Benchmarks +{ + /// + /// Want to check what is faster OfType or Cast when a enurable all has the same items + /// + [Config(typeof(Config))] + public class LinqCastBenchmarks + { + private class Config : ManualConfig + { + public Config() + { + Add(new MemoryDiagnoser()); + } + } + + public LinqCastBenchmarks() + { + _array = new List(); + _array.AddRange(Enumerable.Range(0, 10000).Select(x => x.ToString())); + } + + private readonly List _array; + + [Benchmark(Baseline = true)] + public void OfType() + { + foreach (var i in _array.OfType()) + { + var a = i; + } + } + + [Benchmark()] + public void Cast() + { + foreach (var i in _array.Cast()) + { + var a = i; + } + } + + [Benchmark()] + public void ExplicitCast() + { + foreach (var i in _array) + { + var a = (string)i; + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests.Benchmarks/Program.cs b/src/Umbraco.Tests.Benchmarks/Program.cs index 52736ed6a4..eb49f153cc 100644 --- a/src/Umbraco.Tests.Benchmarks/Program.cs +++ b/src/Umbraco.Tests.Benchmarks/Program.cs @@ -8,23 +8,14 @@ namespace Umbraco.Tests.Benchmarks { public static void Main(string[] args) { - if (args.Length == 1) + var switcher = new BenchmarkSwitcher(new[] { - var type = Assembly.GetExecutingAssembly().GetType("Umbraco.Tests.Benchmarks." +args[0]); - if (type == null) - { - Console.WriteLine("Unknown benchmark."); - } - else - { - var summary = BenchmarkRunner.Run(type); - Console.ReadLine(); - } - } - else - { - Console.WriteLine("?"); - } + typeof(BulkInsertBenchmarks), + typeof(ModelToSqlExpressionHelperBenchmarks), + typeof(XmlBenchmarks), + typeof(LinqCastBenchmarks) + }); + switcher.Run(args); } } } diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 9f6d4e799f..39d77bbc26 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -85,6 +85,7 @@ + From 256ceef875f4c4c7c190b40c2d3025bcdbde49c6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 4 Jan 2017 18:12:28 +1100 Subject: [PATCH 153/599] changes PublishedContentCache to explicitly cast to XmlElement (perf) --- .../XmlPublishedCache/PublishedContentCache.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index e58bc73b25..5033948730 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -179,8 +179,11 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache elt = null; var min = int.MaxValue; - foreach (var e in xml.DocumentElement.ChildNodes.OfType()) + //Don't use OfType or Cast, this is critical code, all ChildNodes are XmlElement so explicitly cast + // https://gist.github.com/Shazwazza/04e2e5642a316f4a87e52dada2901198 + foreach (var n in xml.DocumentElement.ChildNodes) { + var e = (XmlElement) n; var sortOrder = int.Parse(e.GetAttribute("sortOrder")); if (sortOrder < min) { @@ -201,8 +204,11 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache if (hideTopLevelNode && startNodeId <= 0) { - foreach (var e in elt.ChildNodes.OfType()) + //Don't use OfType or Cast, this is critical code, all ChildNodes are XmlElement so explicitly cast + // https://gist.github.com/Shazwazza/04e2e5642a316f4a87e52dada2901198 + foreach (var n in elt.ChildNodes) { + var e = (XmlElement)n; var id = NavigateElementRoute(e, urlParts); if (id > 0) return id; } @@ -224,8 +230,11 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache while (found && i < urlParts.Length) { found = false; - foreach (var child in elt.ChildNodes.OfType()) + //Don't use OfType or Cast, this is critical code, all ChildNodes are XmlElement so explicitly cast + // https://gist.github.com/Shazwazza/04e2e5642a316f4a87e52dada2901198 + foreach (var o in elt.ChildNodes) { + var child = (XmlElement) o; var noNode = UseLegacySchema ? child.Name != "node" : child.GetAttributeNode("isDoc") == null; From 4ab2130d7dead45aadc07d7d745541ecf116867d Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 4 Jan 2017 18:40:00 +1100 Subject: [PATCH 154/599] stop allocating and re-processing config elements every time they are accessed! --- .../UmbracoSettings/ContentElement.cs | 179 +++--------------- .../UmbracoConfigurationElement.cs | 36 ++++ 2 files changed, 59 insertions(+), 156 deletions(-) create mode 100644 src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoConfigurationElement.cs diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs index d743daca6a..aed7f61a06 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs @@ -4,8 +4,7 @@ using System.Configuration; namespace Umbraco.Core.Configuration.UmbracoSettings { - - internal class ContentElement : ConfigurationElement, IContentSection + internal class ContentElement : UmbracoConfigurationElement, IContentSection { [ConfigurationProperty("imaging")] internal ContentImagingElement Imaging @@ -22,25 +21,13 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("ResolveUrlsFromTextString")] internal InnerTextConfigurationElement ResolveUrlsFromTextString { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["ResolveUrlsFromTextString"], - //set the default - false); - } + get { return GetOptionalTextElement("ResolveUrlsFromTextString", false); } } [ConfigurationProperty("UploadAllowDirectories")] internal InnerTextConfigurationElement UploadAllowDirectories { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["UploadAllowDirectories"], - //set the default - true); - } + get { return GetOptionalTextElement("UploadAllowDirectories", true); } } public IEnumerable Error404Collection @@ -63,121 +50,61 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("ensureUniqueNaming")] internal InnerTextConfigurationElement EnsureUniqueNaming { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["ensureUniqueNaming"], - //set the default - true); - } + get { return GetOptionalTextElement("ensureUniqueNaming", true); } } [ConfigurationProperty("TidyEditorContent")] internal InnerTextConfigurationElement TidyEditorContent { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["TidyEditorContent"], - //set the default - false); - } + get { return GetOptionalTextElement("TidyEditorContent", false); } } [ConfigurationProperty("TidyCharEncoding")] internal InnerTextConfigurationElement TidyCharEncoding { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["TidyCharEncoding"], - //set the default - "UTF8"); - } + get { return GetOptionalTextElement("TidyCharEncoding", "UTF8"); } } [ConfigurationProperty("XmlCacheEnabled")] internal InnerTextConfigurationElement XmlCacheEnabled { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["XmlCacheEnabled"], - //set the default - true); - } + get { return GetOptionalTextElement("XmlCacheEnabled", true); } } [ConfigurationProperty("ContinouslyUpdateXmlDiskCache")] internal InnerTextConfigurationElement ContinouslyUpdateXmlDiskCache { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["ContinouslyUpdateXmlDiskCache"], - //set the default - true); - } + get { return GetOptionalTextElement("ContinouslyUpdateXmlDiskCache", true); } } [ConfigurationProperty("XmlContentCheckForDiskChanges")] internal InnerTextConfigurationElement XmlContentCheckForDiskChanges { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["XmlContentCheckForDiskChanges"], - //set the default - false); - } + get { return GetOptionalTextElement("XmlContentCheckForDiskChanges", false); } } [ConfigurationProperty("EnableSplashWhileLoading")] internal InnerTextConfigurationElement EnableSplashWhileLoading { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["EnableSplashWhileLoading"], - //set the default - false); - } + get { return GetOptionalTextElement("EnableSplashWhileLoading", false); } } [ConfigurationProperty("PropertyContextHelpOption")] internal InnerTextConfigurationElement PropertyContextHelpOption { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["PropertyContextHelpOption"], - //set the default - "text"); - } + get { return GetOptionalTextElement("PropertyContextHelpOption", "text"); } } [ConfigurationProperty("UseLegacyXmlSchema")] internal InnerTextConfigurationElement UseLegacyXmlSchema { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["UseLegacyXmlSchema"], - //set the default - false); - } + get { return GetOptionalTextElement("UseLegacyXmlSchema", false); } } [ConfigurationProperty("ForceSafeAliases")] internal InnerTextConfigurationElement ForceSafeAliases { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["ForceSafeAliases"], - //set the default - true); - } + get { return GetOptionalTextElement("ForceSafeAliases", true); } } [ConfigurationProperty("PreviewBadge")] @@ -185,123 +112,63 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { get { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["PreviewBadge"], - //set the default - @"In Preview Mode - click to end"); + return GetOptionalTextElement("PreviewBadge", @"In Preview Mode - click to end"); } } [ConfigurationProperty("UmbracoLibraryCacheDuration")] internal InnerTextConfigurationElement UmbracoLibraryCacheDuration { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["UmbracoLibraryCacheDuration"], - //set the default - 1800); - - } + get { return GetOptionalTextElement("UmbracoLibraryCacheDuration", 1800); } } [ConfigurationProperty("MacroErrors")] internal InnerTextConfigurationElement MacroErrors { - get - { - - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["MacroErrors"], - //set the default - MacroErrorBehaviour.Inline); - } + get { return GetOptionalTextElement("MacroErrors", MacroErrorBehaviour.Inline); } } [Obsolete("This is here so that if this config element exists we won't get a YSOD, it is not used whatsoever and will be removed in future versions")] [ConfigurationProperty("DocumentTypeIconList")] internal InnerTextConfigurationElement DocumentTypeIconList { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["DocumentTypeIconList"], - //set the default - IconPickerBehaviour.HideFileDuplicates); - } + get { return GetOptionalTextElement("DocumentTypeIconList", IconPickerBehaviour.HideFileDuplicates); } } [ConfigurationProperty("disallowedUploadFiles")] internal CommaDelimitedConfigurationElement DisallowedUploadFiles { - get - { - return new OptionalCommaDelimitedConfigurationElement( - (CommaDelimitedConfigurationElement)this["disallowedUploadFiles"], - //set the default - new[] { "ashx", "aspx", "ascx", "config", "cshtml", "vbhtml", "asmx", "air", "axd" }); - - } + get { return GetOptionalDelimitedElement("disallowedUploadFiles", new[] {"ashx", "aspx", "ascx", "config", "cshtml", "vbhtml", "asmx", "air", "axd"}); } } [ConfigurationProperty("cloneXmlContent")] internal InnerTextConfigurationElement CloneXmlContent { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["cloneXmlContent"], - //set the default - true); - } + get { return GetOptionalTextElement("cloneXmlContent", true); } } [ConfigurationProperty("GlobalPreviewStorageEnabled")] internal InnerTextConfigurationElement GlobalPreviewStorageEnabled { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["GlobalPreviewStorageEnabled"], - //set the default - false); - } + get { return GetOptionalTextElement("GlobalPreviewStorageEnabled", false); } } [ConfigurationProperty("defaultDocumentTypeProperty")] internal InnerTextConfigurationElement DefaultDocumentTypeProperty { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["defaultDocumentTypeProperty"], - //set the default - "Textstring"); - } + get { return GetOptionalTextElement("defaultDocumentTypeProperty", "Textstring"); } } [ConfigurationProperty("EnableInheritedDocumentTypes")] internal InnerTextConfigurationElement EnableInheritedDocumentTypes { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement) this["EnableInheritedDocumentTypes"], - //set the default - true); - } + get { return GetOptionalTextElement("EnableInheritedDocumentTypes", true); } } [ConfigurationProperty("EnableInheritedMediaTypes")] internal InnerTextConfigurationElement EnableInheritedMediaTypes { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["EnableInheritedMediaTypes"], - //set the default - true); - } + get { return GetOptionalTextElement("EnableInheritedMediaTypes", true); } } string IContentSection.NotificationEmailAddress diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoConfigurationElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoConfigurationElement.cs new file mode 100644 index 0000000000..063b5324d8 --- /dev/null +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/UmbracoConfigurationElement.cs @@ -0,0 +1,36 @@ +using System.Collections.Concurrent; +using System.Configuration; + +namespace Umbraco.Core.Configuration.UmbracoSettings +{ + /// + /// Base class with shared helper methods + /// + internal class UmbracoConfigurationElement : ConfigurationElement + { + /// + /// Used so the RawElement types are not re-created every time they are accessed + /// + private readonly ConcurrentDictionary _rawElements = new ConcurrentDictionary(); + + protected OptionalInnerTextConfigurationElement GetOptionalTextElement(string name, T defaultVal) + { + return (OptionalInnerTextConfigurationElement) _rawElements.GetOrAdd( + name, + s => new OptionalInnerTextConfigurationElement( + (InnerTextConfigurationElement) this[s], + //set the default + defaultVal)); + } + + protected OptionalCommaDelimitedConfigurationElement GetOptionalDelimitedElement(string name, string[] defaultVal) + { + return (OptionalCommaDelimitedConfigurationElement) _rawElements.GetOrAdd( + name, + s => new OptionalCommaDelimitedConfigurationElement( + (CommaDelimitedConfigurationElement) this[name], + //set the default + defaultVal)); + } + } +} \ No newline at end of file From 466a37aab8832158fe4e968c49f592408428a33d Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 4 Jan 2017 18:42:11 +1100 Subject: [PATCH 155/599] fix build --- src/Umbraco.Core/Umbraco.Core.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index d23c71a247..56958ac0e6 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -281,6 +281,7 @@ + From d889d3e3857632fa5bb6ac9badd3f117eff6beca Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 4 Jan 2017 20:00:30 +1100 Subject: [PATCH 156/599] adds test --- .../Services/ContentServiceTests.cs | 196 +++++++++++------- 1 file changed, 116 insertions(+), 80 deletions(-) diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index ed4a59bab4..7a386dbbbc 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -35,18 +35,54 @@ namespace Umbraco.Tests.Services [SetUp] public override void Initialize() { - base.Initialize(); + base.Initialize(); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); } - - [TearDown] - public override void TearDown() - { - base.TearDown(); - } //TODO Add test to verify there is only ONE newest document/content in cmsDocument table after updating. //TODO Add test to delete specific version (with and without deleting prior versions) and versions by date. + + /// + /// Ensures that we don't unpublish all nodes when a node is deleted that has an invalid path of -1 + /// Regression test: http://issues.umbraco.org/issue/U4-9336 + /// + [Test] + public void Deleting_Node_With_Invalid_Path() + { + var contentService = ServiceContext.ContentService; + var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1); + Assert.IsTrue(contentService.PublishWithStatus(root).Success); + var content = contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0); + Assert.IsTrue(contentService.PublishWithStatus(content).Success); + var hierarchy = CreateContentHierarchy().OrderBy(x => x.Level).ToArray(); + contentService.Save(hierarchy, 0); + foreach (var c in hierarchy) + { + Assert.IsTrue(contentService.PublishWithStatus(c).Success); + } + + //now make the data corrupted :/ + + DatabaseContext.Database.Execute("UPDATE umbracoNode SET path = '-1' WHERE id = @id", new {id = content.Id}); + + //re-get + content = contentService.GetById(content.Id); + + ServiceContext.ContentService.Delete(content); + + //re-get + hierarchy = contentService.GetByIds(hierarchy.Select(x => x.Id).ToArray()).OrderBy(x => x.Level).ToArray(); + + Assert.That(hierarchy.All(c => c.Trashed == false), Is.True); + Assert.That(hierarchy.All(c => c.Path.StartsWith("-1,-20") == false), Is.True); + } + [Test] public void Remove_Scheduled_Publishing_Date() { @@ -104,7 +140,7 @@ namespace Umbraco.Tests.Services results.Add(contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0)); } - var sortedGet = contentService.GetByIds(new[] {results[10].Id, results[5].Id, results[12].Id}).ToArray(); + var sortedGet = contentService.GetByIds(new[] { results[10].Id, results[5].Id, results[12].Id }).ToArray(); // Assert Assert.AreEqual(sortedGet[0].Id, results[10].Id); @@ -121,7 +157,7 @@ namespace Umbraco.Tests.Services // Act for (int i = 0; i < 20; i++) { - contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0); + contentService.CreateContentWithIdentity("Test", -1, "umbTextpage", 0); } // Assert @@ -230,7 +266,7 @@ namespace Umbraco.Tests.Services // Assert //there should be no tags for this entity - var tags = tagService.GetTagsForEntity(content1.Id); + var tags = tagService.GetTagsForEntity(content1.Id); Assert.AreEqual(0, tags.Count()); //these tags should still be returned since they still have actively published content assigned @@ -265,7 +301,7 @@ namespace Umbraco.Tests.Services contentService.MoveToRecycleBin(content2); // Assert - + //there should be no exposed content tags now that nothing is published. var allTags = tagService.GetAllContentTags(); Assert.AreEqual(0, allTags.Count()); @@ -395,7 +431,7 @@ namespace Umbraco.Tests.Services new PropertyType("test", DataTypeDatabaseType.Ntext, "tags") { DataTypeDefinitionId = 1041 - }); + }); contentTypeService.Save(contentType); contentType.AllowedContentTypes = new[] { new ContentTypeSort(new Lazy(() => contentType.Id), 0, contentType.Alias) }; @@ -410,7 +446,7 @@ namespace Umbraco.Tests.Services var child2 = MockedContent.CreateSimpleContent(contentType, "child 2 content", content.Id); child2.SetTags("tags", new[] { "hello2", "world2" }, true); contentService.Save(child2); - + // Act contentService.PublishWithChildrenWithStatus(content, includeUnpublished: true); @@ -461,34 +497,34 @@ namespace Umbraco.Tests.Services new { nodeId = content.Id, propTypeId = propertyTypeId })); } - [Test] - public void Can_Replace_Tag_Data_To_Published_Content() - { + [Test] + public void Can_Replace_Tag_Data_To_Published_Content() + { //Arrange var contentService = ServiceContext.ContentService; var contentTypeService = ServiceContext.ContentTypeService; var contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory", "Mandatory Doc Type", true); contentType.PropertyGroups.First().PropertyTypes.Add( new PropertyType("test", DataTypeDatabaseType.Ntext, "tags") - { - DataTypeDefinitionId = 1041 - }); + { + DataTypeDefinitionId = 1041 + }); contentTypeService.Save(contentType); var content = MockedContent.CreateSimpleContent(contentType, "Tagged content", -1); - - + + // Act content.SetTags("tags", new[] { "hello", "world", "some", "tags" }, true); contentService.Publish(content); // Assert Assert.AreEqual(4, content.Properties["tags"].Value.ToString().Split(',').Distinct().Count()); - var propertyTypeId = contentType.PropertyTypes.Single(x => x.Alias == "tags").Id; - Assert.AreEqual(4, DatabaseContext.Database.ExecuteScalar( - "SELECT COUNT(*) FROM cmsTagRelationship WHERE nodeId=@nodeId AND propertyTypeId=@propTypeId", - new {nodeId = content.Id, propTypeId = propertyTypeId})); - } + var propertyTypeId = contentType.PropertyTypes.Single(x => x.Alias == "tags").Id; + Assert.AreEqual(4, DatabaseContext.Database.ExecuteScalar( + "SELECT COUNT(*) FROM cmsTagRelationship WHERE nodeId=@nodeId AND propertyTypeId=@propTypeId", + new { nodeId = content.Id, propTypeId = propertyTypeId })); + } [Test] public void Can_Append_Tag_Data_To_Published_Content() @@ -506,7 +542,7 @@ namespace Umbraco.Tests.Services var content = MockedContent.CreateSimpleContent(contentType, "Tagged content", -1); content.SetTags("tags", new[] { "hello", "world", "some", "tags" }, true); contentService.PublishWithStatus(content); - + // Act content.SetTags("tags", new[] { "another", "world" }, false); contentService.PublishWithStatus(content); @@ -548,9 +584,9 @@ namespace Umbraco.Tests.Services new { nodeId = content.Id, propTypeId = propertyTypeId })); } - [Test] - public void Can_Remove_Property_Type() - { + [Test] + public void Can_Remove_Property_Type() + { // Arrange var contentService = ServiceContext.ContentService; @@ -560,9 +596,9 @@ namespace Umbraco.Tests.Services // Assert Assert.That(content, Is.Not.Null); Assert.That(content.HasIdentity, Is.False); - } + } - [Test] + [Test] public void Can_Create_Content() { // Arrange @@ -575,7 +611,7 @@ namespace Umbraco.Tests.Services Assert.That(content, Is.Not.Null); Assert.That(content.HasIdentity, Is.False); } - + [Test] public void Can_Create_Content_Without_Explicitly_Set_User() { @@ -595,12 +631,12 @@ namespace Umbraco.Tests.Services public void Can_Save_New_Content_With_Explicit_User() { var user = new User(ServiceContext.UserService.GetUserTypeByAlias("admin")) - { - Name = "Test", - Email = "test@test.com", - Username = "test", + { + Name = "Test", + Email = "test@test.com", + Username = "test", RawPasswordValue = "test" - }; + }; ServiceContext.UserService.Save(user); var content = new Content("Test", -1, ServiceContext.ContentTypeService.GetContentType("umbTextpage")); @@ -857,14 +893,14 @@ namespace Umbraco.Tests.Services var provider = new PetaPocoUnitOfWorkProvider(Logger); using (var uow = provider.GetUnitOfWork()) { - uow.Database.TruncateTable("cmsContentXml"); + uow.Database.TruncateTable("cmsContentXml"); } - + //for this test we are also going to save a revision for a content item that is not published, this is to ensure //that it's published version still makes it into the cmsContentXml table! contentService.Save(allContent.Last()); - + // Act var published = contentService.RePublishAll(0); @@ -872,7 +908,7 @@ namespace Umbraco.Tests.Services Assert.IsTrue(published); using (var uow = provider.GetUnitOfWork()) { - Assert.AreEqual(allContent.Count(), uow.Database.ExecuteScalar("select count(*) from cmsContentXml")); + Assert.AreEqual(allContent.Count(), uow.Database.ExecuteScalar("select count(*) from cmsContentXml")); } } @@ -889,7 +925,7 @@ namespace Umbraco.Tests.Services var allContent = rootContent.Concat(rootContent.SelectMany(x => x.Descendants())).ToList(); //for testing we need to clear out the contentXml table so we can see if it worked var provider = new PetaPocoUnitOfWorkProvider(Logger); - + using (var uow = provider.GetUnitOfWork()) { uow.Database.TruncateTable("cmsContentXml"); @@ -899,7 +935,7 @@ namespace Umbraco.Tests.Services contentService.Save(allContent.Last()); // Act - contentService.RePublishAll(new int[]{allContent.Last().ContentTypeId}); + contentService.RePublishAll(new int[] { allContent.Last().ContentTypeId }); // Assert using (var uow = provider.GetUnitOfWork()) @@ -1085,7 +1121,7 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.CreateContent("Home US", - 1, "umbTextpage", 0); + var content = contentService.CreateContent("Home US", -1, "umbTextpage", 0); content.SetValue("author", "Barack Obama"); // Act @@ -1116,7 +1152,7 @@ namespace Umbraco.Tests.Services var savedVersion = content.Version; // Act - var publishedDescendants = ((ContentService) contentService).GetPublishedDescendants(root).ToList(); + var publishedDescendants = ((ContentService)contentService).GetPublishedDescendants(root).ToList(); // Assert Assert.That(rootPublished, Is.True); @@ -1146,7 +1182,7 @@ namespace Umbraco.Tests.Services { // Arrange var contentService = ServiceContext.ContentService; - var content = contentService.CreateContent("Home US", - 1, "umbTextpage", 0); + var content = contentService.CreateContent("Home US", -1, "umbTextpage", 0); content.SetValue("author", "Barack Obama"); // Act @@ -1166,7 +1202,7 @@ namespace Umbraco.Tests.Services var contentType = contentTypeService.GetContentType("umbTextpage"); Content subpage = MockedContent.CreateSimpleContent(contentType, "Text Subpage 1", NodeDto.NodeIdSeed + 2); Content subpage2 = MockedContent.CreateSimpleContent(contentType, "Text Subpage 2", NodeDto.NodeIdSeed + 2); - var list = new List {subpage, subpage2}; + var list = new List { subpage, subpage2 }; // Act contentService.Save(list, 0); @@ -1186,9 +1222,9 @@ namespace Umbraco.Tests.Services contentService.Save(hierarchy, 0); Assert.That(hierarchy.Any(), Is.True); - Assert.That(hierarchy.Any(x => x.HasIdentity == false), Is.False); - //all parent id's should be ok, they are lazy and if they equal zero an exception will be thrown - Assert.DoesNotThrow(() => hierarchy.Any(x => x.ParentId != 0)); + Assert.That(hierarchy.Any(x => x.HasIdentity == false), Is.False); + //all parent id's should be ok, they are lazy and if they equal zero an exception will be thrown + Assert.DoesNotThrow(() => hierarchy.Any(x => x.ParentId != 0)); } @@ -1375,7 +1411,7 @@ namespace Umbraco.Tests.Services var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage"); var temp = MockedContent.CreateSimpleContent(contentType, "Simple Text Page", -1); var prop = temp.Properties.First(); - temp.SetTags(prop.Alias, new[] {"hello", "world"}, true); + temp.SetTags(prop.Alias, new[] { "hello", "world" }, true); var status = contentService.PublishWithStatus(temp); // Act @@ -1418,14 +1454,14 @@ namespace Umbraco.Tests.Services [Test] public void Can_Save_Lazy_Content() - { - var unitOfWork = PetaPocoUnitOfWorkProvider.CreateUnitOfWork(Mock.Of()); + { + var unitOfWork = PetaPocoUnitOfWorkProvider.CreateUnitOfWork(Mock.Of()); var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage"); var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1); var c = new Lazy(() => MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Page", root.Id)); var c2 = new Lazy(() => MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Subpage", c.Value.Id)); - var list = new List> {c, c2}; + var list = new List> { c, c2 }; ContentTypeRepository contentTypeRepository; using (var repository = CreateRepository(unitOfWork, out contentTypeRepository)) @@ -1443,9 +1479,9 @@ namespace Umbraco.Tests.Services Assert.That(c2.Value.Id > 0, Is.True); Assert.That(c.Value.ParentId > 0, Is.True); - Assert.That(c2.Value.ParentId > 0, Is.True); + Assert.That(c2.Value.ParentId > 0, Is.True); } - + } [Test] @@ -1468,20 +1504,20 @@ namespace Umbraco.Tests.Services Assert.That(hasPublishedVersion, Is.True); } - [Test] - public void Can_Verify_Property_Types_On_Content() - { + [Test] + public void Can_Verify_Property_Types_On_Content() + { // Arrange - var contentTypeService = ServiceContext.ContentTypeService; + var contentTypeService = ServiceContext.ContentTypeService; var contentType = MockedContentTypes.CreateAllTypesContentType("allDataTypes", "All DataTypes"); contentTypeService.Save(contentType); - var contentService = ServiceContext.ContentService; - var content = MockedContent.CreateAllTypesContent(contentType, "Random Content", -1); + var contentService = ServiceContext.ContentService; + var content = MockedContent.CreateAllTypesContent(contentType, "Random Content", -1); contentService.Save(content); - var id = content.Id; + var id = content.Id; // Act - var sut = contentService.GetById(id); + var sut = contentService.GetById(id); // Arrange Assert.That(sut.GetValue("isTrue"), Is.True); @@ -1496,7 +1532,7 @@ namespace Umbraco.Tests.Services Assert.That(sut.GetValue("dateTime").ToString("G"), Is.EqualTo(content.GetValue("dateTime").ToString("G"))); Assert.That(sut.GetValue("colorPicker"), Is.EqualTo("black")); //that one is gone in 7.4 - //Assert.That(sut.GetValue("folderBrowser"), Is.Null); + //Assert.That(sut.GetValue("folderBrowser"), Is.Null); Assert.That(sut.GetValue("ddlMultiple"), Is.EqualTo("1234,1235")); Assert.That(sut.GetValue("rbList"), Is.EqualTo("random")); Assert.That(sut.GetValue("date").ToString("G"), Is.EqualTo(content.GetValue("date").ToString("G"))); @@ -1507,23 +1543,23 @@ namespace Umbraco.Tests.Services Assert.That(sut.GetValue("memberPicker"), Is.EqualTo(1092)); Assert.That(sut.GetValue("relatedLinks"), Is.EqualTo("")); Assert.That(sut.GetValue("tags"), Is.EqualTo("this,is,tags")); - } + } - [Test] - public void Can_Delete_Previous_Versions_Not_Latest() - { + [Test] + public void Can_Delete_Previous_Versions_Not_Latest() + { // Arrange var contentService = ServiceContext.ContentService; var content = contentService.GetById(NodeDto.NodeIdSeed + 4); - var version = content.Version; + var version = content.Version; - // Act + // Act contentService.DeleteVersion(NodeDto.NodeIdSeed + 4, version, true, 0); var sut = contentService.GetById(NodeDto.NodeIdSeed + 4); // Assert Assert.That(sut.Version, Is.EqualTo(version)); - } + } [Test] public void Ensure_Content_Xml_Created() @@ -1542,7 +1578,7 @@ namespace Umbraco.Tests.Services } contentService.Publish(content); - + using (var uow = provider.GetUnitOfWork()) { Assert.IsTrue(uow.Database.Exists(content.Id)); @@ -1559,10 +1595,10 @@ namespace Umbraco.Tests.Services contentService.Save(content); var provider = new PetaPocoUnitOfWorkProvider(Logger); - + using (var uow = provider.GetUnitOfWork()) { - Assert.IsTrue(uow.Database.SingleOrDefault("WHERE nodeId=@nodeId AND versionId = @versionId", new{nodeId = content.Id, versionId = content.Version}) != null); + Assert.IsTrue(uow.Database.SingleOrDefault("WHERE nodeId=@nodeId AND versionId = @versionId", new { nodeId = content.Id, versionId = content.Version }) != null); } } @@ -1665,11 +1701,11 @@ namespace Umbraco.Tests.Services var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage"); var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1); - var list = new List(); + var list = new List(); for (int i = 0; i < 10; i++) { - var content = MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Page " + i, root); + var content = MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Page " + i, root); list.Add(content); list.AddRange(CreateChildrenOf(contentType, content, 4)); @@ -1680,12 +1716,12 @@ namespace Umbraco.Tests.Services return list; } - private IEnumerable CreateChildrenOf(IContentType contentType, IContent content, int depth) + private IEnumerable CreateChildrenOf(IContentType contentType, IContent content, int depth) { var list = new List(); for (int i = 0; i < depth; i++) { - var c = MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Subpage " + i, content); + var c = MockedContent.CreateSimpleContent(contentType, "Hierarchy Simple Text Subpage " + i, content); list.Add(c); Debug.Print("Created: 'Hierarchy Simple Text Subpage {0}' - Depth: {1}", i, depth); From 339957bd9171c1d61dfd94305a7adc9e1a3af6be Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 4 Jan 2017 10:43:56 +0100 Subject: [PATCH 157/599] remove unnecessary dependencies --- .../common/overlays/querybuilder/querybuilder.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js index 41da773146..d04b7f35a3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function QueryBuilderOverlayController($scope, $element, templateQueryResource, assetsService, angularHelper) { + function QueryBuilderOverlayController($scope, templateQueryResource) { var vm = this; From ddad642a880140aacc0aabb8270c3b3c520def00 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 4 Jan 2017 10:45:40 +0100 Subject: [PATCH 158/599] fix callbacks --- .../directives/components/umbdatetimepicker.directive.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js index 6f5c5abe09..d7e44df113 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js @@ -96,7 +96,7 @@ Use this directive to render a date time picker } function onHide(event) { - if (scope.onChange) { + if (scope.onHide) { scope.$apply(function(){ // callback scope.onHide({event: event}); @@ -132,7 +132,7 @@ Use this directive to render a date time picker } function onUpdate(event) { - if (scope.onShow) { + if (scope.onUpdate) { scope.$apply(function(){ // callback scope.onUpdate({event: event}); From 42841824bf0280b120891d4fabdcada2a4af7000 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 4 Jan 2017 11:31:46 +0100 Subject: [PATCH 159/599] add client side validation --- .../src/views/templates/edit.controller.js | 27 ++++++++++++++++--- .../template-editor-controller.spec.js | 4 +++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index beac32be95..a2cc29a0d4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -1,10 +1,11 @@ (function () { "use strict"; - function TemplatesEditController($scope, $routeParams, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, treeService, angularHelper) { + function TemplatesEditController($scope, $routeParams, $timeout, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, treeService, contentEditingHelper, localizationService, angularHelper) { var vm = this; var oldMasterTemplateAlias = null; + var localizeSaving = localizationService.localize("general_saving"); vm.page = {}; vm.page.loading = true; @@ -20,8 +21,18 @@ vm.template.content = vm.editor.getValue(); - templateResource.save(vm.template).then(function (saved) { - + contentEditingHelper.contentEditorPerformSave({ + statusMessage: localizeSaving, + saveMethod: templateResource.save, + scope: $scope, + content: vm.template, + //We do not redirect on failure for templates - this is because it is not possible to actually save the doc + // type when server side validation fails - as opposed to content where we are capable of saving the content + // item if server side validation fails + redirectOnFailure: false, + rebindCallback: function (orignal, saved) {} + }).then(function (saved) { + notificationsService.success("Template saved"); vm.page.saveButtonState = "success"; vm.template = saved; @@ -58,9 +69,17 @@ }, function (err) { - notificationsService.error("Template save failed"); + vm.page.saveButtonState = "error"; + + localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_validationFailedMessage").then(function(msgValue) { + notificationsService.error(headerValue, msgValue); + }); + }); + }); + }; vm.init = function () { diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js index aae0694876..a5009f5eb8 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js @@ -89,6 +89,10 @@ getSectionState : function() { return {}; } }, macroService: {}, + contentEditingHelper: {}, + localizationService: { + localize: resolvedPromise({}) + }, angularHelper: { getCurrentForm: function() { return { From f200ab423deee091dbf40dab20b088f074fe463b Mon Sep 17 00:00:00 2001 From: Claus Date: Wed, 4 Jan 2017 11:59:07 +0100 Subject: [PATCH 160/599] updated test to fail and updated notes. --- src/Umbraco.Tests/Services/ContentServiceTests.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 7a386dbbbc..afffc8ad1e 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -50,6 +50,8 @@ namespace Umbraco.Tests.Services /// /// Ensures that we don't unpublish all nodes when a node is deleted that has an invalid path of -1 + /// Note: it is actually the MoveToRecycleBin happening on the initial deletion of a node through the UI + /// that causes the issue. /// Regression test: http://issues.umbraco.org/issue/U4-9336 /// [Test] @@ -68,13 +70,17 @@ namespace Umbraco.Tests.Services } //now make the data corrupted :/ - DatabaseContext.Database.Execute("UPDATE umbracoNode SET path = '-1' WHERE id = @id", new {id = content.Id}); - //re-get + // need to clear the caches otherwise the ContentService will just serve is a cached version with the non-updated path from db. + ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearAllCache(); + + //re-get with the corrupt path content = contentService.GetById(content.Id); - ServiceContext.ContentService.Delete(content); + // Note to Shan: put a breakpoint at ContentService.cs line: 1021 + // here we get all descendants by the path of the node being moved to bin, and unpublish all of them. + ServiceContext.ContentService.MoveToRecycleBin(content); //re-get hierarchy = contentService.GetByIds(hierarchy.Select(x => x.Id).ToArray()).OrderBy(x => x.Level).ToArray(); From 18d8223d7e6e8acff93c9cb1acd93fe5a70dee6d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 4 Jan 2017 13:04:48 +0100 Subject: [PATCH 161/599] fixes: U4-9338 Add focus to code editor on load and after closing dialogs --- .../src/views/templates/edit.controller.js | 48 ++++++++++++++++--- .../template-editor-controller.spec.js | 1 + 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index beac32be95..3cf454c886 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function TemplatesEditController($scope, $routeParams, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, treeService, angularHelper) { + function TemplatesEditController($scope, $routeParams, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, treeService, angularHelper, $timeout) { var vm = this; var oldMasterTemplateAlias = null; @@ -104,6 +104,7 @@ // save state of master template to use for comparison when syncing the tree on save oldMasterTemplateAlias = angular.copy(template.masterTemplateAlias); + // ace configuration vm.aceOption = { mode: "razor", theme: "chrome", @@ -114,15 +115,23 @@ onLoad: function(_editor) { vm.editor = _editor; - //initial cursor placement - vm.editor.navigateFileEnd(); - persistCurrentLocation(); + // initial cursor placement + // Keep cursor in name field if we are create a new template + // else set the cursor at the bottom of the code editor + if(!$routeParams.create) { + $timeout(function(){ + vm.editor.navigateFileEnd(); + vm.editor.focus(); + persistCurrentLocation(); + }); + } //change on blur, focus vm.editor.on("blur", persistCurrentLocation); vm.editor.on("focus", persistCurrentLocation); } } + }; vm.openPageFieldOverlay = openPageFieldOverlay; @@ -174,8 +183,11 @@ }, close: function(oldModel) { + // close the dialog vm.insertOverlay.show = false; vm.insertOverlay = null; + // focus editor + vm.editor.focus(); } }; @@ -197,6 +209,13 @@ vm.macroPickerOverlay.show = false; vm.macroPickerOverlay = null; + }, + close: function(oldModel) { + // close the dialog + vm.macroPickerOverlay.show = false; + vm.macroPickerOverlay = null; + // focus editor + vm.editor.focus(); } }; } @@ -214,8 +233,11 @@ vm.pageFieldOverlay = null; }, close: function (model) { + // close the dialog vm.pageFieldOverlay.show = false; vm.pageFieldOverlay = null; + // focus editor + vm.editor.focus(); } }; } @@ -239,8 +261,11 @@ vm.dictionaryItemOverlay = null; }, close: function (model) { + // close dialog vm.dictionaryItemOverlay.show = false; vm.dictionaryItemOverlay = null; + // focus editor + vm.editor.focus(); } }; } @@ -263,8 +288,11 @@ vm.partialItemOverlay = null; }, close: function (model) { + // close dialog vm.partialItemOverlay.show = false; vm.partialItemOverlay = null; + // focus editor + vm.editor.focus(); } }; } @@ -293,8 +321,11 @@ }, close: function (model) { + // close dialog vm.queryBuilderOverlay.show = false; vm.queryBuilderOverlay = null; + // focus editor + vm.editor.focus(); } }; } @@ -326,10 +357,11 @@ }, close: function(model) { - + // close dialog vm.sectionsOverlay.show = false; vm.sectionsOverlay = null; - + // focus editor + vm.editor.focus(); } } } @@ -367,8 +399,11 @@ vm.masterTemplateOverlay = null; }, close: function(oldModel) { + // close dialog vm.masterTemplateOverlay.show = false; vm.masterTemplateOverlay = null; + // focus editor + vm.editor.focus(); } }; @@ -441,6 +476,7 @@ vm.editor.clearSelection(); vm.editor.navigateFileStart(); + vm.editor.focus(); // set form state to $dirty setFormState("dirty"); diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js index aae0694876..7820beaf16 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js @@ -47,6 +47,7 @@ getCursorPosition: function() {}, getValue: function() {}, setValue: function() {}, + focus: function() {}, clearSelection: function() {}, navigateFileStart: function() {} }; From 13ed3303f50c8ff9f226ace28823bec882b16b95 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 4 Jan 2017 12:56:32 +0100 Subject: [PATCH 162/599] U4-9337 - url routing perfs --- src/Umbraco.Web/Models/ContentExtensions.cs | 6 +- .../PublishedContentCache.cs | 139 +++++++----------- src/Umbraco.Web/Routing/DefaultUrlProvider.cs | 11 -- .../Routing/PublishedContentRequestEngine.cs | 26 ++++ .../Routing/RedirectTrackingEventHandler.cs | 3 +- .../Routing/UrlProviderExtensions.cs | 58 +++++--- 6 files changed, 116 insertions(+), 127 deletions(-) diff --git a/src/Umbraco.Web/Models/ContentExtensions.cs b/src/Umbraco.Web/Models/ContentExtensions.cs index cfee14a2a8..d1f153125e 100644 --- a/src/Umbraco.Web/Models/ContentExtensions.cs +++ b/src/Umbraco.Web/Models/ContentExtensions.cs @@ -46,9 +46,7 @@ namespace Umbraco.Web.Models { var route = umbracoContext == null ? null // for tests only - : umbracoContext.ContentCache.GetRouteById(contentId); // cached - - if (route != null && route.StartsWith("err/")) route = null; + : umbracoContext.ContentCache.GetRouteById(contentId); // may be cached var domainHelper = new DomainHelper(domainService); IDomain domain; @@ -73,7 +71,7 @@ namespace Umbraco.Web.Models } else { - // if content is published then we have a (cached) route + // if content is published then we have a route // from which we can figure out the domain var pos = route.IndexOf('/'); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index 5033948730..0b027deb92 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -98,42 +98,10 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // else actually determine the route route = DetermineRouteById(umbracoContext, preview, contentId); - // node not found - if (route == null) - return null; - - // find the content back, detect routes collisions: we should find ourselves back, - // else it means that another content with "higher priority" is sharing the same route. - // perf impact: - // - non-colliding, adds one complete "by route" lookup, only on the first time a url is computed (then it's cached anyways) - // - colliding, adds one "by route" lookup, the first time the url is computed, then one dictionary looked each time it is computed again - // assuming no collisions, the impact is one complete "by route" lookup the first time each url is computed - // - // U4-9121 - this lookup is too expensive when computing a large amount of urls on a front-end (eg menu) - // ... thinking about moving the lookup out of the path into its own async task, so we are not reporting errors - // in the back-office anymore, but at least we are not polluting the cache - // instead, refactored DeterminedIdByRoute to stop using XPath, with a 16x improvement according to benchmarks - // will it be enough? - - var loopId = preview ? 0 : _routesCache.GetNodeId(route); // might be cached already in case of collision - if (loopId == 0) - { - var content = DetermineIdByRoute(umbracoContext, preview, route, GlobalSettings.HideTopLevelNodeFromPath); - - // add the other route to cache so next time we have it already - if (content != null && preview == false) - AddToCacheIfDeepestRoute(umbracoContext, content, route); - - loopId = content == null ? 0 : content.Id; // though... 0 here would be quite weird? - } - - // cache if we have a route and not previewing and it's not a colliding route - // (the result of DetermineRouteById is always the deepest route) - if (/*route != null &&*/ preview == false && loopId == contentId) - _routesCache.Store(contentId, route); - - // return route if no collision, else report collision - return loopId == contentId ? route : ("err/" + loopId); + // may be null if node not found + // do NOT cache the route: it may be colliding - and since checking for a collision implies + // doing one DetermineIdByRoute anyways we are not causing any perf penalty by not caching + return route; } IPublishedContent DetermineIdByRoute(UmbracoContext umbracoContext, bool preview, string route, bool hideTopLevelNode) @@ -149,18 +117,25 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache //check if we can find the node in our xml cache var id = NavigateRoute(umbracoContext, preview, startNodeId, path, hideTopLevelNode); - if (id > 0) return GetById(umbracoContext, preview, id); + return id > 0 ? GetById(umbracoContext, preview, id) : null; + } - // if hideTopLevelNodePath is true then for url /foo we looked for /*/foo - // but maybe that was the url of a non-default top-level node, so we also - // have to look for /foo (see note in ApplyHideTopLevelNodeFromPath). - if (hideTopLevelNode && path.Length > 1 && path.IndexOf('/', 1) < 0) + private static XmlElement GetXmlElementChildWithLowestSortOrder(XmlNode element) + { + XmlElement elt = null; + var min = int.MaxValue; + foreach (var n in element.ChildNodes) { - var id2 = NavigateRoute(umbracoContext, preview, startNodeId, path, false); - if (id2 > 0) return GetById(umbracoContext, preview, id2); - } + var e = n as XmlElement; + if (e == null) continue; - return null; + var sortOrder = int.Parse(e.GetAttribute("sortOrder")); + if (sortOrder >= min) continue; + + min = sortOrder; + elt = e; + } + return elt; } private int NavigateRoute(UmbracoContext umbracoContext, bool preview, int startNodeId, string path, bool hideTopLevelNode) @@ -177,20 +152,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return elt == null ? -1 : startNodeId; } - elt = null; - var min = int.MaxValue; - //Don't use OfType or Cast, this is critical code, all ChildNodes are XmlElement so explicitly cast - // https://gist.github.com/Shazwazza/04e2e5642a316f4a87e52dada2901198 - foreach (var n in xml.DocumentElement.ChildNodes) - { - var e = (XmlElement) n; - var sortOrder = int.Parse(e.GetAttribute("sortOrder")); - if (sortOrder < min) - { - min = sortOrder; - elt = e; - } - } + elt = GetXmlElementChildWithLowestSortOrder(xml.DocumentElement); return elt == null ? -1 : int.Parse(elt.GetAttribute("id")); } @@ -208,10 +170,16 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // https://gist.github.com/Shazwazza/04e2e5642a316f4a87e52dada2901198 foreach (var n in elt.ChildNodes) { - var e = (XmlElement)n; + var e = n as XmlElement; + if (e == null) continue; + var id = NavigateElementRoute(e, urlParts); if (id > 0) return id; } + + if (urlParts.Length == 1) + return NavigateElementRoute(elt, urlParts); + return -1; } @@ -232,9 +200,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache found = false; //Don't use OfType or Cast, this is critical code, all ChildNodes are XmlElement so explicitly cast // https://gist.github.com/Shazwazza/04e2e5642a316f4a87e52dada2901198 + var sortOrder = -1; foreach (var o in elt.ChildNodes) { - var child = (XmlElement) o; + var child = o as XmlElement; + if (child == null) continue; + var noNode = UseLegacySchema ? child.Name != "node" : child.GetAttributeNode("isDoc") == null; @@ -242,8 +213,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache if (child.GetAttribute("urlName") != urlParts[i]) continue; found = true; + + var so = int.Parse(child.GetAttribute("sortOrder")); + if (sortOrder >= 0 && so >= sortOrder) continue; + + sortOrder = so; elt = child; - break; } i++; } @@ -252,7 +227,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache string DetermineRouteById(UmbracoContext umbracoContext, bool preview, int contentId) { - var elt = GetXml(umbracoContext, preview).GetElementById(contentId.ToString(CultureInfo.InvariantCulture)); + var xml = GetXml(umbracoContext, preview); + var elt = xml.GetElementById(contentId.ToString(CultureInfo.InvariantCulture)); if (elt == null) return null; var domainHelper = GetDomainHelper(umbracoContext.Application.Services.DomainService); @@ -279,7 +255,18 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // no domain, respect HideTopLevelNodeFromPath for legacy purposes if (hasDomains == false && GlobalSettings.HideTopLevelNodeFromPath) - ApplyHideTopLevelNodeFromPath(umbracoContext, eltId, eltParentId, pathParts); + { + if (eltParentId == -1) + { + var rootNode = GetXmlElementChildWithLowestSortOrder(xml.DocumentElement); + if (rootNode != null && rootNode == elt) + pathParts.RemoveAt(pathParts.Count - 1); + } + else + { + pathParts.RemoveAt(pathParts.Count - 1); + } + } // assemble the route pathParts.Reverse(); @@ -289,30 +276,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return route; } - static void ApplyHideTopLevelNodeFromPath(UmbracoContext umbracoContext, int nodeId, int parentId, IList pathParts) - { - // in theory if hideTopLevelNodeFromPath is true, then there should be only once - // top-level node, or else domains should be assigned. but for backward compatibility - // we add this check - we look for the document matching "/" and if it's not us, then - // we do not hide the top level path - // it has to be taken care of in GetByRoute too so if - // "/foo" fails (looking for "/*/foo") we try also "/foo". - // this does not make much sense anyway esp. if both "/foo/" and "/bar/foo" exist, but - // that's the way it works pre-4.10 and we try to be backward compat for the time being - if (parentId == -1) - { - var rootNode = umbracoContext.ContentCache.GetByRoute("/", true); - if (rootNode == null) - throw new Exception("Failed to get node at /."); - if (rootNode.Id == nodeId) // remove only if we're the default node - pathParts.RemoveAt(pathParts.Count - 1); - } - else - { - pathParts.RemoveAt(pathParts.Count - 1); - } - } - #endregion #region XPath Strings diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index d55095d151..6b3c0fe69b 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -65,14 +65,6 @@ namespace Umbraco.Web.Routing return null; } - if (route.StartsWith("err/")) - { - LogHelper.Debug( - "Page with nodeId={0} has a colliding url with page with nodeId={1}.", - () => id, () => route.Substring(4)); - return "#err-" + route.Substring(4); - } - var domainHelper = new DomainHelper(umbracoContext.Application.Services.DomainService); // extract domainUri and path @@ -115,9 +107,6 @@ namespace Umbraco.Web.Routing return null; } - if (route.StartsWith("err/")) - return null; - var domainHelper = new DomainHelper(umbracoContext.Application.Services.DomainService); // extract domainUri and path diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs index 03a50a4b99..05b6b0e468 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs @@ -67,6 +67,32 @@ namespace Umbraco.Web.Routing #region Public + /// + /// Tries to route the request. + /// + internal bool TryRouteRequest() + { + // disabled - is it going to change the routing? + //_pcr.OnPreparing(); + + FindDomain(); + if (_pcr.IsRedirect) return false; + if (_pcr.HasPublishedContent) return true; + FindPublishedContent(); + if (_pcr.IsRedirect) return false; + + // don't handle anything - we just want to ensure that we find the content + //HandlePublishedContent(); + //FindTemplate(); + //FollowExternalRedirect(); + //HandleWildcardDomains(); + + // disabled - we just want to ensure that we find the content + //_pcr.OnPrepared(); + + return _pcr.HasPublishedContent; + } + /// /// Prepares the request. /// diff --git a/src/Umbraco.Web/Routing/RedirectTrackingEventHandler.cs b/src/Umbraco.Web/Routing/RedirectTrackingEventHandler.cs index b13cb7b296..80e4192f72 100644 --- a/src/Umbraco.Web/Routing/RedirectTrackingEventHandler.cs +++ b/src/Umbraco.Web/Routing/RedirectTrackingEventHandler.cs @@ -295,8 +295,7 @@ namespace Umbraco.Web.Routing private static bool IsNotRoute(string route) { // null if content not found - // err/- if collision or anomaly or ... - return route == null || route.StartsWith("err/"); + return route == null; } // gets a value indicating whether server is 'slave' diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs index c6e08c16c5..54ef8a42b5 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; +using System.Web.Security; using Umbraco.Core.Models; using umbraco; +using Umbraco.Core; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; namespace Umbraco.Web.Routing @@ -62,34 +65,45 @@ namespace Umbraco.Web.Routing { urls.Add(ui.Text("content", "getUrlException", umbracoContext.Security.CurrentUser)); } - else if (url.StartsWith("#err-")) + else { - // route error, report - var id = int.Parse(url.Substring(5)); - var o = umbracoContext.ContentCache.GetById(id); - string s; - if (o == null) + // test for collisions + var uri = new Uri(url.TrimEnd('/'), UriKind.RelativeOrAbsolute); + if (uri.IsAbsoluteUri == false) uri = uri.MakeAbsolute(UmbracoContext.Current.CleanedUmbracoUrl); + var pcr = new PublishedContentRequest(uri, UmbracoContext.Current.RoutingContext, UmbracoConfig.For.UmbracoSettings().WebRouting, s => Roles.Provider.GetRolesForUser(s)); + pcr.Engine.TryRouteRequest(); + + if (pcr.HasPublishedContent == false) { - s = "(unknown)"; + urls.Add(ui.Text("content", "routeError", "(error)", umbracoContext.Security.CurrentUser)); + } + else if (pcr.PublishedContent.Id != content.Id) + { + var o = pcr.PublishedContent; + string s; + if (o == null) + { + s = "(unknown)"; + } + else + { + var l = new List(); + while (o != null) + { + l.Add(o.Name); + o = o.Parent; + } + l.Reverse(); + s = "/" + string.Join("/", l) + " (id=" + pcr.PublishedContent.Id + ")"; + + } + urls.Add(ui.Text("content", "routeError", s, umbracoContext.Security.CurrentUser)); } else { - var l = new List(); - while (o != null) - { - l.Add(o.Name); - o = o.Parent; - } - l.Reverse(); - s = "/" + string.Join("/", l) + " (id=" + id + ")"; - + urls.Add(url); + urls.AddRange(urlProvider.GetOtherUrls(content.Id)); } - urls.Add(ui.Text("content", "routeError", s, umbracoContext.Security.CurrentUser)); - } - else - { - urls.Add(url); - urls.AddRange(urlProvider.GetOtherUrls(content.Id)); } return urls; } From 13007647706181ee8bfe14667267c287a6735445 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 4 Jan 2017 14:12:45 +0000 Subject: [PATCH 163/599] Adding in server side validation - but need to clarify that this is the 'right' way to do it that wires up correctly/magically back to the UI components --- src/Umbraco.Web/Editors/TemplateController.cs | 13 +++++++++++++ .../Models/ContentEditing/TemplateDisplay.cs | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs index eec76ca34e..d0c17b7f4b 100644 --- a/src/Umbraco.Web/Editors/TemplateController.cs +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -6,8 +6,10 @@ using System.Web.Http; using AutoMapper; using Umbraco.Core.IO; using Umbraco.Core.Models; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; @@ -86,6 +88,17 @@ namespace Umbraco.Web.Editors /// public TemplateDisplay PostSave(TemplateDisplay display) { + + //Checking the submitted is valid with the Required attributes decorated on the ViewModel + if (ModelState.IsValid == false) + { + //Unsure of the standard or set way to do this?! + //throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse("MISSING NAME")); + + throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); + } + + if (display.Id > 0) { // update diff --git a/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs index dacccb8ceb..3c1ebe16c3 100644 --- a/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs @@ -1,21 +1,26 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; +using Umbraco.Core.Models.Validation; namespace Umbraco.Web.Models.ContentEditing { [DataContract(Name = "template", Namespace = "")] public class TemplateDisplay { + [DataMember(Name = "id")] public int Id { get; set; } + [Required] [DataMember(Name = "name")] public string Name { get; set; } + [RequiredForPersistence] [DataMember(Name = "alias")] public string Alias { get; set; } From 0e31fcf3db20e19f9f1dd790094cc067c06b5089 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 4 Jan 2017 15:36:29 +0100 Subject: [PATCH 164/599] U4-9337 - fix tests --- .../Routing/NiceUrlProviderTests.cs | 18 ++---------------- .../NiceUrlsProviderWithDomainsTests.cs | 13 ++----------- .../Routing/UrlsWithNestedDomains.cs | 10 +++++----- 3 files changed, 9 insertions(+), 32 deletions(-) diff --git a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs index 075205ef5c..c6970cbeb3 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs @@ -72,23 +72,9 @@ namespace Umbraco.Tests.Routing var cache = routingContext.UmbracoContext.ContentCache.InnerCache as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual(8, cachedRoutes.Count); - foreach (var sample in samples) - { - Assert.IsTrue(cachedRoutes.ContainsKey(sample.Key)); - Assert.AreEqual(sample.Value, cachedRoutes[sample.Key]); - } - - var cachedIds = cache.RoutesCache.GetCachedIds(); - Assert.AreEqual(8, cachedIds.Count); - - foreach (var sample in samples) - { - var key = sample.Value; - Assert.IsTrue(cachedIds.ContainsKey(key)); - Assert.AreEqual(sample.Key, cachedIds[key]); - } + // GetUrl does not write to cache + Assert.AreEqual(0, cachedRoutes.Count); } // test hideTopLevelNodeFromPath false diff --git a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs index 8cc975954b..6e71efcbf3 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs @@ -303,18 +303,9 @@ namespace Umbraco.Tests.Routing var cache = routingContext.UmbracoContext.ContentCache.InnerCache as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual(7, cachedRoutes.Count); - var cachedIds = cache.RoutesCache.GetCachedIds(); - Assert.AreEqual(7, cachedIds.Count); - - CheckRoute(cachedRoutes, cachedIds, 1001, "1001/"); - CheckRoute(cachedRoutes, cachedIds, 10011, "10011/"); - CheckRoute(cachedRoutes, cachedIds, 100111, "10011/1001-1-1"); - CheckRoute(cachedRoutes, cachedIds, 10012, "10012/"); - CheckRoute(cachedRoutes, cachedIds, 100121, "10012/1001-2-1"); - CheckRoute(cachedRoutes, cachedIds, 10013, "1001/1001-3"); - CheckRoute(cachedRoutes, cachedIds, 1002, "/1002"); + // GetUrl does not write to cache + Assert.AreEqual(0, cachedRoutes.Count); // use the cache Assert.AreEqual("/", routingContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false)); diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index 2e868f9f44..29a60232fa 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -44,8 +44,8 @@ namespace Umbraco.Tests.Routing // check that the proper route has been cached var cache = routingContext.UmbracoContext.ContentCache.InnerCache as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); + //var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + //Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); // route a rogue url url = "http://domain1.com/1001-1/1001-1-1"; @@ -61,9 +61,9 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(100111, pcr.PublishedContent.Id); // has the cache been polluted? - cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); // no - //Assert.AreEqual("1001/1001-1/1001-1-1", cachedRoutes[100111]); // yes + //cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + //Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); // no + ////Assert.AreEqual("1001/1001-1/1001-1-1", cachedRoutes[100111]); // yes // what's the nice url now? Assert.AreEqual("http://domain2.com/1001-1-1/", routingContext.UrlProvider.GetUrl(100111)); // good From c472d4975e0eb8657158e2b0ba304d7a79973e84 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 4 Jan 2017 16:31:47 +0100 Subject: [PATCH 165/599] Add some extra logging to ScheduledPublishing to more easily see where errors are coming from --- .../WebServices/ScheduledPublishController.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/WebServices/ScheduledPublishController.cs b/src/Umbraco.Web/WebServices/ScheduledPublishController.cs index fcf07126fb..433930dda9 100644 --- a/src/Umbraco.Web/WebServices/ScheduledPublishController.cs +++ b/src/Umbraco.Web/WebServices/ScheduledPublishController.cs @@ -44,7 +44,17 @@ namespace Umbraco.Web.WebServices } catch (Exception ee) { - LogHelper.Error("Error executing scheduled task", ee); + var errorMessage = "Error executing scheduled task"; + if (HttpContext != null && HttpContext.Request != null) + { + if (HttpContext.Request.Url != null) + errorMessage = string.Format("{0} | Request to {1}", errorMessage, HttpContext.Request.Url); + if (HttpContext.Request.UserHostAddress != null) + errorMessage = string.Format("{0} | Coming from {1}", errorMessage, HttpContext.Request.UserHostAddress); + if (HttpContext.Request.UrlReferrer != null) + errorMessage = string.Format("{0} | Referrer {1}", errorMessage, HttpContext.Request.UrlReferrer); + } + LogHelper.Error(errorMessage, ee); Response.StatusCode = 400; From 05cb30d79c218c88af96cd1d903db579c876e69a Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 4 Jan 2017 17:01:26 +0100 Subject: [PATCH 166/599] Trim the file name so that it doesn't get accepted by the server if it's a disallowed file --- .../Editors/ContentControllerBase.cs | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs index 495217cec2..ab3145cd79 100644 --- a/src/Umbraco.Web/Editors/ContentControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs @@ -88,35 +88,37 @@ namespace Umbraco.Web.Editors where TPersisted : IContentBase { //Map the property values - foreach (var p in contentItem.ContentDto.Properties) + foreach (var property in contentItem.ContentDto.Properties) { //get the dbo property - var dboProperty = contentItem.PersistedContent.Properties[p.Alias]; + var dboProperty = contentItem.PersistedContent.Properties[property.Alias]; //create the property data to send to the property editor - var d = new Dictionary(); + var dictionary = new Dictionary(); //add the files if any - var files = contentItem.UploadedFiles.Where(x => x.PropertyAlias == p.Alias).ToArray(); - if (files.Length > 0) - { - d.Add("files", files); - } + var files = contentItem.UploadedFiles.Where(x => x.PropertyAlias == property.Alias).ToArray(); + + foreach (var file in files) + file.FileName = file.FileName.TrimEnd(); + + if (files.Any()) + dictionary.Add("files", files); - var data = new ContentPropertyData(p.Value, p.PreValues, d); + var data = new ContentPropertyData(property.Value, property.PreValues, dictionary); //get the deserialized value from the property editor - if (p.PropertyEditor == null) + if (property.PropertyEditor == null) { - LogHelper.Warn("No property editor found for property " + p.Alias); + LogHelper.Warn("No property editor found for property " + property.Alias); } else { - var valueEditor = p.PropertyEditor.ValueEditor; + var valueEditor = property.PropertyEditor.ValueEditor; //don't persist any bound value if the editor is readonly if (valueEditor.IsReadOnly == false) { - var propVal = p.PropertyEditor.ValueEditor.ConvertEditorToDb(data, dboProperty.Value); - var supportTagsAttribute = TagExtractor.GetAttribute(p.PropertyEditor); + var propVal = property.PropertyEditor.ValueEditor.ConvertEditorToDb(data, dboProperty.Value); + var supportTagsAttribute = TagExtractor.GetAttribute(property.PropertyEditor); if (supportTagsAttribute != null) { TagExtractor.SetPropertyTags(dboProperty, data, propVal, supportTagsAttribute); From 8be0f683906873f18ce888e866d5a92742c6111e Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 5 Jan 2017 10:29:03 +1100 Subject: [PATCH 167/599] Adds logic to validate an entity's path when it is used to GetDescendants, adds logic to fix an entity's path when it's state is going to be persisted when GetDescendants is used (i.e. MoveToRecycleBin), added this logic to Media too, adds unit tests to support --- .../Models/UmbracoEntityExtensions.cs | 72 ++++++++++ src/Umbraco.Core/Services/ContentService.cs | 40 +++++- src/Umbraco.Core/Services/MediaService.cs | 116 ++++++++++------ .../Models/UmbracoEntityTests.cs | 129 ++++++++++++++++++ .../Services/ContentServiceTests.cs | 16 ++- 5 files changed, 318 insertions(+), 55 deletions(-) diff --git a/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs index 987be2a276..1b4cc20d73 100644 --- a/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs +++ b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs @@ -4,12 +4,84 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web.UI.WebControls; +using Umbraco.Core.Logging; using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Models { internal static class UmbracoEntityExtensions { + /// + /// Does a quick check on the entity's set path to ensure that it's valid and consistent + /// + /// + /// + public static bool ValidatePath(this IUmbracoEntity entity) + { + //don't validate if it's empty and it has no id + if (entity.HasIdentity == false && entity.Path.IsNullOrWhiteSpace()) + return true; + + if (entity.Path.IsNullOrWhiteSpace()) + return false; + + var pathParts = entity.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if (pathParts.Length < 2) + { + //a path cannot be less than 2 parts, at a minimum it must be root (-1) and it's own id + return false; + } + + if (entity.ParentId != default(int) && pathParts[pathParts.Length - 2] != entity.ParentId.ToInvariantString()) + { + //the 2nd last id in the path must be it's parent id + return false; + } + + return true; + } + + /// + /// This will validate the entity's path and if it's invalid it will fix it, if fixing is required it will recursively + /// check and fix all ancestors if required. + /// + /// + /// + /// A callback specified to retrieve the parent entity of the entity + /// A callback specified to update a fixed entity + public static void EnsureValidPath(this T entity, + ILogger logger, + Func getParent, + Action update) + where T: IUmbracoEntity + { + if (entity.HasIdentity == false) + throw new InvalidOperationException("Could not ensure the entity path, the entity has not been assigned an identity"); + + if (entity.ValidatePath() == false) + { + logger.Warn(typeof(UmbracoEntityExtensions), string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", entity.Id, entity.Path, entity.ParentId)); + if (entity.ParentId == -1) + { + entity.Path = string.Concat("-1,", entity.Id); + //path changed, update it + update(entity); + } + else + { + var parent = getParent(entity); + if (parent == null) + throw new NullReferenceException("Could not ensure path for entity " + entity.Id + " could not resolve it's parent " + entity.ParentId); + + //the parent must also be valid! + parent.EnsureValidPath(logger, getParent, update); + + entity.Path = string.Concat(parent.Path, ",", entity.Id); + //path changed, update it + update(entity); + } + } + } public static bool HasChildren(this IUmbracoEntity entity) { diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index a4c6d354c1..e0edaa38c4 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; +using System.IO; using System.Linq; using System.Threading; using System.Xml; @@ -11,6 +12,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Membership; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; @@ -714,7 +716,12 @@ namespace Umbraco.Core.Services /// An Enumerable list of objects public IEnumerable GetDescendants(IContent content) { - using (var repository = RepositoryFactory.CreateContentRepository(UowProvider.GetUnitOfWork())) + //This is a check to ensure that the path is correct for this entity to avoid problems like: http://issues.umbraco.org/issue/U4-9336 due to data corruption + if (content.ValidatePath() == false) + throw new InvalidDataException(string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", content.Id, content.Path, content.ParentId)); + + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { var pathMatch = content.Path + ","; var query = Query.Builder.Where(x => x.Path.StartsWith(pathMatch) && x.Id != content.Id); @@ -996,6 +1003,10 @@ namespace Umbraco.Core.Services using (new WriteLock(Locker)) { + //Hack: this ensures that the entity's path is valid and if not it fixes/persists it + //see: http://issues.umbraco.org/issue/U4-9336 + content.EnsureValidPath(Logger, entity => GetById(entity.ParentId), QuickUpdate); + var originalPath = content.Path; if (Trashing.IsRaisedEventCancelled( @@ -1683,16 +1694,16 @@ namespace Umbraco.Core.Services /// True if sorting succeeded, otherwise False public bool Sort(IEnumerable items, int userId = 0, bool raiseEvents = true) { + var asArray = items.ToArray(); if (raiseEvents) { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(items), this)) + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(asArray), this)) return false; } var shouldBePublished = new List(); var shouldBeSaved = new List(); - - var asArray = items.ToArray(); + using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); @@ -1812,6 +1823,23 @@ namespace Umbraco.Core.Services #region Private Methods + /// + /// Hack: This is used to fix some data if an entity's properties are invalid/corrupt + /// + /// + private void QuickUpdate(IContent content) + { + if (content == null) throw new ArgumentNullException("content"); + if (content.HasIdentity == false) throw new InvalidOperationException("Cannot update an entity without an Identity"); + + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateContentRepository(uow)) + { + repository.AddOrUpdate(content); + uow.Commit(); + } + } + private void Audit(AuditType type, string message, int userId, int objectId) { var uow = UowProvider.GetUnitOfWork(); @@ -1919,6 +1947,10 @@ namespace Umbraco.Core.Services using (new WriteLock(Locker)) { + //Hack: this ensures that the entity's path is valid and if not it fixes/persists it + //see: http://issues.umbraco.org/issue/U4-9336 + content.EnsureValidPath(Logger, entity => GetById(entity.ParentId), QuickUpdate); + var result = new List>(); //Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index a1182ce80a..bd373f1ec0 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; +using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading; @@ -527,6 +528,10 @@ namespace Umbraco.Core.Services /// An Enumerable flat list of objects public IEnumerable GetDescendants(IMedia media) { + //This is a check to ensure that the path is correct for this entity to avoid problems like: http://issues.umbraco.org/issue/U4-9336 due to data corruption + if (media.ValidatePath() == false) + throw new InvalidDataException(string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", media.Id, media.Path, media.ParentId)); + var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateMediaRepository(uow)) { @@ -619,7 +624,7 @@ namespace Umbraco.Core.Services public IMedia GetMediaByPath(string mediaPath) { var umbracoFileValue = mediaPath; - + const string Pattern = ".*[_][0-9]+[x][0-9]+[.].*"; var isResized = Regex.IsMatch(mediaPath, Pattern); @@ -998,56 +1003,63 @@ namespace Umbraco.Core.Services { if (media == null) throw new ArgumentNullException("media"); - var originalPath = media.Path; - var evtMsgs = EventMessagesFactory.Get(); - if (Trashing.IsRaisedEventCancelled( - new MoveEventArgs(new MoveEventInfo(media, originalPath, Constants.System.RecycleBinMedia)), this)) + using (new WriteLock(Locker)) { - return OperationStatus.Cancelled(evtMsgs); - } + //Hack: this ensures that the entity's path is valid and if not it fixes/persists it + //see: http://issues.umbraco.org/issue/U4-9336 + media.EnsureValidPath(Logger, entity => GetById(entity.ParentId), QuickUpdate); - var moveInfo = new List> - { - new MoveEventInfo(media, originalPath, Constants.System.RecycleBinMedia) - }; + var originalPath = media.Path; - //Find Descendants, which will be moved to the recycle bin along with the parent/grandparent. - var descendants = GetDescendants(media).OrderBy(x => x.Level).ToList(); - - var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateMediaRepository(uow)) - { - //TODO: This should be part of the repo! - - //Remove 'published' xml from the cmsContentXml table for the unpublished media - uow.Database.Delete("WHERE nodeId = @Id", new { Id = media.Id }); - - media.ChangeTrashedState(true, Constants.System.RecycleBinMedia); - repository.AddOrUpdate(media); - - //Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId - foreach (var descendant in descendants) + if (Trashing.IsRaisedEventCancelled( + new MoveEventArgs(new MoveEventInfo(media, originalPath, Constants.System.RecycleBinMedia)), this)) { - //Remove 'published' xml from the cmsContentXml table for the unpublished media - uow.Database.Delete("WHERE nodeId = @Id", new { Id = descendant.Id }); - - descendant.ChangeTrashedState(true, descendant.ParentId); - repository.AddOrUpdate(descendant); - - moveInfo.Add(new MoveEventInfo(descendant, descendant.Path, descendant.ParentId)); + return OperationStatus.Cancelled(evtMsgs); } - uow.Commit(); + var moveInfo = new List> + { + new MoveEventInfo(media, originalPath, Constants.System.RecycleBinMedia) + }; + + //Find Descendants, which will be moved to the recycle bin along with the parent/grandparent. + var descendants = GetDescendants(media).OrderBy(x => x.Level).ToList(); + + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateMediaRepository(uow)) + { + //TODO: This should be part of the repo! + + //Remove 'published' xml from the cmsContentXml table for the unpublished media + uow.Database.Delete("WHERE nodeId = @Id", new { Id = media.Id }); + + media.ChangeTrashedState(true, Constants.System.RecycleBinMedia); + repository.AddOrUpdate(media); + + //Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId + foreach (var descendant in descendants) + { + //Remove 'published' xml from the cmsContentXml table for the unpublished media + uow.Database.Delete("WHERE nodeId = @Id", new { Id = descendant.Id }); + + descendant.ChangeTrashedState(true, descendant.ParentId); + repository.AddOrUpdate(descendant); + + moveInfo.Add(new MoveEventInfo(descendant, descendant.Path, descendant.ParentId)); + } + + uow.Commit(); + } + + Trashed.RaiseEvent( + new MoveEventArgs(false, evtMsgs, moveInfo.ToArray()), this); + + Audit(AuditType.Move, "Move Media to Recycle Bin performed by user", userId, media.Id); + + return OperationStatus.Success(evtMsgs); } - - Trashed.RaiseEvent( - new MoveEventArgs(false, evtMsgs, moveInfo.ToArray()), this); - - Audit(AuditType.Move, "Move Media to Recycle Bin performed by user", userId, media.Id); - - return OperationStatus.Success(evtMsgs); } /// @@ -1064,8 +1076,6 @@ namespace Umbraco.Core.Services ((IMediaServiceOperations)this).Delete(media, userId); } - - /// /// Permanently deletes versions from an object prior to a specific date. /// This method will never delete the latest version of a content item. @@ -1154,7 +1164,6 @@ namespace Umbraco.Core.Services public bool Sort(IEnumerable items, int userId = 0, bool raiseEvents = true) { var asArray = items.ToArray(); - if (raiseEvents) { if (Saving.IsRaisedEventCancelled(new SaveEventArgs(asArray), this)) @@ -1316,6 +1325,23 @@ namespace Umbraco.Core.Services } } + /// + /// Hack: This is used to fix some data if an entity's properties are invalid/corrupt + /// + /// + private void QuickUpdate(IMedia media) + { + if (media == null) throw new ArgumentNullException("media"); + if (media.HasIdentity == false) throw new InvalidOperationException("Cannot update an entity without an Identity"); + + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateMediaRepository(uow)) + { + repository.AddOrUpdate(media); + uow.Commit(); + } + } + #region Event Handlers /// diff --git a/src/Umbraco.Tests/Models/UmbracoEntityTests.cs b/src/Umbraco.Tests/Models/UmbracoEntityTests.cs index 651e955f33..7186474999 100644 --- a/src/Umbraco.Tests/Models/UmbracoEntityTests.cs +++ b/src/Umbraco.Tests/Models/UmbracoEntityTests.cs @@ -1,7 +1,10 @@ using System; using System.Diagnostics; +using Moq; using NUnit.Framework; +using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Serialization; namespace Umbraco.Tests.Models @@ -9,6 +12,132 @@ namespace Umbraco.Tests.Models [TestFixture] public class UmbracoEntityTests { + [Test] + public void Validate_Path() + { + var entity = new UmbracoEntity(); + + //it's empty with no id so we need to allow it + Assert.IsTrue(entity.ValidatePath()); + + entity.Id = 1234; + + //it has an id but no path, so we can't allow it + Assert.IsFalse(entity.ValidatePath()); + + entity.Path = "-1"; + + //invalid path + Assert.IsFalse(entity.ValidatePath()); + + entity.Path = string.Concat("-1,", entity.Id); + + //valid path + Assert.IsTrue(entity.ValidatePath()); + } + + [Test] + public void Ensure_Path_Throws_Without_Id() + { + var entity = new UmbracoEntity(); + + //no id assigned + Assert.Throws(() => entity.EnsureValidPath(Mock.Of(), umbracoEntity => new UmbracoEntity(), umbracoEntity => { })); + } + + [Test] + public void Ensure_Path_Throws_Without_Parent() + { + var entity = new UmbracoEntity {Id = 1234}; + + //no parent found + Assert.Throws(() => entity.EnsureValidPath(Mock.Of(), umbracoEntity => null, umbracoEntity => { })); + } + + [Test] + public void Ensure_Path_Entity_At_Root() + { + var entity = new UmbracoEntity + { + Id = 1234, + ParentId = -1 + }; + + + entity.EnsureValidPath(Mock.Of(), umbracoEntity => null, umbracoEntity => { }); + + //works because it's under the root + Assert.AreEqual("-1,1234", entity.Path); + } + + [Test] + public void Ensure_Path_Entity_Valid_Parent() + { + var entity = new UmbracoEntity + { + Id = 1234, + ParentId = 888 + }; + + entity.EnsureValidPath(Mock.Of(), umbracoEntity => umbracoEntity.ParentId == 888 ? new UmbracoEntity{Id = 888, Path = "-1,888"} : null, umbracoEntity => { }); + + //works because the parent was found + Assert.AreEqual("-1,888,1234", entity.Path); + } + + [Test] + public void Ensure_Path_Entity_Valid_Recursive_Parent() + { + var parentA = new UmbracoEntity + { + Id = 999, + ParentId = -1 + }; + + var parentB = new UmbracoEntity + { + Id = 888, + ParentId = 999 + }; + + var parentC = new UmbracoEntity + { + Id = 777, + ParentId = 888 + }; + + var entity = new UmbracoEntity + { + Id = 1234, + ParentId = 777 + }; + + Func getParent = umbracoEntity => + { + switch (umbracoEntity.ParentId) + { + case 999: + return parentA; + case 888: + return parentB; + case 777: + return parentC; + case 1234: + return entity; + default: + return null; + } + }; + + //this will recursively fix all paths + entity.EnsureValidPath(Mock.Of(), getParent, umbracoEntity => { }); + + Assert.AreEqual("-1,999", parentA.Path); + Assert.AreEqual("-1,999,888", parentB.Path); + Assert.AreEqual("-1,999,888,777", parentC.Path); + Assert.AreEqual("-1,999,888,777,1234", entity.Path); + } + [Test] public void UmbracoEntity_Can_Be_Initialized_From_Dynamic() { diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index afffc8ad1e..72a58861e2 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -55,7 +55,7 @@ namespace Umbraco.Tests.Services /// Regression test: http://issues.umbraco.org/issue/U4-9336 /// [Test] - public void Deleting_Node_With_Invalid_Path() + public void Moving_Node_To_Recycle_Bin_With_Invalid_Path() { var contentService = ServiceContext.ContentService; var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1); @@ -72,15 +72,19 @@ namespace Umbraco.Tests.Services //now make the data corrupted :/ DatabaseContext.Database.Execute("UPDATE umbracoNode SET path = '-1' WHERE id = @id", new {id = content.Id}); - // need to clear the caches otherwise the ContentService will just serve is a cached version with the non-updated path from db. - ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearAllCache(); - //re-get with the corrupt path content = contentService.GetById(content.Id); - // Note to Shan: put a breakpoint at ContentService.cs line: 1021 // here we get all descendants by the path of the node being moved to bin, and unpublish all of them. - ServiceContext.ContentService.MoveToRecycleBin(content); + // since the path is invalid, there's logic in here to fix that if it's possible and re-persist the entity. + var moveResult = ServiceContext.ContentService.WithResult().MoveToRecycleBin(content); + + Assert.IsTrue(moveResult.Success); + + //re-get with the fixed/moved path + content = contentService.GetById(content.Id); + + Assert.AreEqual("-1,-20," + content.Id, content.Path); //re-get hierarchy = contentService.GetByIds(hierarchy.Select(x => x.Id).ToArray()).OrderBy(x => x.Level).ToArray(); From 530a80d7eaa480ff23cca0d1e0e519896b1d9901 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 5 Jan 2017 10:43:33 +1100 Subject: [PATCH 168/599] Adds a check to validate the NodeDto when persisting the item to validate that it's path is correct this should prevent any data corruption in the future - if the data corruption actually came from Core --- .../Models/UmbracoEntityExtensions.cs | 30 +++++++++++++++++++ .../Repositories/ContentRepository.cs | 2 ++ .../Repositories/MediaRepository.cs | 2 ++ 3 files changed, 34 insertions(+) diff --git a/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs index 1b4cc20d73..42e1a0d415 100644 --- a/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs +++ b/src/Umbraco.Core/Models/UmbracoEntityExtensions.cs @@ -1,16 +1,46 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web.UI.WebControls; using Umbraco.Core.Logging; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Rdbms; namespace Umbraco.Core.Models { internal static class UmbracoEntityExtensions { + /// + /// Does a quick check on the entity's set path to ensure that it's valid and consistent + /// + /// + /// + public static void ValidatePathWithException(this NodeDto entity) + { + //don't validate if it's empty and it has no id + if (entity.NodeId == default(int) && entity.Path.IsNullOrWhiteSpace()) + return; + + if (entity.Path.IsNullOrWhiteSpace()) + throw new InvalidDataException(string.Format("The content item {0} has an empty path: {1} with parentID: {2}", entity.NodeId, entity.Path, entity.ParentId)); + + var pathParts = entity.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if (pathParts.Length < 2) + { + //a path cannot be less than 2 parts, at a minimum it must be root (-1) and it's own id + throw new InvalidDataException(string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", entity.NodeId, entity.Path, entity.ParentId)); + } + + if (entity.ParentId != default(int) && pathParts[pathParts.Length - 2] != entity.ParentId.ToInvariantString()) + { + //the 2nd last id in the path must be it's parent id + throw new InvalidDataException(string.Format("The content item {0} has an invalid path: {1} with parentID: {2}", entity.NodeId, entity.Path, entity.ParentId)); + } + } + /// /// Does a quick check on the entity's set path to ensure that it's valid and consistent /// diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 90f005f68e..14e6c0e9a6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -342,6 +342,7 @@ namespace Umbraco.Core.Persistence.Repositories //Update with new correct path nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId); + nodeDto.ValidatePathWithException(); Database.Update(nodeDto); //Update entity with correct values @@ -479,6 +480,7 @@ namespace Umbraco.Core.Persistence.Repositories //Updates the (base) node data - umbracoNode var nodeDto = dto.ContentVersionDto.ContentDto.NodeDto; + nodeDto.ValidatePathWithException(); var o = Database.Update(nodeDto); //Only update this DTO if the contentType has actually changed diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 092b7df025..08c2f7e0be 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -324,6 +324,7 @@ namespace Umbraco.Core.Persistence.Repositories //Update with new correct path nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId); + nodeDto.ValidatePathWithException(); Database.Update(nodeDto); //Update entity with correct values @@ -397,6 +398,7 @@ namespace Umbraco.Core.Persistence.Repositories //Updates the (base) node data - umbracoNode var nodeDto = dto.ContentDto.NodeDto; + nodeDto.ValidatePathWithException(); var o = Database.Update(nodeDto); //Only update this DTO if the contentType has actually changed From 1df65bc3ce1d450485cf163a979a488980a1b724 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 5 Jan 2017 16:00:45 +1100 Subject: [PATCH 169/599] Updates to latest ImageProcessor.Web to fix mime types when stored outside web root --- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- src/Umbraco.Web.UI/packages.config | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 79796e6bb7..19bab66cb3 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -138,8 +138,8 @@ ..\packages\ImageProcessor.2.5.0\lib\net45\ImageProcessor.dll - - ..\packages\ImageProcessor.Web.4.7.0\lib\net45\ImageProcessor.Web.dll + + ..\packages\ImageProcessor.Web.4.7.1\lib\net45\ImageProcessor.Web.dll False diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 2a87d3dce9..b708ff8de0 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -6,7 +6,7 @@ - + From c2359a96f89ae1331a5fdff159a951656cf51192 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 5 Jan 2017 17:09:37 +1100 Subject: [PATCH 170/599] missed nuspec update --- build/NuSpecs/UmbracoCms.Core.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 03818d13f8..ab79494f71 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -34,7 +34,7 @@ - + From 8b479b57e5be5cc097ca01d4c2f4345ed1a5e6b3 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 5 Jan 2017 08:45:04 +0100 Subject: [PATCH 171/599] Change query condition from name to alias --- src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs index a1047b8f19..a49ddeb404 100644 --- a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs +++ b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs @@ -24,6 +24,9 @@ private static string BuildConditionString(this QueryCondition condition, string prefix, int token = -1) { + + + var operand = string.Empty; var value = string.Empty; var constraintValue = string.Empty; @@ -56,10 +59,10 @@ operand = " <= "; break; case Operathor.Contains: - value = string.Format("{0}{1}.Contains({2})", prefix, condition.Property.Name, constraintValue); + value = string.Format("{0}{1}.Contains({2})", prefix, condition.Property.Alias, constraintValue); break; case Operathor.NotContains: - value = string.Format("!{0}{1}.Contains({2})", prefix, condition.Property.Name, constraintValue); + value = string.Format("!{0}{1}.Contains({2})", prefix, condition.Property.Alias, constraintValue); break; default : operand = " == "; @@ -69,7 +72,7 @@ if (string.IsNullOrEmpty(value) == false) return value; - return string.Format("{0}{1}{2}{3}", prefix, condition.Property.Name, operand, constraintValue); + return string.Format("{0}{1}{2}{3}", prefix, condition.Property.Alias, operand, constraintValue); } } From d5e66252dc828d82f031569a29a1d2ff03b6a174 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 5 Jan 2017 09:25:30 +0100 Subject: [PATCH 172/599] U4-9337 - minor simplification --- .../XmlPublishedCache/PublishedContentCache.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index 0b027deb92..d9b0363408 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -172,15 +172,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { var e = n as XmlElement; if (e == null) continue; - + var id = NavigateElementRoute(e, urlParts); if (id > 0) return id; } - if (urlParts.Length == 1) - return NavigateElementRoute(elt, urlParts); - - return -1; + if (urlParts.Length > 1) + return -1; } return NavigateElementRoute(elt, urlParts); @@ -205,7 +203,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { var child = o as XmlElement; if (child == null) continue; - + var noNode = UseLegacySchema ? child.Name != "node" : child.GetAttributeNode("isDoc") == null; From 2041e85b9e5d2284df4cb02f59988d46921e0496 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 5 Jan 2017 10:46:40 +0100 Subject: [PATCH 173/599] U4-9337 - add doc and test --- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 99a83c9c55..d90c0e6345 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -160,6 +160,7 @@ + From 89be1cf5d2ce5c7feb0d24b51c9a2914d611e4e0 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 5 Jan 2017 10:50:03 +0100 Subject: [PATCH 174/599] U4-9337 - with missing file... --- .../Routing/NiceUrlRoutesTests.cs | 256 ++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs diff --git a/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs b/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs new file mode 100644 index 0000000000..79f2b2b64d --- /dev/null +++ b/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs @@ -0,0 +1,256 @@ +using System; +using NUnit.Framework; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web.PublishedCache.XmlPublishedCache; +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.Routing +{ + [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerFixture)] + [TestFixture] + public class NiceUrlRoutesTests : BaseRoutingTest + { + #region Test Setup + + protected override void FreezeResolution() + { + SiteDomainHelperResolver.Current = new SiteDomainHelperResolver(new SiteDomainHelper()); + + base.FreezeResolution(); + } + + private IUmbracoSettingsSection _umbracoSettings; + + public override void Initialize() + { + base.Initialize(); + + //generate new mock settings and assign so we can configure in individual tests + _umbracoSettings = SettingsForTests.GenerateMockSettings(); + SettingsForTests.ConfigureSettings(_umbracoSettings); + } + + protected override string GetXmlContent(int templateId) + { + return @" + + +]> + + + + + + + + + + + + + + + + + + + + + + + +"; + } + + #endregion + + /* + * Just so it's documented somewhere, as of jan. 2017, routes obey the following pseudo-code: + +GetByRoute(route, hide = null): + + route is "[id]/[path]" + + hide = hide ?? global.hide + + root = id ? node(id) : document + + content = cached(route) ?? DetermineIdByRoute(route, hide) + + # route is "1234/path/to/content", finds "content" + # but if there is domain 5678 on "to", the *true* route of "content" is "5678/content" + # so although the route does match, we don't cache it + # there are not other reason not to cache it + + if content and no domain between root and content: + cache route + + return content + + +DetermineIdByRoute(route, hide): + + route is "[id]/[path]" + + try return NavigateRoute(id ?? 0, path, hide:hide) + return null + + +NavigateRoute(id, path, hide): + + if path: + if id: + start = node(id) + else: + start = document + + # 'navigate ... from ...' uses lowest sortOrder in case of collision + + if hide and ![id]: + # if hiding, then for "/foo" we want to look for "/[any]/foo" + for each child of start: + try return navigate path from child + + # but if it fails, we also want to try "/foo" + # fail now if more than one part eg "/foo/bar" + if path is "/[any]/...": + fail + + try return navigate path from start + + else: + if id: + return node(id) + else: + return root node with lowest sortOrder + + +GetRouteById(id): + + + route = cached(id) + if route: + return route + + # never cache the route, it may be colliding + + return DetermineRouteById(id) + + + +DetermineRouteById(id): + + + node = node(id) + + walk up from node to domain or root, assemble parts = url segments + + if !domain and global.hide: + if id.parent: + # got /top/[path]content, can remove /top + remove top part + else: + # got /content, should remove only if it is the + # node with lowest sort order + root = root node with lowest sortOrder + if root == node: + remove top part + + compose path from parts + route = assemble "[domain.id]/[path]" + return route + + */ + + /* + * The Xml structure for the following tests is: + * + * root + * A 1000 + * B 1001 + * C 1002 + * D 1003 + * X 2000 + * Y 2001 + * Z 2002 + * A 2003 + * B 2004 + * C 2005 + * E 2006 + * + * And the tests should verify all the quirks that are due to + * hideTopLevelFromPath + */ + + [TestCase(1000, false, "/a")] + [TestCase(1001, false, "/a/b")] + [TestCase(1002, false, "/a/b/c")] + [TestCase(1003, false, "/a/b/c/d")] + [TestCase(2000, false, "/x")] + [TestCase(2001, false, "/x/y")] + [TestCase(2002, false, "/x/y/z")] + [TestCase(2003, false, "/x/a")] + [TestCase(2004, false, "/x/b")] + [TestCase(2005, false, "/x/b/c")] + [TestCase(2006, false, "/x/b/e")] + [TestCase(1000, true, "/")] + [TestCase(1001, true, "/b")] + [TestCase(1002, true, "/b/c")] + [TestCase(1003, true, "/b/c/d")] + [TestCase(2000, true, "/x")] + [TestCase(2001, true, "/y")] + [TestCase(2002, true, "/y/z")] + [TestCase(2003, true, "/a")] + [TestCase(2004, true, "/b")] // collision! + [TestCase(2005, true, "/b/c")] // collision! + [TestCase(2006, true, "/b/e")] // risky! + public void GetRouteById(int id, bool hide, string expected) + { + var umbracoContext = GetUmbracoContext("/test", 0); + var cache = umbracoContext.ContentCache.InnerCache as PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + + SettingsForTests.HideTopLevelNodeFromPath = hide; + + var route = cache.GetRouteById(umbracoContext, false, id); + Assert.AreEqual(expected, route); + } + + [TestCase("/", false, 1000)] + [TestCase("/a", false, 1000)] // yes! + [TestCase("/a/b", false, 1001)] + [TestCase("/a/b/c", false, 1002)] + [TestCase("/a/b/c/d", false, 1003)] + [TestCase("/x", false, 2000)] + [TestCase("/", true, 1000)] + [TestCase("/a", true, 2003)] + [TestCase("/a/b", true, -1)] + [TestCase("/x", true, 2000)] // oops! + [TestCase("/x/y", true, -1)] // yes! + [TestCase("/y", true, 2001)] + [TestCase("/y/z", true, 2002)] + [TestCase("/b", true, 1001)] // (hence the 2004 collision) + [TestCase("/b/c", true, 1002)] // (hence the 2005 collision) + public void GetByRoute(string route, bool hide, int expected) + { + var umbracoContext = GetUmbracoContext("/test", 0); + var cache = umbracoContext.ContentCache.InnerCache as PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + + SettingsForTests.HideTopLevelNodeFromPath = hide; + + var content = cache.GetByRoute(umbracoContext, false, route); + if (expected < 0) + { + Assert.IsNull(content); + } + else + { + Assert.IsNotNull(content); + Assert.AreEqual(expected, content.Id); + } + } + } +} From 2196ab3713f3e9d655ca13fdb4ce0b30d7586bff Mon Sep 17 00:00:00 2001 From: Claus Date: Thu, 5 Jan 2017 11:10:21 +0100 Subject: [PATCH 175/599] changing this to use ToSafeFileName instead of just trimming for blank characters. issuing a request with any character not allowed in a file path, appended to the filename - allows you to trick the validation like with the blank characters appended. --- src/Umbraco.Web/Editors/ContentControllerBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs index ab3145cd79..0cddb6fcd5 100644 --- a/src/Umbraco.Web/Editors/ContentControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs @@ -99,7 +99,7 @@ namespace Umbraco.Web.Editors var files = contentItem.UploadedFiles.Where(x => x.PropertyAlias == property.Alias).ToArray(); foreach (var file in files) - file.FileName = file.FileName.TrimEnd(); + file.FileName = file.FileName.ToSafeFileName(); if (files.Any()) dictionary.Add("files", files); From 12e48d069b08395e40f8696f54f1fabf2363c68f Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 5 Jan 2017 11:34:38 +0100 Subject: [PATCH 176/599] Fixes issue with DateTime values and strongly typed queries --- .../overlays/querybuilder/querybuilder.html | 28 +++++++++++-------- .../components/umb-date-time-picker.html | 2 +- .../Models/TemplateQuery/QueryCondition.cs | 21 +++++++++++++- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html index 906bf0f84d..36f729adb0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html @@ -2,7 +2,7 @@
    -
    +
    I want @@ -67,15 +67,21 @@
    - - - - - - - + + + + + + + + + + + + + + @@ -125,7 +131,7 @@
     {{model.result.queryExpression}}
    -		
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-date-time-picker.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-date-time-picker.html index aaf8cfe62d..6b37381113 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-date-time-picker.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-date-time-picker.html @@ -1,5 +1,5 @@
    - + diff --git a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs index a49ddeb404..36593736d3 100644 --- a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs +++ b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs @@ -31,11 +31,27 @@ var value = string.Empty; var constraintValue = string.Empty; + //if a token is used, use a token placeholder, otherwise, use the actual value if(token >= 0){ constraintValue = string.Format("@{0}", token); }else { - constraintValue = condition.Property.Type == "string" ? string.Format("\"{0}\"", condition.ConstraintValue) : condition.ConstraintValue; + + //modify the format of the constraint value + switch (condition.Property.Type) + { + case "string": + constraintValue = string.Format("\"{0}\"", condition.ConstraintValue); + break; + case "datetime": + constraintValue = string.Format("DateTime.Parse(\"{0}\")", condition.ConstraintValue); + break; + default: + constraintValue = condition.ConstraintValue; + break; + } + + // constraintValue = condition.Property.Type == "string" ? string.Format("\"{0}\"", condition.ConstraintValue) : condition.ConstraintValue; } switch (condition.Term.Operathor) @@ -69,9 +85,12 @@ break; } + if (string.IsNullOrEmpty(value) == false) return value; + + return string.Format("{0}{1}{2}{3}", prefix, condition.Property.Alias, operand, constraintValue); } From e6b1ecb8516ec413449e06ef1f7236b58f6b4c44 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 5 Jan 2017 11:51:54 +0100 Subject: [PATCH 177/599] Better way of cleaning the file extension to prevent XSS attacks --- src/Umbraco.Web/Editors/MediaController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 7a7e349a3c..fb17d9462e 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -525,7 +525,8 @@ namespace Umbraco.Web.Editors foreach (var file in result.FileData) { var fileName = file.Headers.ContentDisposition.FileName.Trim(new[] { '\"' }).TrimEnd(); - var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower(); + var safeFileName = fileName.ToSafeFileName(); + var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower(); if (UmbracoConfig.For.UmbracoSettings().Content.DisallowedUploadFiles.Contains(ext) == false) { From 7ebf51abb700a3684d39c506c35bba4035eaca7e Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 5 Jan 2017 14:58:05 +0100 Subject: [PATCH 178/599] deploy-102 - guid the relation types --- src/Umbraco.Core/Constants-ObjectTypes.cs | 10 ++++ .../Models/Rdbms/RelationTypeDto.cs | 4 ++ src/Umbraco.Core/Models/UmbracoObjectTypes.cs | 9 +++- .../Factories/RelationTypeFactory.cs | 6 ++- .../Migrations/Initial/BaseDataCreation.cs | 8 ++- .../AddRelationTypeUniqueIdColumn.cs | 49 +++++++++++++++++++ .../Interfaces/IRelationTypeRepository.cs | 9 ++-- .../Repositories/RelationTypeRepository.cs | 20 ++++++++ src/Umbraco.Core/Services/IRelationService.cs | 7 +++ src/Umbraco.Core/Services/RelationService.cs | 13 +++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + 11 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddRelationTypeUniqueIdColumn.cs diff --git a/src/Umbraco.Core/Constants-ObjectTypes.cs b/src/Umbraco.Core/Constants-ObjectTypes.cs index 3f9974166c..8edc552012 100644 --- a/src/Umbraco.Core/Constants-ObjectTypes.cs +++ b/src/Umbraco.Core/Constants-ObjectTypes.cs @@ -152,6 +152,16 @@ namespace Umbraco.Core /// Guid for a Lock object. ///
    public static readonly Guid LockObjectGuid = new Guid(LockObject); + + /// + /// Guid for a relation type. + /// + public const string RelationType = "B1988FAD-8675-4F47-915A-B3A602BC5D8D"; + + /// + /// Guid for a relation type. + /// + public static readonly Guid RelationTypeGuid = new Guid(RelationType); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/RelationTypeDto.cs b/src/Umbraco.Core/Models/Rdbms/RelationTypeDto.cs index 8a6eb5c02a..54052f58a3 100644 --- a/src/Umbraco.Core/Models/Rdbms/RelationTypeDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/RelationTypeDto.cs @@ -15,6 +15,10 @@ namespace Umbraco.Core.Models.Rdbms [PrimaryKeyColumn(IdentitySeed = NodeIdSeed)] public int Id { get; set; } + [Column("typeUniqueId")] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelationType_UniqueId")] + public Guid UniqueId { get; set; } + [Column("dual")] public bool Dual { get; set; } diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs index 2be2010301..3909af2a55 100644 --- a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs +++ b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs @@ -130,8 +130,13 @@ namespace Umbraco.Core.Models ///
    [UmbracoObjectType(Constants.ObjectTypes.DataTypeContainer)] [FriendlyName("Data Type Container")] - DataTypeContainer - + DataTypeContainer, + /// + /// Relation type + /// + [UmbracoObjectType(Constants.ObjectTypes.RelationType)] + [FriendlyName("Relation Type")] + RelationType } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs index 98d4f30042..b112535817 100644 --- a/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/RelationTypeFactory.cs @@ -16,6 +16,7 @@ namespace Umbraco.Core.Persistence.Factories entity.DisableChangeTracking(); entity.Id = dto.Id; + entity.Key = dto.UniqueId; entity.IsBidirectional = dto.Dual; entity.Name = dto.Name; @@ -38,10 +39,13 @@ namespace Umbraco.Core.Persistence.Factories ChildObjectType = entity.ChildObjectType, Dual = entity.IsBidirectional, Name = entity.Name, - ParentObjectType = entity.ParentObjectType + ParentObjectType = entity.ParentObjectType, + UniqueId = entity.Key }; if (entity.HasIdentity) + { dto.Id = entity.Id; + } return dto; } diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index 6128096943..9b5d2bc2c8 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -292,8 +292,12 @@ namespace Umbraco.Core.Persistence.Migrations.Initial private void CreateUmbracoRelationTypeData() { - _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 1, Alias = Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = true, Name = Constants.Conventions.RelationTypes.RelateDocumentOnCopyName }); - _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 2, Alias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = false, Name = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName }); + var relationType = new RelationTypeDto { Id = 1, Alias = Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = true, Name = Constants.Conventions.RelationTypes.RelateDocumentOnCopyName }; + relationType.UniqueId = (relationType.Alias + "____" + relationType.Name).ToGuid(); + _database.Insert("umbracoRelationType", "id", false, relationType); + relationType = new RelationTypeDto { Id = 2, Alias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = false, Name = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName }; + relationType.UniqueId = (relationType.Alias + "____" + relationType.Name).ToGuid(); + _database.Insert("umbracoRelationType", "id", false, relationType); } private void CreateCmsTaskTypeData() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddRelationTypeUniqueIdColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddRelationTypeUniqueIdColumn.cs new file mode 100644 index 0000000000..8a6641c523 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddRelationTypeUniqueIdColumn.cs @@ -0,0 +1,49 @@ +using System; +using System.Linq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero +{ + [Migration("7.6.0", 0, GlobalSettings.UmbracoMigrationName)] + public class AddRelationTypeUniqueIdColumn : MigrationBase + { + public AddRelationTypeUniqueIdColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) + : base(sqlSyntax, logger) + { } + + public override void Up() + { + var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToArray(); + + if (columns.Any(x => x.TableName.InvariantEquals("umbracoRelationType") && x.ColumnName.InvariantEquals("typeUniqueId")) == false) + { + Create.Column("typeUniqueId").OnTable("umbracoRelationType").AsGuid().Nullable(); + Execute.Code(UpdateRelationTypeGuids); + Alter.Table("umbracoRelationType").AlterColumn("typeUniqueId").AsGuid().NotNullable(); + Create.Index("IX_umbracoRelationType_UniqueId").OnTable("umbracoRelationType").OnColumn("typeUniqueId") + .Ascending() + .WithOptions().NonClustered() + .WithOptions().Unique(); + } + } + + private static string UpdateRelationTypeGuids(Database database) + { + var updates = database.Query("SELECT id, alias, name FROM umbracoRelationType") + .Select(relationType => Tuple.Create((int) relationType.id, ((string) relationType.alias + "____" + (string) relationType.name).ToGuid())) + .ToList(); + + foreach (var update in updates) + database.Execute("UPDATE umbracoRelationType set typeUniqueId=@guid WHERE id=@id", new { guid = update.Item2, id = update.Item1 }); + + return string.Empty; + } + + public override void Down() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationTypeRepository.cs index d28f81bce0..0cf6357d24 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRelationTypeRepository.cs @@ -1,9 +1,8 @@ -using Umbraco.Core.Models; +using System; +using Umbraco.Core.Models; namespace Umbraco.Core.Persistence.Repositories { - public interface IRelationTypeRepository : IRepositoryQueryable - { - - } + public interface IRelationTypeRepository : IRepositoryQueryable, IReadRepository + { } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs index c0a110feca..2c54897ff7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs @@ -44,6 +44,17 @@ namespace Umbraco.Core.Persistence.Repositories return GetAll().FirstOrDefault(x => x.Id == id); } + public IRelationType Get(Guid id) + { + // use the underlying GetAll which will force cache all content types + return GetAll().FirstOrDefault(x => x.Key == id); + } + + public bool Exists(Guid id) + { + return Get(id) != null; + } + protected override IEnumerable PerformGetAll(params int[] ids) { var sql = GetBaseQuery(false); @@ -57,6 +68,15 @@ namespace Umbraco.Core.Persistence.Repositories return dtos.Select(x => DtoToEntity(x, factory)); } + public IEnumerable GetAll(params Guid[] ids) + { + // should not happen due to the cache policy + if (ids.Any()) + throw new NotImplementedException(); + + return GetAll(new int[0]); + } + protected override IEnumerable PerformGetByQuery(IQuery query) { var sqlClause = GetBaseQuery(false); diff --git a/src/Umbraco.Core/Services/IRelationService.cs b/src/Umbraco.Core/Services/IRelationService.cs index 3719993a75..5fb1f3c8cc 100644 --- a/src/Umbraco.Core/Services/IRelationService.cs +++ b/src/Umbraco.Core/Services/IRelationService.cs @@ -21,6 +21,13 @@ namespace Umbraco.Core.Services /// A object IRelationType GetRelationTypeById(int id); + /// + /// Gets a by its Id + /// + /// Id of the + /// A object + IRelationType GetRelationTypeById(Guid id); + /// /// Gets a by its Alias /// diff --git a/src/Umbraco.Core/Services/RelationService.cs b/src/Umbraco.Core/Services/RelationService.cs index b4a37fc1d1..69f79e756e 100644 --- a/src/Umbraco.Core/Services/RelationService.cs +++ b/src/Umbraco.Core/Services/RelationService.cs @@ -48,6 +48,19 @@ namespace Umbraco.Core.Services } } + /// + /// Gets a by its Id + /// + /// Id of the + /// A object + public IRelationType GetRelationTypeById(Guid id) + { + using (var repository = RepositoryFactory.CreateRelationTypeRepository(UowProvider.GetUnitOfWork())) + { + return repository.Get(id); + } + } + /// /// Gets a by its Alias /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index c128051bfd..0741470f51 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -438,6 +438,7 @@ + From a10b59d03b16b56e80191bceb160d32a77de0c35 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 5 Jan 2017 17:14:14 +0100 Subject: [PATCH 179/599] Revert "Fixes: U4-9217 - Sanitize tags before storing them in the database" This reverts commit 47c8e6854ee2aafc99fa7fc0b7d901fc59020adb. --- .../lib/umbraco/Extensions.js | 16 ---------------- .../propertyeditors/tags/tags.controller.js | 3 +-- .../PropertyEditors/TagsPropertyEditor.cs | 10 +--------- 3 files changed, 2 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js index 3c148f0535..b70a6b12bc 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js @@ -69,22 +69,6 @@ }; } - if (!String.prototype.htmlEncode) { - /** htmlEncode extension method for string */ - String.prototype.htmlEncode = function () { - //create a in-memory div, set it's inner text(which jQuery automatically encodes) - //then grab the encoded contents back out. The div never exists on the page. - return $('
    ').text(this).html(); - }; - } - - if (!String.prototype.htmlDecode) { - /** htmlDecode extension method for string */ - String.prototype.htmlDecode = function () { - return $('
    ').html(this).text(); - }; - } - if (!String.prototype.startsWith) { /** startsWith extension method for string */ String.prototype.startsWith = function (str) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js index f965b812d8..a1e48bbc99 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js @@ -1,6 +1,6 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.TagsController", - function ($rootScope, $scope, $log, assetsService, umbRequestHelper, angularHelper, $timeout, $element, $sanitize) { + function ($rootScope, $scope, $log, assetsService, umbRequestHelper, angularHelper, $timeout, $element) { var $typeahead; @@ -41,7 +41,6 @@ angular.module("umbraco") //Helper method to add a tag on enter or on typeahead select function addTag(tagToAdd) { - tagToAdd = String(tagToAdd).htmlEncode(); if (tagToAdd != null && tagToAdd.length > 0) { if ($scope.model.value.indexOf(tagToAdd) < 0) { $scope.model.value.push(tagToAdd); diff --git a/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs index be228bf3ef..b44c65b157 100644 --- a/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/TagsPropertyEditor.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; -using System.Net; using System.Runtime.InteropServices; using Newtonsoft.Json.Linq; using Umbraco.Core; @@ -61,14 +60,7 @@ namespace Umbraco.Web.PropertyEditors public override object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue) { var json = editorValue.Value as JArray; - return json == null - ? null - : json.Select(x => x.Value()).Where(x => x.IsNullOrWhiteSpace() == false) - //First we will decode it as html because we know that if this is not a malicious post that the value is - // already Html encoded by the tags JavaScript controller. Then we'll re-Html Encode it to ensure that in case this - // is a malicious post (i.e. someone is submitting data manually by modifying the request). - .Select(WebUtility.HtmlDecode) - .Select(WebUtility.HtmlEncode); + return json == null ? null : json.Select(x => x.Value()); } /// From f5f9ff29cec4f64906c9bab59f6e75da14e2cc70 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 6 Jan 2017 14:32:56 +1100 Subject: [PATCH 180/599] Discovered we are not passing the ISqlSyntaxProvider correctly to where so it defaults to the global one which causes some issues for libraries such as UmbracoIdentity --- src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs | 8 ++++++++ .../Persistence/Querying/PocoToSqlExpressionVisitor.cs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs index d2fa98ef12..cf7ae44a3e 100644 --- a/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs +++ b/src/Umbraco.Core/Persistence/PetaPocoSqlExtensions.cs @@ -27,6 +27,7 @@ namespace Umbraco.Core.Persistence return sql.From(sqlSyntax.GetQuotedTableName(tableName)); } + [Obsolete("Use the overload specifying ISqlSyntaxProvider instead")] public static Sql Where(this Sql sql, Expression> predicate) { var expresionist = new PocoToSqlExpressionVisitor(); @@ -34,6 +35,13 @@ namespace Umbraco.Core.Persistence return sql.Where(whereExpression, expresionist.GetSqlParameters()); } + public static Sql Where(this Sql sql, Expression> predicate, ISqlSyntaxProvider sqlSyntax) + { + var expresionist = new PocoToSqlExpressionVisitor(sqlSyntax); + var whereExpression = expresionist.Visit(predicate); + return sql.Where(whereExpression, expresionist.GetSqlParameters()); + } + private static string GetFieldName(Expression> fieldSelector, ISqlSyntaxProvider sqlSyntax) { var field = ExpressionHelper.FindProperty(fieldSelector) as PropertyInfo; diff --git a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs index 6b527296df..e0a4f07e48 100644 --- a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs +++ b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs @@ -14,6 +14,14 @@ namespace Umbraco.Core.Persistence.Querying { private readonly Database.PocoData _pd; + + public PocoToSqlExpressionVisitor(ISqlSyntaxProvider syntaxProvider) + : base(syntaxProvider) + { + + } + + [Obsolete("Use the overload the specifies a SqlSyntaxProvider")] public PocoToSqlExpressionVisitor() : base(SqlSyntaxContext.SqlSyntaxProvider) { From 59cd4b5c5c0e49643b495776509ba3ac8478369e Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 6 Jan 2017 17:40:03 +1100 Subject: [PATCH 181/599] Fixes more allocations at the config level --- .../ContentScriptEditorElement.cs | 26 ++---------- .../ImagingAutoFillUploadFieldElement.cs | 34 +++------------ .../UmbracoSettings/LoggingElement.cs | 42 +++---------------- .../UmbracoSettings/NotificationsElement.cs | 10 +---- .../UmbracoSettings/RequestHandlerElement.cs | 18 ++------ .../UmbracoSettings/SecurityElement.cs | 42 +++---------------- .../UmbracoSettings/TemplatesElement.cs | 34 +++------------ 7 files changed, 31 insertions(+), 175 deletions(-) diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentScriptEditorElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentScriptEditorElement.cs index 0d14609caa..f60e964a04 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentScriptEditorElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentScriptEditorElement.cs @@ -3,42 +3,24 @@ using System.Configuration; namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class ContentScriptEditorElement : ConfigurationElement + internal class ContentScriptEditorElement : UmbracoConfigurationElement { [ConfigurationProperty("scriptFolderPath")] internal InnerTextConfigurationElement ScriptFolderPath { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["scriptFolderPath"], - //set the default - "/scripts"); - } + get { return GetOptionalTextElement("scriptFolderPath", "/scripts"); } } [ConfigurationProperty("scriptFileTypes")] internal OptionalCommaDelimitedConfigurationElement ScriptFileTypes { - get - { - return new OptionalCommaDelimitedConfigurationElement( - (OptionalCommaDelimitedConfigurationElement)this["scriptFileTypes"], - //set the default - new[] { "js", "xml" }); - } + get { return GetOptionalDelimitedElement("scriptFileTypes", new[] {"js", "xml"}); } } [ConfigurationProperty("scriptDisableEditor")] internal InnerTextConfigurationElement ScriptEditorDisable { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement) this["scriptDisableEditor"], - //set the default - false); - } + get { return GetOptionalTextElement("scriptDisableEditor", false); } } } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ImagingAutoFillUploadFieldElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ImagingAutoFillUploadFieldElement.cs index 0dfc4afc00..eafe43817d 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ImagingAutoFillUploadFieldElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ImagingAutoFillUploadFieldElement.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class ImagingAutoFillUploadFieldElement : ConfigurationElement, IImagingAutoFillUploadField + internal class ImagingAutoFillUploadFieldElement : UmbracoConfigurationElement, IImagingAutoFillUploadField { /// /// Allow setting internally so we can create a default @@ -17,49 +17,25 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("widthFieldAlias")] internal InnerTextConfigurationElement WidthFieldAlias { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["widthFieldAlias"], - //set the default - "umbracoWidth"); - } + get { return GetOptionalTextElement("widthFieldAlias", "umbracoWidth"); } } [ConfigurationProperty("heightFieldAlias")] internal InnerTextConfigurationElement HeightFieldAlias { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["heightFieldAlias"], - //set the default - "umbracoHeight"); - } + get { return GetOptionalTextElement("heightFieldAlias", "umbracoHeight"); } } [ConfigurationProperty("lengthFieldAlias")] internal InnerTextConfigurationElement LengthFieldAlias { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["lengthFieldAlias"], - //set the default - "umbracoBytes"); - } + get { return GetOptionalTextElement("lengthFieldAlias", "umbracoBytes"); } } [ConfigurationProperty("extensionFieldAlias")] internal InnerTextConfigurationElement ExtensionFieldAlias { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["extensionFieldAlias"], - //set the default - "umbracoExtension"); - } + get { return GetOptionalTextElement("extensionFieldAlias", "umbracoExtension"); } } string IImagingAutoFillUploadField.Alias diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/LoggingElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/LoggingElement.cs index f95a9c7e76..39e6327b3a 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/LoggingElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/LoggingElement.cs @@ -3,67 +3,37 @@ using System.Configuration; namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class LoggingElement : ConfigurationElement, ILoggingSection + internal class LoggingElement : UmbracoConfigurationElement, ILoggingSection { [ConfigurationProperty("autoCleanLogs")] internal InnerTextConfigurationElement AutoCleanLogs { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["autoCleanLogs"], - //set the default - false); - } + get { return GetOptionalTextElement("autoCleanLogs", false); } } [ConfigurationProperty("enableLogging")] internal InnerTextConfigurationElement EnableLogging { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["enableLogging"], - //set the default - true); - } + get { return GetOptionalTextElement("enableLogging", true); } } [ConfigurationProperty("enableAsyncLogging")] internal InnerTextConfigurationElement EnableAsyncLogging { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["enableAsyncLogging"], - //set the default - true); - } + get { return GetOptionalTextElement("enableAsyncLogging", true); } } [ConfigurationProperty("cleaningMiliseconds")] internal InnerTextConfigurationElement CleaningMiliseconds { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["cleaningMiliseconds"], - //set the default - -1); - } + get { return GetOptionalTextElement("cleaningMiliseconds", -1); } } [ConfigurationProperty("maxLogAge")] internal InnerTextConfigurationElement MaxLogAge { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["maxLogAge"], - //set the default - -1); - } + get { return GetOptionalTextElement("maxLogAge", -1); } } [ConfigurationCollection(typeof(DisabledLogTypesCollection), AddItemName = "logTypeAlias")] diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/NotificationsElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/NotificationsElement.cs index 16eb943887..89e3f447ee 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/NotificationsElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/NotificationsElement.cs @@ -2,7 +2,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class NotificationsElement : ConfigurationElement + internal class NotificationsElement : UmbracoConfigurationElement { [ConfigurationProperty("email")] internal InnerTextConfigurationElement NotificationEmailAddress @@ -13,13 +13,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings [ConfigurationProperty("disableHtmlEmail")] internal InnerTextConfigurationElement DisableHtmlEmail { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement) this["disableHtmlEmail"], - //set the default - false); - } + get { return GetOptionalTextElement("disableHtmlEmail", false); } } } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/RequestHandlerElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/RequestHandlerElement.cs index 9dc4a94824..779d33c8b8 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/RequestHandlerElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/RequestHandlerElement.cs @@ -5,30 +5,18 @@ using System.Collections.Generic; namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class RequestHandlerElement : ConfigurationElement, IRequestHandlerSection + internal class RequestHandlerElement : UmbracoConfigurationElement, IRequestHandlerSection { [ConfigurationProperty("useDomainPrefixes")] public InnerTextConfigurationElement UseDomainPrefixes { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["useDomainPrefixes"], - //set the default - false); - } + get { return GetOptionalTextElement("useDomainPrefixes", false); } } [ConfigurationProperty("addTrailingSlash")] public InnerTextConfigurationElement AddTrailingSlash { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["addTrailingSlash"], - //set the default - true); - } + get { return GetOptionalTextElement("addTrailingSlash", true); } } private UrlReplacingElement _defaultUrlReplacing; diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs index f280b3e20c..ddb168ddbd 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs @@ -2,66 +2,36 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class SecurityElement : ConfigurationElement, ISecuritySection + internal class SecurityElement : UmbracoConfigurationElement, ISecuritySection { [ConfigurationProperty("keepUserLoggedIn")] internal InnerTextConfigurationElement KeepUserLoggedIn { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["keepUserLoggedIn"], - //set the default - true); - } + get { return GetOptionalTextElement("keepUserLoggedIn", true); } } [ConfigurationProperty("hideDisabledUsersInBackoffice")] internal InnerTextConfigurationElement HideDisabledUsersInBackoffice { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["hideDisabledUsersInBackoffice"], - //set the default - false); - } + get { return GetOptionalTextElement("hideDisabledUsersInBackoffice", false); } } [ConfigurationProperty("allowPasswordReset")] internal InnerTextConfigurationElement AllowPasswordReset { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["allowPasswordReset"], - //set the default - true); - } + get { return GetOptionalTextElement("allowPasswordReset", true); } } [ConfigurationProperty("authCookieName")] internal InnerTextConfigurationElement AuthCookieName { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["authCookieName"], - //set the default - Constants.Web.AuthCookieName); - } + get { return GetOptionalTextElement("authCookieName", Constants.Web.AuthCookieName); } } [ConfigurationProperty("authCookieDomain")] internal InnerTextConfigurationElement AuthCookieDomain { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["authCookieDomain"], - //set the default - null); - } + get { return GetOptionalTextElement("authCookieDomain", null); } } bool ISecuritySection.KeepUserLoggedIn diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/TemplatesElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/TemplatesElement.cs index c7eb659766..4e249df3a2 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/TemplatesElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/TemplatesElement.cs @@ -3,55 +3,31 @@ using System.Configuration; namespace Umbraco.Core.Configuration.UmbracoSettings { - internal class TemplatesElement : ConfigurationElement, ITemplatesSection + internal class TemplatesElement : UmbracoConfigurationElement, ITemplatesSection { [ConfigurationProperty("useAspNetMasterPages")] internal InnerTextConfigurationElement UseAspNetMasterPages { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["useAspNetMasterPages"], - //set the default - true); - } + get { return GetOptionalTextElement("useAspNetMasterPages", true); } } [ConfigurationProperty("enableSkinSupport")] internal InnerTextConfigurationElement EnableSkinSupport { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["enableSkinSupport"], - //set the default - true); - } + get { return GetOptionalTextElement("enableSkinSupport", true); } } [ConfigurationProperty("defaultRenderingEngine", IsRequired = true)] internal InnerTextConfigurationElement DefaultRenderingEngine { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["defaultRenderingEngine"], - //set the default - RenderingEngine.Mvc); - } + get { return GetOptionalTextElement("defaultRenderingEngine", RenderingEngine.Mvc); } } [Obsolete("This has no affect and will be removed in future versions")] [ConfigurationProperty("enableTemplateFolders")] internal InnerTextConfigurationElement EnableTemplateFolders { - get - { - return new OptionalInnerTextConfigurationElement( - (InnerTextConfigurationElement)this["enableTemplateFolders"], - //set the default - false); - } + get { return GetOptionalTextElement("enableTemplateFolders", false); } } bool ITemplatesSection.UseAspNetMasterPages From aadd0c91297d648ba9876adcb9d6c4e7d6359185 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 6 Jan 2017 09:12:25 +0100 Subject: [PATCH 182/599] U4-9337 - asymmetric route caching --- .../Routing/NiceUrlProviderTests.cs | 28 ++++--- .../Routing/NiceUrlRoutesTests.cs | 81 +++++++++++++++++-- .../NiceUrlsProviderWithDomainsTests.cs | 23 ++++-- .../Routing/UrlsWithNestedDomains.cs | 16 ++-- .../PublishedContentCache.cs | 18 +++-- .../XmlPublishedCache/RoutesCache.cs | 21 +++-- 6 files changed, 138 insertions(+), 49 deletions(-) diff --git a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs index c6970cbeb3..a8e5b1e64d 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Routing } /// - /// This checks that when we retrieve a NiceUrl for multiple items that there are no issues with cache overlap + /// This checks that when we retrieve a NiceUrl for multiple items that there are no issues with cache overlap /// and that they are all cached correctly. /// [Test] @@ -71,11 +71,19 @@ namespace Umbraco.Tests.Routing var cache = routingContext.UmbracoContext.ContentCache.InnerCache as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - // GetUrl does not write to cache - Assert.AreEqual(0, cachedRoutes.Count); - } + var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + Assert.AreEqual(8, cachedRoutes.Count); + + foreach (var sample in samples) + { + Assert.IsTrue(cachedRoutes.ContainsKey(sample.Key)); + Assert.AreEqual(sample.Value, cachedRoutes[sample.Key]); + } + + var cachedIds = cache.RoutesCache.GetCachedIds(); + Assert.AreEqual(0, cachedIds.Count); + } // test hideTopLevelNodeFromPath false [TestCase(1046, "/home/")] @@ -133,10 +141,10 @@ namespace Umbraco.Tests.Routing var routingContext = GetRoutingContext("http://example.com/test", 1111, umbracoSettings: _umbracoSettings); - + Assert.AreEqual("/home/sub1/custom-sub-1/", routingContext.UrlProvider.GetUrl(1177)); - + requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", routingContext.UrlProvider.GetUrl(1177)); @@ -159,14 +167,14 @@ namespace Umbraco.Tests.Routing Assert.AreEqual("#", routingContext.UrlProvider.GetUrl(999999)); - requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); - + requestMock.Setup(x => x.UseDomainPrefixes).Returns(true); + Assert.AreEqual("#", routingContext.UrlProvider.GetUrl(999999)); requestMock.Setup(x => x.UseDomainPrefixes).Returns(false); routingContext.UrlProvider.Mode = UrlProviderMode.Absolute; - + Assert.AreEqual("#", routingContext.UrlProvider.GetUrl(999999)); } } diff --git a/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs b/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs index 79f2b2b64d..131107e8e6 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlRoutesTests.cs @@ -7,6 +7,10 @@ using Umbraco.Web.Routing; namespace Umbraco.Tests.Routing { + // purpose: test the values returned by PublishedContentCache.GetRouteById + // and .GetByRoute (no caching at all, just routing nice urls) including all + // the quirks due to hideTopLevelFromPath and backward compatibility. + [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerFixture)] [TestFixture] public class NiceUrlRoutesTests : BaseRoutingTest @@ -85,7 +89,7 @@ GetByRoute(route, hide = null): # there are not other reason not to cache it if content and no domain between root and content: - cache route + cache route (as trusted) return content @@ -136,7 +140,11 @@ GetRouteById(id): # never cache the route, it may be colliding - return DetermineRouteById(id) + route = DetermineRouteById(id) + if route: + cache route (as not trusted) + + return route @@ -180,8 +188,6 @@ DetermineRouteById(id): * C 2005 * E 2006 * - * And the tests should verify all the quirks that are due to - * hideTopLevelFromPath */ [TestCase(1000, false, "/a")] @@ -214,10 +220,40 @@ DetermineRouteById(id): SettingsForTests.HideTopLevelNodeFromPath = hide; - var route = cache.GetRouteById(umbracoContext, false, id); + const bool preview = true; // make sure we don't cache + var route = cache.GetRouteById(umbracoContext, preview, id); Assert.AreEqual(expected, route); } + [Test] + public void GetRouteByIdCache() + { + var umbracoContext = GetUmbracoContext("/test", 0); + var cache = umbracoContext.ContentCache.InnerCache as PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + + SettingsForTests.HideTopLevelNodeFromPath = false; + + // make sure we cache + PublishedContentCache.UnitTesting = false; + const bool preview = false; + + var route = cache.GetRouteById(umbracoContext, preview, 1000); + Assert.AreEqual("/a", route); + + // GetRouteById registers a non-trusted route, which is cached for + // id -> route queries (fast GetUrl) but *not* for route -> id + // queries (safe inbound routing) + + var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + Assert.AreEqual(1, cachedRoutes.Count); + Assert.IsTrue(cachedRoutes.ContainsKey(1000)); + Assert.AreEqual("/a", cachedRoutes[1000]); + + var cachedIds = cache.RoutesCache.GetCachedIds(); + Assert.AreEqual(0, cachedIds.Count); + } + [TestCase("/", false, 1000)] [TestCase("/a", false, 1000)] // yes! [TestCase("/a/b", false, 1001)] @@ -241,7 +277,8 @@ DetermineRouteById(id): SettingsForTests.HideTopLevelNodeFromPath = hide; - var content = cache.GetByRoute(umbracoContext, false, route); + const bool preview = true; // make sure we don't cache + var content = cache.GetByRoute(umbracoContext, preview, route); if (expected < 0) { Assert.IsNull(content); @@ -252,5 +289,37 @@ DetermineRouteById(id): Assert.AreEqual(expected, content.Id); } } + + [Test] + public void GetByRouteCache() + { + var umbracoContext = GetUmbracoContext("/test", 0); + var cache = umbracoContext.ContentCache.InnerCache as PublishedContentCache; + if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + + SettingsForTests.HideTopLevelNodeFromPath = false; + + // make sure we cache + PublishedContentCache.UnitTesting = false; + const bool preview = false; + + var content = cache.GetByRoute(umbracoContext, preview, "/a/b/c"); + Assert.IsNotNull(content); + Assert.AreEqual(1002, content.Id); + + // GetByRoute registers a trusted route, which is cached both for + // id -> route queries (fast GetUrl) and for route -> id queries + // (fast inbound routing) + + var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + Assert.AreEqual(1, cachedRoutes.Count); + Assert.IsTrue(cachedRoutes.ContainsKey(1002)); + Assert.AreEqual("/a/b/c", cachedRoutes[1002]); + + var cachedIds = cache.RoutesCache.GetCachedIds(); + Assert.AreEqual(1, cachedIds.Count); + Assert.IsTrue(cachedIds.ContainsKey("/a/b/c")); + Assert.AreEqual(1002, cachedIds["/a/b/c"]); + } } } diff --git a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs index 6e71efcbf3..ab25e0b43c 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs @@ -79,7 +79,7 @@ namespace Umbraco.Tests.Routing protected override string GetXmlContent(int templateId) { return @" - ]> @@ -302,10 +302,20 @@ namespace Umbraco.Tests.Routing var cache = routingContext.UmbracoContext.ContentCache.InnerCache as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - // GetUrl does not write to cache - Assert.AreEqual(0, cachedRoutes.Count); + var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + Assert.AreEqual(7, cachedRoutes.Count); + + var cachedIds = cache.RoutesCache.GetCachedIds(); + Assert.AreEqual(0, cachedIds.Count); + + CheckRoute(cachedRoutes, cachedIds, 1001, "1001/"); + CheckRoute(cachedRoutes, cachedIds, 10011, "10011/"); + CheckRoute(cachedRoutes, cachedIds, 100111, "10011/1001-1-1"); + CheckRoute(cachedRoutes, cachedIds, 10012, "10012/"); + CheckRoute(cachedRoutes, cachedIds, 100121, "10012/1001-2-1"); + CheckRoute(cachedRoutes, cachedIds, 10013, "1001/1001-3"); + CheckRoute(cachedRoutes, cachedIds, 1002, "/1002"); // use the cache Assert.AreEqual("/", routingContext.UrlProvider.GetUrl(1001, new Uri("http://domain1.com"), false)); @@ -319,12 +329,11 @@ namespace Umbraco.Tests.Routing Assert.AreEqual("http://domain1.com/fr/1001-2-1/", routingContext.UrlProvider.GetUrl(100121, new Uri("http://domain2.com"), false)); } - void CheckRoute(IDictionary routes, IDictionary ids, int id, string route) + private static void CheckRoute(IDictionary routes, IDictionary ids, int id, string route) { Assert.IsTrue(routes.ContainsKey(id)); Assert.AreEqual(route, routes[id]); - Assert.IsTrue(ids.ContainsKey(route)); - Assert.AreEqual(id, ids[route]); + Assert.IsFalse(ids.ContainsKey(route)); } [Test] diff --git a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs index 29a60232fa..19420e3120 100644 --- a/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/src/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs @@ -26,7 +26,7 @@ namespace Umbraco.Tests.Routing public void DoNotPolluteCache() { SettingsForTests.UseDirectoryUrls = true; - SettingsForTests.HideTopLevelNodeFromPath = false; // ignored w/domains + SettingsForTests.HideTopLevelNodeFromPath = false; // ignored w/domains var settings = SettingsForTests.GenerateMockSettings(); var request = Mock.Get(settings.RequestHandler); @@ -36,7 +36,7 @@ namespace Umbraco.Tests.Routing RoutingContext routingContext; string url = "http://domain1.com/1001-1/1001-1-1"; - + // get the nice url for 100111 routingContext = GetRoutingContext(url, 9999, umbracoSettings: settings); Assert.AreEqual("http://domain2.com/1001-1-1/", routingContext.UrlProvider.GetUrl(100111, true)); @@ -44,8 +44,8 @@ namespace Umbraco.Tests.Routing // check that the proper route has been cached var cache = routingContext.UmbracoContext.ContentCache.InnerCache as PublishedContentCache; if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - //var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - //Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); + var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); // route a rogue url url = "http://domain1.com/1001-1/1001-1-1"; @@ -61,9 +61,9 @@ namespace Umbraco.Tests.Routing Assert.AreEqual(100111, pcr.PublishedContent.Id); // has the cache been polluted? - //cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - //Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); // no - ////Assert.AreEqual("1001/1001-1/1001-1-1", cachedRoutes[100111]); // yes + cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); // no + //Assert.AreEqual("1001/1001-1/1001-1-1", cachedRoutes[100111]); // yes // what's the nice url now? Assert.AreEqual("http://domain2.com/1001-1-1/", routingContext.UrlProvider.GetUrl(100111)); // good @@ -89,7 +89,7 @@ namespace Umbraco.Tests.Routing protected override string GetXmlContent(int templateId) { return @" - ]> diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index d9b0363408..c8ddd77afd 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -2,11 +2,9 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Runtime.CompilerServices; -using System.Text; using System.Xml; using System.Xml.XPath; using Umbraco.Core.Configuration; -using Umbraco.Core.Logging; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; @@ -18,7 +16,6 @@ using umbraco.BusinessLogic; using umbraco.presentation.preview; using Umbraco.Core.Services; using GlobalSettings = umbraco.GlobalSettings; -using Task = System.Threading.Tasks.Task; namespace Umbraco.Web.PublishedCache.XmlPublishedCache { @@ -83,7 +80,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache && DomainHelper.ExistsDomainInPath(umbracoContext.Application.Services.DomainService.GetAll(false), content.Path, domainRootNodeId) == false; if (deepest) - _routesCache.Store(content.Id, route); + _routesCache.Store(content.Id, route, true); // trusted route } public virtual string GetRouteById(UmbracoContext umbracoContext, bool preview, int contentId) @@ -98,9 +95,16 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // else actually determine the route route = DetermineRouteById(umbracoContext, preview, contentId); - // may be null if node not found - // do NOT cache the route: it may be colliding - and since checking for a collision implies - // doing one DetermineIdByRoute anyways we are not causing any perf penalty by not caching + // node not found + if (route == null) + return null; + + // cache the route BUT do NOT trust it as it can be a colliding route + // meaning if we GetRouteById again, we'll get it from cache, but it + // won't be used for inbound routing + if (preview == false) + _routesCache.Store(contentId, route, false); + return route; } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/RoutesCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/RoutesCache.cs index 9c8eed322b..4e775b3253 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/RoutesCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/RoutesCache.cs @@ -83,10 +83,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache /// /// The node identified. /// The route. - public void Store(int nodeId, string route) + /// A value indicating whether the value can be trusted for inbound routing. + public void Store(int nodeId, string route, bool trust) { _routes.AddOrUpdate(nodeId, i => route, (i, s) => route); - _nodeIds.AddOrUpdate(route, i => nodeId, (i, s) => nodeId); + if (trust) + _nodeIds.AddOrUpdate(route, i => nodeId, (i, s) => nodeId); } /// @@ -119,15 +121,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache /// The node identifier. public void ClearNode(int nodeId) { - if (!_routes.ContainsKey(nodeId)) return; - - string key; - if (!_routes.TryGetValue(nodeId, out key)) return; - - int val; - _nodeIds.TryRemove(key, out val); - string val2; - _routes.TryRemove(nodeId, out val2); + string route; + if (_routes.TryRemove(nodeId, out route)) + { + int id; + _nodeIds.TryRemove(route, out id); + } } /// From 5b88c57d94c8b194278e0341f940f65bd1056f31 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Mon, 9 Jan 2017 13:05:20 +0000 Subject: [PATCH 183/599] Replace RequiredForPersistance with Required --- src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs index 3c1ebe16c3..235eb3d360 100644 --- a/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs @@ -20,7 +20,7 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "name")] public string Name { get; set; } - [RequiredForPersistence] + [Required] [DataMember(Name = "alias")] public string Alias { get; set; } From b2815d5e63c384f04c3c7619dd0b6db393325c47 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Mon, 9 Jan 2017 13:34:20 +0000 Subject: [PATCH 184/599] Remove commented code --- src/Umbraco.Web/Editors/TemplateController.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs index d0c17b7f4b..54c43b4f29 100644 --- a/src/Umbraco.Web/Editors/TemplateController.cs +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -92,9 +92,6 @@ namespace Umbraco.Web.Editors //Checking the submitted is valid with the Required attributes decorated on the ViewModel if (ModelState.IsValid == false) { - //Unsure of the standard or set way to do this?! - //throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse("MISSING NAME")); - throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } From 1a9f336a59bf8d34064d224de3d4b4c026be5bcb Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Mon, 9 Jan 2017 14:45:40 +0100 Subject: [PATCH 185/599] Add support for GUIDs in media and content pickers --- .../mediaPicker/mediapicker.controller.js | 7 +- .../Editors/BackOfficeController.cs | 2 +- src/Umbraco.Web/Editors/MediaController.cs | 64 +++++++++++++++++-- .../Editors/MediaTypeController.cs | 19 +++++- .../Trees/ContentTreeController.cs | 6 +- .../Trees/ContentTreeControllerBase.cs | 60 ++++++++++++++--- 6 files changed, 137 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js index 43c65d99ee..3b6c39e84a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js @@ -1,7 +1,7 @@ //used for the media picker dialog angular.module("umbraco") .controller("Umbraco.Overlays.MediaPickerController", - function($scope, mediaResource, umbRequestHelper, entityResource, $log, mediaHelper, mediaTypeHelper, eventsService, treeService, $element, $timeout, $cookies, $cookieStore, localizationService) { + function ($scope, mediaResource, umbRequestHelper, entityResource, $log, mediaHelper, mediaTypeHelper, eventsService, treeService, $element, $timeout, $cookies, localStorageService, localizationService) { if (!$scope.model.title) { $scope.model.title = localizationService.localize("defaultdialogs_selectMedia"); @@ -15,7 +15,7 @@ angular.module("umbraco") $scope.multiPicker = (dialogOptions.multiPicker && dialogOptions.multiPicker !== "0") ? true : false; $scope.startNodeId = dialogOptions.startNodeId ? dialogOptions.startNodeId : -1; $scope.cropSize = dialogOptions.cropSize; - $scope.lastOpenedNode = $cookieStore.get("umbLastOpenedMediaNodeId"); + $scope.lastOpenedNode = localStorageService.get("umbLastOpenedMediaNodeId"); if ($scope.onlyImages) { $scope.acceptedFileTypes = mediaHelper .formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes); @@ -133,8 +133,7 @@ angular.module("umbraco") }); $scope.currentFolder = folder; - // for some reason i cannot set cookies with cookieStore - document.cookie = "umbLastOpenedMediaNodeId=" + folder.id; + localStorageService.set("umbLastOpenedMediaNodeId", folder.id); }; diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 199ffd9bed..74e616e756 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -262,7 +262,7 @@ namespace Umbraco.Web.Editors }, { "mediaTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetAllowedChildren(0)) + controller => controller.GetAllowedChildren("0")) }, { "macroApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 7a7e349a3c..1f95034c34 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -36,6 +36,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Persistence.FaultHandling; using Umbraco.Web.UI; using Notification = Umbraco.Web.Models.ContentEditing.Notification; +using Umbraco.Core.Persistence; namespace Umbraco.Web.Editors { @@ -175,7 +176,37 @@ namespace Umbraco.Web.Editors /// Returns the child media objects /// [FilterAllowedOutgoingMedia(typeof(IEnumerable>), "Items")] - public PagedResult> GetChildren(int id, + public PagedResult> GetChildren(string id, + int pageNumber = 0, + int pageSize = 0, + string orderBy = "SortOrder", + Direction orderDirection = Direction.Ascending, + bool orderBySystemField = true, + string filter = "") + { + int idInt; Guid idGuid; + + if (Guid.TryParse(id, out idGuid)) + { + var entity = Services.EntityService.GetByKey(idGuid); + if (entity != null) + { + return getChildren(entity.Id, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter); + } + else + { + throw new EntityNotFoundException(id, "The passed id doesn't exist"); + } + } + else if (int.TryParse(id, out idInt)) + { + return getChildren(idInt, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter); + } + + throw new InvalidCastException("Id must be either an integer or a Guid"); + } + + private PagedResult> getChildren(int id, int pageNumber = 0, int pageSize = 0, string orderBy = "SortOrder", @@ -448,12 +479,37 @@ namespace Umbraco.Web.Editors } //get the string json from the request - int parentId; - if (int.TryParse(result.FormData["currentFolder"], out parentId) == false) + int parentId; bool entityFound; + string currentFolderId = result.FormData["currentFolder"]; + if (int.TryParse(currentFolderId, out parentId) == false) { - return Request.CreateValidationErrorResponse("The request was not formatted correctly, the currentFolder is not an integer"); + // if a guid then try to look up the entity + Guid idGuid; + if (Guid.TryParse(currentFolderId, out idGuid)) + { + var entity = Services.EntityService.GetByKey(idGuid); + if (entity != null) + { + entityFound = true; + parentId = entity.Id; + } + else + { + throw new EntityNotFoundException(currentFolderId, "The passed id doesn't exist"); + } + } + else + { + return Request.CreateValidationErrorResponse("The request was not formatted correctly, the currentFolder is not an integer or Guid"); + } + + if (entityFound == false) + { + return Request.CreateValidationErrorResponse("The request was not formatted correctly, the currentFolder is not an integer or Guid"); + } } + //ensure the user has access to this folder by parent id! if (CheckPermissions( new Dictionary(), diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index 0f83ad6d03..a40b83c9ea 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -11,6 +11,8 @@ using System.Net; using System.Net.Http; using Umbraco.Web.WebApi; using Umbraco.Core.Services; +using Umbraco.Core.Models.EntityBase; +using System; namespace Umbraco.Web.Editors { @@ -174,7 +176,22 @@ namespace Umbraco.Web.Editors /// /// [UmbracoTreeAuthorize(Constants.Trees.MediaTypes, Constants.Trees.Media)] - public IEnumerable GetAllowedChildren(int contentId) + public IEnumerable GetAllowedChildren(string contentId) + { + Guid idGuid = Guid.Empty; + int idInt; + if (Guid.TryParse(contentId, out idGuid)) { + var entity = ApplicationContext.Services.EntityService.GetByKey(idGuid); + return getAllowedChildren(entity.Id); + } else if (int.TryParse(contentId, out idInt)) + { + return getAllowedChildren(idInt); + } + + throw new InvalidCastException("Id must be either an integer or a Guid"); + } + + private IEnumerable getAllowedChildren(int contentId) { if (contentId == Constants.System.RecycleBinContent) return Enumerable.Empty(); diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index 9f33a44ea9..9a5f90ff89 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -202,11 +202,13 @@ namespace Umbraco.Web.Trees /// protected override bool HasPathAccess(string id, FormDataCollection queryStrings) { - var content = Services.ContentService.GetById(int.Parse(id)); - if (content == null) + var entity = GetEntityFromId(id); + if (entity == null) { return false; } + + IContent content = Services.ContentService.GetById(entity.Id); return Security.CurrentUser.HasPathAccess(content); } diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 3708c6fd4f..99ec60be75 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -96,7 +96,7 @@ namespace Umbraco.Web.Trees LogHelper.Warn("The user " + Security.CurrentUser.Username + " does not have access to the tree node " + id); return new TreeNodeCollection(); } - + // So there's an alt id specified, it's not the root node and the user has access to it, great! But there's one thing we // need to consider: // If the tree is being rendered in a dialog view we want to render only the children of the specified id, but @@ -110,7 +110,7 @@ namespace Umbraco.Web.Trees id = Constants.System.Root.ToString(CultureInfo.InvariantCulture); } } - + var entities = GetChildEntities(id); nodes.AddRange(entities.Select(entity => GetSingleTreeNode(entity, id, queryStrings)).Where(node => node != null)); return nodes; @@ -122,11 +122,24 @@ namespace Umbraco.Web.Trees protected IEnumerable GetChildEntities(string id) { + // use helper method to ensure we support both integer and guid lookups int iid; - if (int.TryParse(id, out iid) == false) + + // if it's the root node, we won't use the look up + if (id != "-1") { - throw new InvalidCastException("The id for the media tree must be an integer"); + var idEntity = GetEntityFromId(id); + if (idEntity == null) + { + throw new EntityNotFoundException(id, "The passed id doesn't exist"); + } + iid = idEntity.Id; } + else + { + iid = int.Parse(id); + } + //if a request is made for the root node data but the user's start node is not the default, then // we need to return their start node data @@ -134,11 +147,12 @@ namespace Umbraco.Web.Trees { //just return their single start node, it will show up under the 'Content' label var startNode = Services.EntityService.Get(UserStartNode, UmbracoObjectType); - if (startNode == null) + if (startNode != null) + return new[] { startNode }; + else { throw new EntityNotFoundException(UserStartNode, "User's start content node could not be found"); } - return new[] { startNode }; } return Services.EntityService.GetChildren(iid, UmbracoObjectType).ToArray(); @@ -176,9 +190,9 @@ namespace Umbraco.Web.Trees { id = altStartId; } - + var nodes = GetTreeNodesInternal(id, queryStrings); - + //only render the recycle bin if we are not in dialog and the start id id still the root if (IsDialog(queryStrings) == false && id == Constants.System.Root.ToInvariantString()) { @@ -210,8 +224,9 @@ namespace Umbraco.Web.Trees /// private TreeNodeCollection GetTreeNodesInternal(string id, FormDataCollection queryStrings) { + IUmbracoEntity current = GetEntityFromId(id); + //before we get the children we need to see if this is a container node - var current = Services.EntityService.Get(int.Parse(id), UmbracoObjectType); //test if the parent is a listview / container if (current != null && current.IsContainer()) @@ -286,5 +301,32 @@ namespace Umbraco.Web.Trees { return allowedUserOptions.Select(x => x.Action).OfType().Any(); } + + /// + /// Get an entity via an id that can be either an integer or a Guid + /// + /// + /// + internal IUmbracoEntity GetEntityFromId(string id) + { + IUmbracoEntity entity; + Guid idGuid = Guid.Empty; + int idInt; + if (Guid.TryParse(id, out idGuid)) + { + entity = Services.EntityService.GetByKey(idGuid, UmbracoObjectType); + + } + else if (int.TryParse(id, out idInt)) + { + entity = Services.EntityService.Get(idInt, UmbracoObjectType); + } + else + { + throw new InvalidCastException("Id must be either an integer or a Guid"); + } + + return entity; + } } } \ No newline at end of file From cfd8435d684a3f3e8f5841feb327245cefb537fe Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 9 Jan 2017 17:49:12 +0100 Subject: [PATCH 186/599] deploy-150 - wire GetSize in services --- src/Umbraco.Core/IO/FileSystemExtensions.cs | 7 ++- .../Repositories/FileRepository.cs | 29 ++++++++--- .../Interfaces/IPartialViewRepository.cs | 1 + .../Interfaces/IScriptRepository.cs | 1 + .../Interfaces/IStylesheetRepository.cs | 1 + .../Interfaces/ITemplateRepository.cs | 2 + .../Interfaces/IXsltFileRepository.cs | 1 + .../Repositories/StylesheetRepository.cs | 14 ++++++ .../Repositories/TemplateRepository.cs | 5 ++ src/Umbraco.Core/Services/FileService.cs | 48 +++++++++++++++++++ src/Umbraco.Core/Services/IFileService.cs | 42 ++++++++++++++++ src/Umbraco.Core/Services/IMediaService.cs | 7 +++ src/Umbraco.Core/Services/MediaService.cs | 5 ++ 13 files changed, 152 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Core/IO/FileSystemExtensions.cs b/src/Umbraco.Core/IO/FileSystemExtensions.cs index aeb6f9a42a..be09f1e310 100644 --- a/src/Umbraco.Core/IO/FileSystemExtensions.cs +++ b/src/Umbraco.Core/IO/FileSystemExtensions.cs @@ -38,13 +38,12 @@ namespace Umbraco.Core.IO } // GetSize has been added to IFileSystem2 but not IFileSystem - // this is implementing GetSize for IFileSystem, the old way public static long GetSize(this IFileSystem fs, string path) { - // if we reach this point, fs is *not* IFileSystem2 - // so it's not FileSystemWrapper nor shadow nor anything we know - // so... fall back to the old & inefficient method + var fs2 = fs as IFileSystem2; + if (fs2 != null) return fs2.GetSize(path); + // this is implementing GetSize for IFileSystem, the old way using (var file = fs.OpenFile(path)) { return file.Length; diff --git a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs index 7ce0d71097..03a8b7e824 100644 --- a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs @@ -234,13 +234,28 @@ namespace Umbraco.Core.Persistence.Repositories } } - /// - /// Dispose any disposable properties - /// - /// - /// Dispose the unit of work - /// - protected override void DisposeResources() + public long GetFileSize(string filename) + { + if (FileSystem.FileExists(filename) == false) + return -1; + + try + { + return FileSystem.GetSize(filename); + } + catch + { + return -1; // deal with race conds + } + } + + /// + /// Dispose any disposable properties + /// + /// + /// Dispose the unit of work + /// + protected override void DisposeResources() { _work.DisposeIfDisposable(); } diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs index 7c8391a77a..27342fe643 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs @@ -10,5 +10,6 @@ namespace Umbraco.Core.Persistence.Repositories bool ValidatePartialView(IPartialView partialView); Stream GetFileContentStream(string filepath); void SetFileContent(string filepath, Stream content); + long GetFileSize(string filepath); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs index eacb818faa..c2c0a0ae84 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs @@ -8,5 +8,6 @@ namespace Umbraco.Core.Persistence.Repositories bool ValidateScript(Script script); Stream GetFileContentStream(string filepath); void SetFileContent(string filepath, Stream content); + long GetFileSize(string filepath); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IStylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IStylesheetRepository.cs index 323f11d339..5fadf7b01c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IStylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IStylesheetRepository.cs @@ -8,5 +8,6 @@ namespace Umbraco.Core.Persistence.Repositories bool ValidateStylesheet(Stylesheet stylesheet); Stream GetFileContentStream(string filepath); void SetFileContent(string filepath, Stream content); + long GetFileSize(string filepath); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs index 59845f53f0..fd04e21d75 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs @@ -72,5 +72,7 @@ namespace Umbraco.Core.Persistence.Repositories /// The filesystem path to the template. /// The content of the template. void SetFileContent(string filepath, Stream content); + + long GetFileSize(string filepath); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IXsltFileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IXsltFileRepository.cs index 8bff985400..4c56b6eb18 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IXsltFileRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IXsltFileRepository.cs @@ -8,5 +8,6 @@ namespace Umbraco.Core.Persistence.Repositories bool ValidateXsltFile(XsltFile xsltFile); Stream GetFileContentStream(string filepath); void SetFileContent(string filepath, Stream content); + long GetFileSize(string filepath); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs index 45fff2e43b..7515b8513a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs @@ -144,6 +144,20 @@ namespace Umbraco.Core.Persistence.Repositories FileSystem.AddFile(filepath, content, true); } + public long GetFileSize(string filepath) + { + if (FileSystem.FileExists(filepath) == false) return -1; + + try + { + return FileSystem.GetSize(filepath); + } + catch + { + return -1; // deal with race conds + } + } + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index 620ad10e7d..41743601fb 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -513,6 +513,11 @@ namespace Umbraco.Core.Persistence.Repositories GetFileSystem(filepath).AddFile(filepath, content, true); } + public long GetFileSize(string filepath) + { + return GetFileSystem(filepath).GetSize(filepath); + } + private IFileSystem GetFileSystem(string filepath) { var ext = Path.GetExtension(filepath); diff --git a/src/Umbraco.Core/Services/FileService.cs b/src/Umbraco.Core/Services/FileService.cs index aa722e0133..6360ad8988 100644 --- a/src/Umbraco.Core/Services/FileService.cs +++ b/src/Umbraco.Core/Services/FileService.cs @@ -569,6 +569,14 @@ namespace Umbraco.Core.Services } } + public long GetTemplateFileSize(string filepath) + { + using (var repository = RepositoryFactory.CreateTemplateRepository(UowProvider.GetUnitOfWork())) + { + return repository.GetFileSize(filepath); + } + } + #endregion public Stream GetStylesheetFileContentStream(string filepath) @@ -587,6 +595,14 @@ namespace Umbraco.Core.Services } } + public long GetStylesheetFileSize(string filepath) + { + using (var repository = RepositoryFactory.CreateStylesheetRepository(UowProvider.GetUnitOfWork())) + { + return repository.GetFileSize(filepath); + } + } + public Stream GetScriptFileContentStream(string filepath) { using (var repository = RepositoryFactory.CreateScriptRepository(UowProvider.GetUnitOfWork())) @@ -603,6 +619,14 @@ namespace Umbraco.Core.Services } } + public long GetScriptFileSize(string filepath) + { + using (var repository = RepositoryFactory.CreateScriptRepository(UowProvider.GetUnitOfWork())) + { + return repository.GetFileSize(filepath); + } + } + public Stream GetXsltFileContentStream(string filepath) { using (var repository = RepositoryFactory.CreateXsltFileRepository(UowProvider.GetUnitOfWork())) @@ -619,6 +643,14 @@ namespace Umbraco.Core.Services } } + public long GetXsltFileSize(string filepath) + { + using (var repository = RepositoryFactory.CreateXsltFileRepository(UowProvider.GetUnitOfWork())) + { + return repository.GetFileSize(filepath); + } + } + #region Partial Views public IEnumerable GetPartialViewSnippetNames(params string[] filterNames) @@ -890,6 +922,14 @@ namespace Umbraco.Core.Services } } + public long GetPartialViewMacroFileSize(string filepath) + { + using (var repository = GetPartialViewRepository(PartialViewType.PartialViewMacro, UowProvider.GetUnitOfWork())) + { + return repository.GetFileSize(filepath); + } + } + public Stream GetPartialViewFileContentStream(string filepath) { using (var repository = GetPartialViewRepository(PartialViewType.PartialView, UowProvider.GetUnitOfWork())) @@ -906,6 +946,14 @@ namespace Umbraco.Core.Services } } + public long GetPartialViewFileSize(string filepath) + { + using (var repository = GetPartialViewRepository(PartialViewType.PartialView, UowProvider.GetUnitOfWork())) + { + return repository.GetFileSize(filepath); + } + } + #endregion private void Audit(AuditType type, string message, int userId, int objectId) diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index 7d266ba64c..5115a25087 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -254,6 +254,13 @@ namespace Umbraco.Core.Services /// The content of the template. void SetTemplateFileContent(string filepath, Stream content); + /// + /// Gets the size of a template. + /// + /// The filesystem path to the template. + /// The size of the template. + long GetTemplateFileSize(string filepath); + /// /// Gets the content of a stylesheet as a stream. /// @@ -268,6 +275,13 @@ namespace Umbraco.Core.Services /// The content of the stylesheet. void SetStylesheetFileContent(string filepath, Stream content); + /// + /// Gets the size of a stylesheet. + /// + /// The filesystem path to the stylesheet. + /// The size of the stylesheet. + long GetStylesheetFileSize(string filepath); + /// /// Gets the content of a script file as a stream. /// @@ -282,6 +296,13 @@ namespace Umbraco.Core.Services /// The content of the script file. void SetScriptFileContent(string filepath, Stream content); + /// + /// Gets the size of a script file. + /// + /// The filesystem path to the script file. + /// The size of the script file. + long GetScriptFileSize(string filepath); + /// /// Gets the content of a XSLT file as a stream. /// @@ -296,6 +317,13 @@ namespace Umbraco.Core.Services /// The content of the XSLT file. void SetXsltFileContent(string filepath, Stream content); + /// + /// Gets the size of a XSLT file. + /// + /// The filesystem path to the XSLT file. + /// The size of the XSLT file. + long GetXsltFileSize(string filepath); + /// /// Gets the content of a macro partial view as a stream. /// @@ -310,6 +338,13 @@ namespace Umbraco.Core.Services /// The content of the macro partial view. void SetPartialViewMacroFileContent(string filepath, Stream content); + /// + /// Gets the size of a macro partial view. + /// + /// The filesystem path to the macro partial view. + /// The size of the macro partial view. + long GetPartialViewMacroFileSize(string filepath); + /// /// Gets the content of a partial view as a stream. /// @@ -323,5 +358,12 @@ namespace Umbraco.Core.Services /// The filesystem path to the partial view. /// The content of the partial view. void SetPartialViewFileContent(string filepath, Stream content); + + /// + /// Gets the size of a partial view. + /// + /// The filesystem path to the partial view. + /// The size of the partial view. + long GetPartialViewFileSize(string filepath); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 257ee10d9c..510a1e26ca 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -430,6 +430,13 @@ namespace Umbraco.Core.Services /// The content of the media. void SetMediaFileContent(string filepath, Stream content); + /// + /// Gets the size of a media. + /// + /// The filesystem path to the media. + /// The size of the media. + long GetMediaFileSize(string filepath); + /// /// Deletes a media file and all thumbnails. /// diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index b7cc002d96..f582ef1eaa 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -1361,6 +1361,11 @@ namespace Umbraco.Core.Services _mediaFileSystem.AddFile(filepath, stream, true); } + public long GetMediaFileSize(string filepath) + { + return _mediaFileSystem.GetSize(filepath); + } + public void DeleteMediaFile(string filepath) { _mediaFileSystem.DeleteFile(filepath, true); From 17c9d19ebb7107966097537f33c45fb3c2d748ff Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 9 Jan 2017 17:49:12 +0100 Subject: [PATCH 187/599] deploy-150 - wire GetSize in services --- src/Umbraco.Core/IO/FileSystemExtensions.cs | 7 ++- .../Repositories/FileRepository.cs | 29 ++++++++--- .../Interfaces/IPartialViewRepository.cs | 1 + .../Interfaces/IScriptRepository.cs | 1 + .../Interfaces/IStylesheetRepository.cs | 1 + .../Interfaces/ITemplateRepository.cs | 2 + .../Interfaces/IXsltFileRepository.cs | 1 + .../Repositories/StylesheetRepository.cs | 14 ++++++ .../Repositories/TemplateRepository.cs | 5 ++ src/Umbraco.Core/Services/FileService.cs | 48 +++++++++++++++++++ src/Umbraco.Core/Services/IFileService.cs | 42 ++++++++++++++++ src/Umbraco.Core/Services/IMediaService.cs | 7 +++ src/Umbraco.Core/Services/MediaService.cs | 5 ++ 13 files changed, 152 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Core/IO/FileSystemExtensions.cs b/src/Umbraco.Core/IO/FileSystemExtensions.cs index aeb6f9a42a..be09f1e310 100644 --- a/src/Umbraco.Core/IO/FileSystemExtensions.cs +++ b/src/Umbraco.Core/IO/FileSystemExtensions.cs @@ -38,13 +38,12 @@ namespace Umbraco.Core.IO } // GetSize has been added to IFileSystem2 but not IFileSystem - // this is implementing GetSize for IFileSystem, the old way public static long GetSize(this IFileSystem fs, string path) { - // if we reach this point, fs is *not* IFileSystem2 - // so it's not FileSystemWrapper nor shadow nor anything we know - // so... fall back to the old & inefficient method + var fs2 = fs as IFileSystem2; + if (fs2 != null) return fs2.GetSize(path); + // this is implementing GetSize for IFileSystem, the old way using (var file = fs.OpenFile(path)) { return file.Length; diff --git a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs index 7ce0d71097..03a8b7e824 100644 --- a/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/FileRepository.cs @@ -234,13 +234,28 @@ namespace Umbraco.Core.Persistence.Repositories } } - /// - /// Dispose any disposable properties - /// - /// - /// Dispose the unit of work - /// - protected override void DisposeResources() + public long GetFileSize(string filename) + { + if (FileSystem.FileExists(filename) == false) + return -1; + + try + { + return FileSystem.GetSize(filename); + } + catch + { + return -1; // deal with race conds + } + } + + /// + /// Dispose any disposable properties + /// + /// + /// Dispose the unit of work + /// + protected override void DisposeResources() { _work.DisposeIfDisposable(); } diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs index 7c8391a77a..27342fe643 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IPartialViewRepository.cs @@ -10,5 +10,6 @@ namespace Umbraco.Core.Persistence.Repositories bool ValidatePartialView(IPartialView partialView); Stream GetFileContentStream(string filepath); void SetFileContent(string filepath, Stream content); + long GetFileSize(string filepath); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs index eacb818faa..c2c0a0ae84 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IScriptRepository.cs @@ -8,5 +8,6 @@ namespace Umbraco.Core.Persistence.Repositories bool ValidateScript(Script script); Stream GetFileContentStream(string filepath); void SetFileContent(string filepath, Stream content); + long GetFileSize(string filepath); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IStylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IStylesheetRepository.cs index 323f11d339..5fadf7b01c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IStylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IStylesheetRepository.cs @@ -8,5 +8,6 @@ namespace Umbraco.Core.Persistence.Repositories bool ValidateStylesheet(Stylesheet stylesheet); Stream GetFileContentStream(string filepath); void SetFileContent(string filepath, Stream content); + long GetFileSize(string filepath); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs index 59845f53f0..fd04e21d75 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/ITemplateRepository.cs @@ -72,5 +72,7 @@ namespace Umbraco.Core.Persistence.Repositories /// The filesystem path to the template. /// The content of the template. void SetFileContent(string filepath, Stream content); + + long GetFileSize(string filepath); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IXsltFileRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IXsltFileRepository.cs index 8bff985400..4c56b6eb18 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IXsltFileRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IXsltFileRepository.cs @@ -8,5 +8,6 @@ namespace Umbraco.Core.Persistence.Repositories bool ValidateXsltFile(XsltFile xsltFile); Stream GetFileContentStream(string filepath); void SetFileContent(string filepath, Stream content); + long GetFileSize(string filepath); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs index 45fff2e43b..7515b8513a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/StylesheetRepository.cs @@ -144,6 +144,20 @@ namespace Umbraco.Core.Persistence.Repositories FileSystem.AddFile(filepath, content, true); } + public long GetFileSize(string filepath) + { + if (FileSystem.FileExists(filepath) == false) return -1; + + try + { + return FileSystem.GetSize(filepath); + } + catch + { + return -1; // deal with race conds + } + } + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index 620ad10e7d..41743601fb 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -513,6 +513,11 @@ namespace Umbraco.Core.Persistence.Repositories GetFileSystem(filepath).AddFile(filepath, content, true); } + public long GetFileSize(string filepath) + { + return GetFileSystem(filepath).GetSize(filepath); + } + private IFileSystem GetFileSystem(string filepath) { var ext = Path.GetExtension(filepath); diff --git a/src/Umbraco.Core/Services/FileService.cs b/src/Umbraco.Core/Services/FileService.cs index aa722e0133..6360ad8988 100644 --- a/src/Umbraco.Core/Services/FileService.cs +++ b/src/Umbraco.Core/Services/FileService.cs @@ -569,6 +569,14 @@ namespace Umbraco.Core.Services } } + public long GetTemplateFileSize(string filepath) + { + using (var repository = RepositoryFactory.CreateTemplateRepository(UowProvider.GetUnitOfWork())) + { + return repository.GetFileSize(filepath); + } + } + #endregion public Stream GetStylesheetFileContentStream(string filepath) @@ -587,6 +595,14 @@ namespace Umbraco.Core.Services } } + public long GetStylesheetFileSize(string filepath) + { + using (var repository = RepositoryFactory.CreateStylesheetRepository(UowProvider.GetUnitOfWork())) + { + return repository.GetFileSize(filepath); + } + } + public Stream GetScriptFileContentStream(string filepath) { using (var repository = RepositoryFactory.CreateScriptRepository(UowProvider.GetUnitOfWork())) @@ -603,6 +619,14 @@ namespace Umbraco.Core.Services } } + public long GetScriptFileSize(string filepath) + { + using (var repository = RepositoryFactory.CreateScriptRepository(UowProvider.GetUnitOfWork())) + { + return repository.GetFileSize(filepath); + } + } + public Stream GetXsltFileContentStream(string filepath) { using (var repository = RepositoryFactory.CreateXsltFileRepository(UowProvider.GetUnitOfWork())) @@ -619,6 +643,14 @@ namespace Umbraco.Core.Services } } + public long GetXsltFileSize(string filepath) + { + using (var repository = RepositoryFactory.CreateXsltFileRepository(UowProvider.GetUnitOfWork())) + { + return repository.GetFileSize(filepath); + } + } + #region Partial Views public IEnumerable GetPartialViewSnippetNames(params string[] filterNames) @@ -890,6 +922,14 @@ namespace Umbraco.Core.Services } } + public long GetPartialViewMacroFileSize(string filepath) + { + using (var repository = GetPartialViewRepository(PartialViewType.PartialViewMacro, UowProvider.GetUnitOfWork())) + { + return repository.GetFileSize(filepath); + } + } + public Stream GetPartialViewFileContentStream(string filepath) { using (var repository = GetPartialViewRepository(PartialViewType.PartialView, UowProvider.GetUnitOfWork())) @@ -906,6 +946,14 @@ namespace Umbraco.Core.Services } } + public long GetPartialViewFileSize(string filepath) + { + using (var repository = GetPartialViewRepository(PartialViewType.PartialView, UowProvider.GetUnitOfWork())) + { + return repository.GetFileSize(filepath); + } + } + #endregion private void Audit(AuditType type, string message, int userId, int objectId) diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index 7d266ba64c..5115a25087 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -254,6 +254,13 @@ namespace Umbraco.Core.Services /// The content of the template. void SetTemplateFileContent(string filepath, Stream content); + /// + /// Gets the size of a template. + /// + /// The filesystem path to the template. + /// The size of the template. + long GetTemplateFileSize(string filepath); + /// /// Gets the content of a stylesheet as a stream. /// @@ -268,6 +275,13 @@ namespace Umbraco.Core.Services /// The content of the stylesheet. void SetStylesheetFileContent(string filepath, Stream content); + /// + /// Gets the size of a stylesheet. + /// + /// The filesystem path to the stylesheet. + /// The size of the stylesheet. + long GetStylesheetFileSize(string filepath); + /// /// Gets the content of a script file as a stream. /// @@ -282,6 +296,13 @@ namespace Umbraco.Core.Services /// The content of the script file. void SetScriptFileContent(string filepath, Stream content); + /// + /// Gets the size of a script file. + /// + /// The filesystem path to the script file. + /// The size of the script file. + long GetScriptFileSize(string filepath); + /// /// Gets the content of a XSLT file as a stream. /// @@ -296,6 +317,13 @@ namespace Umbraco.Core.Services /// The content of the XSLT file. void SetXsltFileContent(string filepath, Stream content); + /// + /// Gets the size of a XSLT file. + /// + /// The filesystem path to the XSLT file. + /// The size of the XSLT file. + long GetXsltFileSize(string filepath); + /// /// Gets the content of a macro partial view as a stream. /// @@ -310,6 +338,13 @@ namespace Umbraco.Core.Services /// The content of the macro partial view. void SetPartialViewMacroFileContent(string filepath, Stream content); + /// + /// Gets the size of a macro partial view. + /// + /// The filesystem path to the macro partial view. + /// The size of the macro partial view. + long GetPartialViewMacroFileSize(string filepath); + /// /// Gets the content of a partial view as a stream. /// @@ -323,5 +358,12 @@ namespace Umbraco.Core.Services /// The filesystem path to the partial view. /// The content of the partial view. void SetPartialViewFileContent(string filepath, Stream content); + + /// + /// Gets the size of a partial view. + /// + /// The filesystem path to the partial view. + /// The size of the partial view. + long GetPartialViewFileSize(string filepath); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 257ee10d9c..510a1e26ca 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -430,6 +430,13 @@ namespace Umbraco.Core.Services /// The content of the media. void SetMediaFileContent(string filepath, Stream content); + /// + /// Gets the size of a media. + /// + /// The filesystem path to the media. + /// The size of the media. + long GetMediaFileSize(string filepath); + /// /// Deletes a media file and all thumbnails. /// diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index b7cc002d96..f582ef1eaa 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -1361,6 +1361,11 @@ namespace Umbraco.Core.Services _mediaFileSystem.AddFile(filepath, stream, true); } + public long GetMediaFileSize(string filepath) + { + return _mediaFileSystem.GetSize(filepath); + } + public void DeleteMediaFile(string filepath) { _mediaFileSystem.DeleteFile(filepath, true); From 4c330c865434a8b29b8c26ecf405dfbb6c331679 Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Mon, 9 Jan 2017 22:54:28 +0100 Subject: [PATCH 188/599] Implements CodeFileController --- .../Editors/BackOfficeController.cs | 4 + src/Umbraco.Web/Editors/CodeFileController.cs | 155 ++++++++++++++++++ .../Models/ContentEditing/CodeFileDisplay.cs | 20 +++ .../Models/Mapping/CodeFileDisplayMapper.cs | 47 ++++++ src/Umbraco.Web/Umbraco.Web.csproj | 3 + 5 files changed, 229 insertions(+) create mode 100644 src/Umbraco.Web/Editors/CodeFileController.cs create mode 100644 src/Umbraco.Web/Models/ContentEditing/CodeFileDisplay.cs create mode 100644 src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 199ffd9bed..50c8b2e82c 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -359,6 +359,10 @@ namespace Umbraco.Web.Editors { "healthCheckBaseUrl", Url.GetUmbracoApiServiceBaseUrl( controller => controller.GetAllHealthChecks()) + }, + { + "codeFileApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetByPath("", "")) } } }, diff --git a/src/Umbraco.Web/Editors/CodeFileController.cs b/src/Umbraco.Web/Editors/CodeFileController.cs new file mode 100644 index 0000000000..7c1e1d2430 --- /dev/null +++ b/src/Umbraco.Web/Editors/CodeFileController.cs @@ -0,0 +1,155 @@ +using AutoMapper; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http; +using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi; + +namespace Umbraco.Web.Editors +{ + [PluginController("UmbracoApi")] + public class CodeFileController : UmbracoAuthorizedJsonController + { + + public HttpResponseMessage PostCreate(string type, CodeFileDisplay display) + { + switch (type) + { + case "partialView": + var view = new PartialView(display.VirtualPath); + var result = Services.FileService.CreatePartialView(view, display.Snippet, Security.CurrentUser.Id); + return result.Success == true ? Request.CreateResponse(HttpStatusCode.OK, result.Result) : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); + case "partialViewMacro": + var viewMacro = new PartialView(display.VirtualPath); + var resultMacro = Services.FileService.CreatePartialViewMacro(viewMacro, display.Snippet, Security.CurrentUser.Id); + return resultMacro.Success == true ? Request.CreateResponse(HttpStatusCode.OK, resultMacro.Result) : Request.CreateNotificationValidationErrorResponse(resultMacro.Exception.Message); + case "script": + var script = new Script(display.VirtualPath); + Services.FileService.SaveScript(script, Security.CurrentUser.Id); + return Request.CreateResponse(HttpStatusCode.OK); + default: + throw new ArgumentException("File Type not supported", "type"); + } + } + + + public CodeFileDisplay GetByPath(string type, string virtualPath) + { + switch (type) + { + case "partialView": + var view = Services.FileService.GetPartialView(virtualPath); + return view == null ? null : Mapper.Map(view); + case "partialViewMacro": + var viewMacro = Services.FileService.GetPartialViewMacro(virtualPath); + return viewMacro == null ? null : Mapper.Map(viewMacro); + case "script": + var script = Services.FileService.GetScriptByName(virtualPath); + return script == null ? null : Mapper.Map(script); + default: + throw new ArgumentException("File Type not supported", "type"); + } + } + + [HttpDelete] + public HttpResponseMessage Delete(string type, string virtualPath) + { + switch (type) + { + case "partialView": + if (Services.FileService.DeletePartialView(virtualPath, Security.CurrentUser.Id)) + { + return Request.CreateResponse(HttpStatusCode.OK); + } + else + { + return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View found with the specified path"); + } + break; + case "partialViewMacro": + if (Services.FileService.DeletePartialViewMacro(virtualPath, Security.CurrentUser.Id)) + { + return Request.CreateResponse(HttpStatusCode.OK); + } + else + { + return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro found with the specified path"); + } + break; + case "script": + if (Services.FileService.GetScriptByName(virtualPath) != null) + { + Services.FileService.DeleteScript(virtualPath, Security.CurrentUser.Id); + return Request.CreateResponse(HttpStatusCode.OK); + } + else + { + return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script found with the specified path"); + } + break; + default: + throw new ArgumentException("File Type not supported", "type"); + } + } + + public CodeFileDisplay PostSave(string type, CodeFileDisplay display) + { + if (display == null) + { + throw new ArgumentNullException("No file object has been passed"); + } + else + { + switch (type) + { + case "partialView": + var view = Services.FileService.GetPartialView(display.VirtualPath); + if (view != null) + { + view.Content = display.Content; + Services.FileService.SavePartialView(view, Security.CurrentUser.Id); + } + else + { + throw new ArgumentNullException($"File doesn't exist - {display.VirtualPath}"); + } + break; + case "partialViewMacro": + var viewMacro = Services.FileService.GetPartialViewMacro(display.VirtualPath); + if (viewMacro != null) + { + viewMacro.Content = display.Content; + Services.FileService.SavePartialViewMacro(viewMacro, Security.CurrentUser.Id); + } + else + { + throw new ArgumentNullException($"File doesn't exist - {display.VirtualPath}"); + } + break; + case "script": + var script = Services.FileService.GetScriptByName(display.VirtualPath); + if (script != null) + { + script.Content = display.Content; + Services.FileService.SaveScript(script, Security.CurrentUser.Id); + } + else + { + throw new ArgumentNullException($"File doesn't exist - {display.VirtualPath}"); + } + break; + default: + throw new ArgumentException("File Type not supported", "type"); + } + return display; + } + } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/CodeFileDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/CodeFileDisplay.cs new file mode 100644 index 0000000000..1ab5f1ddb4 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/CodeFileDisplay.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "scriptFile", Namespace = "")] + public class CodeFileDisplay + { + [DataMember(Name = "virtualPath")] + public string VirtualPath { get; set; } + [DataMember(Name = "content")] + public string Content { get; set; } + [DataMember(Name = "snippet")] + public string Snippet { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs b/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs new file mode 100644 index 0000000000..d777d27c43 --- /dev/null +++ b/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Models.Mapping; +using Umbraco.Core.Models; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Models.Mapping +{ + public class CodeFileDisplayMapper : MapperConfiguration + { + public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) + { + config.CreateMap() + .ForMember(x => x.Snippet, exp => exp.Ignore()); + + config.CreateMap() + .ForMember(x => x.Snippet, exp => exp.Ignore()); + + config.CreateMap() + .ForMember(x => x.Key, exp => exp.Ignore()) + .ForMember(x => x.Path, exp => exp.Ignore()) + .ForMember(x => x.CreateDate, exp => exp.Ignore()) + .ForMember(x => x.UpdateDate, exp => exp.Ignore()) + .ForMember(x => x.Path, exp => exp.Ignore()) + .ForMember(x => x.Alias, exp => exp.Ignore()) + .ForMember(x => x.Name, exp => exp.Ignore()) + .ForMember(x => x.OriginalPath, exp => exp.Ignore()) + .ForMember(x => x.HasIdentity, exp => exp.Ignore()); + + config.CreateMap() + .ForMember(x => x.Key, exp => exp.Ignore()) + .ForMember(x => x.Path, exp => exp.Ignore()) + .ForMember(x => x.CreateDate, exp => exp.Ignore()) + .ForMember(x => x.UpdateDate, exp => exp.Ignore()) + .ForMember(x => x.Path, exp => exp.Ignore()) + .ForMember(x => x.Alias, exp => exp.Ignore()) + .ForMember(x => x.Name, exp => exp.Ignore()) + .ForMember(x => x.OriginalPath, exp => exp.Ignore()) + .ForMember(x => x.HasIdentity, exp => exp.Ignore()); + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 1db9416f86..14c3e44dba 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -277,6 +277,7 @@ + @@ -328,10 +329,12 @@ + + From 055ae0d126a023a03027de032ffa5374da0f1867 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 10 Jan 2017 11:36:35 +1100 Subject: [PATCH 189/599] updates latest IP mem leak fixes --- src/Umbraco.Core/Umbraco.Core.csproj | 4 ++-- src/Umbraco.Core/packages.config | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 12 +++++++----- src/Umbraco.Web.UI/packages.config | 4 ++-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 56958ac0e6..17be8e62df 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -49,8 +49,8 @@ ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll - - ..\packages\ImageProcessor.2.5.0\lib\net45\ImageProcessor.dll + + ..\packages\ImageProcessor.2.5.1\lib\net45\ImageProcessor.dll ..\packages\log4net-mediumtrust.2.0.0\lib\log4net.dll diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config index e132059da8..221e4063f6 100644 --- a/src/Umbraco.Core/packages.config +++ b/src/Umbraco.Core/packages.config @@ -2,7 +2,7 @@ - + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 19bab66cb3..7b2dd06c89 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -135,11 +135,11 @@ False ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll - - ..\packages\ImageProcessor.2.5.0\lib\net45\ImageProcessor.dll + + ..\packages\ImageProcessor.2.5.1\lib\net45\ImageProcessor.dll - - ..\packages\ImageProcessor.Web.4.7.1\lib\net45\ImageProcessor.Web.dll + + ..\packages\ImageProcessor.Web.4.7.2\lib\net45\ImageProcessor.Web.dll False @@ -654,7 +654,9 @@ - + + Designer + log4net.config diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index b708ff8de0..6919e10f64 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -5,8 +5,8 @@ - - + + From d11e787624d1976cb7b3e110091eef6df315e2ba Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 10 Jan 2017 11:37:31 +1100 Subject: [PATCH 190/599] updates Nuspec with IP version --- build/NuSpecs/UmbracoCms.Core.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index ab79494f71..400a85cbe3 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -33,8 +33,8 @@ - - + + From 6531d027fc37edb4def4a80e4217c19bd74ab20d Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 10 Jan 2017 11:55:36 +1100 Subject: [PATCH 191/599] fixes error with server side validation + data tyep editor --- .../src/views/datatypes/datatype.edit.controller.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js index d8f8557fa5..02d5a62432 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js @@ -178,8 +178,6 @@ function DataTypeEditController($scope, $routeParams, $location, appState, navig //share state editorState.set($scope.content); - - dataTypeHelper.rebindChangedProperties($scope.content, data); }); } From d66c57cfd08704ac6de4be356469848989f9bfde Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 10 Jan 2017 12:05:48 +1100 Subject: [PATCH 192/599] Ensures that the Name field shows validation correctly with server side validation - needs Mads to verify this is ok. --- .../src/views/components/editor/umb-editor-header.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html index c274892c65..558f94d387 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html @@ -26,7 +26,10 @@ ng-model="name" ng-class="{'name-is-empty': $parent.name===null || $parent.name===''}" umb-auto-focus + val-server-field="Name" required /> +
    Required name
    +
    {{ name }}
    From 77d47f0b2845eb0604cf73b8d59eee950d8fc211 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 10 Jan 2017 12:29:30 +1100 Subject: [PATCH 193/599] fixes error with server side validation + data tyep editor, ensures template controller and data type controller inherit from BackOfficeNotificationsController, ensures BackOfficeNotificationsController is attributed with PrefixlessBodyModelValidator --- .../src/views/datatypes/datatype.edit.controller.js | 2 -- .../Editors/BackOfficeNotificationsController.cs | 2 ++ src/Umbraco.Web/Editors/ContentTypeController.cs | 11 +---------- src/Umbraco.Web/Editors/DataTypeController.cs | 2 +- src/Umbraco.Web/Editors/TemplateController.cs | 2 +- .../ContentEditing/GetAvailableCompositionsFilter.cs | 11 +++++++++++ .../Models/ContentEditing/TemplateDisplay.cs | 8 +++++++- src/Umbraco.Web/WebApi/JsonCamelCaseFormatter.cs | 2 +- .../WebApi/PrefixlessBodyModelValidatorAttribute.cs | 2 +- 9 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js index d8f8557fa5..02d5a62432 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/datatype.edit.controller.js @@ -178,8 +178,6 @@ function DataTypeEditController($scope, $routeParams, $location, appState, navig //share state editorState.set($scope.content); - - dataTypeHelper.rebindChangedProperties($scope.content, data); }); } diff --git a/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs b/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs index f4350bc596..0910ec936e 100644 --- a/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeNotificationsController.cs @@ -1,3 +1,4 @@ +using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Editors @@ -8,6 +9,7 @@ namespace Umbraco.Web.Editors /// currently in the request. ///
    [AppendCurrentEventMessages] + [PrefixlessBodyModelValidator] public abstract class BackOfficeNotificationsController : UmbracoAuthorizedJsonController { protected BackOfficeNotificationsController() diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 45a2821130..d6e6cbccf4 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -106,16 +106,7 @@ namespace Umbraco.Web.Editors /// Returns the avilable compositions for this content type /// This has been wrapped in a dto instead of simple parameters to support having multiple parameters in post request body /// - /// - /// - /// This is normally an empty list but if additional content type aliases are passed in, any content types containing those aliases will be filtered out - /// along with any content types that have matching property types that are included in the filtered content types - /// - /// - /// This is normally an empty list but if additional property type aliases are passed in, any content types that have these aliases will be filtered out. - /// This is required because in the case of creating/modifying a content type because new property types being added to it are not yet persisted so cannot - /// be looked up via the db, they need to be passed in. - /// + /// /// [HttpPost] public HttpResponseMessage GetAvailableCompositeContentTypes(GetAvailableCompositionsFilter filter) diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index 931d00968b..b3e306a48c 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -33,7 +33,7 @@ namespace Umbraco.Web.Editors [PluginController("UmbracoApi")] [UmbracoTreeAuthorize(Constants.Trees.DataTypes, Constants.Trees.DocumentTypes, Constants.Trees.MediaTypes, Constants.Trees.MemberTypes)] [EnableOverrideAuthorization] - public class DataTypeController : UmbracoAuthorizedJsonController + public class DataTypeController : BackOfficeNotificationsController { /// /// Gets data type by name diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs index eec76ca34e..fb93a8f34e 100644 --- a/src/Umbraco.Web/Editors/TemplateController.cs +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -15,7 +15,7 @@ namespace Umbraco.Web.Editors { [PluginController("UmbracoApi")] [UmbracoTreeAuthorize(Constants.Trees.Templates)] - public class TemplateController : UmbracoAuthorizedJsonController + public class TemplateController : BackOfficeNotificationsController { /// /// Gets data type by alias diff --git a/src/Umbraco.Web/Models/ContentEditing/GetAvailableCompositionsFilter.cs b/src/Umbraco.Web/Models/ContentEditing/GetAvailableCompositionsFilter.cs index d9bc169c8f..61d61f2108 100644 --- a/src/Umbraco.Web/Models/ContentEditing/GetAvailableCompositionsFilter.cs +++ b/src/Umbraco.Web/Models/ContentEditing/GetAvailableCompositionsFilter.cs @@ -3,7 +3,18 @@ namespace Umbraco.Web.Models.ContentEditing public class GetAvailableCompositionsFilter { public int ContentTypeId { get; set; } + + /// + /// This is normally an empty list but if additional property type aliases are passed in, any content types that have these aliases will be filtered out. + /// This is required because in the case of creating/modifying a content type because new property types being added to it are not yet persisted so cannot + /// be looked up via the db, they need to be passed in. + /// public string[] FilterPropertyTypes { get; set; } + + /// + /// This is normally an empty list but if additional content type aliases are passed in, any content types containing those aliases will be filtered out + /// along with any content types that have matching property types that are included in the filtered content types + /// public string[] FilterContentTypes { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs index dacccb8ceb..bbe1e63573 100644 --- a/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; namespace Umbraco.Web.Models.ContentEditing { [DataContract(Name = "template", Namespace = "")] - public class TemplateDisplay + public class TemplateDisplay : INotificationModel { [DataMember(Name = "id")] public int Id { get; set; } @@ -30,5 +30,11 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "masterTemplateAlias")] public string MasterTemplateAlias { get; set; } + + /// + /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. + /// + [DataMember(Name = "notifications")] + public List Notifications { get; private set; } } } diff --git a/src/Umbraco.Web/WebApi/JsonCamelCaseFormatter.cs b/src/Umbraco.Web/WebApi/JsonCamelCaseFormatter.cs index b54d6102c0..de1383706e 100644 --- a/src/Umbraco.Web/WebApi/JsonCamelCaseFormatter.cs +++ b/src/Umbraco.Web/WebApi/JsonCamelCaseFormatter.cs @@ -8,7 +8,7 @@ using Newtonsoft.Json.Serialization; namespace Umbraco.Web.WebApi { /// - /// Applying this attribute to any webapi controller will ensure that it only contains one json formatter compatible with the angular json vulnerability prevention. + /// Applying this attribute to any webapi controller will ensure that it only contains one json formatter with a camelCase formatter /// public class JsonCamelCaseFormatter : Attribute, IControllerConfiguration { diff --git a/src/Umbraco.Web/WebApi/PrefixlessBodyModelValidatorAttribute.cs b/src/Umbraco.Web/WebApi/PrefixlessBodyModelValidatorAttribute.cs index e018881f8a..2593acfbe0 100644 --- a/src/Umbraco.Web/WebApi/PrefixlessBodyModelValidatorAttribute.cs +++ b/src/Umbraco.Web/WebApi/PrefixlessBodyModelValidatorAttribute.cs @@ -6,7 +6,7 @@ using System.Web.Http.Validation; namespace Umbraco.Web.WebApi { /// - /// Applying this attribute to any webapi controller will ensure that it only contains one json formatter compatible with the angular json vulnerability prevention. + /// Applying this attribute to any webapi controller will ensure that the is of type /// internal class PrefixlessBodyModelValidatorAttribute : Attribute, IControllerConfiguration { From 358dd7f32b3055269e40c07042c2398a27d6907c Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 10 Jan 2017 07:30:35 +0100 Subject: [PATCH 194/599] Move ImageProcessor.Web.Config to not be in the .Core NuGet, it's not a dll --- build/NuSpecs/UmbracoCms.Core.nuspec | 1 - build/NuSpecs/UmbracoCms.nuspec | 1 + src/SolutionInfo.cs | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 400a85cbe3..219437d5e0 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -35,7 +35,6 @@ - diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index b56c8e6205..320f32de2b 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -18,6 +18,7 @@ + diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 3a27c3ddb2..7ed0a29d79 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -2,7 +2,7 @@ using System.Resources; [assembly: AssemblyCompany("Umbraco")] -[assembly: AssemblyCopyright("Copyright © Umbraco 2016")] +[assembly: AssemblyCopyright("Copyright © Umbraco 2017")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] From 4c461a733bc3c02cd302d9ca7af14ddbec95d45e Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 10 Jan 2017 12:16:12 +0100 Subject: [PATCH 195/599] remove inline validation on name --- src/Umbraco.Web.UI.Client/src/less/panel.less | 2 ++ .../src/views/components/editor/umb-editor-header.html | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index 19159638a4..ded8411dea 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -484,6 +484,8 @@ input.umb-panel-header-description { font-size: 12px; margin-left: 2px; margin-top: 3px; + height: 25px; + line-height: 25px; } .umb-editor-drawer-content { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html index 558f94d387..f218e983e7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-header.html @@ -28,8 +28,6 @@ umb-auto-focus val-server-field="Name" required /> -
    Required name
    -
    {{ name }}
    From 9718324e6eb4243cf162e61db6f4915968c48b2d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 10 Jan 2017 12:22:18 +0100 Subject: [PATCH 196/599] add check for master not being equal to template --- src/Umbraco.Web/Editors/TemplateController.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs index 256f0df45b..58a4053de1 100644 --- a/src/Umbraco.Web/Editors/TemplateController.cs +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -110,11 +110,16 @@ namespace Umbraco.Web.Editors { if (string.IsNullOrEmpty(display.MasterTemplateAlias) == false) { + var master = Services.FileService.GetTemplate(display.MasterTemplateAlias); - if (master != null) - { - template.SetMasterTemplate(master); + if(master == null || master.Id == display.Id) + { + template.SetMasterTemplate(null); + }else + { + template.SetMasterTemplate(master); } + } else { From f75e0cf4ba54fa4bafc433b2605ced275f9d046d Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Tue, 10 Jan 2017 12:47:00 +0100 Subject: [PATCH 197/599] Updates to PR based on Review --- src/Umbraco.Web/Editors/MediaController.cs | 4 ++-- src/Umbraco.Web/Editors/MediaTypeController.cs | 8 ++++---- src/Umbraco.Web/Trees/ContentTreeControllerBase.cs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 1f95034c34..5dd0c3073f 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -195,7 +195,7 @@ namespace Umbraco.Web.Editors } else { - throw new EntityNotFoundException(id, "The passed id doesn't exist"); + throw new HttpResponseException(HttpStatusCode.NotFound); } } else if (int.TryParse(id, out idInt)) @@ -203,7 +203,7 @@ namespace Umbraco.Web.Editors return getChildren(idInt, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter); } - throw new InvalidCastException("Id must be either an integer or a Guid"); + throw new HttpResponseException(HttpStatusCode.NotFound); } private PagedResult> getChildren(int id, diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index a40b83c9ea..67b8ae6d1f 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -182,16 +182,16 @@ namespace Umbraco.Web.Editors int idInt; if (Guid.TryParse(contentId, out idGuid)) { var entity = ApplicationContext.Services.EntityService.GetByKey(idGuid); - return getAllowedChildren(entity.Id); + return GetAllowedChildrenInternal(entity.Id); } else if (int.TryParse(contentId, out idInt)) { - return getAllowedChildren(idInt); + return GetAllowedChildrenInternal(idInt); } - throw new InvalidCastException("Id must be either an integer or a Guid"); + throw new HttpResponseException(HttpStatusCode.NotFound); } - private IEnumerable getAllowedChildren(int contentId) + private IEnumerable GetAllowedChildrenInternal(int contentId) { if (contentId == Constants.System.RecycleBinContent) return Enumerable.Empty(); diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 99ec60be75..027e4e488c 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -131,7 +131,7 @@ namespace Umbraco.Web.Trees var idEntity = GetEntityFromId(id); if (idEntity == null) { - throw new EntityNotFoundException(id, "The passed id doesn't exist"); + throw new HttpResponseException(HttpStatusCode.NotFound); } iid = idEntity.Id; } @@ -323,7 +323,7 @@ namespace Umbraco.Web.Trees } else { - throw new InvalidCastException("Id must be either an integer or a Guid"); + return null; } return entity; From 14e402878ae12fd9db11f2b43bf6f536d00ec151 Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Tue, 10 Jan 2017 14:49:19 +0100 Subject: [PATCH 198/599] Merge conflict fixes --- src/Umbraco.Core/Services/MediaService.cs | 38 +++++++++++++++++-- .../Editors/BackOfficeController.cs | 8 ++++ .../Editors/ContentControllerBase.cs | 7 ++++ src/Umbraco.Web/Umbraco.Web.csproj | 13 ++++++- 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index bd373f1ec0..8e4d98ddb7 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -7,19 +7,18 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Xml.Linq; -using Umbraco.Core.Auditing; using Umbraco.Core.Configuration; using Umbraco.Core.Events; +using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Media; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Core.Publishing; namespace Umbraco.Core.Services { @@ -36,6 +35,7 @@ namespace Umbraco.Core.Services private readonly EntityXmlSerializer _entitySerializer = new EntityXmlSerializer(); private readonly IDataTypeService _dataTypeService; private readonly IUserService _userService; + private readonly MediaFileSystem _mediaFileSystem = FileSystemProviderManager.Current.MediaFileSystem; public MediaService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory, IDataTypeService dataTypeService, IUserService userService) : base(provider, repositoryFactory, logger, eventMessagesFactory) @@ -1325,6 +1325,38 @@ namespace Umbraco.Core.Services } } + public Stream GetMediaFileContentStream(string filepath) + { + if (_mediaFileSystem.FileExists(filepath) == false) + return null; + try + { + return _mediaFileSystem.OpenFile(filepath); + } + catch + { + return null; // deal with race conds + } + } + + public void SetMediaFileContent(string filepath, Stream stream) + { + _mediaFileSystem.AddFile(filepath, stream, true); + } + + public void DeleteMediaFile(string filepath) + { + _mediaFileSystem.DeleteFile(filepath, true); + } + + public void GenerateThumbnails(string filepath, PropertyType propertyType) + { + using (var filestream = _mediaFileSystem.OpenFile(filepath)) + { + _mediaFileSystem.GenerateThumbnails(filestream, filepath, propertyType); + } + } + /// /// Hack: This is used to fix some data if an entity's properties are invalid/corrupt /// diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 50c8b2e82c..376606ff5c 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -332,6 +332,10 @@ namespace Umbraco.Web.Editors "tagApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( controller => controller.GetAllTags(null)) }, + { + "templateApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetById(0)) + }, { "memberTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl( controller => controller.GetNodes("-1", null)) @@ -360,6 +364,10 @@ namespace Umbraco.Web.Editors "healthCheckBaseUrl", Url.GetUmbracoApiServiceBaseUrl( controller => controller.GetAllHealthChecks()) }, + { + "templateQueryApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.PostTemplateQuery(null)) + }, { "codeFileApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( controller => controller.GetByPath("", "")) diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs index 0cddb6fcd5..cba29060b3 100644 --- a/src/Umbraco.Web/Editors/ContentControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs @@ -97,6 +97,13 @@ namespace Umbraco.Web.Editors var dictionary = new Dictionary(); //add the files if any var files = contentItem.UploadedFiles.Where(x => x.PropertyAlias == property.Alias).ToArray(); + if (files.Length > 0) + { + dictionary.Add("files", files); + // add extra things needed to figure out where to put the files + dictionary.Add("cuid", contentItem.PersistedContent.Key); + dictionary.Add("puid", dboProperty.PropertyType.Key); + } foreach (var file in files) file.FileName = file.FileName.ToSafeFileName(); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 14c3e44dba..f9f6446491 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -133,6 +133,10 @@ ..\packages\Microsoft.AspNet.Identity.Owin.2.2.1\lib\net45\Microsoft.AspNet.Identity.Owin.dll + + ..\packages\Microsoft.AspNet.SignalR.Core.2.2.1\lib\net45\Microsoft.AspNet.SignalR.Core.dll + True + ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll @@ -186,6 +190,10 @@ + + ..\packages\System.Threading.Tasks.Dataflow.4.6.0\lib\netstandard1.1\System.Threading.Tasks.Dataflow.dll + True + 3.5 @@ -277,6 +285,7 @@ + @@ -328,11 +337,13 @@ + + @@ -504,7 +515,6 @@ - @@ -2208,6 +2218,7 @@ umbraco_org_umbraco_update_CheckForUpgrade + 11.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v11.0 From 0a53f7636c803c86a9ee0a2df8ad3437c9c7a1af Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 10 Jan 2017 15:01:45 +0100 Subject: [PATCH 199/599] remove orange color from discard changes button --- .../src/views/common/notifications/confirmroutechange.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/notifications/confirmroutechange.html b/src/Umbraco.Web.UI.Client/src/views/common/notifications/confirmroutechange.html index a772aa0159..6d47084f08 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/notifications/confirmroutechange.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/notifications/confirmroutechange.html @@ -2,6 +2,6 @@

    You have unsaved changes

    Are you sure you want to navigate away from this page? - you have unsaved changes

    - +
    From 845dd4673c5f00d60d34e6eb0f43a4facb98f598 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 10 Jan 2017 15:15:56 +0100 Subject: [PATCH 200/599] Add client side file for partial view editor --- .../common/services/templatehelper.service.js | 57 ++++ .../views/common/overlays/insert/insert.html | 8 +- .../src/views/partialviews/edit.controller.js | 315 ++++++++++++++++++ .../src/views/partialviews/edit.html | 129 +++++++ .../src/views/templates/edit.controller.js | 43 ++- .../template-editor-controller.spec.js | 7 + 6 files changed, 532 insertions(+), 27 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/services/templatehelper.service.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html diff --git a/src/Umbraco.Web.UI.Client/src/common/services/templatehelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/templatehelper.service.js new file mode 100644 index 0000000000..01e544c78d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/templatehelper.service.js @@ -0,0 +1,57 @@ +(function() { + 'use strict'; + + function templateHelperService() { + + //crappy hack due to dictionary items not in umbracoNode table + function getInsertDictionarySnippet(nodeName) { + return "@Umbraco.GetDictionaryValue(\"" + nodeName + "\")"; + } + + function getInsertPartialSnippet(nodeName) { + return "@Html.Partial(\"" + nodeName + "\")"; + } + + function getQuerySnippet(queryExpression) { + var code = "\n@{\n" + "\tvar selection = " + queryExpression + ";\n}\n"; + code += "
    \n\n"; + return code; + } + + function getRenderBodySnippet() { + return "@RenderBody()"; + } + + function getRenderSectionSnippet(sectionName, mandatory) { + return "@RenderSection(\"" + sectionName + "\", " + mandatory + ")"; + } + + function getAddSectionSnippet(sectionName) { + return "@section " + sectionName + "\r\n{\r\n\r\n\t{0}\r\n\r\n}\r\n"; + } + + //////////// + + var service = { + getInsertDictionarySnippet: getInsertDictionarySnippet, + getInsertPartialSnippet: getInsertPartialSnippet, + getQuerySnippet: getQuerySnippet, + getRenderBodySnippet: getRenderBodySnippet, + getRenderSectionSnippet: getRenderSectionSnippet, + getAddSectionSnippet: getAddSectionSnippet + }; + + return service; + + } + + angular.module('umbraco.services').factory('templateHelper', templateHelperService); + + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html index e5ea8a3ddf..921058ea5a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html @@ -2,19 +2,19 @@
    -
    +
    Value
    Displays the value of a named field from the current page, with options to modify the value or fallback to alternative values.
    -
    +
    Partial view
    A partial view is a separate template file which can be rendered inside another template, it's great for reusing markup or for separating complex templates into separate files.
    -
    +
    Macro
    A Macro is a configurable component which is great for @@ -23,7 +23,7 @@
    -
    +
    Dictionary
    A dictionary item is a placeholder for a translatable piece of text, which makes it easy to create designs for multilingual websites.
    diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js new file mode 100644 index 0000000000..45065a8e89 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js @@ -0,0 +1,315 @@ +(function () { + "use strict"; + + function PartialViewsEditController($scope, $routeParams, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, angularHelper, $timeout, contentEditingHelper, localizationService, templateHelper) { + + var vm = this; + var localizeSaving = localizationService.localize("general_saving"); + + vm.page = {}; + vm.page.loading = true; + vm.partialView = {}; + vm.partialViewPathPrefix = "Partials /"; + + //menu + vm.page.menu = {}; + vm.page.menu.currentSection = appState.getSectionState("currentSection"); + vm.page.menu.currentNode = null; + + // bind functions to view model + vm.save = save; + vm.openPageFieldOverlay = openPageFieldOverlay; + vm.openDictionaryItemOverlay = openDictionaryItemOverlay; + vm.openQueryBuilderOverlay = openQueryBuilderOverlay; + vm.openMacroOverlay = openMacroOverlay; + vm.openInsertOverlay = openInsertOverlay; + + /* Functions bound to view model */ + + function save() { + + vm.page.saveButtonState = "busy"; + vm.partialView.content = vm.editor.getValue(); + + contentEditingHelper.contentEditorPerformSave({ + statusMessage: localizeSaving, + saveMethod: templateResource.save, + scope: $scope, + content: vm.partialView, + //We do not redirect on failure for stylesheets - this is because it is not possible to actually save the doc + // type when server side validation fails - as opposed to content where we are capable of saving the content + // item if server side validation fails + redirectOnFailure: false, + rebindCallback: function (orignal, saved) {} + }).then(function (saved) { + + notificationsService.success("Partial View saved"); + vm.page.saveButtonState = "success"; + vm.partialView = saved; + + //sync state + editorState.set(vm.partialView); + + // normal tree sync + navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + + // clear $dirty state on form + setFormState("pristine"); + + }, function (err) { + + vm.page.saveButtonState = "error"; + + localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_validationFailedMessage").then(function(msgValue) { + notificationsService.error(headerValue, msgValue); + }); + }); + + }); + + } + + function openInsertOverlay() { + + vm.insertOverlay = { + view: "insert", + allowedTypes: { + macro: true, + dictionary: true, + umbracoField: true + }, + hideSubmitButton: true, + show: true, + submit: function(model) { + + switch(model.insert.type) { + case "macro": + var macroObject = macroService.collectValueData(model.insert.selectedMacro, model.insert.macroParams, "Mvc"); + insert(macroObject.syntax); + break; + + case "dictionary": + var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name); + insert(code); + break; + + case "partial": + var code = templateHelper.getInsertPartialSnippet(model.insert.node.name); + insert(code); + break; + + case "umbracoField": + insert(model.insert.umbracoField); + break; + } + + vm.insertOverlay.show = false; + vm.insertOverlay = null; + + }, + close: function(oldModel) { + // close the dialog + vm.insertOverlay.show = false; + vm.insertOverlay = null; + // focus editor + vm.editor.focus(); + } + }; + + } + + + function openMacroOverlay() { + + vm.macroPickerOverlay = { + view: "macropicker", + dialogData: {}, + show: true, + title: "Insert macro", + submit: function (model) { + + var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); + insert(macroObject.syntax); + + vm.macroPickerOverlay.show = false; + vm.macroPickerOverlay = null; + + }, + close: function(oldModel) { + // close the dialog + vm.macroPickerOverlay.show = false; + vm.macroPickerOverlay = null; + // focus editor + vm.editor.focus(); + } + }; + } + + + function openPageFieldOverlay() { + vm.pageFieldOverlay = { + submitButtonLabel: "Insert", + closeButtonlabel: "Cancel", + view: "insertfield", + show: true, + submit: function (model) { + insert(model.umbracoField); + vm.pageFieldOverlay.show = false; + vm.pageFieldOverlay = null; + }, + close: function (model) { + // close the dialog + vm.pageFieldOverlay.show = false; + vm.pageFieldOverlay = null; + // focus editor + vm.editor.focus(); + } + }; + } + + + function openDictionaryItemOverlay() { + vm.dictionaryItemOverlay = { + view: "treepicker", + section: "settings", + treeAlias: "dictionary", + entityType: "dictionary", + multiPicker: false, + show: true, + title: "Insert dictionary item", + select: function(node){ + + var code = templateHelper.getInsertDictionarySnippet(node.name); + insert(code); + + vm.dictionaryItemOverlay.show = false; + vm.dictionaryItemOverlay = null; + }, + close: function (model) { + // close dialog + vm.dictionaryItemOverlay.show = false; + vm.dictionaryItemOverlay = null; + // focus editor + vm.editor.focus(); + } + }; + } + + function openQueryBuilderOverlay() { + vm.queryBuilderOverlay = { + view: "querybuilder", + show: true, + title: "Query for content", + + submit: function (model) { + + var code = templateHelper.getQuerySnippet(model.result.queryExpression); + insert(code); + + vm.queryBuilderOverlay.show = false; + vm.queryBuilderOverlay = null; + }, + + close: function (model) { + // close dialog + vm.queryBuilderOverlay.show = false; + vm.queryBuilderOverlay = null; + // focus editor + vm.editor.focus(); + } + }; + } + + /* Local functions */ + + function init() { + //we need to load this somewhere, for now its here. + assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css"); + if ($routeParams.create) { + templateResource.getScaffold().then(function (partialView) { + ready(partialView); + }); + } else { + templateResource.getById($routeParams.id).then(function (partialView) { + ready(partialView); + }); + } + } + + function ready(partialView) { + + vm.page.loading = false; + vm.partialView = partialView; + + //sync state + editorState.set(vm.partialView); + navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + + // ace configuration + vm.aceOption = { + mode: "razor", + theme: "chrome", + showPrintMargin: false, + advanced: { + fontSize: '14px' + }, + onLoad: function(_editor) { + vm.editor = _editor; + + // initial cursor placement + // Keep cursor in name field if we are create a new template + // else set the cursor at the bottom of the code editor + if(!$routeParams.create) { + $timeout(function(){ + vm.editor.navigateFileEnd(); + vm.editor.focus(); + persistCurrentLocation(); + }); + } + + //change on blur, focus + vm.editor.on("blur", persistCurrentLocation); + vm.editor.on("focus", persistCurrentLocation); + } + } + + } + + function insert(str) { + vm.editor.moveCursorToPosition(vm.currentPosition); + vm.editor.insert(str); + vm.editor.focus(); + + // set form state to $dirty + setFormState("dirty"); + } + + function persistCurrentLocation() { + vm.currentPosition = vm.editor.getCursorPosition(); + } + + function setFormState(state) { + + // get the current form + var currentForm = angularHelper.getCurrentForm($scope); + + // set state + if(state === "dirty") { + currentForm.$setDirty(); + } else if(state === "pristine") { + currentForm.$setPristine(); + } + } + + + init(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.PartialViews.EditController", PartialViewsEditController); +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html new file mode 100644 index 0000000000..647844e3d5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html @@ -0,0 +1,129 @@ +
    + + + +
    + + + + + + + + +
    + +
    + + + + +
    + +
    + +
    +
    + + +
    + + + + + + + + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index f661b82e7b..f12c82fc3c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function TemplatesEditController($scope, $routeParams, $timeout, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, treeService, contentEditingHelper, localizationService, angularHelper) { + function TemplatesEditController($scope, $routeParams, $timeout, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, treeService, contentEditingHelper, localizationService, angularHelper, templateHelper) { var vm = this; var oldMasterTemplateAlias = null; @@ -169,26 +169,30 @@ vm.insertOverlay = { view: "insert", + allowedTypes: { + macro: true, + dictionary: true, + partial: true, + umbracoField: true + }, hideSubmitButton: true, show: true, submit: function(model) { switch(model.insert.type) { - case "macro": + case "macro": var macroObject = macroService.collectValueData(model.insert.selectedMacro, model.insert.macroParams, "Mvc"); insert(macroObject.syntax); break; case "dictionary": - //crappy hack due to dictionary items not in umbracoNode table - var code = "@Umbraco.GetDictionaryValue(\"" + model.insert.node.name + "\")"; + var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name); insert(code); break; case "partial": - //crappy hack due to dictionary items not in umbracoNode table - var code = "@Html.Partial(\"" + model.insert.node.name + "\")"; + var code = templateHelper.getInsertPartialSnippet(model.insert.node.name); insert(code); break; @@ -272,8 +276,7 @@ show: true, title: "Insert dictionary item", select: function(node){ - //crappy hack due to dictionary items not in umbracoNode table - var code = "@Umbraco.GetDictionaryValue(\"" + node.name + "\")"; + var code = templateHelper.getInsertDictionarySnippet(node.name); insert(code); vm.dictionaryItemOverlay.show = false; @@ -299,8 +302,8 @@ show: true, title: "Insert Partial view", select: function(node){ - //crappy hack due to dictionary items not in umbracoNode table - var code = "@Html.Partial(\"" + node.name + "\")"; + + var code = templateHelper.getInsertPartialSnippet(node.name); insert(code); vm.partialItemOverlay.show = false; @@ -321,18 +324,9 @@ view: "querybuilder", show: true, title: "Query for content", - submit: function (model) { - var code = "\n@{\n" + "\tvar selection = " + model.result.queryExpression + ";\n}\n"; - code += "
      \n" + - "\t@foreach(var item in selection){\n" + - "\t\t
    • \n" + - "\t\t\t@item.Name\n" + - "\t\t
    • \n" + - "\t}\n" + - "
    \n\n"; - + var code = templateHelper.getQuerySnippet(model.result.queryExpression); insert(code); vm.queryBuilderOverlay.show = false; @@ -360,15 +354,18 @@ submit: function(model) { if (model.insertType === 'renderBody') { - insert("@RenderBody()"); + var code = templateHelper.getRenderBodySnippet(); + insert(code); } if (model.insertType === 'renderSection') { - insert("@RenderSection(\"" + model.renderSectionName + "\", " + model.mandatoryRenderSection + ")"); + var code = templateHelper.getRenderSectionSnippet(model.renderSectionName, model.mandatoryRenderSection); + insert(code); } if (model.insertType === 'addSection') { - wrap("@section " + model.sectionName + "\r\n{\r\n\r\n\t{0}\r\n\r\n}\r\n"); + var code = templateHelper.getAddSectionSnippet(model.sectionName); + wrap(code); } vm.sectionsOverlay.show = false; diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js index 8208999536..6af9d12d73 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/templates/template-editor-controller.spec.js @@ -101,6 +101,13 @@ $setPristine: function() {} } } + }, + templateHelper: { + getInsertDictionary: function() { return ""; }, + getInsertPartialSnippet: function() { return ""; }, + getQuerySnippet: function() { return ""; }, + getRenderBodySnippet: function() { return ""; }, + getRenderSectionSnippet: function() { return ""; } } }); } From 23304aca0b32322b3e039f688475609945781d48 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 10 Jan 2017 15:16:16 +0100 Subject: [PATCH 201/599] add unit tests for template helper --- .../common/services/template-helper.spec.js | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js new file mode 100644 index 0000000000..e1ba0670ed --- /dev/null +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/template-helper.spec.js @@ -0,0 +1,84 @@ +describe('service: templateHelper', function () { + + var templateHelper; + + beforeEach(module("umbraco.services")); + + beforeEach(inject(function ($injector) { + templateHelper = $injector.get('templateHelper'); + })); + + afterEach(inject(function ($rootScope) { + $rootScope.$apply(); + })); + + describe('getInsertDictionarySnippet', function () { + + it('should return the snippet for inserting a dictionary item', function () { + var snippet = '@Umbraco.GetDictionaryValue("nodeName")'; + expect(templateHelper.getInsertDictionarySnippet('nodeName')).toBe(snippet); + }); + + }); + + describe('getInsertPartialSnippet', function () { + + it('should return the snippet for inserting a partial', function () { + var snippet = '@Html.Partial("nodeName")'; + expect(templateHelper.getInsertPartialSnippet("nodeName")).toBe(snippet); + }); + + }); + + describe('getQuerySnippet', function () { + + it('should return the snippet for a query', function () { + var queryExpression = "queryExpression"; + var snippet = "\n@{\n" + "\tvar selection = " + queryExpression + ";\n}\n"; + snippet += "
      \n" + + "\t@foreach(var item in selection){\n" + + "\t\t
    • \n" + + "\t\t\t@item.Name\n" + + "\t\t
    • \n" + + "\t}\n" + + "
    \n\n"; + + expect(templateHelper.getQuerySnippet(queryExpression)).toBe(snippet); + }); + + }); + + describe('getRenderBodySnippet', function () { + + it('should return the snippet for render body', function () { + var snippet = '@RenderBody()'; + expect(templateHelper.getRenderBodySnippet()).toBe(snippet); + }); + + }); + + describe('getRenderSectionSnippet', function () { + + it('should return the snippet for defining a section', function () { + var snippet = '@RenderSection("sectionName", false)'; + expect(templateHelper.getRenderSectionSnippet("sectionName", false)).toBe(snippet); + }); + + it('should return the snippet for defining a mandatory section', function () { + var snippet = '@RenderSection("sectionName", true)'; + expect(templateHelper.getRenderSectionSnippet("sectionName", true)).toBe(snippet); + }); + + }); + + describe('getAddSectionSnippet', function () { + + it('should return the snippet for implementing a section', function () { + var sectionName = "sectionName"; + var snippet = "@section " + sectionName + "\r\n{\r\n\r\n\t{0}\r\n\r\n}\r\n"; + expect(templateHelper.getAddSectionSnippet(sectionName)).toEqual(snippet); + }); + + }); + +}); \ No newline at end of file From f036bc90c3e5abf9f733cf0c4f5ba9a96de2a358 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Tue, 10 Jan 2017 16:54:46 +0100 Subject: [PATCH 202/599] Fixes failing mapper test --- src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs b/src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs index 0b9b297f44..d673e573a8 100644 --- a/src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/TemplateModelMapper.cs @@ -10,7 +10,8 @@ namespace Umbraco.Web.Models.Mapping { public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) { - config.CreateMap(); + config.CreateMap() + .ForMember(x => x.Notifications, exp => exp.Ignore()); config.CreateMap() .ForMember(x => x.Key, exp => exp.Ignore()) From d563eb07a229ec1ed4c4738d7af8a9d164869cf3 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Tue, 10 Jan 2017 16:55:03 +0100 Subject: [PATCH 203/599] Adds .FirstChild(string alias) extension to Model.Content --- src/Umbraco.Web/PublishedContentExtensions.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 5caa08729a..7972f109f9 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -1777,6 +1777,17 @@ namespace Umbraco.Web return content.Children().FirstOrDefault(); } + /// + /// Gets the first child of the content, of a given content type. + /// + /// The content. + /// The content type alias. + /// The first child of content, of the given content type. + public static IPublishedContent FirstChild(this IPublishedContent content, string alias) + { + return content.Children( alias ).FirstOrDefault(); + } + public static IPublishedContent FirstChild(this IPublishedContent content, Func predicate) { return content.Children(predicate).FirstOrDefault(); From 0b7f60c0f8114d0ca41297f83f75e362aada510e Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Tue, 10 Jan 2017 16:55:30 +0100 Subject: [PATCH 204/599] Fixes to issues with query generation Ensures its typed content and children is called as a method --- src/Umbraco.Web/Editors/TemplateQueryController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs index b6e086d7cc..e3c63dd22e 100644 --- a/src/Umbraco.Web/Editors/TemplateQueryController.cs +++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs @@ -103,7 +103,7 @@ namespace Umbraco.Web.Editors { // we did not find the path sb.Clear(); - sb.AppendFormat("Umbraco.Content({0})", model.Source.Id); + sb.AppendFormat("Umbraco.TypedContent({0})", model.Source.Id); pointerNode = targetNode; } } @@ -126,7 +126,7 @@ namespace Umbraco.Web.Editors timer.Start(); contents = pointerNode.Children; timer.Stop(); - sb.Append(".Children"); + sb.Append(".Children()"); } //setup 2 clauses, 1 for returning, 1 for testing From 98279052e6dd2d7e8a18cb9a7e058c2a58b987b3 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 11 Jan 2017 10:07:43 +0100 Subject: [PATCH 205/599] remove lorem ipsum from the sections dialog --- .../overlays/templatesections/templatesections.controller.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.controller.js index cb5da443b6..d8cc93c1b8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.controller.js @@ -11,10 +11,6 @@ $scope.model.title = "Sections"; } - if(!$scope.model.subtitle) { - $scope.model.subtitle = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; - } - vm.select = select; function onInit() { From e8e0b105592821ae1cec85a595f136cbe60556ae Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 11 Jan 2017 14:26:56 +0100 Subject: [PATCH 206/599] fix issue with inserting partials from folders --- .../src/views/templates/edit.controller.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index f661b82e7b..e67273850f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -188,7 +188,8 @@ case "partial": //crappy hack due to dictionary items not in umbracoNode table - var code = "@Html.Partial(\"" + model.insert.node.name + "\")"; + var nodeNameWithPath = model.insert.node.id.replace(".cshtml", ""); + var code = "@Html.Partial(\"" + nodeNameWithPath + "\")"; insert(code); break; @@ -300,7 +301,8 @@ title: "Insert Partial view", select: function(node){ //crappy hack due to dictionary items not in umbracoNode table - var code = "@Html.Partial(\"" + node.name + "\")"; + var nodeNameWithPath = node.id.replace(".cshtml", ""); + var code = "@Html.Partial(\"" + nodeNameWithPath + "\")"; insert(code); vm.partialItemOverlay.show = false; From 5a2836eb6d4c9dea7e78502f81791873606b5566 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 12 Jan 2017 13:45:17 +1100 Subject: [PATCH 207/599] bumps version --- build/UmbracoVersion.txt | 2 +- src/SolutionInfo.cs | 2 +- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index 096e4d6b5c..8e0c551771 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,3 +1,3 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) 7.6.0 -alpha044 +alpha047 diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 5e904fab70..ed91c2f67c 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -12,4 +12,4 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyFileVersion("7.6.0")] -[assembly: AssemblyInformationalVersion("7.6.0-alpha044")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("7.6.0-alpha047")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 03b1827221..963870654e 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration /// Gets the version comment (like beta or RC). /// /// The version comment. - public static string CurrentComment { get { return "alpha044"; } } + public static string CurrentComment { get { return "alpha047"; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx From 8a11b0f7a81231ef0cf6e24a531682cc890ca59c Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 12 Jan 2017 17:33:30 +1100 Subject: [PATCH 208/599] publisizes a constant (moves it to where it should be), publicizes another required class for deploy, bumps version --- build/UmbracoVersion.txt | 2 +- src/SolutionInfo.cs | 2 +- src/Umbraco.Core/ApplicationContext.cs | 2 +- .../Configuration/GlobalSettings.cs | 7 ++-- .../Configuration/UmbracoVersion.cs | 2 +- src/Umbraco.Core/Constants-System.cs | 3 ++ src/Umbraco.Core/CoreBootManager.cs | 2 +- src/Umbraco.Core/DatabaseContext.cs | 32 +++++++++---------- src/Umbraco.Core/Events/MigrationEventArgs.cs | 10 +++--- src/Umbraco.Core/Models/UmbracoObjectTypes.cs | 8 ++++- .../Migrations/Initial/BaseDataCreation.cs | 2 +- .../Initial/DatabaseSchemaCreation.cs | 6 ++-- .../Initial/DatabaseSchemaResult.cs | 2 +- .../RemoveUmbracoAppConstraints.cs | 2 +- .../AddPreviewXmlTable.cs | 2 +- .../AddIndexToCmsMacroPropertyTable.cs | 2 +- .../AddIndexToCmsMacroTable.cs | 2 +- .../AddPropertyEditorAliasColumn.cs | 2 +- .../AlterCmsMacroPropertyTable.cs | 2 +- .../AlterTagRelationsTable.cs | 2 +- .../TargetVersionSeven/AlterTagsTable.cs | 2 +- .../TargetVersionSeven/AlterUserTable.cs | 2 +- .../AssignMissingKeysAndIndexes.cs | 2 +- .../TargetVersionSeven/DropControlIdColumn.cs | 2 +- .../RemoveCmsMacroPropertyTypeTable.cs | 2 +- .../UpdateControlIdToPropertyEditorAlias.cs | 2 +- .../UpdateRelatedLinksData.cs | 2 +- .../AddLockObjects.cs | 2 +- .../AddLockTable.cs | 2 +- .../UpdateAllowedMediaTypesAtRoot.cs | 2 +- .../AddRedirectUrlTable.cs | 2 +- .../EnsureServersLockObject.cs | 2 +- .../RemoveStylesheetDataAndTablesAgain.cs | 2 +- .../UpdateUniqueIndexOnCmsPropertyData.cs | 2 +- .../AddDataDecimalColumn.cs | 2 +- .../AddUmbracoDeployTables.cs | 2 +- .../AddUniqueIdPropertyTypeGroupColumn.cs | 2 +- ...EnsureContentTypeUniqueIdsAreConsistent.cs | 2 +- .../FixListViewMediaSortOrder.cs | 2 +- .../RemoveParentIdPropertyTypeGroupColumn.cs | 2 +- .../AssignMissingPrimaryForMySqlKeys.cs | 2 +- .../AddMacroUniqueIdColumn.cs | 2 +- .../AddRelationTypeUniqueIdColumn.cs | 2 +- .../RemovePropertyDataIdIndex.cs | 2 +- .../RemoveUmbracoDeployTables.cs | 2 +- .../UpdateUserLanguagesToIsoCode.cs | 2 +- .../EnsureMigrationsTableIdentityIsCorrect.cs | 2 +- .../AddExternalLoginsTable.cs | 2 +- ...reignKeysForLanguageAndDictionaryTables.cs | 2 +- .../AddMigrationTable.cs | 2 +- .../AddPublicAccessTables.cs | 2 +- .../AddRelationTypeForDocumentOnDelete.cs | 2 +- .../AddServerRegistrationColumnsAndLock.cs | 2 +- .../AddUniqueIdPropertyTypeColumn.cs | 2 +- .../AddUserColumns.cs | 2 +- .../CleanUpCorruptedPublishedFlags.cs | 2 +- .../CreateCacheInstructionTable.cs | 2 +- .../MigrateAndRemoveTemplateMasterColumn.cs | 2 +- .../MigrateStylesheetDataToFile.cs | 2 +- .../MovePublicAccessXmlDataToDb.cs | 2 +- .../RemoveHelpTextColumn.cs | 2 +- .../RemoveLanguageLocaleColumn.cs | 2 +- .../RemoveStylesheetDataAndTables.cs | 2 +- .../RemoveUmbracoLoginsTable.cs | 2 +- .../UpdateUniqueIdToHaveCorrectIndexType.cs | 2 +- .../AddIndexToUmbracoNodeTable.cs | 2 +- .../AddMissingForeignKeyForContentType.cs | 2 +- .../AlterDataTypePreValueTable.cs | 2 +- .../RemoveCmsDocumentAliasColumn.cs | 2 +- .../TargetVersionSix/DeleteAppTables.cs | 2 +- .../EnsureAppsTreesUpdated.cs | 2 +- .../MoveMasterContentTypeData.cs | 2 +- .../NewCmsContentType2ContentTypeTable.cs | 2 +- .../RemoveMasterContentTypeColumn.cs | 2 +- .../TargetVersionSix/RenameCmsTabTable.cs | 2 +- .../TargetVersionSix/RenameTabIdColumn.cs | 2 +- ...teCmsContentTypeAllowedContentTypeTable.cs | 2 +- .../UpdateCmsContentTypeTable.cs | 2 +- .../UpdateCmsContentVersionTable.cs | 2 +- .../UpdateCmsPropertyTypeGroupTable.cs | 2 +- .../CreateServerRegistryTable.cs | 2 +- .../AddChangeDocumentTypePermission.cs | 4 +-- .../AdditionalIndexesAndKeys.cs | 4 +-- .../AssignMissingPrimaryForMySqlKeys.cs | 4 +-- .../AssignMissingPrimaryForMySqlKeys2.cs | 2 +- .../ChangePasswordColumn.cs | 4 +-- .../UpdateToNewMemberPropertyAliases.cs | 4 +-- .../UpdatePropertyTypesAndGroups.cs | 2 +- .../UnitOfWork/PetaPocoUnitOfWorkProvider.cs | 4 +-- .../Security/MembershipProviderBase.cs | 3 +- .../Migrations/MigrationIssuesTests.cs | 4 +-- .../TargetVersionSixthMigrationsTest.cs | 2 +- .../Migrations/Upgrades/BaseUpgradeTest.cs | 2 +- .../Upgrades/SqlCeDataUpgradeTest.cs | 2 +- .../Migrations/Upgrades/SqlCeUpgradeTest.cs | 2 +- .../Upgrades/ValidateOlderSchemaTest.cs | 2 +- .../Persistence/BaseTableByTableTest.cs | 2 +- .../Persistence/DatabaseContextTests.cs | 4 +-- .../Services/ThreadSafetyServiceTest.cs | 2 +- .../TestHelpers/BaseDatabaseFactoryTest.cs | 4 +-- .../TestHelpers/BaseUmbracoApplicationTest.cs | 2 +- src/Umbraco.Tests/TestHelpers/TestHelper.cs | 4 +-- src/Umbraco.Web/Install/InstallHelper.cs | 2 +- .../InstallSteps/DatabaseConfigureStep.cs | 2 +- .../InstallSteps/DatabaseInstallStep.cs | 6 ++-- .../InstallSteps/DatabaseUpgradeStep.cs | 2 +- .../Install/InstallSteps/NewInstallStep.cs | 2 +- .../ContentEditing/UmbracoEntityTypes.cs | 7 +++- .../ClearCsrfCookiesAfterUpgrade.cs | 3 +- ...ediaXmlCacheForDeletedItemsAfterUpgrade.cs | 2 +- .../EnsureListViewDataTypeIsCreated.cs | 2 +- .../OverwriteStylesheetFilesFromTempFiles.cs | 2 +- .../PublishAfterUpgradeToVersionSixth.cs | 2 +- .../RebuildXmlCachesAfterUpgrade.cs | 2 +- src/umbraco.businesslogic/Application.cs | 2 +- src/umbraco.businesslogic/GlobalSettings.cs | 4 +-- .../datatype/DataEditorSettingsStorage.cs | 3 +- src/umbraco.datalayer/DataLayerHelper.cs | 3 +- 118 files changed, 170 insertions(+), 155 deletions(-) diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index 8e0c551771..1672ad53d0 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,3 +1,3 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) 7.6.0 -alpha047 +alpha048 diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index ed91c2f67c..b092457e25 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -12,4 +12,4 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyFileVersion("7.6.0")] -[assembly: AssemblyInformationalVersion("7.6.0-alpha047")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("7.6.0-alpha048")] \ No newline at end of file diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index e47ef04650..e5bac73edb 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -296,7 +296,7 @@ namespace Umbraco.Core // if we have a db context available, if we don't then we are not installed anyways if (DatabaseContext.IsDatabaseConfigured && DatabaseContext.CanConnect) { - var found = Services.MigrationEntryService.FindEntry(GlobalSettings.UmbracoMigrationName, UmbracoVersion.GetSemanticVersion()); + var found = Services.MigrationEntryService.FindEntry(Constants.System.UmbracoMigrationName, UmbracoVersion.GetSemanticVersion()); if (found == null) { //we haven't executed this migration in this environment, so even though the config versions match, diff --git a/src/Umbraco.Core/Configuration/GlobalSettings.cs b/src/Umbraco.Core/Configuration/GlobalSettings.cs index 525bff2999..c28f398333 100644 --- a/src/Umbraco.Core/Configuration/GlobalSettings.cs +++ b/src/Umbraco.Core/Configuration/GlobalSettings.cs @@ -212,7 +212,7 @@ namespace Umbraco.Core.Configuration { get { - var settings = ConfigurationManager.ConnectionStrings[UmbracoConnectionName]; + var settings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; var connectionString = string.Empty; if (settings != null) @@ -241,10 +241,7 @@ namespace Umbraco.Core.Configuration } } } - - //TODO: Move these to constants! - public const string UmbracoConnectionName = "umbracoDbDSN"; - public const string UmbracoMigrationName = "Umbraco"; + /// /// Gets or sets the configuration status. This will return the version number of the currently installed umbraco instance. diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 963870654e..cd759cba82 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration /// Gets the version comment (like beta or RC). /// /// The version comment. - public static string CurrentComment { get { return "alpha047"; } } + public static string CurrentComment { get { return "alpha048"; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx diff --git a/src/Umbraco.Core/Constants-System.cs b/src/Umbraco.Core/Constants-System.cs index bc86d1717f..d1ac6fbb38 100644 --- a/src/Umbraco.Core/Constants-System.cs +++ b/src/Umbraco.Core/Constants-System.cs @@ -25,6 +25,9 @@ public const int DefaultContentListViewDataTypeId = -95; public const int DefaultMediaListViewDataTypeId = -96; public const int DefaultMembersListViewDataTypeId = -97; + + public const string UmbracoConnectionName = "umbracoDbDSN"; + public const string UmbracoMigrationName = "Umbraco"; } public static class DatabaseProviders diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index 4ef08bd02f..e98cd31dbf 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -105,7 +105,7 @@ namespace Umbraco.Core LegacyParameterEditorAliasConverter.CreateMappingsForCoreEditors(); //create database and service contexts for the app context - var dbFactory = new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName, ProfilingLogger.Logger); + var dbFactory = new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, ProfilingLogger.Logger); Database.Mapper = new PetaPocoMapper(); var dbContext = new DatabaseContext( diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs index 2464e1ba7b..483e124e35 100644 --- a/src/Umbraco.Core/DatabaseContext.cs +++ b/src/Umbraco.Core/DatabaseContext.cs @@ -195,14 +195,14 @@ namespace Umbraco.Core return _providerName; _providerName = Constants.DatabaseProviders.SqlServer; - if (ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName] != null) + if (ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName] != null) { - if (string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ProviderName) == false) - _providerName = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ProviderName; + if (string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName].ProviderName) == false) + _providerName = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName].ProviderName; } else { - throw new InvalidOperationException("Can't find a connection string with the name '" + GlobalSettings.UmbracoConnectionName + "'"); + throw new InvalidOperationException("Can't find a connection string with the name '" + Constants.System.UmbracoConnectionName + "'"); } return _providerName; } @@ -388,9 +388,9 @@ namespace Umbraco.Core { //Set the connection string for the new datalayer var connectionStringSettings = string.IsNullOrEmpty(providerName) - ? new ConnectionStringSettings(GlobalSettings.UmbracoConnectionName, + ? new ConnectionStringSettings(Constants.System.UmbracoConnectionName, connectionString) - : new ConnectionStringSettings(GlobalSettings.UmbracoConnectionName, + : new ConnectionStringSettings(Constants.System.UmbracoConnectionName, connectionString, providerName); _connectionString = connectionString; @@ -401,10 +401,10 @@ namespace Umbraco.Core var connectionstrings = xml.Root.DescendantsAndSelf("connectionStrings").Single(); // Update connectionString if it exists, or else create a new appSetting for the given key and value - var setting = connectionstrings.Descendants("add").FirstOrDefault(s => s.Attribute("name").Value == GlobalSettings.UmbracoConnectionName); + var setting = connectionstrings.Descendants("add").FirstOrDefault(s => s.Attribute("name").Value == Constants.System.UmbracoConnectionName); if (setting == null) connectionstrings.Add(new XElement("add", - new XAttribute("name", GlobalSettings.UmbracoConnectionName), + new XAttribute("name", Constants.System.UmbracoConnectionName), new XAttribute("connectionString", connectionStringSettings), new XAttribute("providerName", providerName))); else @@ -429,23 +429,23 @@ namespace Umbraco.Core /// internal void Initialize() { - var databaseSettings = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName]; + var databaseSettings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; if (databaseSettings != null && string.IsNullOrWhiteSpace(databaseSettings.ConnectionString) == false && string.IsNullOrWhiteSpace(databaseSettings.ProviderName) == false) { var providerName = Constants.DatabaseProviders.SqlServer; string connString = null; - if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ProviderName)) + if (!string.IsNullOrEmpty(ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName].ProviderName)) { - providerName = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ProviderName; - connString = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName].ConnectionString; + providerName = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName].ProviderName; + connString = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName].ConnectionString; } Initialize(providerName, connString); } - else if (ConfigurationManager.AppSettings.ContainsKey(GlobalSettings.UmbracoConnectionName) && string.IsNullOrEmpty(ConfigurationManager.AppSettings[GlobalSettings.UmbracoConnectionName]) == false) + else if (ConfigurationManager.AppSettings.ContainsKey(Constants.System.UmbracoConnectionName) && string.IsNullOrEmpty(ConfigurationManager.AppSettings[Constants.System.UmbracoConnectionName]) == false) { //A valid connectionstring does not exist, but the legacy appSettings key was found, so we'll reconfigure the conn.string. - var legacyConnString = ConfigurationManager.AppSettings[GlobalSettings.UmbracoConnectionName]; + var legacyConnString = ConfigurationManager.AppSettings[Constants.System.UmbracoConnectionName]; if (legacyConnString.ToLowerInvariant().Contains("sqlce4umbraco")) { ConfigureEmbeddedDatabaseConnection(); @@ -476,7 +476,7 @@ namespace Umbraco.Core } //Remove the legacy connection string, so we don't end up in a loop if something goes wrong. - GlobalSettings.RemoveSetting(GlobalSettings.UmbracoConnectionName); + GlobalSettings.RemoveSetting(Constants.System.UmbracoConnectionName); } else @@ -672,7 +672,7 @@ namespace Umbraco.Core //DO the upgrade! - var runner = new MigrationRunner(migrationEntryService, _logger, currentInstalledVersion, UmbracoVersion.GetSemanticVersion(), GlobalSettings.UmbracoMigrationName); + var runner = new MigrationRunner(migrationEntryService, _logger, currentInstalledVersion, UmbracoVersion.GetSemanticVersion(), Constants.System.UmbracoMigrationName); var upgraded = runner.Execute(database, true); diff --git a/src/Umbraco.Core/Events/MigrationEventArgs.cs b/src/Umbraco.Core/Events/MigrationEventArgs.cs index 89dfe56294..6afe9bd754 100644 --- a/src/Umbraco.Core/Events/MigrationEventArgs.cs +++ b/src/Umbraco.Core/Events/MigrationEventArgs.cs @@ -31,13 +31,13 @@ namespace Umbraco.Core.Events [Obsolete("Use constructor accepting a product name instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion, bool canCancel) - : this(eventObject, null, configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName, canCancel) + : this(eventObject, null, configuredVersion, targetVersion, Constants.System.UmbracoMigrationName, canCancel) { } [Obsolete("Use constructor accepting SemVersion instances and a product name instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public MigrationEventArgs(IList eventObject, Version configuredVersion, Version targetVersion, bool canCancel) - : this(eventObject, null, new SemVersion(configuredVersion), new SemVersion(targetVersion), GlobalSettings.UmbracoMigrationName, canCancel) + : this(eventObject, null, new SemVersion(configuredVersion), new SemVersion(targetVersion), Constants.System.UmbracoMigrationName, canCancel) { } /// @@ -74,7 +74,7 @@ namespace Umbraco.Core.Events MigrationContext = migrationContext; ConfiguredSemVersion = configuredVersion; TargetSemVersion = targetVersion; - ProductName = GlobalSettings.UmbracoMigrationName; + ProductName = Constants.System.UmbracoMigrationName; } /// @@ -97,13 +97,13 @@ namespace Umbraco.Core.Events [Obsolete("Use constructor accepting a product name instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion) - : this(eventObject, null, configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName, false) + : this(eventObject, null, configuredVersion, targetVersion, Constants.System.UmbracoMigrationName, false) { } [Obsolete("Use constructor accepting SemVersion instances and a product name instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public MigrationEventArgs(IList eventObject, Version configuredVersion, Version targetVersion) - : this(eventObject, null, new SemVersion(configuredVersion), new SemVersion(targetVersion), GlobalSettings.UmbracoMigrationName, false) + : this(eventObject, null, new SemVersion(configuredVersion), new SemVersion(targetVersion), Constants.System.UmbracoMigrationName, false) { } /// diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs index 3909af2a55..02dc44c4ce 100644 --- a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs +++ b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs @@ -1,4 +1,6 @@ -using Umbraco.Core.CodeAnnotations; +using System; +using System.ComponentModel; +using Umbraco.Core.CodeAnnotations; namespace Umbraco.Core.Models { @@ -17,6 +19,8 @@ namespace Umbraco.Core.Models /// [UmbracoObjectType(Constants.ObjectTypes.ContentItemType)] [FriendlyName("Content Item Type")] + [Obsolete("This is not used and will be removed in future versions")] + [EditorBrowsable(EditorBrowsableState.Never)] ContentItemType, /// @@ -67,6 +71,8 @@ namespace Umbraco.Core.Models /// [UmbracoObjectType(Constants.ObjectTypes.ContentItem)] [FriendlyName("Content Item")] + [Obsolete("This is not used and will be removed in future versions")] + [EditorBrowsable(EditorBrowsableState.Never)] ContentItem, /// diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index 9b5d2bc2c8..3c7fc5ed42 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -310,7 +310,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial var dto = new MigrationDto { Id = 1, - Name = GlobalSettings.UmbracoMigrationName, + Name = Constants.System.UmbracoMigrationName, Version = UmbracoVersion.GetSemanticVersion().ToString(), CreateDate = DateTime.Now }; diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs index d00e82bf71..83ae1de7e1 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial /// /// Represents the initial database schema creation by running CreateTable for all DTOs against the db. /// - internal class DatabaseSchemaCreation + public class DatabaseSchemaCreation { /// /// Constructor @@ -345,7 +345,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial /// Raises the event. /// /// The instance containing the event data. - protected internal virtual void FireBeforeCreation(DatabaseCreationEventArgs e) + internal virtual void FireBeforeCreation(DatabaseCreationEventArgs e) { if (BeforeCreation != null) { @@ -361,7 +361,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial /// Raises the event. /// /// The instance containing the event data. - protected virtual void FireAfterCreation(DatabaseCreationEventArgs e) + internal virtual void FireAfterCreation(DatabaseCreationEventArgs e) { if (AfterCreation != null) { diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs index 14caeb2a8f..a34db10839 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaResult.cs @@ -47,7 +47,7 @@ namespace Umbraco.Core.Persistence.Migrations.Initial if (ValidTables.Any(x => x.InvariantEquals("umbracoMigration"))) { - var allMigrations = migrationEntryService.GetAll(GlobalSettings.UmbracoMigrationName); + var allMigrations = migrationEntryService.GetAll(Constants.System.UmbracoMigrationName); mostrecent = allMigrations.OrderByDescending(x => x.Version).Select(x => x.Version).FirstOrDefault(); } diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs index 0f648d34eb..062ec4f22b 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourNineZero/RemoveUmbracoAppConstraints.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero { - [MigrationAttribute("4.9.0", 0, GlobalSettings.UmbracoMigrationName)] + [MigrationAttribute("4.9.0", 0, Constants.System.UmbracoMigrationName)] public class RemoveUmbracoAppConstraints : MigrationBase { public RemoveUmbracoAppConstraints(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourOneZero/AddPreviewXmlTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourOneZero/AddPreviewXmlTable.cs index 4e8d3165fb..4ac06d1531 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourOneZero/AddPreviewXmlTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionFourOneZero/AddPreviewXmlTable.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionFourOneZero { - [Migration("4.1.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("4.1.0", 0, Constants.System.UmbracoMigrationName)] public class AddPreviewXmlTable : MigrationBase { public AddPreviewXmlTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs index b61ac55448..8dfe51f8dc 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroPropertyTable.cs @@ -10,7 +10,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven /// /// Creats a unique index across two columns so we cannot have duplicate property aliases for one macro /// - [Migration("7.0.0", 5, GlobalSettings.UmbracoMigrationName)] + [Migration("7.0.0", 5, Constants.System.UmbracoMigrationName)] public class AddIndexToCmsMacroPropertyTable : MigrationBase { private readonly bool _skipIndexCheck; diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs index d18cb430c0..e6c237d4d6 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddIndexToCmsMacroTable.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven /// /// Creates a unique index on the macro alias so we cannot have duplicates by alias /// - [Migration("7.0.0", 4, GlobalSettings.UmbracoMigrationName)] + [Migration("7.0.0", 4, Constants.System.UmbracoMigrationName)] public class AddIndexToCmsMacroTable : MigrationBase { private readonly bool _forTesting; diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs index e20c2cfb0b..474da442be 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AddPropertyEditorAliasColumn.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { - [Migration("7.0.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("7.0.0", 0, Constants.System.UmbracoMigrationName)] public class AddPropertyEditorAliasColumn : MigrationBase { public AddPropertyEditorAliasColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs index 9154c6c985..474b8a3c13 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterCmsMacroPropertyTable.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven /// needs to be changed to editorAlias, we'll do this by removing the constraint,changing the macroPropertyType to the new /// editorAlias column (and maintaing data so we can reference it) /// - [Migration("7.0.0", 6, GlobalSettings.UmbracoMigrationName)] + [Migration("7.0.0", 6, Constants.System.UmbracoMigrationName)] public class AlterCmsMacroPropertyTable : MigrationBase { public AlterCmsMacroPropertyTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs index d822b7593a..704fcffc84 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagRelationsTable.cs @@ -9,7 +9,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { - [Migration("7.0.0", 8, GlobalSettings.UmbracoMigrationName)] + [Migration("7.0.0", 8, Constants.System.UmbracoMigrationName)] public class AlterTagRelationsTable : MigrationBase { public AlterTagRelationsTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs index d069d8222d..32bd9d1403 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterTagsTable.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { - [Migration("7.0.0", 9, GlobalSettings.UmbracoMigrationName)] + [Migration("7.0.0", 9, Constants.System.UmbracoMigrationName)] public class AlterTagsTable : MigrationBase { public AlterTagsTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs index 28c3eb15f2..d82d73f7c3 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AlterUserTable.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { - [Migration("7.0.0", 3, GlobalSettings.UmbracoMigrationName)] + [Migration("7.0.0", 3, Constants.System.UmbracoMigrationName)] public class AlterUserTable : MigrationBase { public AlterUserTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AssignMissingKeysAndIndexes.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AssignMissingKeysAndIndexes.cs index 9de1ea0871..c76031e4e4 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AssignMissingKeysAndIndexes.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/AssignMissingKeysAndIndexes.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven /// and it wasn't a MySQL install. /// see: http://issues.umbraco.org/issue/U4-5707 /// - [Migration("7.0.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("7.0.0", 0, Constants.System.UmbracoMigrationName)] public class AssignMissingKeysAndIndexes : MigrationBase { public AssignMissingKeysAndIndexes(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs index 0917411f8b..7928255968 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/DropControlIdColumn.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { - [Migration("7.0.0", 2, GlobalSettings.UmbracoMigrationName)] + [Migration("7.0.0", 2, Constants.System.UmbracoMigrationName)] public class DropControlIdColumn : MigrationBase { public DropControlIdColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs index f4f4b9065c..1c83ef5972 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/RemoveCmsMacroPropertyTypeTable.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { - [Migration("7.0.0", 7, GlobalSettings.UmbracoMigrationName)] + [Migration("7.0.0", 7, Constants.System.UmbracoMigrationName)] public class RemoveCmsMacroPropertyTypeTable : MigrationBase { public RemoveCmsMacroPropertyTypeTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs index ef464667dd..f94fb7ce2c 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateControlIdToPropertyEditorAlias.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven /// /// Updates the data in the changed propertyEditorAlias column after it has been changed by ChangeControlIdColumn /// - [Migration("7.0.0", 1, GlobalSettings.UmbracoMigrationName)] + [Migration("7.0.0", 1, Constants.System.UmbracoMigrationName)] public class UpdateControlIdToPropertyEditorAlias : MigrationBase { public UpdateControlIdToPropertyEditorAlias(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs index 749996ea36..d29935acd2 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSeven/UpdateRelatedLinksData.cs @@ -16,7 +16,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSeven { - [Migration("7.0.0", 10, GlobalSettings.UmbracoMigrationName)] + [Migration("7.0.0", 10, Constants.System.UmbracoMigrationName)] public class UpdateRelatedLinksData : MigrationBase { public UpdateRelatedLinksData(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockObjects.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockObjects.cs index c56ae0e2f1..39d139601c 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockObjects.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockObjects.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveFive { - [Migration("7.5.5", 101, GlobalSettings.UmbracoMigrationName)] + [Migration("7.5.5", 101, Constants.System.UmbracoMigrationName)] public class AddLockObjects : MigrationBase { public AddLockObjects(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockTable.cs index 5dc1720a2a..9774ed62cf 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/AddLockTable.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveFive { - [Migration("7.5.5", 100, GlobalSettings.UmbracoMigrationName)] + [Migration("7.5.5", 100, Constants.System.UmbracoMigrationName)] public class AddLockTable : MigrationBase { public AddLockTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/UpdateAllowedMediaTypesAtRoot.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/UpdateAllowedMediaTypesAtRoot.cs index c9a0d509e6..1acb979ff6 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/UpdateAllowedMediaTypesAtRoot.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveFive/UpdateAllowedMediaTypesAtRoot.cs @@ -7,7 +7,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveFiv /// /// See: http://issues.umbraco.org/issue/U4-4196 /// - [Migration("7.5.5", 1, GlobalSettings.UmbracoMigrationName)] + [Migration("7.5.5", 1, Constants.System.UmbracoMigrationName)] public class UpdateAllowedMediaTypesAtRoot : MigrationBase { public UpdateAllowedMediaTypesAtRoot(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/AddRedirectUrlTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/AddRedirectUrlTable.cs index 508c0f284b..829631c7f4 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/AddRedirectUrlTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/AddRedirectUrlTable.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZero { - [Migration("7.5.0", 100, GlobalSettings.UmbracoMigrationName)] + [Migration("7.5.0", 100, Constants.System.UmbracoMigrationName)] public class AddRedirectUrlTable : MigrationBase { public AddRedirectUrlTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/EnsureServersLockObject.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/EnsureServersLockObject.cs index 1bfef5ef6d..4c83a70fe2 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/EnsureServersLockObject.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/EnsureServersLockObject.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZer // This migration exists for 7.3.0 but it seems like it was not always running properly // if you're upgrading from 7.3.0 or higher than we add this migration, if you're upgrading // from 7.3.0 or lower then you will already get this migration in the migration to get to 7.3.0 - [Migration("7.3.0", "7.5.0", 10, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", "7.5.0", 10, Constants.System.UmbracoMigrationName)] public class EnsureServersLockObject : MigrationBase { public EnsureServersLockObject(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/RemoveStylesheetDataAndTablesAgain.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/RemoveStylesheetDataAndTablesAgain.cs index 96523e25e8..f8c4c14bbe 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/RemoveStylesheetDataAndTablesAgain.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/RemoveStylesheetDataAndTablesAgain.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZer /// /// This is here to re-remove these tables, we dropped them in 7.3 but new installs created them again so we're going to re-drop them /// - [Migration("7.5.0", 1, GlobalSettings.UmbracoMigrationName)] + [Migration("7.5.0", 1, Constants.System.UmbracoMigrationName)] public class RemoveStylesheetDataAndTablesAgain : MigrationBase { public RemoveStylesheetDataAndTablesAgain(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/UpdateUniqueIndexOnCmsPropertyData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/UpdateUniqueIndexOnCmsPropertyData.cs index f8e6abe42e..8ac4d86290 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/UpdateUniqueIndexOnCmsPropertyData.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFiveZero/UpdateUniqueIndexOnCmsPropertyData.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFiveZer /// /// See: http://issues.umbraco.org/issue/U4-8522 /// - [Migration("7.5.0", 2, GlobalSettings.UmbracoMigrationName)] + [Migration("7.5.0", 2, Constants.System.UmbracoMigrationName)] public class UpdateUniqueIndexOnCmsPropertyData : MigrationBase { public UpdateUniqueIndexOnCmsPropertyData(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddDataDecimalColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddDataDecimalColumn.cs index 442b92d2b5..26fb006e82 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddDataDecimalColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddDataDecimalColumn.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero { - [Migration("7.4.0", 1, GlobalSettings.UmbracoMigrationName)] + [Migration("7.4.0", 1, Constants.System.UmbracoMigrationName)] public class AddDataDecimalColumn : MigrationBase { public AddDataDecimalColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUmbracoDeployTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUmbracoDeployTables.cs index a39cea2ee0..0a1e6058a9 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUmbracoDeployTables.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUmbracoDeployTables.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero { - [Migration("7.4.0", 5, GlobalSettings.UmbracoMigrationName)] + [Migration("7.4.0", 5, Constants.System.UmbracoMigrationName)] public class AddUmbracoDeployTables : MigrationBase { public AddUmbracoDeployTables(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUniqueIdPropertyTypeGroupColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUniqueIdPropertyTypeGroupColumn.cs index fe600f6b69..5f987fc966 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUniqueIdPropertyTypeGroupColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/AddUniqueIdPropertyTypeGroupColumn.cs @@ -9,7 +9,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero { - [Migration("7.4.0", 2, GlobalSettings.UmbracoMigrationName)] + [Migration("7.4.0", 2, Constants.System.UmbracoMigrationName)] public class AddUniqueIdPropertyTypeGroupColumn : MigrationBase { public AddUniqueIdPropertyTypeGroupColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs index 20200a3230..c24048a6a7 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/EnsureContentTypeUniqueIdsAreConsistent.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZer /// alias, so we need to ensure that these are initially consistent on /// all environments (based on the alias). /// - [Migration("7.4.0", 3, GlobalSettings.UmbracoMigrationName)] + [Migration("7.4.0", 3, Constants.System.UmbracoMigrationName)] public class EnsureContentTypeUniqueIdsAreConsistent : MigrationBase { public EnsureContentTypeUniqueIdsAreConsistent(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/FixListViewMediaSortOrder.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/FixListViewMediaSortOrder.cs index e1fa9e9257..7d5714218a 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/FixListViewMediaSortOrder.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/FixListViewMediaSortOrder.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero { - [Migration("7.4.0", 4, GlobalSettings.UmbracoMigrationName)] + [Migration("7.4.0", 4, Constants.System.UmbracoMigrationName)] public class FixListViewMediaSortOrder : MigrationBase { public FixListViewMediaSortOrder(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/RemoveParentIdPropertyTypeGroupColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/RemoveParentIdPropertyTypeGroupColumn.cs index 3d285c2715..5f4120f243 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/RemoveParentIdPropertyTypeGroupColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenFourZero/RemoveParentIdPropertyTypeGroupColumn.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenFourZero { - [Migration("7.4.0", 4, GlobalSettings.UmbracoMigrationName)] + [Migration("7.4.0", 4, Constants.System.UmbracoMigrationName)] public class RemoveParentIdPropertyTypeGroupColumn : MigrationBase { public RemoveParentIdPropertyTypeGroupColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenOneZero/AssignMissingPrimaryForMySqlKeys.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenOneZero/AssignMissingPrimaryForMySqlKeys.cs index 6fa0eaa5dc..904a4a9349 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenOneZero/AssignMissingPrimaryForMySqlKeys.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenOneZero/AssignMissingPrimaryForMySqlKeys.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenOneZero // this is because when the 7.0.0 migrations are executed, this primary key get's created so if this migration is also executed // we will get exceptions because it is trying to create the PK two times. - [Migration("7.0.0", "7.1.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("7.0.0", "7.1.0", 0, Constants.System.UmbracoMigrationName)] public class AssignMissingPrimaryForMySqlKeys : MigrationBase { public AssignMissingPrimaryForMySqlKeys(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddMacroUniqueIdColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddMacroUniqueIdColumn.cs index c453ee58b2..fee0f37513 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddMacroUniqueIdColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddMacroUniqueIdColumn.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero { - [Migration("7.6.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("7.6.0", 0, Constants.System.UmbracoMigrationName)] public class AddMacroUniqueIdColumn : MigrationBase { public AddMacroUniqueIdColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddRelationTypeUniqueIdColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddRelationTypeUniqueIdColumn.cs index 8a6641c523..3fe72bb052 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddRelationTypeUniqueIdColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/AddRelationTypeUniqueIdColumn.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero { - [Migration("7.6.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("7.6.0", 0, Constants.System.UmbracoMigrationName)] public class AddRelationTypeUniqueIdColumn : MigrationBase { public AddRelationTypeUniqueIdColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemovePropertyDataIdIndex.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemovePropertyDataIdIndex.cs index b50c8e5f94..e965e4dd06 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemovePropertyDataIdIndex.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemovePropertyDataIdIndex.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero /// /// See: http://issues.umbraco.org/issue/U4-9188 /// - [Migration("7.6.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("7.6.0", 0, Constants.System.UmbracoMigrationName)] public class UpdateUniqueIndexOnCmsPropertyData : MigrationBase { public UpdateUniqueIndexOnCmsPropertyData(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemoveUmbracoDeployTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemoveUmbracoDeployTables.cs index ab227ebb01..33bcf5dbcb 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemoveUmbracoDeployTables.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSixZero/RemoveUmbracoDeployTables.cs @@ -9,7 +9,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSixZero { - [Migration("7.6.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("7.6.0", 0, Constants.System.UmbracoMigrationName)] public class RemoveUmbracoDeployTables : MigrationBase { public RemoveUmbracoDeployTables(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeOne/UpdateUserLanguagesToIsoCode.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeOne/UpdateUserLanguagesToIsoCode.cs index 50f78ca66d..c3d7857d75 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeOne/UpdateUserLanguagesToIsoCode.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeOne/UpdateUserLanguagesToIsoCode.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeOn /// /// This fixes the storage of user languages from the old format like en_us to en-US /// - [Migration("7.3.1", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.1", 0, Constants.System.UmbracoMigrationName)] public class UpdateUserLanguagesToIsoCode : MigrationBase { public UpdateUserLanguagesToIsoCode(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeTwo/EnsureMigrationsTableIdentityIsCorrect.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeTwo/EnsureMigrationsTableIdentityIsCorrect.cs index 91b4bd6438..a52abf1331 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeTwo/EnsureMigrationsTableIdentityIsCorrect.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeTwo/EnsureMigrationsTableIdentityIsCorrect.cs @@ -10,7 +10,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeTw /// This reinserts all migrations in the migrations table to account for initial rows inserted /// on creation without identity enabled. /// - [Migration("7.3.2", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.2", 0, Constants.System.UmbracoMigrationName)] public class EnsureMigrationsTableIdentityIsCorrect : MigrationBase { public EnsureMigrationsTableIdentityIsCorrect(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddExternalLoginsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddExternalLoginsTable.cs index 0fbec244b2..b5bb24e40e 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddExternalLoginsTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddExternalLoginsTable.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 9, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 9, Constants.System.UmbracoMigrationName)] public class AddExternalLoginsTable : MigrationBase { public AddExternalLoginsTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddForeignKeysForLanguageAndDictionaryTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddForeignKeysForLanguageAndDictionaryTables.cs index eea56031d4..0baf5bff1a 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddForeignKeysForLanguageAndDictionaryTables.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddForeignKeysForLanguageAndDictionaryTables.cs @@ -9,7 +9,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 14, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 14, Constants.System.UmbracoMigrationName)] public class AddForeignKeysForLanguageAndDictionaryTables : MigrationBase { public AddForeignKeysForLanguageAndDictionaryTables(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddMigrationTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddMigrationTable.cs index 079dbe1465..856a1d8ff6 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddMigrationTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddMigrationTable.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 11, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 11, Constants.System.UmbracoMigrationName)] public class AddMigrationTable : MigrationBase { public AddMigrationTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddPublicAccessTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddPublicAccessTables.cs index 16c0a923ae..b4ec1c20a6 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddPublicAccessTables.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddPublicAccessTables.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 6, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 6, Constants.System.UmbracoMigrationName)] public class AddPublicAccessTables : MigrationBase { public AddPublicAccessTables(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs index c9db282d85..4865a77ab8 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 0, Constants.System.UmbracoMigrationName)] public class AddRelationTypeForDocumentOnDelete : MigrationBase { public AddRelationTypeForDocumentOnDelete(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddServerRegistrationColumnsAndLock.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddServerRegistrationColumnsAndLock.cs index 6a14e408a9..00ab602343 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddServerRegistrationColumnsAndLock.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddServerRegistrationColumnsAndLock.cs @@ -7,7 +7,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 17, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 17, Constants.System.UmbracoMigrationName)] public class AddServerRegistrationColumnsAndLock : MigrationBase { public AddServerRegistrationColumnsAndLock(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs index 7589c7000d..070ef5d512 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUniqueIdPropertyTypeColumn.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 13, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 13, Constants.System.UmbracoMigrationName)] public class AddUniqueIdPropertyTypeColumn : MigrationBase { public AddUniqueIdPropertyTypeColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUserColumns.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUserColumns.cs index 46fde95005..01a3c61791 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUserColumns.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddUserColumns.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 10, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 10, Constants.System.UmbracoMigrationName)] public class AddUserColumns : MigrationBase { public AddUserColumns(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CleanUpCorruptedPublishedFlags.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CleanUpCorruptedPublishedFlags.cs index d62ad7645d..f8dfc0e8bb 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CleanUpCorruptedPublishedFlags.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CleanUpCorruptedPublishedFlags.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe /// Older corrupted dbs might have multiple published flags for a content item, this shouldn't be possible /// so we need to clear the content flag on the older version /// - [Migration("7.3.0", 18, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 18, Constants.System.UmbracoMigrationName)] public class CleanUpCorruptedPublishedFlags : MigrationBase { public CleanUpCorruptedPublishedFlags(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs index afa5a1e675..d51aceb679 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs @@ -10,7 +10,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 1, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 1, Constants.System.UmbracoMigrationName)] public class CreateCacheInstructionTable : MigrationBase { public CreateCacheInstructionTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs index 01db36abd9..59236106ab 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateAndRemoveTemplateMasterColumn.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe /// /// Remove the master column after we've migrated all of the values into the 'ParentId' and Path column of Umbraco node /// - [Migration("7.3.0", 1, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 1, Constants.System.UmbracoMigrationName)] public class MigrateAndRemoveTemplateMasterColumn : MigrationBase { diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateStylesheetDataToFile.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateStylesheetDataToFile.cs index 35e4befc55..40d2a6f183 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateStylesheetDataToFile.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MigrateStylesheetDataToFile.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe /// These files will then be copied over once the entire migration is complete so that if any migration fails and the db changes are /// rolled back, the original files won't be affected. /// - [Migration("7.3.0", 2, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 2, Constants.System.UmbracoMigrationName)] public class MigrateStylesheetDataToFile : MigrationBase { public MigrateStylesheetDataToFile(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MovePublicAccessXmlDataToDb.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MovePublicAccessXmlDataToDb.cs index d60385926b..16d1c6fbf5 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MovePublicAccessXmlDataToDb.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/MovePublicAccessXmlDataToDb.cs @@ -10,7 +10,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 7, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 7, Constants.System.UmbracoMigrationName)] public class MovePublicAccessXmlDataToDb : MigrationBase { public MovePublicAccessXmlDataToDb(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveHelpTextColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveHelpTextColumn.cs index d4911bd65a..5370ec0de3 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveHelpTextColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveHelpTextColumn.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 8, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 8, Constants.System.UmbracoMigrationName)] public class RemoveHelpTextColumn : MigrationBase { public RemoveHelpTextColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveLanguageLocaleColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveLanguageLocaleColumn.cs index a793cc6bbc..bf57364615 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveLanguageLocaleColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveLanguageLocaleColumn.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 4, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 4, Constants.System.UmbracoMigrationName)] public class RemoveLanguageLocaleColumn : MigrationBase { public RemoveLanguageLocaleColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveStylesheetDataAndTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveStylesheetDataAndTables.cs index da6d1b957d..e6b195cbf4 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveStylesheetDataAndTables.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveStylesheetDataAndTables.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 3, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 3, Constants.System.UmbracoMigrationName)] public class RemoveStylesheetDataAndTables : MigrationBase { public RemoveStylesheetDataAndTables(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveUmbracoLoginsTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveUmbracoLoginsTable.cs index b842ec041a..5fef478cc6 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveUmbracoLoginsTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/RemoveUmbracoLoginsTable.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 15, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 15, Constants.System.UmbracoMigrationName)] public class RemoveUmbracoLoginsTable : MigrationBase { public RemoveUmbracoLoginsTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/UpdateUniqueIdToHaveCorrectIndexType.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/UpdateUniqueIdToHaveCorrectIndexType.cs index ee6849c1e4..8eeca45cb1 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/UpdateUniqueIdToHaveCorrectIndexType.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/UpdateUniqueIdToHaveCorrectIndexType.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero { - [Migration("7.3.0", 5, GlobalSettings.UmbracoMigrationName)] + [Migration("7.3.0", 5, Constants.System.UmbracoMigrationName)] public class UpdateUniqueIdToHaveCorrectIndexType : MigrationBase { public UpdateUniqueIdToHaveCorrectIndexType(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AddIndexToUmbracoNodeTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AddIndexToUmbracoNodeTable.cs index 87c39b5f7e..7d5109a1db 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AddIndexToUmbracoNodeTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AddIndexToUmbracoNodeTable.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenTwoZero { - [Migration("7.2.0", 3, GlobalSettings.UmbracoMigrationName)] + [Migration("7.2.0", 3, Constants.System.UmbracoMigrationName)] public class AddIndexToUmbracoNodeTable : MigrationBase { private readonly bool _skipIndexCheck; diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AddMissingForeignKeyForContentType.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AddMissingForeignKeyForContentType.cs index 31cb6132cc..47b1f334d3 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AddMissingForeignKeyForContentType.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AddMissingForeignKeyForContentType.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenTwoZero { - [Migration("7.2.0", 1, GlobalSettings.UmbracoMigrationName)] + [Migration("7.2.0", 1, Constants.System.UmbracoMigrationName)] public class AddMissingForeignKeyForContentType : MigrationBase { public AddMissingForeignKeyForContentType(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AlterDataTypePreValueTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AlterDataTypePreValueTable.cs index e0d8e1b5dd..ba3938dd18 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AlterDataTypePreValueTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/AlterDataTypePreValueTable.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenTwoZero { - [Migration("7.2.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("7.2.0", 0, Constants.System.UmbracoMigrationName)] public class AlterDataTypePreValueTable : MigrationBase { public AlterDataTypePreValueTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/RemoveCmsDocumentAliasColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/RemoveCmsDocumentAliasColumn.cs index 2a8440af64..e67a8e7756 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/RemoveCmsDocumentAliasColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenTwoZero/RemoveCmsDocumentAliasColumn.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenTwoZero { - [Migration("7.2.0", 2, GlobalSettings.UmbracoMigrationName)] + [Migration("7.2.0", 2, Constants.System.UmbracoMigrationName)] public class RemoveCmsDocumentAliasColumn : MigrationBase { public RemoveCmsDocumentAliasColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs index a5b9bc1415..b4d394bec2 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/DeleteAppTables.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [Migration("6.0.0", 10, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 10, Constants.System.UmbracoMigrationName)] public class DeleteAppTables : MigrationBase { public DeleteAppTables(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/EnsureAppsTreesUpdated.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/EnsureAppsTreesUpdated.cs index 219936e0e2..49cedc6dc7 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/EnsureAppsTreesUpdated.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/EnsureAppsTreesUpdated.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [Migration("6.0.0", 9, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 9, Constants.System.UmbracoMigrationName)] public class EnsureAppsTreesUpdated : MigrationBase { public EnsureAppsTreesUpdated(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/MoveMasterContentTypeData.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/MoveMasterContentTypeData.cs index 3548d19c33..6e77050fd4 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/MoveMasterContentTypeData.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/MoveMasterContentTypeData.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [Migration("6.0.0", 5, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 5, Constants.System.UmbracoMigrationName)] public class MoveMasterContentTypeData : MigrationBase { public MoveMasterContentTypeData(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/NewCmsContentType2ContentTypeTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/NewCmsContentType2ContentTypeTable.cs index 622cefc44c..ee427bb517 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/NewCmsContentType2ContentTypeTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/NewCmsContentType2ContentTypeTable.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [Migration("6.0.0", 4, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 4, Constants.System.UmbracoMigrationName)] public class NewCmsContentType2ContentTypeTable : MigrationBase { public NewCmsContentType2ContentTypeTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RemoveMasterContentTypeColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RemoveMasterContentTypeColumn.cs index 7042e3c21f..e8964ced28 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RemoveMasterContentTypeColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RemoveMasterContentTypeColumn.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [Migration("6.0.0", 6, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 6, Constants.System.UmbracoMigrationName)] public class RemoveMasterContentTypeColumn : MigrationBase { public RemoveMasterContentTypeColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameCmsTabTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameCmsTabTable.cs index 9c97cd3444..c0738383d2 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameCmsTabTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameCmsTabTable.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [Migration("6.0.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 0, Constants.System.UmbracoMigrationName)] public class RenameCmsTabTable : MigrationBase { public RenameCmsTabTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameTabIdColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameTabIdColumn.cs index 14d3049d7e..fff439c03d 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameTabIdColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RenameTabIdColumn.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [Migration("6.0.0", 7, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 7, Constants.System.UmbracoMigrationName)] public class RenameTabIdColumn : MigrationBase { public RenameTabIdColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeAllowedContentTypeTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeAllowedContentTypeTable.cs index ede56d08e9..11168a69cb 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeAllowedContentTypeTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeAllowedContentTypeTable.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [Migration("6.0.0", 3, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 3, Constants.System.UmbracoMigrationName)] public class UpdateCmsContentTypeAllowedContentTypeTable : MigrationBase { public UpdateCmsContentTypeAllowedContentTypeTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeTable.cs index 0443c7f0bf..eb57f87c95 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentTypeTable.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [Migration("6.0.0", 2, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 2, Constants.System.UmbracoMigrationName)] public class UpdateCmsContentTypeTable : MigrationBase { public UpdateCmsContentTypeTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentVersionTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentVersionTable.cs index f78048fcb1..cee55fee41 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentVersionTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsContentVersionTable.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [Migration("6.0.0", 8, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 8, Constants.System.UmbracoMigrationName)] public class UpdateCmsContentVersionTable : MigrationBase { public UpdateCmsContentVersionTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs index 4b13c8b51d..7e0244bc57 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/UpdateCmsPropertyTypeGroupTable.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix { - [Migration("6.0.0", 1, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", 1, Constants.System.UmbracoMigrationName)] public class UpdateCmsPropertyTypeGroupTable : MigrationBase { public UpdateCmsPropertyTypeGroupTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixOneZero/CreateServerRegistryTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixOneZero/CreateServerRegistryTable.cs index 9037422960..bcd7ac5d6f 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixOneZero/CreateServerRegistryTable.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixOneZero/CreateServerRegistryTable.cs @@ -10,7 +10,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixOneZero { - [Migration("6.1.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("6.1.0", 0, Constants.System.UmbracoMigrationName)] public class CreateServerRegistryTable : MigrationBase { public CreateServerRegistryTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs index bad80e7c3a..42a652b166 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AddChangeDocumentTypePermission.cs @@ -6,8 +6,8 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero { - [Migration("7.1.0", 3, GlobalSettings.UmbracoMigrationName)] - [Migration("6.2.0", 3, GlobalSettings.UmbracoMigrationName)] + [Migration("7.1.0", 3, Constants.System.UmbracoMigrationName)] + [Migration("6.2.0", 3, Constants.System.UmbracoMigrationName)] public class AddChangeDocumentTypePermission : MigrationBase { public AddChangeDocumentTypePermission(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs index 0938d9be91..64f2970bdf 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AdditionalIndexesAndKeys.cs @@ -8,8 +8,8 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero { - [Migration("7.1.0", 1, GlobalSettings.UmbracoMigrationName)] - [Migration("6.2.0", 1, GlobalSettings.UmbracoMigrationName)] + [Migration("7.1.0", 1, Constants.System.UmbracoMigrationName)] + [Migration("6.2.0", 1, Constants.System.UmbracoMigrationName)] public class AdditionalIndexesAndKeys : MigrationBase { public AdditionalIndexesAndKeys(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys.cs index d8ecf855eb..5f2d6292c1 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys.cs @@ -8,8 +8,8 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero { //see: http://issues.umbraco.org/issue/U4-4430 - [Migration("7.1.0", 0, GlobalSettings.UmbracoMigrationName)] - [Migration("6.2.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("7.1.0", 0, Constants.System.UmbracoMigrationName)] + [Migration("6.2.0", 0, Constants.System.UmbracoMigrationName)] public class AssignMissingPrimaryForMySqlKeys : MigrationBase { public AssignMissingPrimaryForMySqlKeys(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys2.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys2.cs index b41bdea124..da7a5ce609 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys2.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/AssignMissingPrimaryForMySqlKeys2.cs @@ -8,7 +8,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero //We have to target this specifically to ensure this DOES NOT execute if upgrading from a version previous to 6.0, // this is because when the 6.0.0 migrations are executed, this primary key get's created so if this migration is also executed // we will get exceptions because it is trying to create the PK two times. - [Migration("6.0.0", "6.2.0", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.0", "6.2.0", 0, Constants.System.UmbracoMigrationName)] public class AssignMissingPrimaryForMySqlKeys2 : MigrationBase { public AssignMissingPrimaryForMySqlKeys2(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/ChangePasswordColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/ChangePasswordColumn.cs index cad1c0e127..327291f34b 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/ChangePasswordColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/ChangePasswordColumn.cs @@ -5,8 +5,8 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero { - [Migration("7.1.0", 2, GlobalSettings.UmbracoMigrationName)] - [Migration("6.2.0", 2, GlobalSettings.UmbracoMigrationName)] + [Migration("7.1.0", 2, Constants.System.UmbracoMigrationName)] + [Migration("6.2.0", 2, Constants.System.UmbracoMigrationName)] public class ChangePasswordColumn : MigrationBase { public ChangePasswordColumn(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/UpdateToNewMemberPropertyAliases.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/UpdateToNewMemberPropertyAliases.cs index 454d0e18ed..4038bdd1e9 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/UpdateToNewMemberPropertyAliases.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixTwoZero/UpdateToNewMemberPropertyAliases.cs @@ -7,8 +7,8 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixTwoZero { - [Migration("7.1.0", 4, GlobalSettings.UmbracoMigrationName)] - [Migration("6.2.0", 4, GlobalSettings.UmbracoMigrationName)] + [Migration("7.1.0", 4, Constants.System.UmbracoMigrationName)] + [Migration("6.2.0", 4, Constants.System.UmbracoMigrationName)] public class UpdateToNewMemberPropertyAliases : MigrationBase { public UpdateToNewMemberPropertyAliases(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs index 32c81700a8..15817d313d 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSixZeroOne/UpdatePropertyTypesAndGroups.cs @@ -7,7 +7,7 @@ using Umbraco.Core.Persistence.SqlSyntax; namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSixZeroOne { - [Migration("6.0.2", 0, GlobalSettings.UmbracoMigrationName)] + [Migration("6.0.2", 0, Constants.System.UmbracoMigrationName)] public class UpdatePropertyTypesAndGroups : MigrationBase { public UpdatePropertyTypesAndGroups(ISqlSyntaxProvider sqlSyntax, ILogger logger) : base(sqlSyntax, logger) diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs index 8c909fc554..97d066d572 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork [Obsolete("Use the constructor specifying an ILogger instead")] public PetaPocoUnitOfWorkProvider() - : this(new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName, LoggerResolver.Current.Logger)) + : this(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, LoggerResolver.Current.Logger)) { } @@ -27,7 +27,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// Parameterless constructor uses defaults /// public PetaPocoUnitOfWorkProvider(ILogger logger) - : this(new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName, logger)) + : this(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, logger)) { } diff --git a/src/Umbraco.Core/Security/MembershipProviderBase.cs b/src/Umbraco.Core/Security/MembershipProviderBase.cs index 3e02d6aec2..859d76b9e7 100644 --- a/src/Umbraco.Core/Security/MembershipProviderBase.cs +++ b/src/Umbraco.Core/Security/MembershipProviderBase.cs @@ -76,7 +76,8 @@ namespace Umbraco.Core.Security private bool _requiresQuestionAndAnswer; private bool _requiresUniqueEmail; private string _customHashAlgorithmType ; - internal bool UseLegacyEncoding; + + public bool UseLegacyEncoding { get; private set; } #region Properties diff --git a/src/Umbraco.Tests/Migrations/MigrationIssuesTests.cs b/src/Umbraco.Tests/Migrations/MigrationIssuesTests.cs index 8b51037d14..e2d5e4873d 100644 --- a/src/Umbraco.Tests/Migrations/MigrationIssuesTests.cs +++ b/src/Umbraco.Tests/Migrations/MigrationIssuesTests.cs @@ -115,7 +115,7 @@ namespace Umbraco.Tests.Migrations logger, new SemVersion(7, 4, 0), new SemVersion(7, 5, 0), - GlobalSettings.UmbracoMigrationName, + Constants.System.UmbracoMigrationName, //pass in explicit migrations new DeleteRedirectUrlTable(SqlSyntax, logger), @@ -128,7 +128,7 @@ namespace Umbraco.Tests.Migrations Assert.IsTrue(upgraded); } - [Migration("7.5.0", 99, GlobalSettings.UmbracoMigrationName)] + [Migration("7.5.0", 99, Constants.System.UmbracoMigrationName)] public class DeleteRedirectUrlTable : MigrationBase { public DeleteRedirectUrlTable(ISqlSyntaxProvider sqlSyntax, ILogger logger) diff --git a/src/Umbraco.Tests/Migrations/TargetVersionSixthMigrationsTest.cs b/src/Umbraco.Tests/Migrations/TargetVersionSixthMigrationsTest.cs index 51822c8098..6d01d63c79 100644 --- a/src/Umbraco.Tests/Migrations/TargetVersionSixthMigrationsTest.cs +++ b/src/Umbraco.Tests/Migrations/TargetVersionSixthMigrationsTest.cs @@ -77,7 +77,7 @@ namespace Umbraco.Tests.Migrations var migrationRunner = new MigrationRunner( Mock.Of(), - Logger, configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName); + Logger, configuredVersion, targetVersion, Constants.System.UmbracoMigrationName); var migrations = migrationRunner.OrderedUpgradeMigrations(foundMigrations).ToList(); diff --git a/src/Umbraco.Tests/Migrations/Upgrades/BaseUpgradeTest.cs b/src/Umbraco.Tests/Migrations/Upgrades/BaseUpgradeTest.cs index 3a3bbe7aa7..0c8f57771f 100644 --- a/src/Umbraco.Tests/Migrations/Upgrades/BaseUpgradeTest.cs +++ b/src/Umbraco.Tests/Migrations/Upgrades/BaseUpgradeTest.cs @@ -63,7 +63,7 @@ namespace Umbraco.Tests.Migrations.Upgrades logger, configuredVersion, targetVersion, - GlobalSettings.UmbracoMigrationName, + Constants.System.UmbracoMigrationName, //pass in explicit migrations new Core.Persistence.Migrations.Upgrades.TargetVersionFourNineZero.RemoveUmbracoAppConstraints(sql, logger), new DeleteAppTables(sql, logger), diff --git a/src/Umbraco.Tests/Migrations/Upgrades/SqlCeDataUpgradeTest.cs b/src/Umbraco.Tests/Migrations/Upgrades/SqlCeDataUpgradeTest.cs index 2ea7a40e57..54b970c033 100644 --- a/src/Umbraco.Tests/Migrations/Upgrades/SqlCeDataUpgradeTest.cs +++ b/src/Umbraco.Tests/Migrations/Upgrades/SqlCeDataUpgradeTest.cs @@ -31,7 +31,7 @@ namespace Umbraco.Tests.Migrations.Upgrades //Setup the MigrationRunner var migrationRunner = new MigrationRunner( Mock.Of(), - Mock.Of(), configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName); + Mock.Of(), configuredVersion, targetVersion, Constants.System.UmbracoMigrationName); bool upgraded = migrationRunner.Execute(db, provider, true); diff --git a/src/Umbraco.Tests/Migrations/Upgrades/SqlCeUpgradeTest.cs b/src/Umbraco.Tests/Migrations/Upgrades/SqlCeUpgradeTest.cs index f7080b7bc0..bdaf3f4bfd 100644 --- a/src/Umbraco.Tests/Migrations/Upgrades/SqlCeUpgradeTest.cs +++ b/src/Umbraco.Tests/Migrations/Upgrades/SqlCeUpgradeTest.cs @@ -42,7 +42,7 @@ namespace Umbraco.Tests.Migrations.Upgrades //Create the Sql CE database //Get the connectionstring settings from config - var settings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName]; + var settings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; using (var engine = new SqlCeEngine(settings.ConnectionString)) { engine.CreateDatabase(); diff --git a/src/Umbraco.Tests/Migrations/Upgrades/ValidateOlderSchemaTest.cs b/src/Umbraco.Tests/Migrations/Upgrades/ValidateOlderSchemaTest.cs index a375eb6850..5f7da6e401 100644 --- a/src/Umbraco.Tests/Migrations/Upgrades/ValidateOlderSchemaTest.cs +++ b/src/Umbraco.Tests/Migrations/Upgrades/ValidateOlderSchemaTest.cs @@ -66,7 +66,7 @@ namespace Umbraco.Tests.Migrations.Upgrades } //Get the connectionstring settings from config - var settings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName]; + var settings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; Resolution.Freeze(); diff --git a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs index 3322507c49..e8c994f1c0 100644 --- a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs +++ b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs @@ -51,7 +51,7 @@ namespace Umbraco.Tests.Persistence var dbContext = new DatabaseContext( - new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName, _logger), + new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, _logger), _logger, SqlSyntaxProvider, Constants.DatabaseProviders.SqlCe); var repositoryFactory = new RepositoryFactory(cacheHelper, _logger, SqlSyntaxProvider, SettingsForTests.GenerateMockSettings()); diff --git a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs index 1b42daa47c..bf0d3a736b 100644 --- a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs +++ b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Persistence SafeCallContext.Clear(); _dbContext = new DatabaseContext( - new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, Mock.Of()), + new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, Mock.Of()), Mock.Of(), new SqlCeSyntaxProvider(), Constants.DatabaseProviders.SqlCe); //unfortunately we have to set this up because the PetaPocoExtensions require singleton access @@ -79,7 +79,7 @@ namespace Umbraco.Tests.Persistence } //Get the connectionstring settings from config - var settings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName]; + var settings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; //by default the conn string is: Datasource=|DataDirectory|UmbracoPetaPocoTests.sdf;Flush Interval=1; //we'll just replace the sdf file with our custom one: diff --git a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs index 00e744c28b..504dc0d475 100644 --- a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs +++ b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs @@ -233,7 +233,7 @@ namespace Umbraco.Tests.Services { var db = _databases.GetOrAdd( Thread.CurrentThread.ManagedThreadId, - i => new UmbracoDatabase(Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName, _logger)); + i => new UmbracoDatabase(Constants.System.UmbracoConnectionName, _logger)); return db; } diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index c34620432f..873cf6ada4 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -151,9 +151,9 @@ namespace Umbraco.Tests.TestHelpers var path = TestHelper.CurrentAssemblyDirectory; //Get the connectionstring settings from config - var settings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName]; + var settings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; ConfigurationManager.AppSettings.Set( - Core.Configuration.GlobalSettings.UmbracoConnectionName, + Constants.System.UmbracoConnectionName, GetDbConnectionString()); _dbPath = string.Concat(path, "\\UmbracoPetaPocoTests.sdf"); diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index 642bad4a82..e0453557fa 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -164,7 +164,7 @@ namespace Umbraco.Tests.TestHelpers var evtMsgs = new TransientMessagesFactory(); var applicationContext = new ApplicationContext( //assign the db context - new DatabaseContext(new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, Logger), Logger, sqlSyntax, Constants.DatabaseProviders.SqlCe), + new DatabaseContext(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, Logger), Logger, sqlSyntax, Constants.DatabaseProviders.SqlCe), //assign the service context new ServiceContext(repoFactory, new PetaPocoUnitOfWorkProvider(Logger), new FileUnitOfWorkProvider(), new PublishingStrategy(evtMsgs, Logger), CacheHelper, Logger, evtMsgs), CacheHelper, diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index de62bff271..b8deae0621 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.TestHelpers /// public static void ClearDatabase() { - var databaseSettings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName]; + var databaseSettings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; var dataHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString, false) as SqlCEHelper; if (dataHelper == null) @@ -39,7 +39,7 @@ namespace Umbraco.Tests.TestHelpers public static void DropForeignKeys(string table) { - var databaseSettings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName]; + var databaseSettings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; var dataHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString, false) as SqlCEHelper; if (dataHelper == null) diff --git a/src/Umbraco.Web/Install/InstallHelper.cs b/src/Umbraco.Web/Install/InstallHelper.cs index d39b16f6ac..de101394c1 100644 --- a/src/Umbraco.Web/Install/InstallHelper.cs +++ b/src/Umbraco.Web/Install/InstallHelper.cs @@ -146,7 +146,7 @@ namespace Umbraco.Web.Install { get { - var databaseSettings = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName]; + var databaseSettings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; if (GlobalSettings.ConfigurationStatus.IsNullOrWhiteSpace() && _umbContext.Application.DatabaseContext.IsConnectionStringConfigured(databaseSettings) == false) { diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs b/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs index 4854919efb..ce28a26176 100644 --- a/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs @@ -83,7 +83,7 @@ namespace Umbraco.Web.Install.InstallSteps private bool ShouldDisplayView() { //If the connection string is already present in web.config we don't need to show the settings page and we jump to installing/upgrading. - var databaseSettings = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName]; + var databaseSettings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; if (_applicationContext.DatabaseContext.IsConnectionStringConfigured(databaseSettings)) { diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs index 57c8e67465..b75a310546 100644 --- a/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs @@ -48,13 +48,13 @@ namespace Umbraco.Web.Install.InstallSteps internal static void HandleConnectionStrings() { // Remove legacy umbracoDbDsn configuration setting if it exists and connectionstring also exists - if (ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName] != null) + if (ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName] != null) { - GlobalSettings.RemoveSetting(GlobalSettings.UmbracoConnectionName); + GlobalSettings.RemoveSetting(Constants.System.UmbracoConnectionName); } else { - var ex = new ArgumentNullException(string.Format("ConfigurationManager.ConnectionStrings[{0}]", GlobalSettings.UmbracoConnectionName), "Install / upgrade did not complete successfully, umbracoDbDSN was not set in the connectionStrings section"); + var ex = new ArgumentNullException(string.Format("ConfigurationManager.ConnectionStrings[{0}]", Constants.System.UmbracoConnectionName), "Install / upgrade did not complete successfully, umbracoDbDSN was not set in the connectionStrings section"); LogHelper.Error("", ex); throw ex; } diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs b/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs index 678ae427e5..c3ae313898 100644 --- a/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs @@ -58,7 +58,7 @@ namespace Umbraco.Web.Install.InstallSteps return false; } - var databaseSettings = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName]; + var databaseSettings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; if (_applicationContext.DatabaseContext.IsConnectionStringConfigured(databaseSettings)) { diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index fd36d8e4e6..775fcaaf9c 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -118,7 +118,7 @@ namespace Umbraco.Web.Install.InstallSteps public override bool RequiresExecution(UserModel model) { //now we have to check if this is really a new install, the db might be configured and might contain data - var databaseSettings = ConfigurationManager.ConnectionStrings[GlobalSettings.UmbracoConnectionName]; + var databaseSettings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; //if there's already a version then there should def be a user but in some cases someone may have // left a version number in there but cleared out their db conn string, in that case, it's really a new install. diff --git a/src/Umbraco.Web/Models/ContentEditing/UmbracoEntityTypes.cs b/src/Umbraco.Web/Models/ContentEditing/UmbracoEntityTypes.cs index 824dcb2b91..c68ee3c655 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UmbracoEntityTypes.cs +++ b/src/Umbraco.Web/Models/ContentEditing/UmbracoEntityTypes.cs @@ -1,4 +1,7 @@ -namespace Umbraco.Web.Models.ContentEditing +using System; +using System.ComponentModel; + +namespace Umbraco.Web.Models.ContentEditing { /// /// Represents the type's of Umbraco entities that can be resolved from the EntityController @@ -53,6 +56,8 @@ /// /// Content Item /// + [Obsolete("This is not used and will be removed in future versions")] + [EditorBrowsable(EditorBrowsableState.Never)] ContentItem, /// diff --git a/src/Umbraco.Web/Strategies/Migrations/ClearCsrfCookiesAfterUpgrade.cs b/src/Umbraco.Web/Strategies/Migrations/ClearCsrfCookiesAfterUpgrade.cs index 4a316fe96f..f1d7f761af 100644 --- a/src/Umbraco.Web/Strategies/Migrations/ClearCsrfCookiesAfterUpgrade.cs +++ b/src/Umbraco.Web/Strategies/Migrations/ClearCsrfCookiesAfterUpgrade.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Events; using Umbraco.Core.Persistence.Migrations; using Umbraco.Web.WebApi.Filters; using umbraco.interfaces; +using Umbraco.Core; using Umbraco.Core.Configuration; namespace Umbraco.Web.Strategies.Migrations @@ -15,7 +16,7 @@ namespace Umbraco.Web.Strategies.Migrations { protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e) { - if (e.ProductName != GlobalSettings.UmbracoMigrationName) return; + if (e.ProductName != Constants.System.UmbracoMigrationName) return; if (HttpContext.Current == null) return; diff --git a/src/Umbraco.Web/Strategies/Migrations/ClearMediaXmlCacheForDeletedItemsAfterUpgrade.cs b/src/Umbraco.Web/Strategies/Migrations/ClearMediaXmlCacheForDeletedItemsAfterUpgrade.cs index cf9ddeafb2..52144ae383 100644 --- a/src/Umbraco.Web/Strategies/Migrations/ClearMediaXmlCacheForDeletedItemsAfterUpgrade.cs +++ b/src/Umbraco.Web/Strategies/Migrations/ClearMediaXmlCacheForDeletedItemsAfterUpgrade.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.Strategies.Migrations { protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e) { - if (e.ProductName != GlobalSettings.UmbracoMigrationName) return; + if (e.ProductName != Constants.System.UmbracoMigrationName) return; var target70 = new Version(7, 0, 0); diff --git a/src/Umbraco.Web/Strategies/Migrations/EnsureListViewDataTypeIsCreated.cs b/src/Umbraco.Web/Strategies/Migrations/EnsureListViewDataTypeIsCreated.cs index 270fe4eace..45eb0a92b3 100644 --- a/src/Umbraco.Web/Strategies/Migrations/EnsureListViewDataTypeIsCreated.cs +++ b/src/Umbraco.Web/Strategies/Migrations/EnsureListViewDataTypeIsCreated.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.Strategies.Migrations { protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e) { - if (e.ProductName != GlobalSettings.UmbracoMigrationName) return; + if (e.ProductName != Constants.System.UmbracoMigrationName) return; var target720 = new Version(7, 2, 0); diff --git a/src/Umbraco.Web/Strategies/Migrations/OverwriteStylesheetFilesFromTempFiles.cs b/src/Umbraco.Web/Strategies/Migrations/OverwriteStylesheetFilesFromTempFiles.cs index 9a8b6a6044..a4e4349372 100644 --- a/src/Umbraco.Web/Strategies/Migrations/OverwriteStylesheetFilesFromTempFiles.cs +++ b/src/Umbraco.Web/Strategies/Migrations/OverwriteStylesheetFilesFromTempFiles.cs @@ -23,7 +23,7 @@ namespace Umbraco.Web.Strategies.Migrations { protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e) { - if (e.ProductName != GlobalSettings.UmbracoMigrationName) return; + if (e.ProductName != Constants.System.UmbracoMigrationName) return; var target73 = new Version(7, 3, 0); diff --git a/src/Umbraco.Web/Strategies/Migrations/PublishAfterUpgradeToVersionSixth.cs b/src/Umbraco.Web/Strategies/Migrations/PublishAfterUpgradeToVersionSixth.cs index 7be8d818d3..70f0b58f1a 100644 --- a/src/Umbraco.Web/Strategies/Migrations/PublishAfterUpgradeToVersionSixth.cs +++ b/src/Umbraco.Web/Strategies/Migrations/PublishAfterUpgradeToVersionSixth.cs @@ -20,7 +20,7 @@ namespace Umbraco.Web.Strategies.Migrations { protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e) { - if (e.ProductName != GlobalSettings.UmbracoMigrationName) return; + if (e.ProductName != Constants.System.UmbracoMigrationName) return; var target = new Version(6, 0, 0); if (e.ConfiguredVersion < target) diff --git a/src/Umbraco.Web/Strategies/Migrations/RebuildXmlCachesAfterUpgrade.cs b/src/Umbraco.Web/Strategies/Migrations/RebuildXmlCachesAfterUpgrade.cs index e62a738675..5f8f8b8593 100644 --- a/src/Umbraco.Web/Strategies/Migrations/RebuildXmlCachesAfterUpgrade.cs +++ b/src/Umbraco.Web/Strategies/Migrations/RebuildXmlCachesAfterUpgrade.cs @@ -23,7 +23,7 @@ namespace Umbraco.Web.Strategies.Migrations { protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e) { - if (e.ProductName != GlobalSettings.UmbracoMigrationName) return; + if (e.ProductName != Constants.System.UmbracoMigrationName) return; var v730 = new Semver.SemVersion(new Version(7, 3, 0)); diff --git a/src/umbraco.businesslogic/Application.cs b/src/umbraco.businesslogic/Application.cs index 2fd7b3cc02..ad6822019b 100644 --- a/src/umbraco.businesslogic/Application.cs +++ b/src/umbraco.businesslogic/Application.cs @@ -44,7 +44,7 @@ namespace umbraco.BusinessLogic try { - const string umbracoDsn = Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName; + const string umbracoDsn = Constants.System.UmbracoConnectionName; var databaseSettings = ConfigurationManager.ConnectionStrings[umbracoDsn]; if (databaseSettings != null) diff --git a/src/umbraco.businesslogic/GlobalSettings.cs b/src/umbraco.businesslogic/GlobalSettings.cs index 3d3721f783..9bc725c7c4 100644 --- a/src/umbraco.businesslogic/GlobalSettings.cs +++ b/src/umbraco.businesslogic/GlobalSettings.cs @@ -79,7 +79,7 @@ namespace umbraco /// Gets the database connection string /// /// The database connection string. - [Obsolete("Use System.ConfigurationManager.ConnectionStrings to get the connection with the key Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName instead")] + [Obsolete("Use System.ConfigurationManager.ConnectionStrings to get the connection with the key Constants.System.UmbracoConnectionName instead")] public static string DbDSN { get { return Umbraco.Core.Configuration.GlobalSettings.DbDsn; } @@ -359,7 +359,7 @@ namespace umbraco { get { - var databaseSettings = ConfigurationManager.ConnectionStrings[Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName]; + var databaseSettings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; var dataHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString, false); if (HttpContext.Current != null) diff --git a/src/umbraco.cms/businesslogic/datatype/DataEditorSettingsStorage.cs b/src/umbraco.cms/businesslogic/datatype/DataEditorSettingsStorage.cs index 5d2434164f..ddebb2ee2d 100644 --- a/src/umbraco.cms/businesslogic/datatype/DataEditorSettingsStorage.cs +++ b/src/umbraco.cms/businesslogic/datatype/DataEditorSettingsStorage.cs @@ -4,6 +4,7 @@ using System.Configuration; using System.Linq; using System.Text; using umbraco.DataLayer; +using Umbraco.Core; namespace umbraco.cms.businesslogic.datatype { @@ -14,7 +15,7 @@ namespace umbraco.cms.businesslogic.datatype public DataEditorSettingsStorage() { - var databaseSettings = ConfigurationManager.ConnectionStrings[Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName]; + var databaseSettings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; var dataHelper = DataLayerHelper.CreateSqlHelper(databaseSettings.ConnectionString, false); init(DataLayerHelper.CreateSqlHelper(dataHelper.ConnectionString, false)); diff --git a/src/umbraco.datalayer/DataLayerHelper.cs b/src/umbraco.datalayer/DataLayerHelper.cs index 7952b737ec..f79a90a0f1 100644 --- a/src/umbraco.datalayer/DataLayerHelper.cs +++ b/src/umbraco.datalayer/DataLayerHelper.cs @@ -10,6 +10,7 @@ using System; using System.Configuration; using System.Data.Common; using System.Reflection; +using Umbraco.Core; namespace umbraco.DataLayer { @@ -72,7 +73,7 @@ namespace umbraco.DataLayer throw new ArgumentException("Bad connection string.", "connectionString", ex); } - var connectionStringSettings = ConfigurationManager.ConnectionStrings[Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName]; + var connectionStringSettings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; if (forceLegacyConnection == false && connectionStringSettings != null) SetDataHelperNames(connectionStringSettings); From 73f77df5adee6f126b0691769203624b6a45b5d0 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 12 Jan 2017 09:47:33 +0100 Subject: [PATCH 209/599] Ensures VirtualPath is reset if a template alias is changed --- src/Umbraco.Web/Editors/TemplateController.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs index 58a4053de1..83a6bb5fe1 100644 --- a/src/Umbraco.Web/Editors/TemplateController.cs +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -49,7 +49,7 @@ namespace Umbraco.Web.Editors var template = Services.FileService.GetTemplate(id); if (template == null) throw new HttpResponseException(HttpStatusCode.NotFound); - + return Mapper.Map(template); } @@ -103,7 +103,9 @@ namespace Umbraco.Web.Editors if (template == null) throw new HttpResponseException(HttpStatusCode.NotFound); - var changeMaster = template.MasterTemplateAlias != display.MasterTemplateAlias; + var changeMaster = template.MasterTemplateAlias != display.MasterTemplateAlias; + var changeAlias = template.Alias != display.Alias; + Mapper.Map(display, template); if (changeMaster) @@ -128,7 +130,12 @@ namespace Umbraco.Web.Editors } } - Services.FileService.SaveTemplate(template); + Services.FileService.SaveTemplate(template); + + if (changeAlias) + { + template = Services.FileService.GetTemplate(template.Id); + } Mapper.Map(template, display); } @@ -144,6 +151,7 @@ namespace Umbraco.Web.Editors } var template = Services.FileService.CreateTemplateWithIdentity(display.Name, display.Content, master); + //template = Services.FileService.GetTemplate(template.Id); Mapper.Map(template, display); } From 20850632314e1c8dca0a2d5a3a9eed9731436fed Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 12 Jan 2017 10:07:44 +0100 Subject: [PATCH 210/599] Fixes issue with creating template below a parent template --- .../src/common/resources/template.resource.js | 5 +- .../src/views/templates/edit.controller.js | 2 +- src/Umbraco.Web/Editors/TemplateController.cs | 305 +++++++++--------- 3 files changed, 162 insertions(+), 150 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js index c90bb1766d..3052d532bb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js @@ -107,13 +107,14 @@ function templateResource($q, $http, umbDataFormatter, umbRequestHelper) { * @returns {Promise} resourcePromise object containing the template scaffold. * */ - getScaffold: function () { + getScaffold: function (id) { return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( "templateApiBaseUrl", - "GetEmpty")), + "GetScaffold", + [{ id: id }] )), "Failed to retrieve data for empty template"); }, diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index e67273850f..8e2b8337ad 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -95,7 +95,7 @@ if($routeParams.create){ - templateResource.getScaffold().then(function(template){ + templateResource.getScaffold(($routeParams.id)).then(function (template) { vm.ready(template); }); diff --git a/src/Umbraco.Web/Editors/TemplateController.cs b/src/Umbraco.Web/Editors/TemplateController.cs index 83a6bb5fe1..40effc10fa 100644 --- a/src/Umbraco.Web/Editors/TemplateController.cs +++ b/src/Umbraco.Web/Editors/TemplateController.cs @@ -1,161 +1,172 @@ -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Web.Http; -using AutoMapper; -using Umbraco.Core.IO; -using Umbraco.Core.Models; -using Umbraco.Core.Services; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Mvc; -using Umbraco.Web.WebApi; -using Umbraco.Web.WebApi.Filters; -using Constants = Umbraco.Core.Constants; - -namespace Umbraco.Web.Editors -{ - [PluginController("UmbracoApi")] - [UmbracoTreeAuthorize(Constants.Trees.Templates)] - public class TemplateController : BackOfficeNotificationsController - { - /// - /// Gets data type by alias - /// - /// - /// - public TemplateDisplay GetByAlias(string alias) - { - var template = Services.FileService.GetTemplate(alias); - return template == null ? null : Mapper.Map(template); - } - - /// - /// Get all templates - /// - /// - public IEnumerable GetAll() - { - return Services.FileService.GetTemplates().Select(Mapper.Map); - } - - /// - /// Gets the content json for the content id - /// - /// - /// - public TemplateDisplay GetById(int id) - { - var template = Services.FileService.GetTemplate(id); - if (template == null) - throw new HttpResponseException(HttpStatusCode.NotFound); - - return Mapper.Map(template); - } - - /// - /// Deletes a template wth a given ID - /// - /// - /// - [HttpDelete] - [HttpPost] - public HttpResponseMessage DeleteById(int id) - { - var template = Services.FileService.GetTemplate(id); - if (template == null) - throw new HttpResponseException(HttpStatusCode.NotFound); - - Services.FileService.DeleteTemplate(template.Alias); - return Request.CreateResponse(HttpStatusCode.OK); - } - - public TemplateDisplay GetEmpty() - { - var dt = new Template("", ""); - var content = ViewHelper.GetDefaultFileContent(); - - var scaffold = Mapper.Map(dt); - scaffold.Path = "-1"; - scaffold.Content = content + "\r\n\r\n@* the fun starts here *@\r\n\r\n"; - return scaffold; - } - - /// - /// Saves the data type - /// - /// - /// - public TemplateDisplay PostSave(TemplateDisplay display) - { - - //Checking the submitted is valid with the Required attributes decorated on the ViewModel - if (ModelState.IsValid == false) - { - throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); - } - - - if (display.Id > 0) - { - // update - var template = Services.FileService.GetTemplate(display.Id); - if (template == null) - throw new HttpResponseException(HttpStatusCode.NotFound); - +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Web.Http; +using AutoMapper; +using Umbraco.Core.IO; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi; +using Umbraco.Web.WebApi.Filters; +using Constants = Umbraco.Core.Constants; + +namespace Umbraco.Web.Editors +{ + [PluginController("UmbracoApi")] + [UmbracoTreeAuthorize(Constants.Trees.Templates)] + public class TemplateController : BackOfficeNotificationsController + { + /// + /// Gets data type by alias + /// + /// + /// + public TemplateDisplay GetByAlias(string alias) + { + var template = Services.FileService.GetTemplate(alias); + return template == null ? null : Mapper.Map(template); + } + + /// + /// Get all templates + /// + /// + public IEnumerable GetAll() + { + return Services.FileService.GetTemplates().Select(Mapper.Map); + } + + /// + /// Gets the content json for the content id + /// + /// + /// + public TemplateDisplay GetById(int id) + { + var template = Services.FileService.GetTemplate(id); + if (template == null) + throw new HttpResponseException(HttpStatusCode.NotFound); + + return Mapper.Map(template); + } + + /// + /// Deletes a template wth a given ID + /// + /// + /// + [HttpDelete] + [HttpPost] + public HttpResponseMessage DeleteById(int id) + { + var template = Services.FileService.GetTemplate(id); + if (template == null) + throw new HttpResponseException(HttpStatusCode.NotFound); + + Services.FileService.DeleteTemplate(template.Alias); + return Request.CreateResponse(HttpStatusCode.OK); + } + + public TemplateDisplay GetScaffold(int id) + { + //empty default + var dt = new Template("", ""); + dt.Path = "-1"; + + if (id > 0) + { + var master = Services.FileService.GetTemplate(id); + if(master != null) + { + dt.SetMasterTemplate(master); + } + } + + var content = ViewHelper.GetDefaultFileContent( layoutPageAlias: dt.MasterTemplateAlias ); + var scaffold = Mapper.Map(dt); + + scaffold.Content = content + "\r\n\r\n@* the fun starts here *@\r\n\r\n"; + return scaffold; + } + + /// + /// Saves the data type + /// + /// + /// + public TemplateDisplay PostSave(TemplateDisplay display) + { + + //Checking the submitted is valid with the Required attributes decorated on the ViewModel + if (ModelState.IsValid == false) + { + throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); + } + + + if (display.Id > 0) + { + // update + var template = Services.FileService.GetTemplate(display.Id); + if (template == null) + throw new HttpResponseException(HttpStatusCode.NotFound); + var changeMaster = template.MasterTemplateAlias != display.MasterTemplateAlias; var changeAlias = template.Alias != display.Alias; - Mapper.Map(display, template); - - if (changeMaster) - { - if (string.IsNullOrEmpty(display.MasterTemplateAlias) == false) - { - - var master = Services.FileService.GetTemplate(display.MasterTemplateAlias); + Mapper.Map(display, template); + + if (changeMaster) + { + if (string.IsNullOrEmpty(display.MasterTemplateAlias) == false) + { + + var master = Services.FileService.GetTemplate(display.MasterTemplateAlias); if(master == null || master.Id == display.Id) { template.SetMasterTemplate(null); }else { template.SetMasterTemplate(master); - } - - } - else - { - //remove the master - template.SetMasterTemplate(null); - } - } - + } + + } + else + { + //remove the master + template.SetMasterTemplate(null); + } + } + Services.FileService.SaveTemplate(template); if (changeAlias) { template = Services.FileService.GetTemplate(template.Id); - } - - Mapper.Map(template, display); - } - else - { - //create - ITemplate master = null; - if (string.IsNullOrEmpty(display.MasterTemplateAlias) == false) - { - master = Services.FileService.GetTemplate(display.MasterTemplateAlias); - if (master == null) - throw new HttpResponseException(HttpStatusCode.NotFound); - } - - var template = Services.FileService.CreateTemplateWithIdentity(display.Name, display.Content, master); - //template = Services.FileService.GetTemplate(template.Id); - Mapper.Map(template, display); - } - - return display; - } - } -} + } + + Mapper.Map(template, display); + } + else + { + //create + ITemplate master = null; + if (string.IsNullOrEmpty(display.MasterTemplateAlias) == false) + { + master = Services.FileService.GetTemplate(display.MasterTemplateAlias); + if (master == null) + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + var template = Services.FileService.CreateTemplateWithIdentity(display.Name, display.Content, master); + //template = Services.FileService.GetTemplate(template.Id); + Mapper.Map(template, display); + } + + return display; + } + } +} From 16d8c55fa7610856b15166ae98183468532bdb4d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 12 Jan 2017 10:11:03 +0100 Subject: [PATCH 211/599] tree picker search only works for content, media and members so hide for everything else --- .../common/overlays/treepicker/treepicker.controller.js | 5 +++++ .../src/views/common/overlays/treepicker/treepicker.html | 1 + 2 files changed, 6 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js index dcc03f1a76..e69b5ee3ce 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js @@ -38,6 +38,11 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", } } } + + // Search is only working for content, media and member section so we will remove it from everything else + if($scope.section === "content" || $scope.section === "media" || $scope.section === "member" ) { + $scope.enableSearh = true; + } //create the custom query string param for this tree $scope.customTreeParams = dialogOptions.startNodeId ? "startNodeId=" + dialogOptions.startNodeId : ""; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html index 4e9f503013..b009b4d45f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html @@ -2,6 +2,7 @@
    Date: Thu, 12 Jan 2017 11:07:37 +0100 Subject: [PATCH 212/599] update docs for template resource --- .../src/common/resources/template.resource.js | 63 +++++++++++++------ 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js index 3052d532bb..f969864ba1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/template.resource.js @@ -40,21 +40,21 @@ function templateResource($q, $http, umbDataFormatter, umbRequestHelper) { /** * @ngdoc method - * @name umbraco.resources.templateResource#getByName + * @name umbraco.resources.templateResource#getByAlias * @methodOf umbraco.resources.templateResource * * @description - * Gets a template item with a given name + * Gets a template item with a given alias * * ##usage *
    -         * templateResource.getByName("upload")
    -         *    .then(function(datatype) {
    +         * templateResource.getByAlias("upload")
    +         *    .then(function(template) {
              *        alert('its here!');
              *    });
              * 
    * - * @param {String} name Name of template to retrieve + * @param {String} alias Alias of template to retrieve * @returns {Promise} resourcePromise object. * */ @@ -69,6 +69,25 @@ function templateResource($q, $http, umbDataFormatter, umbRequestHelper) { "Failed to retrieve data for template with alias: " + alias); }, + /** + * @ngdoc method + * @name umbraco.resources.templateResource#getAll + * @methodOf umbraco.resources.templateResource + * + * @description + * Gets all templates + * + * ##usage + *
    +         * templateResource.getAll()
    +         *    .then(function(templates) {
    +         *        alert('its here!');
    +         *    });
    +         * 
    + * + * @returns {Promise} resourcePromise object. + * + */ getAll: function () { return umbRequestHelper.resourcePromise( @@ -82,8 +101,8 @@ function templateResource($q, $http, umbDataFormatter, umbRequestHelper) { /** * @ngdoc method - * @name umbraco.resources.contentResource#getScaffold - * @methodOf umbraco.resources.contentResource + * @name umbraco.resources.templateResource#getScaffold + * @methodOf umbraco.resources.templateResource * * @description * Returns a scaffold of an empty template item @@ -93,14 +112,8 @@ function templateResource($q, $http, umbDataFormatter, umbRequestHelper) { * ##usage *
              * templateResource.getScaffold()
    -         *    .then(function(scaffold) {
    -         *        var myType = scaffold;
    -         *        myType.name = "My new template";
    -         *
    -         *        templateResource.save(myType, myType.preValues, true)
    -         *            .then(function(type){
    -         *                alert("Retrieved, updated and saved again");
    -         *            });
    +         *    .then(function(template) {
    +         *        alert('its here!');
              *    });
              * 
    * @@ -134,7 +147,7 @@ function templateResource($q, $http, umbDataFormatter, umbRequestHelper) { * }); * * - * @param {Int} id id of content item to delete + * @param {Int} id id of template to delete * @returns {Promise} resourcePromise object. * */ @@ -148,7 +161,6 @@ function templateResource($q, $http, umbDataFormatter, umbRequestHelper) { "Failed to delete item " + id); }, - /** * @ngdoc method * @name umbraco.resources.templateResource#save @@ -156,15 +168,26 @@ function templateResource($q, $http, umbDataFormatter, umbRequestHelper) { * * @description * Saves or update a template + * + * ##usage + *
    +         * templateResource.save(template)
    +         *    .then(function(template) {
    +         *        alert('its saved!');
    +         *    });
    +         * 
    * - * @param {Object} template object to create/update + * @param {Object} template object to save * @returns {Promise} resourcePromise object. * */ save: function (template) { - return umbRequestHelper.resourcePromise( - $http.post(umbRequestHelper.getApiUrl("templateApiBaseUrl", "PostSave"), template), + $http.post( + umbRequestHelper.getApiUrl( + "templateApiBaseUrl", + "PostSave"), + template), "Failed to save data for template id " + template.id); } }; From 32abebd0a010fecff4faea397f7cfde7cd2604c1 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 12 Jan 2017 11:33:21 +0100 Subject: [PATCH 213/599] Main template editor translated --- .../views/common/overlays/insert/insert.html | 21 +++++----- .../querybuilder/querybuilder.controller.js | 1 - .../overlays/querybuilder/querybuilder.html | 16 +++++--- .../src/views/templates/edit.html | 14 +++---- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 39 +++++++++++++++++-- 5 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html index e5ea8a3ddf..4b92ea86ad 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html @@ -3,29 +3,28 @@
    -
    Value
    -
    Displays the value of a named field from the current page, with options to modify the value or fallback to alternative values.
    +
    +
    -
    Partial view
    +
    - A partial view is a separate template file which can be rendered inside another - template, it's great for reusing markup or for separating complex templates into separate files.
    + +
    -
    Macro
    +
    - A Macro is a configurable component which is great for - reusable parts of your design, where you need the option to provide parameters, - such as galleries, forms and lists. + +
    -
    Dictionary
    -
    A dictionary item is a placeholder for a translatable piece of text, which makes it easy to create designs for multilingual websites.
    +
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js index d04b7f35a3..3939882969 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js @@ -173,7 +173,6 @@ }, 200); onInit(); - } angular.module("umbraco").controller("Umbraco.Overlays.QueryBuilderController", QueryBuilderOverlayController); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html index 36f729adb0..e9be97b7df 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html @@ -5,7 +5,7 @@
    - I want + I Want
    @@ -22,7 +22,7 @@
    - from + from {{vm.query.source.name}} @@ -33,8 +33,12 @@ diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index f7ac08d837..2021be1d9d 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -1025,17 +1025,48 @@ To manage your website, simply open the Umbraco back office and start adding con Preview Styles + Edit template + + Sections Insert content area Insert content area placeholder - Insert dictionary item - Insert Macro - Insert Umbraco page field + + Dictionary item + A dictionary item is a placeholder for a translatable piece of text, which makes it easy to create designs for multilingual websites. + + Macro + + A Macro is a configurable component which is great for + reusable parts of your design, where you need the option to provide parameters, + such as galleries, forms and lists. + + + Value + Displays the value of a named field from the current page, with options to modify the value or fallback to alternative values. + + Partial view + + A partial view is a separate template file which can be rendered inside another + template, it's great for reusing markup or for separating complex templates into separate files. + + Master template - Quick Guide to Umbraco template tags + + Query builder + items returned, in + + I want + from + where + and + everything + + Template + Choose type of content Choose a layout From 04f79ffaf122f84aa1b09a3aa0260377e9f0edf1 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 12 Jan 2017 15:31:21 +0100 Subject: [PATCH 214/599] lazy load ace editor source file - change to an isolated scope and don't rely on ngModel to get it working --- .../components/umbaceeditor.directive.js | 392 +++++++++--------- .../src/views/templates/edit.html | 3 +- src/Umbraco.Web/UI/JavaScript/JsInitialize.js | 2 +- 3 files changed, 195 insertions(+), 202 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js index 6e66e75dd6..c97851bbb9 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbaceeditor.directive.js @@ -1,11 +1,7 @@ (function() { 'use strict'; - function AceEditorDirective(umbAceEditorConfig, assetsService) { - - if (angular.isUndefined(window.ace)) { - throw new Error('ui-ace need ace to work... (o rly?)'); - } + function AceEditorDirective(umbAceEditorConfig, assetsService, angularHelper) { /** * Sets editor options such as the wrapping mode or the syntax checker. @@ -128,213 +124,211 @@ function link(scope, el, attr, ngModel) { - - /** - * Corresponds the umbAceEditorConfig ACE configuration. - * @type object - */ - var options = umbAceEditorConfig.ace || {}; - - /** - * umbAceEditorConfig merged with user options via json in attribute or data binding - * @type object - */ - var opts = angular.extend({}, options, scope.$eval(attr.umbAceEditor)); - - - //load ace libraries here... - - /** - * ACE editor - * @type object - */ - var acee = window.ace.edit(el[0]); - acee.$blockScrolling = Infinity; - - /** - * ACE editor session. - * @type object - * @see [EditSession]{@link http://ace.c9.io/#nav=api&api=edit_session} - */ - var session = acee.getSession(); - - /** - * Reference to a change listener created by the listener factory. - * @function - * @see listenerFactory.onChange - */ - var onChangeListener; - - /** - * Reference to a blur listener created by the listener factory. - * @function - * @see listenerFactory.onBlur - */ - var onBlurListener; - - /** - * Calls a callback by checking its existing. The argument list - * is variable and thus this function is relying on the arguments - * object. - * @throws {Error} If the callback isn't a function - */ - var executeUserCallback = function() { - - /** - * The callback function grabbed from the array-like arguments - * object. The first argument should always be the callback. - * - * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments} - * @type {*} - */ - var callback = arguments[0]; - - /** - * Arguments to be passed to the callback. These are taken - * from the array-like arguments object. The first argument - * is stripped because that should be the callback function. - * - * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments} - * @type {Array} - */ - var args = Array.prototype.slice.call(arguments, 1); - - if (angular.isDefined(callback)) { - scope.$evalAsync(function() { - if (angular.isFunction(callback)) { - callback(args); - } else { - throw new Error('ui-ace use a function as callback.'); - } - }); + // Load in ace library + assetsService.loadJs('lib/ace-builds/src-min-noconflict/ace.js').then(function () { + if (angular.isUndefined(window.ace)) { + throw new Error('ui-ace need ace to work... (o rly?)'); + } else { + // init editor + init(); } - }; - - - - /** - * Listener factory. Until now only change listeners can be created. - * @type object - */ - var listenerFactory = { - /** - * Creates a change listener which propagates the change event - * and the editor session to the callback from the user option - * onChange. It might be exchanged during runtime, if this - * happens the old listener will be unbound. - * - * @param callback callback function defined in the user options - * @see onChangeListener - */ - onChange: function(callback) { - return function(e) { - var newValue = session.getValue(); - - if (ngModel && newValue !== ngModel.$viewValue && - // HACK make sure to only trigger the apply outside of the - // digest loop 'cause ACE is actually using this callback - // for any text transformation ! - !scope.$$phase && !scope.$root.$$phase) { - scope.$evalAsync(function() { - ngModel.$setViewValue(newValue); - }); - } - - executeUserCallback(callback, e, acee); - }; - }, - /** - * Creates a blur listener which propagates the editor session - * to the callback from the user option onBlur. It might be - * exchanged during runtime, if this happens the old listener - * will be unbound. - * - * @param callback callback function defined in the user options - * @see onBlurListener - */ - onBlur: function(callback) { - return function() { - executeUserCallback(callback, acee); - }; - } - }; - - attr.$observe('readonly', function(value) { - acee.setReadOnly(!!value || value === ''); }); - // Value Blind - if (ngModel) { - ngModel.$formatters.push(function(value) { - if (angular.isUndefined(value) || value === null) { - return ''; - } else if (angular.isObject(value) || angular.isArray(value)) { - throw new Error('ui-ace cannot use an object or an array as a model'); + function init() { + + /** + * Corresponds the umbAceEditorConfig ACE configuration. + * @type object + */ + var options = umbAceEditorConfig.ace || {}; + + /** + * umbAceEditorConfig merged with user options via json in attribute or data binding + * @type object + */ + var opts = angular.extend({}, options, scope.umbAceEditor); + + + //load ace libraries here... + + /** + * ACE editor + * @type object + */ + var acee = window.ace.edit(el[0]); + acee.$blockScrolling = Infinity; + + /** + * ACE editor session. + * @type object + * @see [EditSession]{@link http://ace.c9.io/#nav=api&api=edit_session} + */ + var session = acee.getSession(); + + /** + * Reference to a change listener created by the listener factory. + * @function + * @see listenerFactory.onChange + */ + var onChangeListener; + + /** + * Reference to a blur listener created by the listener factory. + * @function + * @see listenerFactory.onBlur + */ + var onBlurListener; + + /** + * Calls a callback by checking its existing. The argument list + * is variable and thus this function is relying on the arguments + * object. + * @throws {Error} If the callback isn't a function + */ + var executeUserCallback = function() { + + /** + * The callback function grabbed from the array-like arguments + * object. The first argument should always be the callback. + * + * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments} + * @type {*} + */ + var callback = arguments[0]; + + /** + * Arguments to be passed to the callback. These are taken + * from the array-like arguments object. The first argument + * is stripped because that should be the callback function. + * + * @see [arguments]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments} + * @type {Array} + */ + var args = Array.prototype.slice.call(arguments, 1); + + if (angular.isDefined(callback)) { + scope.$evalAsync(function() { + if (angular.isFunction(callback)) { + callback(args); + } else { + throw new Error('ui-ace use a function as callback.'); + } + }); } - return value; + }; + + + + /** + * Listener factory. Until now only change listeners can be created. + * @type object + */ + var listenerFactory = { + /** + * Creates a change listener which propagates the change event + * and the editor session to the callback from the user option + * onChange. It might be exchanged during runtime, if this + * happens the old listener will be unbound. + * + * @param callback callback function defined in the user options + * @see onChangeListener + */ + onChange: function(callback) { + return function(e) { + var newValue = session.getValue(); + angularHelper.safeApply(scope, function () { + scope.model = newValue; + }); + executeUserCallback(callback, e, acee); + }; + }, + /** + * Creates a blur listener which propagates the editor session + * to the callback from the user option onBlur. It might be + * exchanged during runtime, if this happens the old listener + * will be unbound. + * + * @param callback callback function defined in the user options + * @see onBlurListener + */ + onBlur: function(callback) { + return function() { + executeUserCallback(callback, acee); + }; + } + }; + + attr.$observe('readonly', function(value) { + acee.setReadOnly(!!value || value === ''); }); - ngModel.$render = function() { - session.setValue(ngModel.$viewValue); + // Value Blind + if(scope.model) { + session.setValue(scope.model); + } + + // Listen for option updates + var updateOptions = function(current, previous) { + if (current === previous) { + return; + } + + opts = angular.extend({}, options, scope.umbAceEditor); + + opts.callbacks = [opts.onLoad]; + if (opts.onLoad !== options.onLoad) { + // also call the global onLoad handler + opts.callbacks.unshift(options.onLoad); + } + + // EVENTS + + // unbind old change listener + session.removeListener('change', onChangeListener); + + // bind new change listener + onChangeListener = listenerFactory.onChange(opts.onChange); + session.on('change', onChangeListener); + + // unbind old blur listener + //session.removeListener('blur', onBlurListener); + acee.removeListener('blur', onBlurListener); + + // bind new blur listener + onBlurListener = listenerFactory.onBlur(opts.onBlur); + acee.on('blur', onBlurListener); + + setOptions(acee, session, opts); }; + + scope.$watch(scope.umbAceEditor, updateOptions, /* deep watch */ true); + + // set the options here, even if we try to watch later, if this + // line is missing things go wrong (and the tests will also fail) + updateOptions(options); + + el.on('$destroy', function() { + acee.session.$stopWorker(); + acee.destroy(); + }); + + scope.$watch(function() { + return [el[0].offsetWidth, el[0].offsetHeight]; + }, function() { + acee.resize(); + acee.renderer.updateFull(); + }, true); + } - // Listen for option updates - var updateOptions = function(current, previous) { - if (current === previous) { - return; - } - opts = angular.extend({}, options, scope.$eval(attr.umbAceEditor)); - - opts.callbacks = [opts.onLoad]; - if (opts.onLoad !== options.onLoad) { - // also call the global onLoad handler - opts.callbacks.unshift(options.onLoad); - } - - // EVENTS - - // unbind old change listener - session.removeListener('change', onChangeListener); - - // bind new change listener - onChangeListener = listenerFactory.onChange(opts.onChange); - session.on('change', onChangeListener); - - // unbind old blur listener - //session.removeListener('blur', onBlurListener); - acee.removeListener('blur', onBlurListener); - - // bind new blur listener - onBlurListener = listenerFactory.onBlur(opts.onBlur); - acee.on('blur', onBlurListener); - - setOptions(acee, session, opts); - }; - - scope.$watch(attr.umbAceEditor, updateOptions, /* deep watch */ true); - - // set the options here, even if we try to watch later, if this - // line is missing things go wrong (and the tests will also fail) - updateOptions(options); - - el.on('$destroy', function() { - acee.session.$stopWorker(); - acee.destroy(); - }); - - scope.$watch(function() { - return [el[0].offsetWidth, el[0].offsetHeight]; - }, function() { - acee.resize(); - acee.renderer.updateFull(); - }, true); - } var directive = { restrict: 'EA', - require: '?ngModel', + scope: { + "umbAceEditor": "=", + "model": "=" + }, link: link }; diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index b0e15e8ad7..81dc9042b3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -89,10 +89,9 @@
    + model="vm.template.content">
    - diff --git a/src/Umbraco.Web/UI/JavaScript/JsInitialize.js b/src/Umbraco.Web/UI/JavaScript/JsInitialize.js index 44a1ae75c7..3be8a58983 100644 --- a/src/Umbraco.Web/UI/JavaScript/JsInitialize.js +++ b/src/Umbraco.Web/UI/JavaScript/JsInitialize.js @@ -16,7 +16,7 @@ 'lib/ng-file-upload/ng-file-upload.min.js', 'lib/angular-local-storage/angular-local-storage.min.js', - "lib/ace-builds/src-min-noconflict/ace.js", + //"lib/ace-builds/src-min-noconflict/ace.js", 'lib/bootstrap/js/bootstrap.2.3.2.min.js', 'lib/bootstrap-tabdrop/bootstrap-tabdrop.js', From 79c2400d27335774c40b3c00e90eb2f7e60174fd Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 12 Jan 2017 18:28:25 +0100 Subject: [PATCH 215/599] U4-9322 - scope --- build/UmbracoVersion.txt | 2 +- src/SolutionInfo.cs | 2 +- src/Umbraco.Core/ApplicationContext.cs | 4 + .../Configuration/UmbracoVersion.cs | 2 +- src/Umbraco.Core/CoreBootManager.cs | 14 +- src/Umbraco.Core/DatabaseContext.cs | 34 ++- .../Persistence/DefaultDatabaseFactory.cs | 122 ++-------- .../Persistence/IDatabaseFactory.cs | 7 + .../Persistence/UmbracoDatabase.cs | 4 +- .../UnitOfWork/PetaPocoUnitOfWork.cs | 26 +- .../UnitOfWork/PetaPocoUnitOfWorkProvider.cs | 22 +- src/Umbraco.Core/Scoping/IScope.cs | 14 ++ src/Umbraco.Core/Scoping/IScopeProvider.cs | 10 + .../Scoping/IScopeProviderInternal.cs | 12 + src/Umbraco.Core/Scoping/NoScope.cs | 45 ++++ src/Umbraco.Core/Scoping/Scope.cs | 120 ++++++++++ src/Umbraco.Core/Scoping/ScopeProvider.cs | 224 ++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 6 + .../BootManagers/CoreBootManagerTests.cs | 3 +- .../Migrations/FindingMigrationsTest.cs | 3 +- src/Umbraco.Tests/MockTests.cs | 5 +- .../Mapping/ContentTypeModelMappingTests.cs | 3 +- .../Persistence/BaseTableByTableTest.cs | 3 +- .../Persistence/DatabaseContextTests.cs | 6 +- src/Umbraco.Tests/Persistence/LocksTests.cs | 4 +- .../Routing/UrlRoutingTestBase.cs | 3 +- .../Services/ThreadSafetyServiceTest.cs | 26 +- .../TestHelpers/BaseDatabaseFactoryTest.cs | 7 +- .../TestHelpers/BaseUmbracoApplicationTest.cs | 3 +- .../Web/Mvc/SurfaceControllerTests.cs | 3 +- .../Web/Mvc/UmbracoViewPageTests.cs | 3 +- src/Umbraco.Web/Scheduling/LogScrubber.cs | 4 +- .../Scheduling/ScheduledPublishing.cs | 4 +- .../ServerRegistrationEventHandler.cs | 4 +- src/Umbraco.Web/WebBootManager.cs | 7 +- .../DataServices/UmbracoContentService.cs | 8 +- 36 files changed, 583 insertions(+), 186 deletions(-) create mode 100644 src/Umbraco.Core/Scoping/IScope.cs create mode 100644 src/Umbraco.Core/Scoping/IScopeProvider.cs create mode 100644 src/Umbraco.Core/Scoping/IScopeProviderInternal.cs create mode 100644 src/Umbraco.Core/Scoping/NoScope.cs create mode 100644 src/Umbraco.Core/Scoping/Scope.cs create mode 100644 src/Umbraco.Core/Scoping/ScopeProvider.cs diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index 1672ad53d0..8e2ddf83f7 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,3 +1,3 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) 7.6.0 -alpha048 +alpha046 diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index b092457e25..dabe368215 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -12,4 +12,4 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyFileVersion("7.6.0")] -[assembly: AssemblyInformationalVersion("7.6.0-alpha048")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("7.6.0-alpha046")] \ No newline at end of file diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index e5bac73edb..88c2d65926 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -7,6 +7,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.ObjectResolution; using Umbraco.Core.Profiling; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Sync; @@ -162,6 +163,9 @@ namespace Umbraco.Core ///
    public static ApplicationContext Current { get; internal set; } + // fixme + public IScopeProvider ScopeProvider { get { return DatabaseContext.ScopeProvider; } } + /// /// Returns the application wide cache accessor /// diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index cd759cba82..7fdbaee91c 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration /// Gets the version comment (like beta or RC). /// /// The version comment. - public static string CurrentComment { get { return "alpha048"; } } + public static string CurrentComment { get { return "alpha046"; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index e98cd31dbf..bc8af45504 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -28,6 +28,7 @@ using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Publishing; using Umbraco.Core.Macros; using Umbraco.Core.Manifest; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Sync; using Umbraco.Core.Strings; @@ -108,8 +109,11 @@ namespace Umbraco.Core var dbFactory = new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, ProfilingLogger.Logger); Database.Mapper = new PetaPocoMapper(); + var scopeProvider = new ScopeProvider(dbFactory); + dbFactory.ScopeProvider = scopeProvider; + var dbContext = new DatabaseContext( - dbFactory, + scopeProvider, ProfilingLogger.Logger, SqlSyntaxProviders.CreateDefault(ProfilingLogger.Logger)); @@ -117,7 +121,7 @@ namespace Umbraco.Core dbContext.Initialize(); //get the service context - var serviceContext = CreateServiceContext(dbContext, dbFactory); + var serviceContext = CreateServiceContext(dbContext, scopeProvider); //set property and singleton from response ApplicationContext.Current = ApplicationContext = CreateApplicationContext(dbContext, serviceContext); @@ -160,15 +164,15 @@ namespace Umbraco.Core /// Creates and returns the service context for the app /// /// - /// + /// /// - protected virtual ServiceContext CreateServiceContext(DatabaseContext dbContext, IDatabaseFactory dbFactory) + protected virtual ServiceContext CreateServiceContext(DatabaseContext dbContext, IScopeProvider scopeProvider) { //default transient factory var msgFactory = new TransientMessagesFactory(); return new ServiceContext( new RepositoryFactory(ApplicationCache, ProfilingLogger.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()), - new PetaPocoUnitOfWorkProvider(dbFactory), + new PetaPocoUnitOfWorkProvider(scopeProvider), new FileUnitOfWorkProvider(), new PublishingStrategy(msgFactory, ProfilingLogger.Logger), ApplicationCache, diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs index 483e124e35..2f7641795f 100644 --- a/src/Umbraco.Core/DatabaseContext.cs +++ b/src/Umbraco.Core/DatabaseContext.cs @@ -14,6 +14,7 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Migrations; using Umbraco.Core.Persistence.Migrations.Initial; using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; namespace Umbraco.Core @@ -26,7 +27,7 @@ namespace Umbraco.Core /// public class DatabaseContext { - private readonly IDatabaseFactory _factory; + internal readonly ScopeProvider ScopeProvider; private readonly ILogger _logger; private readonly SqlSyntaxProviders _syntaxProviders; private bool _configured; @@ -41,8 +42,8 @@ namespace Umbraco.Core private const int ConnectionCheckMinutes = 1; [Obsolete("Use the constructor specifying all dependencies instead")] - public DatabaseContext(IDatabaseFactory factory) - : this(factory, LoggerResolver.Current.Logger, new SqlSyntaxProviders(new ISqlSyntaxProvider[] + public DatabaseContext(IScopeProvider scopeProvider) + : this(scopeProvider, LoggerResolver.Current.Logger, new SqlSyntaxProviders(new ISqlSyntaxProvider[] { new MySqlSyntaxProvider(LoggerResolver.Current.Logger), new SqlCeSyntaxProvider(), @@ -54,16 +55,16 @@ namespace Umbraco.Core /// /// Default constructor /// - /// + /// /// /// - public DatabaseContext(IDatabaseFactory factory, ILogger logger, SqlSyntaxProviders syntaxProviders) + public DatabaseContext(IScopeProvider scopeProvider, ILogger logger, SqlSyntaxProviders syntaxProviders) { - if (factory == null) throw new ArgumentNullException("factory"); + if (scopeProvider == null) throw new ArgumentNullException("scopeProvider"); if (logger == null) throw new ArgumentNullException("logger"); if (syntaxProviders == null) throw new ArgumentNullException("syntaxProviders"); - _factory = factory; + ScopeProvider = (ScopeProvider) scopeProvider; // fixme ugly _logger = logger; _syntaxProviders = syntaxProviders; } @@ -71,16 +72,16 @@ namespace Umbraco.Core /// /// Create a configured DatabaseContext /// - /// + /// /// /// /// - public DatabaseContext(IDatabaseFactory factory, ILogger logger, ISqlSyntaxProvider sqlSyntax, string providerName) + public DatabaseContext(IScopeProvider scopeProvider, ILogger logger, ISqlSyntaxProvider sqlSyntax, string providerName) { _providerName = providerName; SqlSyntax = sqlSyntax; SqlSyntaxContext.SqlSyntaxProvider = SqlSyntax; - _factory = factory; + ScopeProvider = (ScopeProvider) scopeProvider; // fixme ugly _logger = logger; _configured = true; } @@ -110,7 +111,11 @@ namespace Umbraco.Core /// public virtual UmbracoDatabase Database { - get { return _factory.CreateDatabase(); } + get + { + var scope = ScopeProvider.AmbientScope; + return scope != null ? scope.Database : ScopeProvider.CreateNoScope().Database; + } } /// @@ -123,6 +128,8 @@ namespace Umbraco.Core /// will be properly removed from call context and does not interfere with anything else. In most case /// it is not replacing anything, just temporarily installing a database in context. /// + // fixme - this should just entirely be replaced by Scope? + /* public virtual IDisposable UseSafeDatabase(bool force = false) { var factory = _factory as DefaultDatabaseFactory; @@ -138,6 +145,7 @@ namespace Umbraco.Core // create a new, temp, database (will be disposed with UsingDatabase) return new UsingDatabase(null, factory.CreateDatabase()); } + */ /// /// Boolean indicating whether the database has been configured @@ -159,7 +167,7 @@ namespace Umbraco.Core //Don't check again if the timeout period hasn't elapsed //this ensures we don't keep checking the connection too many times in a row like during startup. - //Do check if the _connectionLastChecked is null which means we're just initializing or it could + //Do check if the _connectionLastChecked is null which means we're just initializing or it could //not connect last time it was checked. if ((_connectionLastChecked.HasValue && (DateTime.Now - _connectionLastChecked.Value).TotalMinutes > ConnectionCheckMinutes) || _connectionLastChecked.HasValue == false) @@ -814,6 +822,7 @@ namespace Umbraco.Core return true; } + /* private class UsingDatabase : IDisposable { private readonly UmbracoDatabase _orig; @@ -836,5 +845,6 @@ namespace Umbraco.Core GC.SuppressFinalize(this); } } + */ } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs index fc981b0280..69026962ac 100644 --- a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; -using System.Runtime.Remoting.Messaging; -using System.Web; using Umbraco.Core.Logging; +using Umbraco.Core.Scoping; namespace Umbraco.Core.Persistence { @@ -14,30 +12,17 @@ namespace Umbraco.Core.Persistence /// it will create one per context, otherwise it will be a global singleton object which is NOT thread safe /// since we need (at least) a new instance of the database object per thread. /// - internal class DefaultDatabaseFactory : DisposableObject, IDatabaseFactory + internal class DefaultDatabaseFactory : DisposableObject, IDatabaseFactory2 { private readonly string _connectionStringName; private readonly ILogger _logger; public string ConnectionString { get; private set; } public string ProviderName { get; private set; } - // NO! see notes in v8 HybridAccessorBase - //[ThreadStatic] - //private static volatile UmbracoDatabase _nonHttpInstance; + //private static readonly object Locker = new object(); - private const string ItemKey = "Umbraco.Core.Persistence.DefaultDatabaseFactory"; - - private static UmbracoDatabase NonContextValue - { - get { return (UmbracoDatabase) CallContext.LogicalGetData(ItemKey); } - set - { - if (value == null) CallContext.FreeNamedDataSlot(ItemKey); - else CallContext.LogicalSetData(ItemKey, value); - } - } - - private static readonly object Locker = new object(); + // bwc imposes a weird x-dependency between database factory and scope provider... + public ScopeProvider ScopeProvider { get; set; } /// /// Constructor accepting custom connection string @@ -76,6 +61,10 @@ namespace Umbraco.Core.Persistence public UmbracoDatabase CreateDatabase() { + var scope = ScopeProvider.AmbientScope; + return scope != null ? scope.Database : ScopeProvider.CreateNoScope().Database; + + /* UmbracoDatabase database; // gets or creates a database, using either the call context (if no http context) or @@ -128,9 +117,11 @@ namespace Umbraco.Core.Persistence } return database; + */ } // called by UmbracoDatabase when disposed, so that the factory can de-list it from context + /* internal void OnDispose(UmbracoDatabase disposing) { var value = disposing; @@ -168,6 +159,7 @@ namespace Umbraco.Core.Persistence _databases.Remove(value); #endif } + */ #if DEBUG_DATABASES // helps identifying when non-httpContext databases are created by logging the stack trace @@ -202,6 +194,12 @@ namespace Umbraco.Core.Persistence CallContext } + public UmbracoDatabase CreateNewDatabase() + { + return CreateDatabaseInstance(ContextOwner.None); + + } + internal UmbracoDatabase CreateDatabaseInstance(ContextOwner contextOwner) { var database = string.IsNullOrEmpty(ConnectionString) == false && string.IsNullOrEmpty(ProviderName) == false @@ -221,6 +219,7 @@ namespace Umbraco.Core.Persistence protected override void DisposeResources() { + /* UmbracoDatabase database; if (HttpContext.Current == null) @@ -239,88 +238,7 @@ namespace Umbraco.Core.Persistence } if (database != null) database.Dispose(); // removes it from call context + */ } - - // during tests, the thread static var can leak between tests - // this method provides a way to force-reset the variable - internal void ResetForTests() - { - var value = NonContextValue; - if (value != null) value.Dispose(); - NonContextValue = null; - } - - #region SafeCallContext - - // see notes in SafeCallContext - need to do this since we are using - // the logical call context... - - static DefaultDatabaseFactory() - { - SafeCallContext.Register(DetachAmbientDatabase, AttachAmbientDatabase); - } - - // gets a value indicating whether there is an ambient database - internal static bool HasAmbientDatabase - { - get - { - return HttpContext.Current == null - ? NonContextValue != null - : HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] != null; - } - } - - // detaches the current database - // ie returns the database and remove it from whatever is "context" - internal static UmbracoDatabase DetachAmbientDatabase() - { - UmbracoDatabase database; - - if (HttpContext.Current == null) - { - database = NonContextValue; - NonContextValue = null; - } - else - { - database = (UmbracoDatabase) HttpContext.Current.Items[typeof (DefaultDatabaseFactory)]; - HttpContext.Current.Items.Remove(typeof (DefaultDatabaseFactory)); - } - - if (database != null) database.ContextOwner = ContextOwner.None; - return database; - } - - // attach a current database - // ie assign it to whatever is "context" - // throws if there already is a database - internal static void AttachAmbientDatabase(object o) - { - var database = o as UmbracoDatabase; - if (o != null && database == null) throw new ArgumentException("Not an UmbracoDatabase.", "o"); - - var ambient = DetachAmbientDatabase(); - if (ambient != null) ambient.Dispose(); - - if (HttpContext.Current == null) - { - //if (NonContextValue != null) throw new InvalidOperationException(); - if (database == null) return; - - NonContextValue = database; - database.ContextOwner = ContextOwner.CallContext; - } - else - { - //if (HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] != null) throw new InvalidOperationException(); - if (database == null) return; - - HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] = database; - database.ContextOwner = ContextOwner.HttpContext; - } - } - - #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/IDatabaseFactory.cs b/src/Umbraco.Core/Persistence/IDatabaseFactory.cs index b0efb7f94a..3a80739ec7 100644 --- a/src/Umbraco.Core/Persistence/IDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/IDatabaseFactory.cs @@ -7,6 +7,13 @@ namespace Umbraco.Core.Persistence /// public interface IDatabaseFactory : IDisposable { + // gets or creates the ambient database UmbracoDatabase CreateDatabase(); } + + public interface IDatabaseFactory2 : IDatabaseFactory + { + // creates a new database + UmbracoDatabase CreateNewDatabase(); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs index 0b370c9978..44a3db9424 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core.Persistence /// can then override any additional execution (such as additional loggging, functionality, etc...) that we need to without breaking compatibility since we'll always be exposing /// this object instead of the base PetaPoco database object. /// - public class UmbracoDatabase : Database, IDisposeOnRequestEnd + public class UmbracoDatabase : Database { private readonly ILogger _logger; private readonly Guid _instanceId = Guid.NewGuid(); @@ -259,6 +259,7 @@ namespace Umbraco.Core.Persistence base.BuildSqlDbSpecificPagingQuery(databaseType, skip, take, sql, sqlSelectRemoved, sqlOrderBy, ref args, out sqlPage); } + /* protected override void Dispose(bool disposing) { base.Dispose(disposing); @@ -267,5 +268,6 @@ namespace Umbraco.Core.Persistence #endif if (DatabaseFactory != null) DatabaseFactory.OnDispose(this); } + */ } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs index a5337e854c..6c71614c1a 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Scoping; namespace Umbraco.Core.Persistence.UnitOfWork { @@ -10,6 +10,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// internal class PetaPocoUnitOfWork : DisposableObject, IDatabaseUnitOfWork { + private readonly IScope _scope; /// /// Used for testing @@ -19,16 +20,16 @@ namespace Umbraco.Core.Persistence.UnitOfWork private Guid _key; private readonly Queue _operations = new Queue(); - /// - /// Creates a new unit of work instance - /// - /// - /// - /// This should normally not be used directly and should be created with the UnitOfWorkProvider - /// - internal PetaPocoUnitOfWork(UmbracoDatabase database) - { - Database = database; + /// + /// Creates a new unit of work instance + /// + /// + /// + /// This should normally not be used directly and should be created with the UnitOfWorkProvider + /// + internal PetaPocoUnitOfWork(IScopeProvider scopeProvider) + { + _scope = scopeProvider.CreateScope(); _key = Guid.NewGuid(); InstanceId = Guid.NewGuid(); } @@ -139,7 +140,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork get { return _key; } } - public UmbracoDatabase Database { get; private set; } + public UmbracoDatabase Database {get { return _scope.Database; } } #region Operation @@ -178,6 +179,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork protected override void DisposeResources() { _operations.Clear(); + _scope.Dispose(); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs index 97d066d572..6b901d34f7 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs @@ -1,6 +1,7 @@ using System; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; +using Umbraco.Core.Scoping; namespace Umbraco.Core.Persistence.UnitOfWork { @@ -9,25 +10,25 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// public class PetaPocoUnitOfWorkProvider : IDatabaseUnitOfWorkProvider { - private readonly IDatabaseFactory _dbFactory; + private readonly IScopeProvider _scopeProvider; [Obsolete("Use the constructor specifying an ILogger instead")] public PetaPocoUnitOfWorkProvider() - : this(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, LoggerResolver.Current.Logger)) + : this(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, LoggerResolver.Current.Logger))) { } [Obsolete("Use the constructor specifying an ILogger instead")] public PetaPocoUnitOfWorkProvider(string connectionString, string providerName) - : this(new DefaultDatabaseFactory(connectionString, providerName, LoggerResolver.Current.Logger)) + : this(new ScopeProvider(new DefaultDatabaseFactory(connectionString, providerName, LoggerResolver.Current.Logger))) { } /// /// Parameterless constructor uses defaults /// public PetaPocoUnitOfWorkProvider(ILogger logger) - : this(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, logger)) + : this(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, logger))) { } @@ -39,17 +40,17 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// Connection String to use with Database /// Database Provider for the Connection String public PetaPocoUnitOfWorkProvider(ILogger logger, string connectionString, string providerName) - : this(new DefaultDatabaseFactory(connectionString, providerName, logger)) + : this(new ScopeProvider(new DefaultDatabaseFactory(connectionString, providerName, logger))) { } /// /// Constructor accepting an IDatabaseFactory instance /// - /// - public PetaPocoUnitOfWorkProvider(IDatabaseFactory dbFactory) + /// + public PetaPocoUnitOfWorkProvider(IScopeProvider scopeProvider) { - Mandate.ParameterNotNull(dbFactory, "dbFactory"); - _dbFactory = dbFactory; + Mandate.ParameterNotNull(scopeProvider, "scopeProvider"); + _scopeProvider = scopeProvider; } #region Implementation of IUnitOfWorkProvider @@ -65,7 +66,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// public IDatabaseUnitOfWork GetUnitOfWork() { - return new PetaPocoUnitOfWork(_dbFactory.CreateDatabase()); + return new PetaPocoUnitOfWork(_scopeProvider); } #endregion @@ -76,6 +77,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// internal static IDatabaseUnitOfWork CreateUnitOfWork(ILogger logger) { + // fixme wtf? var provider = new PetaPocoUnitOfWorkProvider(logger); return provider.GetUnitOfWork(); } diff --git a/src/Umbraco.Core/Scoping/IScope.cs b/src/Umbraco.Core/Scoping/IScope.cs new file mode 100644 index 0000000000..f56fcdd018 --- /dev/null +++ b/src/Umbraco.Core/Scoping/IScope.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Umbraco.Core.Events; +using Umbraco.Core.Persistence; + +namespace Umbraco.Core.Scoping +{ + public interface IScope : IDisposeOnRequestEnd // implies IDisposable + { + UmbracoDatabase Database { get; } + IList Messages { get; } + + void Complete(); + } +} diff --git a/src/Umbraco.Core/Scoping/IScopeProvider.cs b/src/Umbraco.Core/Scoping/IScopeProvider.cs new file mode 100644 index 0000000000..112a467cc3 --- /dev/null +++ b/src/Umbraco.Core/Scoping/IScopeProvider.cs @@ -0,0 +1,10 @@ +namespace Umbraco.Core.Scoping +{ + public interface IScopeProvider + { + IScope CreateScope(); + IScope CreateDetachedScope(); + void AttachScope(IScope scope); + IScope DetachScope(); + } +} diff --git a/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs b/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs new file mode 100644 index 0000000000..ad0419f7a5 --- /dev/null +++ b/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Core.Scoping +{ + internal interface IScopeProviderInternal : IScopeProvider + { + } +} diff --git a/src/Umbraco.Core/Scoping/NoScope.cs b/src/Umbraco.Core/Scoping/NoScope.cs new file mode 100644 index 0000000000..d72d201060 --- /dev/null +++ b/src/Umbraco.Core/Scoping/NoScope.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Events; +using Umbraco.Core.Persistence; + +namespace Umbraco.Core.Scoping +{ + internal class NoScope : IScope + { + private readonly ScopeProvider _scopeProvider; + + private UmbracoDatabase _database; + private IList _messages; + + public NoScope(ScopeProvider scopeProvider) + { + _scopeProvider = scopeProvider; + } + + public bool HasDatabase { get { return _database != null; } } + + public UmbracoDatabase Database + { + get { return _database ?? (_database = _scopeProvider.DatabaseFactory.CreateNewDatabase()); } + } + + public bool HasMessages { get { return _messages != null; } } + + public IList Messages + { + get { return _messages ?? (_messages = new List()); } + } + + public void Complete() + { + throw new NotImplementedException(); + } + + public void Dispose() + { + _scopeProvider.Disposing(this); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs new file mode 100644 index 0000000000..327adec8ca --- /dev/null +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Events; +using Umbraco.Core.Persistence; + +namespace Umbraco.Core.Scoping +{ + internal class Scope : IScope + { + private readonly ScopeProvider _scopeProvider; + private bool? _completed; + + private UmbracoDatabase _database; + private IList _messages; + + public Scope(ScopeProvider scopeProvider, bool detachable = false) + { + _scopeProvider = scopeProvider; + Detachable = detachable; + } + + public Scope(ScopeProvider scopeProvider, Scope parent) + : this(scopeProvider) + { + ParentScope = parent; + } + + public Scope(ScopeProvider scopeProvider, NoScope noScope) + : this(scopeProvider) + { + // stealing everything from NoScope + _database = noScope.HasDatabase ? noScope.Database : null; + _messages = noScope.HasMessages ? noScope.Messages : null; + if (_database != null) + { + // must not be in a transaction + if (_database.Connection != null) + throw new Exception(); + } + } + + public bool Detachable { get; private set; } + + public Scope ParentScope { get; set; } + + public Scope OrigScope { get; set; } + + public bool HasDatabase + { + get { return ParentScope == null ? _database != null : ParentScope.HasDatabase; } + } + + public UmbracoDatabase Database + { + get + { + if (ParentScope != null) return ParentScope.Database; + if (_database != null) + { + if (_database.Connection == null) // stolen from noScope + _database.BeginTransaction(); // a scope implies a transaction, always + return _database; + } + _database = _scopeProvider.DatabaseFactory.CreateNewDatabase(); + _database.BeginTransaction(); // a scope implies a transaction, always + return _database; + } + } + + public bool HasMessages + { + get { return ParentScope == null ? _messages != null : ParentScope.HasMessages; } + } + + public IList Messages + { + get + { + if (ParentScope != null) return ParentScope.Messages; + if (_messages == null) + _messages = new List(); + return _messages; + } + } + + public void Complete() + { + if (_completed.HasValue == false) + _completed = true; + } + + public void CompleteChild(bool? completed) + { + if (completed.HasValue) + { + if (completed.Value) + { + // child did complete + // nothing to do + } + else + { + // child did not complete, we cannot complete + _completed = false; + } + } + else + { + // child did not complete, we cannot complete + _completed = false; + } + } + + public void Dispose() + { + _scopeProvider.Disposing(this, _completed); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs new file mode 100644 index 0000000000..cdb51e2017 --- /dev/null +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -0,0 +1,224 @@ +using System; +using System.Runtime.Remoting.Messaging; +using System.Web; +using Umbraco.Core.Persistence; + +namespace Umbraco.Core.Scoping +{ + internal class ScopeProvider : IScopeProvider + { + public ScopeProvider(IDatabaseFactory2 databaseFactory) + { + DatabaseFactory = databaseFactory; + } + + static ScopeProvider() + { + SafeCallContext.Register( + () => + { + var scope = StaticAmbientScope; + StaticAmbientScope = null; + return scope; + }, + scope => + { + var ambient = StaticAmbientScope; + if (ambient != null) + ambient.Dispose(); + StaticAmbientScope = (IScope) scope; + }); + } + + public IDatabaseFactory2 DatabaseFactory { get; private set; } + + private const string ItemKey = "Umbraco.Core.Scoping.IScope"; + + private static IScope CallContextValue + { + get { return (IScope) CallContext.LogicalGetData(ItemKey); } + set + { + if (value == null) CallContext.FreeNamedDataSlot(ItemKey); + else CallContext.LogicalSetData(ItemKey, value); + } + } + + private static IScope HttpContextValue + { + get { return (IScope) HttpContext.Current.Items[ItemKey]; } + set + { + if (value == null) HttpContext.Current.Items.Remove(ItemKey); + else HttpContext.Current.Items[ItemKey] = value; + } + } + + private static IScope StaticAmbientScope + { + get { return HttpContext.Current == null ? CallContextValue : HttpContextValue; } + set + { + if (HttpContext.Current == null) + CallContextValue = value; + else + HttpContextValue = value; + } + } + + public IScope AmbientScope + { + get { return StaticAmbientScope; } + set { StaticAmbientScope = value; } + } + + // fixme should we do... + // using (var s = scopeProvider.AttachScope(other)) + // { + // } + // can't because disposing => detach or commit? cannot tell! + // var scope = scopeProvider.CreateScope(); + // scope = scopeProvider.Detach(); + // scope.Detach(); + // scopeProvider.Attach(scope); + // ... do things ... + // scopeProvider.Detach(); + // scopeProvider.Attach(scope); + // scope.Dispose(); + + public IScope CreateDetachedScope() + { + return new Scope(this, true); + } + + public void AttachScope(IScope other) + { + var otherScope = other as Scope; + if (otherScope == null) + throw new ArgumentException(); + + if (otherScope.Detachable == false) + throw new ArgumentException(); + + var ambient = AmbientScope; + if (ambient == null) + { + AmbientScope = other; + return; + } + + var noScope = ambient as NoScope; + if (noScope != null) + throw new InvalidOperationException(); + + var scope = ambient as Scope; + if (ambient != null && scope == null) + throw new Exception(); + + otherScope.OrigScope = scope; + AmbientScope = otherScope; + } + + public IScope DetachScope() + { + var ambient = AmbientScope; + if (ambient == null) + throw new InvalidOperationException(); + + var noScope = ambient as NoScope; + if (noScope != null) + throw new InvalidOperationException(); + + var scope = ambient as Scope; + if (scope == null) + throw new Exception(); + + if (scope.Detachable == false) + throw new InvalidOperationException(); + + AmbientScope = scope.OrigScope; + scope.OrigScope = null; + return scope; + } + + public IScope CreateScope() + { + var ambient = AmbientScope; + if (ambient == null) + return AmbientScope = new Scope(this); + + var noScope = ambient as NoScope; + if (noScope != null) + { + // peta poco nulls the shared connection after each command unless there's a trx + if (noScope.HasDatabase && noScope.Database.Connection != null) + throw new Exception(); + return AmbientScope = new Scope(this, noScope); + } + + var scope = ambient as Scope; + if (scope == null) throw new Exception(); + + return AmbientScope = new Scope(this, scope); + } + + public IScope CreateNoScope() + { + var ambient = AmbientScope; + if (ambient != null) throw new Exception(); + return AmbientScope = new NoScope(this); + } + + public void Disposing(IScope disposing, bool? completed = null) + { + if (disposing != AmbientScope) + throw new InvalidOperationException(); + + var noScope = disposing as NoScope; + if (noScope != null) + { + // fixme - kinda legacy + if (noScope.HasDatabase) noScope.Database.Dispose(); + AmbientScope = null; + return; + } + + var scope = disposing as Scope; + if (scope == null) + throw new Exception(); + + var parent = scope.ParentScope; + AmbientScope = parent; + + if (parent != null) + { + parent.CompleteChild(completed); + return; + } + + // fixme - a scope is in a transaction only if ... there is a db transaction, or always? + // what shall we do with events if not in a transaction? + + // note - messages + // at the moment we are totally not filtering the messages based on completion + // status, so whether the scope is committed or rolled back makes no difference + + if (completed.HasValue && completed.Value) + { + var database = scope.HasDatabase ? scope.Database : null; + if (database != null) + { + database.CompleteTransaction(); + } + } + else + { + var database = scope.HasDatabase ? scope.Database : null; + if (database != null) + { + database.AbortTransaction(); + } + } + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 950a44502f..3dc2cc31f4 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -512,6 +512,12 @@ + + + + + + diff --git a/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs b/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs index 7e8aa19880..ac7758c9e0 100644 --- a/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs +++ b/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs @@ -14,6 +14,7 @@ using Umbraco.Tests.TestHelpers; using umbraco.interfaces; using Umbraco.Core.Persistence; using Umbraco.Core.Profiling; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; namespace Umbraco.Tests.BootManagers @@ -71,7 +72,7 @@ namespace Umbraco.Tests.BootManagers { var appContext = base.CreateApplicationContext(dbContext, serviceContext); - var dbContextMock = new Mock(Mock.Of(), ProfilingLogger.Logger, Mock.Of(), "test"); + var dbContextMock = new Mock(Mock.Of(), ProfilingLogger.Logger, Mock.Of(), "test"); dbContextMock.Setup(x => x.CanConnect).Returns(true); appContext.DatabaseContext = dbContextMock.Object; diff --git a/src/Umbraco.Tests/Migrations/FindingMigrationsTest.cs b/src/Umbraco.Tests/Migrations/FindingMigrationsTest.cs index 6d49205adb..70ae17fa14 100644 --- a/src/Umbraco.Tests/Migrations/FindingMigrationsTest.cs +++ b/src/Umbraco.Tests/Migrations/FindingMigrationsTest.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Migrations; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Profiling; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Tests.Migrations.Stubs; @@ -39,7 +40,7 @@ namespace Umbraco.Tests.Migrations //This is needed because the Migration resolver is creating migration instances with their full ctors ApplicationContext.EnsureContext( new ApplicationContext( - new DatabaseContext(Mock.Of(), Mock.Of(), sqlSyntax, "test"), + new DatabaseContext(Mock.Of(), Mock.Of(), sqlSyntax, "test"), new ServiceContext(), CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of())), diff --git a/src/Umbraco.Tests/MockTests.cs b/src/Umbraco.Tests/MockTests.cs index d9a8ef785e..a01c74c763 100644 --- a/src/Umbraco.Tests/MockTests.cs +++ b/src/Umbraco.Tests/MockTests.cs @@ -16,6 +16,7 @@ using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Profiling; using Umbraco.Core.Services; using Moq; +using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Web; using Umbraco.Web.Routing; @@ -46,7 +47,7 @@ namespace Umbraco.Tests [Test] public void Can_Create_Db_Context() { - var dbCtx = new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"); + var dbCtx = new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"); Assert.Pass(); } @@ -54,7 +55,7 @@ namespace Umbraco.Tests public void Can_Create_App_Context_With_Services() { var appCtx = new ApplicationContext( - new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"), + new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"), MockHelper.GetMockedServiceContext(), CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of())); diff --git a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs index f0d423cda5..fd7a9997d9 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Profiling; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -40,7 +41,7 @@ namespace Umbraco.Tests.Models.Mapping //Create an app context using mocks var appContext = new ApplicationContext( - new DatabaseContext(Mock.Of(), Mock.Of(), Mock.Of(), "test"), + new DatabaseContext(Mock.Of(), Mock.Of(), Mock.Of(), "test"), //Create service context using mocks new ServiceContext( diff --git a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs index e8c994f1c0..f60182fc9f 100644 --- a/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs +++ b/src/Umbraco.Tests/Persistence/BaseTableByTableTest.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Profiling; using Umbraco.Core.Publishing; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using GlobalSettings = Umbraco.Core.Configuration.GlobalSettings; @@ -51,7 +52,7 @@ namespace Umbraco.Tests.Persistence var dbContext = new DatabaseContext( - new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, _logger), + new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, _logger)), _logger, SqlSyntaxProvider, Constants.DatabaseProviders.SqlCe); var repositoryFactory = new RepositoryFactory(cacheHelper, _logger, SqlSyntaxProvider, SettingsForTests.GenerateMockSettings()); diff --git a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs index bf0d3a736b..7bd4086d9c 100644 --- a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs +++ b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Profiling; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; @@ -27,7 +28,7 @@ namespace Umbraco.Tests.Persistence SafeCallContext.Clear(); _dbContext = new DatabaseContext( - new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, Mock.Of()), + new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, Mock.Of())), Mock.Of(), new SqlCeSyntaxProvider(), Constants.DatabaseProviders.SqlCe); //unfortunately we have to set this up because the PetaPocoExtensions require singleton access @@ -91,9 +92,10 @@ namespace Umbraco.Tests.Persistence } var dbFactory = new DefaultDatabaseFactory(connString, Constants.DatabaseProviders.SqlCe, Mock.Of()); + var scopeProvider = new ScopeProvider(dbFactory); //re-map the dbcontext to the new conn string _dbContext = new DatabaseContext( - dbFactory, + scopeProvider, Mock.Of(), new SqlCeSyntaxProvider(), dbFactory.ProviderName); diff --git a/src/Umbraco.Tests/Persistence/LocksTests.cs b/src/Umbraco.Tests/Persistence/LocksTests.cs index 624a94f39f..ae008e1e63 100644 --- a/src/Umbraco.Tests/Persistence/LocksTests.cs +++ b/src/Umbraco.Tests/Persistence/LocksTests.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Publishing; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Tests.Services; using Umbraco.Tests.TestHelpers; @@ -36,8 +37,9 @@ namespace Umbraco.Tests.Persistence //threading environment, or a single apartment threading environment will not work for this test because //it is multi-threaded. _dbFactory = new ThreadSafetyServiceTest.PerThreadDatabaseFactory(Logger); + var scopeProvider = new ScopeProvider(_dbFactory); //overwrite the local object - ApplicationContext.DatabaseContext = new DatabaseContext(_dbFactory, Logger, new SqlCeSyntaxProvider(), Constants.DatabaseProviders.SqlCe); + ApplicationContext.DatabaseContext = new DatabaseContext(scopeProvider, Logger, new SqlCeSyntaxProvider(), Constants.DatabaseProviders.SqlCe); //disable cache var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); diff --git a/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs b/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs index be9928004b..85c07972a5 100644 --- a/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs +++ b/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs @@ -8,6 +8,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; @@ -61,7 +62,7 @@ namespace Umbraco.Tests.Routing { var settings = SettingsForTests.GetDefault(); return new ApplicationContext( - new DatabaseContext(Mock.Of(), Logger, Mock.Of(), "test"), + new DatabaseContext(Mock.Of(), Logger, Mock.Of(), "test"), GetServiceContext(settings, Logger), CacheHelper, ProfilingLogger) diff --git a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs index 504dc0d475..2a67a8194c 100644 --- a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs +++ b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs @@ -19,6 +19,7 @@ using Umbraco.Tests.TestHelpers.Entities; using umbraco.editorControls.tinyMCE3; using umbraco.interfaces; using Umbraco.Core.Events; +using Umbraco.Core.Scoping; namespace Umbraco.Tests.Services { @@ -39,8 +40,9 @@ namespace Umbraco.Tests.Services //threading environment, or a single apartment threading environment will not work for this test because //it is multi-threaded. _dbFactory = new PerThreadDatabaseFactory(Logger); + var scopeProvider = new ScopeProvider(_dbFactory); //overwrite the local object - ApplicationContext.DatabaseContext = new DatabaseContext(_dbFactory, Logger, new SqlCeSyntaxProvider(), Constants.DatabaseProviders.SqlCe); + ApplicationContext.DatabaseContext = new DatabaseContext(scopeProvider, Logger, new SqlCeSyntaxProvider(), Constants.DatabaseProviders.SqlCe); //disable cache var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); @@ -218,7 +220,7 @@ namespace Umbraco.Tests.Services /// /// Creates a Database object per thread, this mimics the web context which is per HttpContext and is required for the multi-threaded test /// - internal class PerThreadDatabaseFactory : DisposableObject, IDatabaseFactory + internal class PerThreadDatabaseFactory : DisposableObject, IDatabaseFactory2 { private readonly ILogger _logger; @@ -237,7 +239,12 @@ namespace Umbraco.Tests.Services return db; } - protected override void DisposeResources() + public UmbracoDatabase CreateNewDatabase() + { + return new UmbracoDatabase(Constants.System.UmbracoConnectionName, _logger); + } + + protected override void DisposeResources() { //dispose the databases _databases.ForEach(x => x.Value.Dispose()); @@ -249,26 +256,21 @@ namespace Umbraco.Tests.Services /// internal class PerThreadPetaPocoUnitOfWorkProvider : DisposableObject, IDatabaseUnitOfWorkProvider { - private readonly PerThreadDatabaseFactory _dbFactory; + private readonly ScopeProvider _scopeProvider; public PerThreadPetaPocoUnitOfWorkProvider(PerThreadDatabaseFactory dbFactory) { - _dbFactory = dbFactory; + _scopeProvider = new ScopeProvider(dbFactory); } public IDatabaseUnitOfWork GetUnitOfWork() { //Create or get a database instance for this thread. - var db = _dbFactory.CreateDatabase(); - return new PetaPocoUnitOfWork(db); + return new PetaPocoUnitOfWork(_scopeProvider); } protected override void DisposeResources() - { - //dispose the databases - _dbFactory.Dispose(); - } + { } } - } } \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 873cf6ada4..6bd44c2bdd 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -24,6 +24,7 @@ using Umbraco.Web.PublishedCache.XmlPublishedCache; using Umbraco.Web.Security; using umbraco.BusinessLogic; using Umbraco.Core.Events; +using Umbraco.Core.Scoping; namespace Umbraco.Tests.TestHelpers { @@ -70,7 +71,6 @@ namespace Umbraco.Tests.TestHelpers GetDbConnectionString(), GetDbProviderName(), Logger); - _dbFactory.ResetForTests(); base.Initialize(); @@ -92,11 +92,12 @@ namespace Umbraco.Tests.TestHelpers var repositoryFactory = new RepositoryFactory(CacheHelper, Logger, SqlSyntax, SettingsForTests.GenerateMockSettings()); var evtMsgs = new TransientMessagesFactory(); + var scopeProvider = new ScopeProvider(_dbFactory); _appContext = new ApplicationContext( //assign the db context - new DatabaseContext(_dbFactory, Logger, SqlSyntax, GetDbProviderName()), + new DatabaseContext(scopeProvider, Logger, SqlSyntax, GetDbProviderName()), //assign the service context - new ServiceContext(repositoryFactory, new PetaPocoUnitOfWorkProvider(_dbFactory), new FileUnitOfWorkProvider(), new PublishingStrategy(evtMsgs, Logger), CacheHelper, Logger, evtMsgs), + new ServiceContext(repositoryFactory, new PetaPocoUnitOfWorkProvider(scopeProvider), new FileUnitOfWorkProvider(), new PublishingStrategy(evtMsgs, Logger), CacheHelper, Logger, evtMsgs), CacheHelper, ProfilingLogger) { diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index e0453557fa..723d6ecade 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -18,6 +18,7 @@ using Umbraco.Web; using Umbraco.Web.Models.Mapping; using umbraco.BusinessLogic; using Umbraco.Core.Events; +using Umbraco.Core.Scoping; namespace Umbraco.Tests.TestHelpers { @@ -164,7 +165,7 @@ namespace Umbraco.Tests.TestHelpers var evtMsgs = new TransientMessagesFactory(); var applicationContext = new ApplicationContext( //assign the db context - new DatabaseContext(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, Logger), Logger, sqlSyntax, Constants.DatabaseProviders.SqlCe), + new DatabaseContext(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, Logger)), Logger, sqlSyntax, Constants.DatabaseProviders.SqlCe), //assign the service context new ServiceContext(repoFactory, new PetaPocoUnitOfWorkProvider(Logger), new FileUnitOfWorkProvider(), new PublishingStrategy(evtMsgs, Logger), CacheHelper, Logger, evtMsgs), CacheHelper, diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 7c5921c67a..5d0ef96e57 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -14,6 +14,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Profiling; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Web; @@ -74,7 +75,7 @@ namespace Umbraco.Tests.Web.Mvc public void Umbraco_Helper_Not_Null() { var appCtx = new ApplicationContext( - new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"), + new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"), MockHelper.GetMockedServiceContext(), CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of())); diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index 92c9a25b0f..e90ea23068 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -16,6 +16,7 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Profiling; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Stubs; @@ -463,7 +464,7 @@ namespace Umbraco.Tests.Web.Mvc var svcCtx = GetServiceContext(umbracoSettings, logger); var appCtx = new ApplicationContext( - new DatabaseContext(Mock.Of(), logger, Mock.Of(), "test"), + new DatabaseContext(Mock.Of(), logger, Mock.Of(), "test"), svcCtx, CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(logger, Mock.Of())) { IsReady = true }; diff --git a/src/Umbraco.Web/Scheduling/LogScrubber.cs b/src/Umbraco.Web/Scheduling/LogScrubber.cs index 794c927f97..187d0c55a2 100644 --- a/src/Umbraco.Web/Scheduling/LogScrubber.cs +++ b/src/Umbraco.Web/Scheduling/LogScrubber.cs @@ -77,8 +77,8 @@ namespace Umbraco.Web.Scheduling return false; // do NOT repeat, going down } - // running on a background task, requires a safe database (see UsingSafeDatabase doc) - using (ApplicationContext.Current.DatabaseContext.UseSafeDatabase()) + // running on a background task, requires its own (safe) scope + using (ApplicationContext.Current.ScopeProvider.CreateScope()) using (DisposableTimer.DebugDuration("Log scrubbing executing", "Log scrubbing complete")) { Log.CleanLogs(GetLogScrubbingMaximumAge(_settings)); diff --git a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs index 5288f2630a..461abc984a 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs @@ -82,9 +82,9 @@ namespace Umbraco.Web.Scheduling Content = new StringContent(string.Empty) }; - // running on a background task, requires a safe database (see UsingSafeDatabase doc) + // running on a background task, requires its own (safe) scope // (GetAuthenticationHeaderValue uses UserService to load the current user, hence requires a database) - using (ApplicationContext.Current.DatabaseContext.UseSafeDatabase()) + using (ApplicationContext.Current.ScopeProvider.CreateScope()) { //pass custom the authorization header request.Headers.Authorization = AdminTokenAuthorizeAttribute.GetAuthenticationHeaderValue(_appContext); diff --git a/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs b/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs index 93d3aa2f87..720dc0b20e 100644 --- a/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs +++ b/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs @@ -137,8 +137,8 @@ namespace Umbraco.Web.Strategies { try { - // running on a background task, requires a safe database (see UsingSafeDatabase doc) - using (ApplicationContext.Current.DatabaseContext.UseSafeDatabase()) + // running on a background task, requires its own (safe) scope + using (ApplicationContext.Current.ScopeProvider.CreateScope()) { _svc.TouchServer(_serverAddress, _svc.CurrentServerIdentity, _registrar.Options.StaleServerTimeout); } diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index bc41c9c877..afdc482f46 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -45,6 +45,7 @@ using Umbraco.Core.Cache; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Publishing; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Web.Editors; using Umbraco.Web.HealthCheck; @@ -86,15 +87,15 @@ namespace Umbraco.Web /// Creates and returns the service context for the app /// /// - /// + /// /// - protected override ServiceContext CreateServiceContext(DatabaseContext dbContext, IDatabaseFactory dbFactory) + protected override ServiceContext CreateServiceContext(DatabaseContext dbContext, IScopeProvider scopeProvider) { //use a request based messaging factory var evtMsgs = new RequestLifespanMessagesFactory(new SingletonHttpContextAccessor()); return new ServiceContext( new RepositoryFactory(ApplicationCache, ProfilingLogger.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()), - new PetaPocoUnitOfWorkProvider(dbFactory), + new PetaPocoUnitOfWorkProvider(scopeProvider), new FileUnitOfWorkProvider(), new PublishingStrategy(evtMsgs, ProfilingLogger.Logger), ApplicationCache, diff --git a/src/UmbracoExamine/DataServices/UmbracoContentService.cs b/src/UmbracoExamine/DataServices/UmbracoContentService.cs index 4d265bf127..cd6acca8eb 100644 --- a/src/UmbracoExamine/DataServices/UmbracoContentService.cs +++ b/src/UmbracoExamine/DataServices/UmbracoContentService.cs @@ -57,7 +57,7 @@ namespace UmbracoExamine.DataServices [Obsolete("This should no longer be used, latest content will be indexed by using the IContentService directly")] public XDocument GetLatestContentByXPath(string xpath) { - using (_applicationContext.DatabaseContext.UseSafeDatabase()) + using (ApplicationContext.Current.ScopeProvider.CreateScope()) { var xmlContent = XDocument.Parse(""); var rootContent = _applicationContext.Services.ContentService.GetRootContent(); @@ -79,7 +79,7 @@ namespace UmbracoExamine.DataServices /// public bool IsProtected(int nodeId, string path) { - using (_applicationContext.DatabaseContext.UseSafeDatabase()) + using (ApplicationContext.Current.ScopeProvider.CreateScope()) { return _applicationContext.Services.PublicAccessService.IsProtected(path.EnsureEndsWith("," + nodeId)); } @@ -92,9 +92,9 @@ namespace UmbracoExamine.DataServices public IEnumerable GetAllUserPropertyNames() { - using (_applicationContext.DatabaseContext.UseSafeDatabase()) + using (ApplicationContext.Current.ScopeProvider.CreateScope()) { - try + try { var result = _applicationContext.DatabaseContext.Database.Fetch("select distinct alias from cmsPropertyType order by alias"); return result; From 5446e4b0fc2869cf9487df8e188deeb86d5fdafa Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 12 Jan 2017 19:32:44 +0100 Subject: [PATCH 216/599] U4-9322 - IScopeProviderInternal --- src/Umbraco.Core/DatabaseContext.cs | 12 ++++++------ src/Umbraco.Core/Scoping/IScopeProviderInternal.cs | 10 +++------- src/Umbraco.Core/Scoping/ScopeProvider.cs | 2 +- .../BootManagers/CoreBootManagerTests.cs | 2 +- .../Migrations/FindingMigrationsTest.cs | 2 +- src/Umbraco.Tests/MockTests.cs | 4 ++-- .../Models/Mapping/ContentTypeModelMappingTests.cs | 2 +- src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs | 2 +- src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs | 2 +- src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs | 2 +- 10 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs index 2f7641795f..ec5607f641 100644 --- a/src/Umbraco.Core/DatabaseContext.cs +++ b/src/Umbraco.Core/DatabaseContext.cs @@ -27,7 +27,7 @@ namespace Umbraco.Core /// public class DatabaseContext { - internal readonly ScopeProvider ScopeProvider; + internal readonly IScopeProviderInternal ScopeProvider; private readonly ILogger _logger; private readonly SqlSyntaxProviders _syntaxProviders; private bool _configured; @@ -42,7 +42,7 @@ namespace Umbraco.Core private const int ConnectionCheckMinutes = 1; [Obsolete("Use the constructor specifying all dependencies instead")] - public DatabaseContext(IScopeProvider scopeProvider) + internal DatabaseContext(IScopeProviderInternal scopeProvider) : this(scopeProvider, LoggerResolver.Current.Logger, new SqlSyntaxProviders(new ISqlSyntaxProvider[] { new MySqlSyntaxProvider(LoggerResolver.Current.Logger), @@ -58,13 +58,13 @@ namespace Umbraco.Core /// /// /// - public DatabaseContext(IScopeProvider scopeProvider, ILogger logger, SqlSyntaxProviders syntaxProviders) + internal DatabaseContext(IScopeProviderInternal scopeProvider, ILogger logger, SqlSyntaxProviders syntaxProviders) { if (scopeProvider == null) throw new ArgumentNullException("scopeProvider"); if (logger == null) throw new ArgumentNullException("logger"); if (syntaxProviders == null) throw new ArgumentNullException("syntaxProviders"); - ScopeProvider = (ScopeProvider) scopeProvider; // fixme ugly + ScopeProvider = scopeProvider; _logger = logger; _syntaxProviders = syntaxProviders; } @@ -76,12 +76,12 @@ namespace Umbraco.Core /// /// /// - public DatabaseContext(IScopeProvider scopeProvider, ILogger logger, ISqlSyntaxProvider sqlSyntax, string providerName) + internal DatabaseContext(IScopeProviderInternal scopeProvider, ILogger logger, ISqlSyntaxProvider sqlSyntax, string providerName) { _providerName = providerName; SqlSyntax = sqlSyntax; SqlSyntaxContext.SqlSyntaxProvider = SqlSyntax; - ScopeProvider = (ScopeProvider) scopeProvider; // fixme ugly + ScopeProvider = scopeProvider; _logger = logger; _configured = true; } diff --git a/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs b/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs index ad0419f7a5..8726efbfb6 100644 --- a/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs +++ b/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Umbraco.Core.Scoping +namespace Umbraco.Core.Scoping { internal interface IScopeProviderInternal : IScopeProvider { + IScope AmbientScope { get; set; } + IScope CreateNoScope(); } } diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs index cdb51e2017..f3b139827c 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -5,7 +5,7 @@ using Umbraco.Core.Persistence; namespace Umbraco.Core.Scoping { - internal class ScopeProvider : IScopeProvider + internal class ScopeProvider : IScopeProviderInternal { public ScopeProvider(IDatabaseFactory2 databaseFactory) { diff --git a/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs b/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs index ac7758c9e0..eadb96c2ac 100644 --- a/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs +++ b/src/Umbraco.Tests/BootManagers/CoreBootManagerTests.cs @@ -72,7 +72,7 @@ namespace Umbraco.Tests.BootManagers { var appContext = base.CreateApplicationContext(dbContext, serviceContext); - var dbContextMock = new Mock(Mock.Of(), ProfilingLogger.Logger, Mock.Of(), "test"); + var dbContextMock = new Mock(Mock.Of(), ProfilingLogger.Logger, Mock.Of(), "test"); dbContextMock.Setup(x => x.CanConnect).Returns(true); appContext.DatabaseContext = dbContextMock.Object; diff --git a/src/Umbraco.Tests/Migrations/FindingMigrationsTest.cs b/src/Umbraco.Tests/Migrations/FindingMigrationsTest.cs index 70ae17fa14..0e60e0953c 100644 --- a/src/Umbraco.Tests/Migrations/FindingMigrationsTest.cs +++ b/src/Umbraco.Tests/Migrations/FindingMigrationsTest.cs @@ -40,7 +40,7 @@ namespace Umbraco.Tests.Migrations //This is needed because the Migration resolver is creating migration instances with their full ctors ApplicationContext.EnsureContext( new ApplicationContext( - new DatabaseContext(Mock.Of(), Mock.Of(), sqlSyntax, "test"), + new DatabaseContext(Mock.Of(), Mock.Of(), sqlSyntax, "test"), new ServiceContext(), CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of())), diff --git a/src/Umbraco.Tests/MockTests.cs b/src/Umbraco.Tests/MockTests.cs index a01c74c763..8a0dba70da 100644 --- a/src/Umbraco.Tests/MockTests.cs +++ b/src/Umbraco.Tests/MockTests.cs @@ -47,7 +47,7 @@ namespace Umbraco.Tests [Test] public void Can_Create_Db_Context() { - var dbCtx = new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"); + var dbCtx = new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"); Assert.Pass(); } @@ -55,7 +55,7 @@ namespace Umbraco.Tests public void Can_Create_App_Context_With_Services() { var appCtx = new ApplicationContext( - new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"), + new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"), MockHelper.GetMockedServiceContext(), CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of())); diff --git a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs index fd7a9997d9..1ff3447539 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs @@ -41,7 +41,7 @@ namespace Umbraco.Tests.Models.Mapping //Create an app context using mocks var appContext = new ApplicationContext( - new DatabaseContext(Mock.Of(), Mock.Of(), Mock.Of(), "test"), + new DatabaseContext(Mock.Of(), Mock.Of(), Mock.Of(), "test"), //Create service context using mocks new ServiceContext( diff --git a/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs b/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs index 85c07972a5..b4b20547fb 100644 --- a/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs +++ b/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs @@ -62,7 +62,7 @@ namespace Umbraco.Tests.Routing { var settings = SettingsForTests.GetDefault(); return new ApplicationContext( - new DatabaseContext(Mock.Of(), Logger, Mock.Of(), "test"), + new DatabaseContext(Mock.Of(), Logger, Mock.Of(), "test"), GetServiceContext(settings, Logger), CacheHelper, ProfilingLogger) diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 5d0ef96e57..e95b2fe9af 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -75,7 +75,7 @@ namespace Umbraco.Tests.Web.Mvc public void Umbraco_Helper_Not_Null() { var appCtx = new ApplicationContext( - new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"), + new DatabaseContext(new Mock().Object, Mock.Of(), Mock.Of(), "test"), MockHelper.GetMockedServiceContext(), CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of())); diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index e90ea23068..6d776b6d35 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -464,7 +464,7 @@ namespace Umbraco.Tests.Web.Mvc var svcCtx = GetServiceContext(umbracoSettings, logger); var appCtx = new ApplicationContext( - new DatabaseContext(Mock.Of(), logger, Mock.Of(), "test"), + new DatabaseContext(Mock.Of(), logger, Mock.Of(), "test"), svcCtx, CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(logger, Mock.Of())) { IsReady = true }; From ba501dbbb83b35eb77df7bd37e29a8edc743b34a Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 13 Jan 2017 10:43:34 +1100 Subject: [PATCH 217/599] U4-9371 Examine indexes inherited unpublished nodes on index rebuild part 2 --- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../UmbracoContentIndexerTests.cs | 107 ++++++++++++++++++ src/UmbracoExamine/UmbracoContentIndexer.cs | 37 +++--- 3 files changed, 125 insertions(+), 20 deletions(-) create mode 100644 src/Umbraco.Tests/UmbracoExamine/UmbracoContentIndexerTests.cs diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index d90c0e6345..dbb877ee64 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -163,6 +163,7 @@ + diff --git a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentIndexerTests.cs b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentIndexerTests.cs new file mode 100644 index 0000000000..3278130022 --- /dev/null +++ b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentIndexerTests.cs @@ -0,0 +1,107 @@ +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using Moq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models; +using UmbracoExamine; + +namespace Umbraco.Tests.UmbracoExamine +{ + [TestFixture] + public class UmbracoContentIndexerTests : ExamineBaseTest + { + [Test] + public void Get_Serialized_Content_No_Published_Content() + { + var contentSet = new List + { + Mock.Of(c => c.Id == 1 && c.Path == "-1,1" && c.Published && c.Level == 1), + Mock.Of(c => c.Id == 2 && c.Path == "-1,2" && c.Published && c.Level == 1), + Mock.Of(c => c.Id == 3 && c.Path == "-1,3" && c.Published == false && c.Level == 1), // no + Mock.Of(c => c.Id == 4 && c.Path == "-1,4" && c.Published == false && c.Level == 1), // no + + Mock.Of(c => c.Id == 5 && c.Path == "-1,1,5" && c.Published && c.Level == 2), + Mock.Of(c => c.Id == 6 && c.Path == "-1,2,6" && c.Published == false && c.Level == 2), // no + Mock.Of(c => c.Id == 7 && c.Path == "-1,3,7" && c.Published && c.Level == 2), // no + Mock.Of(c => c.Id == 8 && c.Path == "-1,4,8" && c.Published && c.Level == 2), // no + Mock.Of(c => c.Id == 9 && c.Path == "-1,4,9" && c.Published && c.Level == 2), // no + + Mock.Of(c => c.Id == 10 && c.Path == "-1,1,5,10" && c.Published && c.Level == 3), + Mock.Of(c => c.Id == 15 && c.Path == "-1,1,5,15" && c.Published && c.Level == 3), + Mock.Of(c => c.Id == 11 && c.Path == "-1,2,6,11" && c.Published && c.Level == 3), // no + Mock.Of(c => c.Id == 16 && c.Path == "-1,2,6,16" && c.Published && c.Level == 3), // no + Mock.Of(c => c.Id == 12 && c.Path == "-1,3,7,12" && c.Published && c.Level == 3), // no + Mock.Of(c => c.Id == 17 && c.Path == "-1,3,7,17" && c.Published && c.Level == 3), // no + Mock.Of(c => c.Id == 13 && c.Path == "-1,4,8,13" && c.Published && c.Level == 3), // no + Mock.Of(c => c.Id == 18 && c.Path == "-1,4,8,18" && c.Published && c.Level == 3), // no + Mock.Of(c => c.Id == 14 && c.Path == "-1,4,9,14" && c.Published && c.Level == 3), // no + Mock.Of(c => c.Id == 19 && c.Path == "-1,4,9,19" && c.Published && c.Level == 3), // no + }; + + //ensure the rest of the required values are populted + foreach (var content in contentSet) + { + var mock = Mock.Get(content); + mock.Setup(x => x.ContentType).Returns(Mock.Of(type => type.Icon == "hello")); + } + + contentSet.Sort((a, b) => Comparer.Default.Compare(a.Level, b.Level)); + + var published = new HashSet(); + + var result = UmbracoContentIndexer.GetSerializedContent(false, content => new XElement("test"), contentSet, published) + .WhereNotNull() + .ToArray(); + + Assert.AreEqual(5, result.Length); + } + + [Test] + public void Get_Serialized_Content_With_Published_Content() + { + var contentSet = new List + { + Mock.Of(c => c.Id == 1 && c.Path == "-1,1" && c.Published && c.Level == 1), + Mock.Of(c => c.Id == 2 && c.Path == "-1,2" && c.Published && c.Level == 1), + Mock.Of(c => c.Id == 3 && c.Path == "-1,3" && c.Published == false && c.Level == 1), + Mock.Of(c => c.Id == 4 && c.Path == "-1,4" && c.Published == false && c.Level == 1), + + Mock.Of(c => c.Id == 5 && c.Path == "-1,1,5" && c.Published && c.Level == 2), + Mock.Of(c => c.Id == 6 && c.Path == "-1,2,6" && c.Published == false && c.Level == 2), + Mock.Of(c => c.Id == 7 && c.Path == "-1,3,7" && c.Published && c.Level == 2), + Mock.Of(c => c.Id == 8 && c.Path == "-1,4,8" && c.Published && c.Level == 2), + Mock.Of(c => c.Id == 9 && c.Path == "-1,4,9" && c.Published && c.Level == 2), + + Mock.Of(c => c.Id == 10 && c.Path == "-1,1,5,10" && c.Published && c.Level == 3), + Mock.Of(c => c.Id == 15 && c.Path == "-1,1,5,15" && c.Published && c.Level == 3), + Mock.Of(c => c.Id == 11 && c.Path == "-1,2,6,11" && c.Published && c.Level == 3), + Mock.Of(c => c.Id == 16 && c.Path == "-1,2,6,16" && c.Published && c.Level == 3), + Mock.Of(c => c.Id == 12 && c.Path == "-1,3,7,12" && c.Published && c.Level == 3), + Mock.Of(c => c.Id == 17 && c.Path == "-1,3,7,17" && c.Published && c.Level == 3), + Mock.Of(c => c.Id == 13 && c.Path == "-1,4,8,13" && c.Published && c.Level == 3), + Mock.Of(c => c.Id == 18 && c.Path == "-1,4,8,18" && c.Published && c.Level == 3), + Mock.Of(c => c.Id == 14 && c.Path == "-1,4,9,14" && c.Published && c.Level == 3), + Mock.Of(c => c.Id == 19 && c.Path == "-1,4,9,19" && c.Published && c.Level == 3), + }; + + //ensure the rest of the required values are populted + foreach (var content in contentSet) + { + var mock = Mock.Get(content); + mock.Setup(x => x.ContentType).Returns(Mock.Of(type => type.Icon == "hello")); + } + + contentSet.Sort((a, b) => Comparer.Default.Compare(a.Level, b.Level)); + + var published = new HashSet(); + + var result = UmbracoContentIndexer.GetSerializedContent(true, content => new XElement("test"), contentSet, published) + .WhereNotNull() + .ToArray(); + + Assert.AreEqual(19, result.Length); + } + } +} \ No newline at end of file diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index efc3e4a214..ff3e4617e0 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -359,12 +359,7 @@ namespace UmbracoExamine } #endregion - #region Protected - - /// - /// This is a static query, it's parameters don't change so store statically - /// - private IQuery _publishedQuery; + #region Protected protected override void PerformIndexAll(string type) { @@ -395,11 +390,6 @@ namespace UmbracoExamine } else { - if (_publishedQuery == null) - { - _publishedQuery = Query.Builder.Where(x => x.Published == true); - } - //get all paged records but order by level ascending, we need to do this because we need to track which nodes are not published so that we can determine // which descendent nodes are implicitly not published descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "level", Direction.Ascending, true, (string)null); @@ -415,7 +405,12 @@ namespace UmbracoExamine { content = descendants.ToArray(); } - AddNodesToIndex(GetSerializedContent(content, notPublished).WhereNotNull(), type); + + AddNodesToIndex(GetSerializedContent( + SupportUnpublishedContent, + c => _serializer.Serialize(_contentService, _dataTypeService, _userService, c), + content, notPublished).WhereNotNull(), type); + pageIndex++; } while (content.Length == pageSize); @@ -473,17 +468,22 @@ namespace UmbracoExamine } } - private IEnumerable GetSerializedContent(IEnumerable content, ISet notPublished) + internal static IEnumerable GetSerializedContent( + bool supportUnpublishdContent, + Func serializer, + IEnumerable content, + ISet notPublished) { foreach (var c in content) { - if (SupportUnpublishedContent == false) + if (supportUnpublishdContent == false) { //if we don't support published content and this is not published then track it and return null if (c.Published == false) { notPublished.Add(c.Path); yield return null; + continue; } //if we don't support published content, check if this content item exists underneath any already tracked @@ -491,14 +491,11 @@ namespace UmbracoExamine if (notPublished.Any(path => c.Path.StartsWith(string.Format("{0},", path)))) { yield return null; + continue; } - } + } - var xml = _serializer.Serialize( - _contentService, - _dataTypeService, - _userService, - c); + var xml = serializer(c); //add a custom 'icon' attribute xml.Add(new XAttribute("icon", c.ContentType.Icon)); From bde439d102b895bfc281c933beac9e9dcf597f91 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 13 Jan 2017 12:34:58 +1100 Subject: [PATCH 218/599] fixes tests --- .../UmbracoExamine/IndexInitializer.cs | 8 +- src/Umbraco.Tests/UmbracoExamine/IndexTest.cs | 343 +++++++++--------- .../UmbracoExamine/SearchTests.cs | 27 +- src/UmbracoExamine/BaseUmbracoIndexer.cs | 2 +- 4 files changed, 187 insertions(+), 193 deletions(-) diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 00e94ced63..f57b1af213 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -41,7 +41,8 @@ namespace Umbraco.Tests.UmbracoExamine IDataTypeService dataTypeService = null, IMemberService memberService = null, IUserService userService = null, - IContentTypeService contentTypeService = null) + IContentTypeService contentTypeService = null, + bool supportUnpublishedContent = false) { if (dataService == null) { @@ -185,7 +186,10 @@ namespace Umbraco.Tests.UmbracoExamine userService, contentTypeService, analyzer, - false); + false) + { + SupportUnpublishedContent = supportUnpublishedContent + }; //i.IndexSecondsInterval = 1; diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index 5d6812f94a..5f3f5525c1 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -22,211 +22,222 @@ namespace Umbraco.Tests.UmbracoExamine [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] [TestFixture, RequiresSTA] public class IndexTest : ExamineBaseTest - { + { + /// + /// Check that the node signalled as protected in the content service is not present in the index. + /// + [Test] + public void Index_Protected_Content_Not_Indexed() + { - ///// - /// - /// Check that the node signalled as protected in the content service is not present in the index. - /// - [Test] - public void Index_Protected_Content_Not_Indexed() - { + using (var luceneDir = new RAMDirectory()) + using (var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir)) + { + indexer.RebuildIndex(); - var protectedQuery = new BooleanQuery(); - protectedQuery.Add( - new BooleanClause( - new TermQuery(new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content)), - BooleanClause.Occur.MUST)); + var protectedQuery = new BooleanQuery(); + protectedQuery.Add( + new BooleanClause( + new TermQuery(new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content)), + BooleanClause.Occur.MUST)); - protectedQuery.Add( - new BooleanClause( - new TermQuery(new Term(LuceneIndexer.IndexNodeIdFieldName, TestContentService.ProtectedNode.ToString())), - BooleanClause.Occur.MUST)); + protectedQuery.Add( + new BooleanClause( + new TermQuery(new Term(LuceneIndexer.IndexNodeIdFieldName, TestContentService.ProtectedNode.ToString())), + BooleanClause.Occur.MUST)); - var collector = new AllHitsCollector(false, true); - var s = _searcher.GetSearcher(); - s.Search(protectedQuery, collector); + var collector = new AllHitsCollector(false, true); + var s = searcher.GetSearcher(); + s.Search(protectedQuery, collector); - Assert.AreEqual(0, collector.Count, "Protected node should not be indexed"); + Assert.AreEqual(0, collector.Count, "Protected node should not be indexed"); + } - } + } - [Test] - public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID() - { - //change parent id to 1116 - var existingCriteria = _indexer.IndexerData; - _indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes, - 1116); - - //rebuild so it excludes children unless they are under 1116 - _indexer.RebuildIndex(); + [Test] + public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID() + { + using (var luceneDir = new RAMDirectory()) + using (var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir)) + { + indexer.RebuildIndex(); - //ensure that node 2112 doesn't exist - var results = _searcher.Search(_searcher.CreateSearchCriteria().Id(2112).Compile()); - Assert.AreEqual(0, results.Count()); + var mediaService = new TestMediaService(); - //get a node from the data repo (this one exists underneath 2222) - var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]") - .Root - .Elements() - .Where(x => (int)x.Attribute("id") == 2112) - .First(); + //change parent id to 1116 + var existingCriteria = indexer.IndexerData; + indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes, + 1116); - var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112 - Assert.AreEqual("-1,1111,2222,2112", currPath); + //rebuild so it excludes children unless they are under 1116 + indexer.RebuildIndex(); - //now mimic moving 2112 to 1116 - //node.SetAttributeValue("path", currPath.Replace("2222", "1116")); - node.SetAttributeValue("path", "-1,1116,2112"); - node.SetAttributeValue("parentID", "1116"); + //ensure that node 2112 doesn't exist + var results = searcher.Search(searcher.CreateSearchCriteria().Id(2112).Compile()); + Assert.AreEqual(0, results.Count()); - //now reindex the node, this should first delete it and then WILL add it because of the parent id constraint - _indexer.ReIndexNode(node, IndexTypes.Media); + //get a node from the data repo (this one exists underneath 2222) + var node = mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]") + .Root + .Elements() + .First(x => (int)x.Attribute("id") == 2112); - //RESET the parent id - existingCriteria = ((IndexCriteria)_indexer.IndexerData); - _indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes, - null); + var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112 + Assert.AreEqual("-1,1111,2222,2112", currPath); - //now ensure it's deleted - var newResults = _searcher.Search(_searcher.CreateSearchCriteria().Id(2112).Compile()); - Assert.AreEqual(1, newResults.Count()); - } + //now mimic moving 2112 to 1116 + //node.SetAttributeValue("path", currPath.Replace("2222", "1116")); + node.SetAttributeValue("path", "-1,1116,2112"); + node.SetAttributeValue("parentID", "1116"); - [Test] + //now reindex the node, this should first delete it and then WILL add it because of the parent id constraint + indexer.ReIndexNode(node, IndexTypes.Media); + + //RESET the parent id + existingCriteria = ((IndexCriteria)indexer.IndexerData); + indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes, + null); + + //now ensure it's deleted + var newResults = searcher.Search(searcher.CreateSearchCriteria().Id(2112).Compile()); + Assert.AreEqual(1, newResults.Count()); + } + + + } + + [Test] [Ignore] - public void Index_Move_Media_To_Non_Indexable_ParentID() - { - //get a node from the data repo (this one exists underneath 2222) - var node = _mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]") - .Root - .Elements() - .Where(x => (int)x.Attribute("id") == 2112) - .First(); + public void Index_Move_Media_To_Non_Indexable_ParentID() + { + using (var luceneDir = new RAMDirectory()) + using (var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir)) + { + indexer.RebuildIndex(); - var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112 - Assert.AreEqual("-1,1111,2222,2112", currPath); + var mediaService = new TestMediaService(); - //ensure it's indexed - _indexer.ReIndexNode(node, IndexTypes.Media); + //get a node from the data repo (this one exists underneath 2222) + var node = mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]") + .Root + .Elements() + .First(x => (int)x.Attribute("id") == 2112); - //change the parent node id to be the one it used to exist under - var existingCriteria = _indexer.IndexerData; - _indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes, - 2222); + var currPath = (string)node.Attribute("path"); //should be : -1,1111,2222,2112 + Assert.AreEqual("-1,1111,2222,2112", currPath); - //now mimic moving the node underneath 1116 instead of 2222 - node.SetAttributeValue("path", currPath.Replace("2222", "1116")); - node.SetAttributeValue("parentID", "1116"); + //ensure it's indexed + indexer.ReIndexNode(node, IndexTypes.Media); - //now reindex the node, this should first delete it and then NOT add it because of the parent id constraint - _indexer.ReIndexNode(node, IndexTypes.Media); + //change the parent node id to be the one it used to exist under + var existingCriteria = indexer.IndexerData; + indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes, + 2222); - //RESET the parent id - existingCriteria = ((IndexCriteria)_indexer.IndexerData); - _indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes, - null); + //now mimic moving the node underneath 1116 instead of 2222 + node.SetAttributeValue("path", currPath.Replace("2222", "1116")); + node.SetAttributeValue("parentID", "1116"); - //now ensure it's deleted - var results = _searcher.Search(_searcher.CreateSearchCriteria().Id(2112).Compile()); - Assert.AreEqual(0, results.Count()); + //now reindex the node, this should first delete it and then NOT add it because of the parent id constraint + indexer.ReIndexNode(node, IndexTypes.Media); - } + //RESET the parent id + existingCriteria = ((IndexCriteria)indexer.IndexerData); + indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes, + null); + + //now ensure it's deleted + var results = searcher.Search(searcher.CreateSearchCriteria().Id(2112).Compile()); + Assert.AreEqual(0, results.Count()); + } + } - /// - /// This will ensure that all 'Content' (not media) is cleared from the index using the Lucene API directly. - /// We then call the Examine method to re-index Content and do some comparisons to ensure that it worked correctly. - /// - [Test] - public void Index_Reindex_Content() - { - var s = (IndexSearcher)_searcher.GetSearcher(); + /// + /// This will ensure that all 'Content' (not media) is cleared from the index using the Lucene API directly. + /// We then call the Examine method to re-index Content and do some comparisons to ensure that it worked correctly. + /// + [Test] + public void Index_Reindex_Content() + { + using (var luceneDir = new RAMDirectory()) + using (var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir, supportUnpublishedContent:true)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir)) + { + indexer.RebuildIndex(); - //first delete all 'Content' (not media). This is done by directly manipulating the index with the Lucene API, not examine! - - var contentTerm = new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content); - var writer = _indexer.GetIndexWriter(); - writer.DeleteDocuments(contentTerm); - writer.Commit(); - + var s = (IndexSearcher)searcher.GetSearcher(); - //make sure the content is gone. This is done with lucene APIs, not examine! - var collector = new AllHitsCollector(false, true); - var query = new TermQuery(contentTerm); - s = (IndexSearcher)_searcher.GetSearcher(); //make sure the searcher is up do date. - s.Search(query, collector); - Assert.AreEqual(0, collector.Count); + //first delete all 'Content' (not media). This is done by directly manipulating the index with the Lucene API, not examine! - //call our indexing methods - _indexer.IndexAll(IndexTypes.Content); + var contentTerm = new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content); + var writer = indexer.GetIndexWriter(); + writer.DeleteDocuments(contentTerm); + writer.Commit(); + + //make sure the content is gone. This is done with lucene APIs, not examine! + var collector = new AllHitsCollector(false, true); + var query = new TermQuery(contentTerm); + s = (IndexSearcher)searcher.GetSearcher(); //make sure the searcher is up do date. + s.Search(query, collector); + Assert.AreEqual(0, collector.Count); - collector = new AllHitsCollector(false, true); - s = (IndexSearcher)_searcher.GetSearcher(); //make sure the searcher is up do date. - s.Search(query, collector); - //var ids = new List(); - //for (var i = 0; i < collector.Count;i++) - //{ - // ids.Add(s.Doc(collector.GetDocId(i)).GetValues("__NodeId")[0]); - //} - Assert.AreEqual(20, collector.Count); - } + //call our indexing methods + indexer.IndexAll(IndexTypes.Content); - /// - /// This will delete an item from the index and ensure that all children of the node are deleted too! - /// - [Test] + collector = new AllHitsCollector(false, true); + s = (IndexSearcher)searcher.GetSearcher(); //make sure the searcher is up do date. + s.Search(query, collector); + //var ids = new List(); + //for (var i = 0; i < collector.Count;i++) + //{ + // ids.Add(s.Doc(collector.GetDocId(i)).GetValues("__NodeId")[0]); + //} + Assert.AreEqual(21, collector.Count); + } + } + + /// + /// This will delete an item from the index and ensure that all children of the node are deleted too! + /// + [Test] [Ignore] - public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed() - { + public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed() + { - //now delete a node that has children + using (var luceneDir = new RAMDirectory()) + using (var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir)) + { + indexer.RebuildIndex(); - _indexer.DeleteFromIndex(1140.ToString()); - //this node had children: 1141 & 1142, let's ensure they are also removed + //now delete a node that has children - var results = _searcher.Search(_searcher.CreateSearchCriteria().Id(1141).Compile()); - Assert.AreEqual(0, results.Count()); + indexer.DeleteFromIndex(1140.ToString()); + //this node had children: 1141 & 1142, let's ensure they are also removed - results = _searcher.Search(_searcher.CreateSearchCriteria().Id(1142).Compile()); - Assert.AreEqual(0, results.Count()); + var results = searcher.Search(searcher.CreateSearchCriteria().Id(1141).Compile()); + Assert.AreEqual(0, results.Count()); - } - - #region Private methods and properties - - private readonly TestContentService _contentService = new TestContentService(); - private readonly TestMediaService _mediaService = new TestMediaService(); - - private static UmbracoExamineSearcher _searcher; - private static UmbracoContentIndexer _indexer; - - #endregion - - #region Initialize and Cleanup - - private Lucene.Net.Store.Directory _luceneDir; - - public override void TearDown() - { - base.TearDown(); - _luceneDir.Dispose(); + results = searcher.Search(searcher.CreateSearchCriteria().Id(1142).Compile()); + Assert.AreEqual(0, results.Count()); + } + } + + #region Initialize and Cleanup + + public override void TearDown() + { + base.TearDown(); + UmbracoExamineSearcher.DisableInitializationCheck = null; BaseUmbracoIndexer.DisableInitializationCheck = null; } - - public override void Initialize() - { - base.Initialize(); - _luceneDir = new RAMDirectory(); - _indexer = IndexInitializer.GetUmbracoIndexer(_luceneDir); - _indexer.RebuildIndex(); - _searcher = IndexInitializer.GetUmbracoSearcher(_luceneDir); - } - - - #endregion - } + #endregion + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index 9b8b3d50d7..7eb92ad49d 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -20,17 +20,14 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Test_Sort_Order_Sorting() { - //var newIndexFolder = new DirectoryInfo(Path.Combine("App_Data\\SearchTests", Guid.NewGuid().ToString())); - //System.IO.Directory.CreateDirectory(newIndexFolder.FullName); - using (var luceneDir = new RAMDirectory()) - //using (var luceneDir = new SimpleFSDirectory(newIndexFolder)) { var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir, null, new TestDataService() { ContentService = new TestContentService(TestFiles.umbraco_sort) - }); + }, + supportUnpublishedContent:true); indexer.RebuildIndex(); var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); @@ -69,25 +66,7 @@ namespace Umbraco.Tests.UmbracoExamine currentSort = sort; } return true; - } - - //[Test] - //public void Test_Index_Type_With_German_Analyzer() - //{ - // using (var luceneDir = new RAMDirectory()) - // { - // var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir, - // new GermanAnalyzer()); - // indexer.RebuildIndex(); - // var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); - // } - //} - - //private readonly TestContentService _contentService = new TestContentService(); - //private readonly TestMediaService _mediaService = new TestMediaService(); - //private static UmbracoExamineSearcher _searcher; - //private static UmbracoContentIndexer _indexer; - //private Lucene.Net.Store.Directory _luceneDir; + } } } diff --git a/src/UmbracoExamine/BaseUmbracoIndexer.cs b/src/UmbracoExamine/BaseUmbracoIndexer.cs index ebb718898b..9ff68b8685 100644 --- a/src/UmbracoExamine/BaseUmbracoIndexer.cs +++ b/src/UmbracoExamine/BaseUmbracoIndexer.cs @@ -95,7 +95,7 @@ namespace UmbracoExamine /// Determines if the manager will call the indexing methods when content is saved or deleted as /// opposed to cache being updated. /// - public bool SupportUnpublishedContent { get; protected set; } + public bool SupportUnpublishedContent { get; protected internal set; } /// /// The data service used for retreiving and submitting data to the cms From 4e7d2f668bfdd594dc8a56293eee2a42744cb7d4 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 13 Jan 2017 10:26:43 +0100 Subject: [PATCH 219/599] add node preview component --- .../components/umbnodepreview.directive.js | 35 +++++++++ src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../src/less/components/umb-node-preview.less | 71 +++++++++++++++++++ .../views/components/umb-node-preview.html | 13 ++++ 4 files changed, 120 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js new file mode 100644 index 0000000000..51e9b265f5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js @@ -0,0 +1,35 @@ +(function () { + 'use strict'; + + function NodePreviewDirective() { + + function link(scope, el, attr, ctrl) { + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-node-preview.html', + scope: { + icon: "=?", + name: "=", + description: "=?", + sortable: "=?", + allowEdit: "=?", + allowOpen: "=?", + allowRemove: "=?", + onEdit: "&?", + onOpen: "&?", + onRemove: "&?" + }, + link: link + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbNodePreview', NodePreviewDirective); + +})(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 2b53fbddfc..49a98ee219 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -122,6 +122,7 @@ @import "components/notifications/umb-notifications.less"; @import "components/umb-file-dropzone.less"; +@import "components/umb-node-preview.less"; // Utilities @import "utilities/_flexbox.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less new file mode 100644 index 0000000000..952a49f692 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -0,0 +1,71 @@ +.umb-node-preview { + padding: 5px 15px; + margin-bottom: 5px; + border: 2px solid #f8f8f8; + border-radius: 3px; + display: flex; + align-items: center; + background: @white; +} + +.umb-node-preview--sortable { + cursor: move; +} + +.umb-node-preview--sortable:hover { + border-color: #d9d9d9; +} + +.umb-node-preview__icon { + display: flex; + width: 25px; + height: 25px; + justify-content: center; + align-items: center; + font-size: 20px; + margin-right: 10px; + flex: 0 0 auto; +} + +.umb-node-preview__content { + flex: 1 1 auto; +} + +.umb-node-preview__name { + font-size: 13px; + font-weight: bold; + color: @black; +} + +.umb-node-preview__description { + font-size: 12px; +} + +.umb-node-preview__actions { + flex: 0 0 auto; +} + +.umb-node-preview__action { + margin-left: 5px; + margin-right: 5px; + font-size: 16px; +} + +.umb-node-preview__action:hover { + color: @blue; + text-decoration: none; +} + +.umb-node-preview-add { + display: flex; + align-items: center; + justify-content: center; + border: 1px dashed #d9d9d9; + color: @blue; + font-weight: bold; + padding: 5px 15px; +} + +.umb-node-preview-add:hover { + color: @blue; +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html new file mode 100644 index 0000000000..4bcd1f39e2 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html @@ -0,0 +1,13 @@ +
    + +
    +
    {{ name }}
    +
    {{ description }}
    +
    +
    + + + +
    + +
    \ No newline at end of file From 0c709d144b3d805db1b67d66fc637f0eb0882148 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 13 Jan 2017 10:40:03 +0100 Subject: [PATCH 220/599] move mini editor launch logic to helper service so we can open it without the directive - it is needed for the content picker --- .../umblaunchminieditor.directive.js | 65 +-------------- .../services/minieditorhelper.service.js | 79 +++++++++++++++++++ 2 files changed, 81 insertions(+), 63 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umblaunchminieditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umblaunchminieditor.directive.js index 91212a82f8..11b934ce96 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umblaunchminieditor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umblaunchminieditor.directive.js @@ -7,7 +7,7 @@ * Used on a button to launch a mini content editor editor dialog **/ angular.module("umbraco.directives") - .directive('umbLaunchMiniEditor', function (dialogService, editorState, fileManager, contentEditingHelper) { + .directive('umbLaunchMiniEditor', function (miniEditorHelper) { return { restrict: 'A', replace: false, @@ -16,69 +16,8 @@ angular.module("umbraco.directives") }, link: function(scope, element, attrs) { - var launched = false; - element.click(function() { - - if (launched === true) { - return; - } - - launched = true; - - //We need to store the current files selected in the file manager locally because the fileManager - // is a singleton and is shared globally. The mini dialog will also be referencing the fileManager - // and we don't want it to be sharing the same files as the main editor. So we'll store the current files locally here, - // clear them out and then launch the dialog. When the dialog closes, we'll reset the fileManager to it's previous state. - var currFiles = _.groupBy(fileManager.getFiles(), "alias"); - fileManager.clearFiles(); - - //We need to store the original editorState entity because it will need to change when the mini editor is loaded so that - // any property editors that are working with editorState get given the correct entity, otherwise strange things will - // start happening. - var currEditorState = editorState.getCurrent(); - - dialogService.open({ - template: "views/common/dialogs/content/edit.html", - id: scope.node.id, - closeOnSave: true, - tabFilter: ["Generic properties"], - callback: function (data) { - - //set the node name back - scope.node.name = data.name; - - //reset the fileManager to what it was - fileManager.clearFiles(); - _.each(currFiles, function (val, key) { - fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; })); - }); - - //reset the editor state - editorState.set(currEditorState); - - //Now we need to check if the content item that was edited was actually the same content item - // as the main content editor and if so, update all property data - if (data.id === currEditorState.id) { - var changed = contentEditingHelper.reBindChangedProperties(currEditorState, data); - } - - launched = false; - }, - closeCallback: function () { - //reset the fileManager to what it was - fileManager.clearFiles(); - _.each(currFiles, function (val, key) { - fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; })); - }); - - //reset the editor state - editorState.set(currEditorState); - - launched = false; - } - }); - + miniEditorHelper.launchMiniEditor(scope.node); }); } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js new file mode 100644 index 0000000000..9c974f9ca9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js @@ -0,0 +1,79 @@ +(function () { + 'use strict'; + + function miniEditorHelper(dialogService, editorState, fileManager, contentEditingHelper) { + + var launched = false; + + function launchMiniEditor(node) { + + launched = true; + + //We need to store the current files selected in the file manager locally because the fileManager + // is a singleton and is shared globally. The mini dialog will also be referencing the fileManager + // and we don't want it to be sharing the same files as the main editor. So we'll store the current files locally here, + // clear them out and then launch the dialog. When the dialog closes, we'll reset the fileManager to it's previous state. + var currFiles = _.groupBy(fileManager.getFiles(), "alias"); + fileManager.clearFiles(); + + //We need to store the original editorState entity because it will need to change when the mini editor is loaded so that + // any property editors that are working with editorState get given the correct entity, otherwise strange things will + // start happening. + var currEditorState = editorState.getCurrent(); + + dialogService.open({ + template: "views/common/dialogs/content/edit.html", + id: node.id, + closeOnSave: true, + tabFilter: ["Generic properties"], + callback: function (data) { + + //set the node name back + node.name = data.name; + + //reset the fileManager to what it was + fileManager.clearFiles(); + _.each(currFiles, function (val, key) { + fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; })); + }); + + //reset the editor state + editorState.set(currEditorState); + + //Now we need to check if the content item that was edited was actually the same content item + // as the main content editor and if so, update all property data + if (data.id === currEditorState.id) { + var changed = contentEditingHelper.reBindChangedProperties(currEditorState, data); + } + + launched = false; + }, + closeCallback: function () { + //reset the fileManager to what it was + fileManager.clearFiles(); + _.each(currFiles, function (val, key) { + fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; })); + }); + + //reset the editor state + editorState.set(currEditorState); + + launched = false; + } + }); + + } + + var service = { + launchMiniEditor: launchMiniEditor + }; + + return service; + + } + + + angular.module('umbraco.services').factory('miniEditorHelper', miniEditorHelper); + + +})(); From 8e79e15c71c69ddb7c882ee0f0afde897006dd73 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 13 Jan 2017 11:16:15 +0100 Subject: [PATCH 221/599] use new umb node preview component in content picker --- .../contentpicker/contentpicker.controller.js | 8 ++- .../contentpicker/contentpicker.html | 51 +++++++++---------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 667dfd0f22..c02ab7a588 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -1,7 +1,7 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it -function contentPickerController($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location) { +function contentPickerController($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location, $timeout, miniEditorHelper) { function trim(str, chr) { var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g'); @@ -77,6 +77,8 @@ function contentPickerController($scope, dialogService, entityResource, editorSt : "Document"; $scope.allowOpenButton = entityType === "Document" || entityType === "Media"; $scope.allowEditButton = entityType === "Document"; + $scope.allowRemoveButton = true; + $scope.sortable = true; //the dialog options for the picker var dialogOptions = { @@ -200,6 +202,10 @@ function contentPickerController($scope, dialogService, entityResource, editorSt $scope.clear = function () { $scope.renderModel = []; }; + + $scope.openMiniEditor = function(node) { + miniEditorHelper.launchMiniEditor(node); + }; var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { var currIds = _.map($scope.renderModel, function (i) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index 9f5f3fb60f..760063b94b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -1,33 +1,30 @@
    - - - - + +
    + + +
    + + + Add + From 21714f0eacdbd14822212c91faa758c0ed230fe4 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 13 Jan 2017 11:16:59 +0100 Subject: [PATCH 222/599] use new umb node preview component in member group picker --- .../membergrouppicker.controller.js | 1 + .../membergrouppicker/membergrouppicker.html | 37 ++++++++----------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js index 5ebbd37217..aa20a9c43b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.controller.js @@ -8,6 +8,7 @@ function memberGroupPicker($scope, dialogService){ } $scope.renderModel = []; + $scope.allowRemove = true; if ($scope.model.value) { var modelIds = $scope.model.value.split(','); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html index 5258968c00..60ae9cb202 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/membergrouppicker/membergrouppicker.html @@ -1,27 +1,22 @@
    +
    + + +
    - - - + + Add + Date: Fri, 13 Jan 2017 11:17:28 +0100 Subject: [PATCH 223/599] use new umb node preview component in member picker --- .../memberpicker/memberpicker.controller.js | 1 + .../memberpicker/memberpicker.html | 37 ++++++++----------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js index 9f20e121d9..0d4b2817d0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js @@ -8,6 +8,7 @@ function memberPickerController($scope, dialogService, entityResource, $log, ico } $scope.renderModel = []; + $scope.allowRemove = true; var dialogOptions = { multiPicker: false, diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html index b537a80113..a7a4127b18 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.html @@ -1,27 +1,22 @@
    - - - + + Add + Date: Fri, 13 Jan 2017 11:45:49 +0100 Subject: [PATCH 224/599] U4-9377 It is possible to create/save entities with a blank names by using the services directly ensuring that when trying to save an entity with an empty name, directly through a service, an exception will be thrown. --- src/Umbraco.Core/Services/ContentService.cs | 5 +++ .../Services/ContentTypeService.cs | 9 ++++- src/Umbraco.Core/Services/DataTypeService.cs | 9 ++++- src/Umbraco.Core/Services/MacroService.cs | 27 ++++++++------ src/Umbraco.Core/Services/MediaService.cs | 4 ++ src/Umbraco.Core/Services/MemberService.cs | 5 +++ .../Services/MemberTypeService.cs | 5 +++ src/Umbraco.Core/Services/UserService.cs | 22 +++++++++++ .../Services/ContentServiceTests.cs | 11 ++++++ .../Services/ContentTypeServiceTests.cs | 10 +++++ .../Services/DataTypeServiceTests.cs | 13 +++++++ .../Services/MacroServiceTests.cs | 14 ++++++- .../Services/MediaServiceTests.cs | 13 +++++++ .../Services/MemberServiceTests.cs | 12 ++++++ .../Services/MemberTypeServiceTests.cs | 10 +++++ .../Services/UserServiceTests.cs | 37 +++++++++++++++++++ 16 files changed, 190 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index e0edaa38c4..0838a0eb85 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -2192,6 +2192,11 @@ namespace Umbraco.Core.Services } } + if (string.IsNullOrWhiteSpace(content.Name)) + { + throw new ArgumentException("Cannot save content with empty name."); + } + using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 31e86cd128..9f625c27b8 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -719,8 +719,13 @@ namespace Umbraco.Core.Services /// Optional id of the user saving the ContentType public void Save(IContentType contentType, int userId = 0) { - if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs(contentType), this)) - return; + if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs(contentType), this)) + return; + + if (string.IsNullOrWhiteSpace(contentType.Name)) + { + throw new ArgumentException("Cannot save content type with empty name."); + } using (new WriteLock(Locker)) { diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index 2cdcfc76d5..b3fca10798 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -361,8 +361,13 @@ namespace Umbraco.Core.Services /// Id of the user issueing the save public void Save(IDataTypeDefinition dataTypeDefinition, int userId = 0) { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this)) - return; + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this)) + return; + + if (string.IsNullOrWhiteSpace(dataTypeDefinition.Name)) + { + throw new ArgumentException("Cannot save datatype with empty name."); + } var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) diff --git a/src/Umbraco.Core/Services/MacroService.cs b/src/Umbraco.Core/Services/MacroService.cs index 49853eab81..eca5f7e954 100644 --- a/src/Umbraco.Core/Services/MacroService.cs +++ b/src/Umbraco.Core/Services/MacroService.cs @@ -142,19 +142,24 @@ namespace Umbraco.Core.Services /// Optional Id of the user deleting the macro public void Save(IMacro macro, int userId = 0) { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(macro), this)) - return; - - var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateMacroRepository(uow)) - { - repository.AddOrUpdate(macro); - uow.Commit(); + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(macro), this)) + return; - Saved.RaiseEvent(new SaveEventArgs(macro, false), this); - } + if (string.IsNullOrWhiteSpace(macro.Name)) + { + throw new ArgumentException("Cannot save macro with empty name."); + } - Audit(AuditType.Save, "Save Macro performed by user", userId, -1); + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateMacroRepository(uow)) + { + repository.AddOrUpdate(macro); + uow.Commit(); + + Saved.RaiseEvent(new SaveEventArgs(macro, false), this); + } + + Audit(AuditType.Save, "Save Macro performed by user", userId, -1); } ///// diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index bd373f1ec0..50b323ed0a 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -837,7 +837,11 @@ namespace Umbraco.Core.Services { return OperationStatus.Cancelled(evtMsgs); } + } + if (string.IsNullOrWhiteSpace(media.Name)) + { + throw new ArgumentException("Cannot save media with empty name."); } var uow = UowProvider.GetUnitOfWork(); diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index 094539d66e..2ea4f1f023 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -978,6 +978,11 @@ namespace Umbraco.Core.Services } } + if (string.IsNullOrWhiteSpace(entity.Name)) + { + throw new ArgumentException("Cannot save member with empty name."); + } + var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateMemberRepository(uow)) { diff --git a/src/Umbraco.Core/Services/MemberTypeService.cs b/src/Umbraco.Core/Services/MemberTypeService.cs index c6bc9dd24a..164bbc8243 100644 --- a/src/Umbraco.Core/Services/MemberTypeService.cs +++ b/src/Umbraco.Core/Services/MemberTypeService.cs @@ -78,6 +78,11 @@ namespace Umbraco.Core.Services if (Saving.IsRaisedEventCancelled(new SaveEventArgs(memberType), this)) return; + if (string.IsNullOrWhiteSpace(memberType.Name)) + { + throw new ArgumentException("Cannot save MemberType with empty name."); + } + using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 8e984d1e5d..e36e454987 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -125,6 +125,11 @@ namespace Umbraco.Core.Services { if (userType == null) throw new ArgumentNullException("userType"); + if (string.IsNullOrWhiteSpace(username)) + { + throw new ArgumentException("Cannot create user with empty username."); + } + //TODO: PUT lock here!! var uow = UowProvider.GetUnitOfWork(); @@ -312,6 +317,15 @@ namespace Umbraco.Core.Services return; } + if (string.IsNullOrWhiteSpace(entity.Username)) + { + throw new ArgumentException("Cannot save user with empty username."); + } + if (string.IsNullOrWhiteSpace(entity.Name)) + { + throw new ArgumentException("Cannot save user with empty name."); + } + var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateUserRepository(uow)) { @@ -353,6 +367,14 @@ namespace Umbraco.Core.Services { foreach (var member in entities) { + if (string.IsNullOrWhiteSpace(member.Username)) + { + throw new ArgumentException("Cannot save user with empty username."); + } + if (string.IsNullOrWhiteSpace(member.Name)) + { + throw new ArgumentException("Cannot save user with empty name."); + } repository.AddOrUpdate(member); } //commit the whole lot in one go diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 72a58861e2..0078d3e83e 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -668,6 +668,17 @@ namespace Umbraco.Tests.Services Assert.Throws(() => contentService.CreateContent("Test", -1, "umbAliasDoesntExist")); } + [Test] + public void Cannot_Save_Content_With_Empty_Name() + { + // Arrange + var contentService = ServiceContext.ContentService; + var content = new Content(string.Empty, -1, ServiceContext.ContentTypeService.GetContentType("umbTextpage")); + + // Act & Assert + Assert.Throws(() => contentService.Save(content)); + } + [Test] public void Can_Get_Content_By_Id() { diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index bc9ec8a9ad..3fd0e1f6cd 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -717,6 +717,16 @@ namespace Umbraco.Tests.Services Assert.DoesNotThrow(() => service.GetContentType("advancedPage")); } + [Test] + public void Cannot_Save_ContentType_With_Empty_Name() + { + // Arrange + var contentType = MockedContentTypes.CreateSimpleContentType("contentType", string.Empty); + + // Act & Assert + Assert.Throws(() => ServiceContext.ContentTypeService.Save(contentType)); + } + [Test] public void Cannot_Rename_PropertyType_Alias_On_Composition_Which_Would_Cause_Conflict_In_Other_Composition() { diff --git a/src/Umbraco.Tests/Services/DataTypeServiceTests.cs b/src/Umbraco.Tests/Services/DataTypeServiceTests.cs index 073d7af406..ed9fc9a7e7 100644 --- a/src/Umbraco.Tests/Services/DataTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/DataTypeServiceTests.cs @@ -221,5 +221,18 @@ namespace Umbraco.Tests.Services Assert.AreEqual("preVal1", preVals.PreValuesAsArray.First().Value); Assert.AreEqual("preVal2", preVals.PreValuesAsArray.Last().Value); } + + [Test] + public void Cannot_Save_DataType_With_Empty_Name() + { + // Arrange + var dataTypeService = ServiceContext.DataTypeService; + + // Act + var dataTypeDefinition = new DataTypeDefinition(-1, "Test.TestEditor") { Name = string.Empty, DatabaseType = DataTypeDatabaseType.Ntext }; + + // Act & Assert + Assert.Throws(() => dataTypeService.Save(dataTypeDefinition)); + } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Services/MacroServiceTests.cs b/src/Umbraco.Tests/Services/MacroServiceTests.cs index 5e52140bb5..770cc76fa6 100644 --- a/src/Umbraco.Tests/Services/MacroServiceTests.cs +++ b/src/Umbraco.Tests/Services/MacroServiceTests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -217,6 +218,17 @@ namespace Umbraco.Tests.Services } + [Test] + public void Cannot_Save_Macro_With_Empty_Name() + { + // Arrange + var macroService = ServiceContext.MacroService; + var macro = new Macro("test", string.Empty, scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234); + + // Act & Assert + Assert.Throws(() => macroService.Save(macro)); + } + //[Test] //public void Can_Get_Many_By_Alias() //{ diff --git a/src/Umbraco.Tests/Services/MediaServiceTests.cs b/src/Umbraco.Tests/Services/MediaServiceTests.cs index a53257b26a..8193df911c 100644 --- a/src/Umbraco.Tests/Services/MediaServiceTests.cs +++ b/src/Umbraco.Tests/Services/MediaServiceTests.cs @@ -81,6 +81,19 @@ namespace Umbraco.Tests.Services Assert.That(mediaChild.Trashed, Is.False); } + [Test] + public void Cannot_Save_Media_With_Empty_Name() + { + // Arrange + var mediaService = ServiceContext.MediaService; + var mediaType = MockedContentTypes.CreateVideoMediaType(); + ServiceContext.ContentTypeService.Save(mediaType); + var media = mediaService.CreateMedia(string.Empty, -1, "video"); + + // Act & Assert + Assert.Throws(() => mediaService.Save(media)); + } + [Test] public void Ensure_Content_Xml_Created() { diff --git a/src/Umbraco.Tests/Services/MemberServiceTests.cs b/src/Umbraco.Tests/Services/MemberServiceTests.cs index c9cf4e6d5b..522ecc4a8c 100644 --- a/src/Umbraco.Tests/Services/MemberServiceTests.cs +++ b/src/Umbraco.Tests/Services/MemberServiceTests.cs @@ -185,6 +185,18 @@ namespace Umbraco.Tests.Services Assert.AreEqual(2, membersInRole.Count()); } + [Test] + public void Cannot_Save_Member_With_Empty_Name() + { + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType(); + ServiceContext.MemberTypeService.Save(memberType); + IMember member = MockedMember.CreateSimpleMember(memberType, string.Empty, "test@test.com", "pass", "test"); + + // Act & Assert + Assert.Throws(() => ServiceContext.MemberService.Save(member)); + + } + [TestCase("MyTestRole1", "test1", StringPropertyMatchType.StartsWith, 1)] [TestCase("MyTestRole1", "test", StringPropertyMatchType.StartsWith, 3)] [TestCase("MyTestRole1", "test1", StringPropertyMatchType.Exact, 1)] diff --git a/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs b/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs index c81d8c3f4e..8dd18b8b50 100644 --- a/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/MemberTypeServiceTests.cs @@ -173,6 +173,16 @@ namespace Umbraco.Tests.Services } } + [Test] + public void Cannot_Save_MemberType_With_Empty_Name() + { + // Arrange + IMemberType memberType = MockedContentTypes.CreateSimpleMemberType("memberTypeAlias", string.Empty); + + // Act & Assert + Assert.Throws(() => ServiceContext.MemberTypeService.Save(memberType)); + } + //[Test] //public void Can_Save_MemberType_Structure_And_Create_A_Member_Based_On_It() //{ diff --git a/src/Umbraco.Tests/Services/UserServiceTests.cs b/src/Umbraco.Tests/Services/UserServiceTests.cs index c7002ce79d..833ed1771b 100644 --- a/src/Umbraco.Tests/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests/Services/UserServiceTests.cs @@ -490,6 +490,43 @@ namespace Umbraco.Tests.Services Assert.IsTrue(result4.AllowedSections.Contains("test")); } + [Test] + public void Cannot_Create_User_With_Empty_Username() + { + // Arrange + var userService = ServiceContext.UserService; + var userType = userService.GetUserTypeByAlias("admin"); + + // Act & Assert + Assert.Throws(() => userService.CreateUserWithIdentity(string.Empty, "john@umbraco.io", userType)); + } + + [Test] + public void Cannot_Save_User_With_Empty_Username() + { + // Arrange + var userService = ServiceContext.UserService; + var userType = userService.GetUserTypeByAlias("admin"); + var user = userService.CreateUserWithIdentity("John Doe", "john@umbraco.io", userType); + user.Username = string.Empty; + + // Act & Assert + Assert.Throws(() => userService.Save(user)); + } + + [Test] + public void Cannot_Save_User_With_Empty_Name() + { + // Arrange + var userService = ServiceContext.UserService; + var userType = userService.GetUserTypeByAlias("admin"); + var user = userService.CreateUserWithIdentity("John Doe", "john@umbraco.io", userType); + user.Name = string.Empty; + + // Act & Assert + Assert.Throws(() => userService.Save(user)); + } + [Test] public void Get_By_Profile_Username() { From 4fa36cd4247ccab1115869dcb3beba3e60be2c09 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 13 Jan 2017 13:16:47 +0100 Subject: [PATCH 225/599] fine tuning --- .../components/umbnodepreview.directive.js | 2 -- .../src/less/components/umb-node-preview.less | 15 ++++++--- .../views/components/umb-node-preview.html | 5 ++- .../contentpicker/contentpicker.controller.js | 32 +++++++++++-------- .../contentpicker/contentpicker.html | 9 +++--- 5 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js index 51e9b265f5..62a8bc766e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js @@ -16,10 +16,8 @@ name: "=", description: "=?", sortable: "=?", - allowEdit: "=?", allowOpen: "=?", allowRemove: "=?", - onEdit: "&?", onOpen: "&?", onRemove: "&?" }, diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less index 952a49f692..b69d4b4b22 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -1,11 +1,11 @@ .umb-node-preview { padding: 5px 15px; margin-bottom: 5px; - border: 2px solid #f8f8f8; + background: @grayLighter; border-radius: 3px; display: flex; align-items: center; - background: @white; + max-width: 66.6%; } .umb-node-preview--sortable { @@ -38,22 +38,28 @@ } .umb-node-preview__description { - font-size: 12px; + font-size: 11px; + line-height: 1.5em; } .umb-node-preview__actions { flex: 0 0 auto; + display: flex; + align-items: center; } .umb-node-preview__action { margin-left: 5px; margin-right: 5px; - font-size: 16px; + font-size: 13px; + font-weight: bold; + opacity: 0.5; } .umb-node-preview__action:hover { color: @blue; text-decoration: none; + opacity: 1; } .umb-node-preview-add { @@ -64,6 +70,7 @@ color: @blue; font-weight: bold; padding: 5px 15px; + max-width: 66.6%; } .umb-node-preview-add:hover { diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html index 4bcd1f39e2..b811ae8eec 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html @@ -5,9 +5,8 @@
    {{ description }}
    - - - + Open + Remove
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index c02ab7a588..ccf9d865bc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -1,7 +1,7 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it -function contentPickerController($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location, $timeout, miniEditorHelper) { +function contentPickerController($scope, dialogService, entityResource, contentResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location, $timeout, miniEditorHelper) { function trim(str, chr) { var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g'); @@ -221,26 +221,32 @@ function contentPickerController($scope, dialogService, entityResource, editorSt //load current data var modelIds = $scope.model.value ? $scope.model.value.split(',') : []; - entityResource.getByIds(modelIds, entityType).then(function (data) { + var nodePromise = (entityType === "Document") ? contentResource.getByIds(modelIds) : entityResource.getByIds(modelIds, entityType); - //Ensure we populate the render model in the same order that the ids were stored! - _.each(modelIds, function (id, i) { - var entity = _.find(data, function (d) { - return d.id == id; - }); - - if (entity) { - entity.icon = iconHelper.convertFromLegacyIcon(entity.icon); - $scope.renderModel.push({ name: entity.name, id: entity.id, icon: entity.icon, path: entity.path }); - } + nodePromise.then(function (data) { + + _.each(modelIds, function (id, i) { + var entity = _.find(data, function (d) { + return d.id == id; + }); + + if (entity) { + entity.icon = iconHelper.convertFromLegacyIcon(entity.icon); + + var url = (entity.urls && entity.urls.length > 0) ? entity.urls[0] : ""; + var path = ($scope.model.config.showPathOnHover) ? entity.path : ""; + + $scope.renderModel.push({ name: entity.name, id: entity.id, icon: entity.icon, path: path, url: url }); + } - }); + }); //everything is loaded, start the watch on the model startWatch(); }); + } angular.module('umbraco').controller("Umbraco.PropertyEditors.ContentPickerController", contentPickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index 760063b94b..abcb7f20c0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -1,20 +1,19 @@
    - +
    + on-open="openMiniEditor(node)">
    From 9b9db23461b18d9b5b971f389a800d0fd41b1a34 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Fri, 13 Jan 2017 13:20:38 +0100 Subject: [PATCH 226/599] Replaces the last static strings with language keys --- .../overlays/insert/insert.controller.js | 17 ++++---- .../views/common/overlays/insert/insert.html | 2 +- .../overlays/querybuilder/querybuilder.html | 2 +- .../templatesections/templatesections.html | 40 +++++++------------ .../src/views/templates/edit.controller.js | 15 +++---- .../src/views/templates/edit.html | 2 +- .../Models/ContentEditing/TemplateDisplay.cs | 3 ++ 7 files changed, 37 insertions(+), 44 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js index 59a00a5797..b99dd289b1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.controller.js @@ -1,16 +1,16 @@ (function () { "use strict"; - function InsertOverlayController($scope) { + function InsertOverlayController($scope, localizationService) { var vm = this; if(!$scope.model.title) { - $scope.model.title = "Insert"; + $scope.model.title = localizationService.localize("template_insert"); } if(!$scope.model.subtitle) { - $scope.model.subtitle = "Choose what to insert into your template"; + $scope.model.subtitle = localizationService.localize("template_insertDesc"); } vm.openMacroPicker = openMacroPicker; @@ -22,7 +22,7 @@ vm.macroPickerOverlay = { view: "macropicker", - title: "Insert macro", + title: localizationService.localize("template_insertMacro"), dialogData: {}, show: true, submit: function(model) { @@ -45,8 +45,8 @@ function openPageFieldOverlay() { vm.pageFieldOverlay = { - title: "Insert value", - description: "Select a value from the currentpage", + title: localizationService.localize("template_insertPageField"), + description: localizationService.localize("template_insertPageFieldDesc"), submitButtonLabel: "Insert", closeButtonlabel: "Cancel", view: "insertfield", @@ -78,7 +78,8 @@ treeAlias: "dictionary", entityType: "dictionary", multiPicker: false, - title: "Insert dictionary item", + title: localizationService.localize("template_insertDictionaryItem"), + description: localizationService.localize("template_insertDictionaryItemDesc"), show: true, select: function(node){ @@ -108,7 +109,7 @@ entityType: "partialView", multiPicker: false, show: true, - title: "Insert partial view", + title: localizationService.localize("template_insertPartialView"), select: function(node){ diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html index 4b92ea86ad..c2926f6acc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html @@ -24,7 +24,7 @@
    -
    +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html index e9be97b7df..678733f1c8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html @@ -22,7 +22,7 @@
    - from + from {{vm.query.source.name}} diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.html index 1a11d6a46e..ee62c3426d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/templatesections/templatesections.html @@ -1,71 +1,59 @@
    -
    +
    -
    Render child template
    +
    - Renders the contents of a child template, by inserting a - @RenderBody() placeholder. +
    -
    +
    -
    Render a named section
    +
    - Renders a named area of a child template, by insert a @RenderSection(name) placeholder. - This renders an area of a child template which is wrapped in a corresponding @section [name]{ ... } definition. +
    - + -
    - Set the name of the section to render in this area of the template -
    +
    - If mandatory, the child template must contain a @section definition, otherwise an error is shown. +
    -
    +
    -
    Define a named section
    +
    - Defines a part of your template as a named section by wrapping it in - a @section { ... }. This can be rendered in a - specific area of the master of this template, by using @RenderSection. +
    - + -
    - Give the section a name -
    - -
    \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 8e2b8337ad..97aa50cbbf 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -220,7 +220,7 @@ view: "macropicker", dialogData: {}, show: true, - title: "Insert macro", + title: localizationService.localize("template_insertMacro"), submit: function (model) { var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); @@ -247,6 +247,7 @@ closeButtonlabel: "Cancel", view: "insertfield", show: true, + title: localizationService.localize("template_insertPageField"), submit: function (model) { insert(model.umbracoField); vm.pageFieldOverlay.show = false; @@ -271,7 +272,7 @@ entityType: "dictionary", multiPicker: false, show: true, - title: "Insert dictionary item", + title: localizationService.localize("template_insertDictionaryItem"), select: function(node){ //crappy hack due to dictionary items not in umbracoNode table var code = "@Umbraco.GetDictionaryValue(\"" + node.name + "\")"; @@ -298,7 +299,7 @@ entityType: "partialView", multiPicker: false, show: true, - title: "Insert Partial view", + title: localizationService.localize("template_insertPartialView"), select: function(node){ //crappy hack due to dictionary items not in umbracoNode table var nodeNameWithPath = node.id.replace(".cshtml", ""); @@ -322,7 +323,7 @@ vm.queryBuilderOverlay = { view: "querybuilder", show: true, - title: "Query for content", + title: localizationService.localize("template_queryBuilder"), submit: function (model) { @@ -356,7 +357,7 @@ vm.sectionsOverlay = { view: "templatesections", - hasMaster: vm.template.masterTemplateAlias, + isMaster: vm.template.isMasterTemplate, submitButtonLabel: "Insert", show: true, submit: function(model) { @@ -401,7 +402,7 @@ vm.masterTemplateOverlay = { view: "itempicker", - title: "Choose master template", + title: localizationService.localize("template_mastertemplate"), availableItems: availableMasterTemplates, show: true, submit: function(model) { @@ -457,7 +458,7 @@ return templateName; } else { - return "No master"; + return "no master"; } } diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index b0e15e8ad7..2e5593894c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -62,7 +62,7 @@
  • Value
  • Partial view
  • Dictionary
  • -
  • Macro
  • +
  • Macro
  • diff --git a/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs index 8e813bd3d3..91c1aefdb0 100644 --- a/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/TemplateDisplay.cs @@ -36,6 +36,9 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "masterTemplateAlias")] public string MasterTemplateAlias { get; set; } + [DataMember(Name = "isMasterTemplate")] + public bool IsMasterTemplate { get; set; } + /// /// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes. /// From b413a6eeaedf77ae631e6e28afd7363414f9bcf3 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Fri, 13 Jan 2017 13:20:49 +0100 Subject: [PATCH 227/599] English translations --- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 2021be1d9d..3a7a0fda7c 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -1032,6 +1032,9 @@ To manage your website, simply open the Umbraco back office and start adding con Sections Insert content area Insert content area placeholder + + Insert + Choose what to insert into your template Dictionary item A dictionary item is a placeholder for a translatable piece of text, which makes it easy to create designs for multilingual websites. @@ -1053,15 +1056,50 @@ To manage your website, simply open the Umbraco back office and start adding con Master template + No master template + + Render child template + + @RenderBody() placeholder. + ]]> + + + + Define a named section + + @section { ... }. This can be rendered in a + specific area of the parent of this template, by using @RenderSection. + ]]> + + + Render a named section + + @RenderSection(name) placeholder. + This renders an area of a child template which is wrapped in a corresponding @section [name]{ ... } definition. + ]]> + + + Section Name + Section is mandatory + + If mandatory, the child template must contain a @section definition, otherwise an error is shown. + + Query builder items returned, in I want + everything from where and - everything + Template From 99f66fe6a30c1542bcd2084e55e411ef2e9f91a1 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Fri, 13 Jan 2017 13:21:37 +0100 Subject: [PATCH 228/599] The danish language files - WIP The danish language has such elements when used to discusssed semi-technical-english terms --- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 86 +++++++++++++++++-- 1 file changed, 79 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index b0452fb631..3c52d7f1a1 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -1004,17 +1004,89 @@ Mange hilsner fra Umbraco robotten Vis prøve Styles + - Rediger skabelon - Indsæt indholdsområde - Indsæt indholdsområdemarkering - Indsæt ordbogselement - Indsæt makro - Indsæt Umbraco sidefelt + Rediger skabelong + + Sektioner + Insert content area + Insert content area placeholder + + Indsæt + Vælg hvad du vil indsætte + + Oversættelse + Indsætter en oversætbar tekst som skifter efter det sprog som websitet vises i. + + Makro + + En makro er et element som kan have forskellige indstillinger når det indsættes. + Det er glimrende som en genbrugelig del af dit design såsom gallerier, formularer + og lister. + + + Value + Displays the value of a named field from the current page, with options to modify the value or fallback to alternative values. + + Partial view + + A partial view is a separate template file which can be rendered inside another + template, it's great for reusing markup or for separating complex templates into separate files. + + Master skabelon - Lynguide til Umbracos skabelontags + Ingen master skabelon + + Indsæt en underliggende skabelon + + @RenderBody() element. + ]]> + + + + Definer en sektion + + @section { ... }. Herefter kan denne sektion flettes ind i + overliggende skabelon ved at indsætte et @RenderSection element. + ]]> + + + Indsæt en sektion + + @RenderSection(name) element. Den underliggende skabelon skal have + defineret en sektion via et @section [name]{ ... } element. + ]]> + + + Sektion navn + Sektionen er oblikatorisk + + + Hvis oblikatorisk, skal under-skabelonen indeholde en @section definition. + + + + Query builder + sider returneret, på + + Returner + alt + fra + filtre + og + + + Skabelon + + Alternativt felt Alternativ tekst From 975535a9e20c5cf6648b09ddda053bf566ee821c Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Fri, 13 Jan 2017 13:33:55 +0100 Subject: [PATCH 229/599] Merge of insert overlay view --- .../views/common/overlays/insert/insert.html | 69 +++++++------------ 1 file changed, 26 insertions(+), 43 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html index b0f6301570..b2e7cc033c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/insert/insert.html @@ -1,63 +1,46 @@
    -
    - -
    Value
    -
    Displays the value of a named field from the current page, with options to modify the value or fallback to alternative values.
    -
    +
    - -
    +
    - +
    - -
    +
    - +
    - -
    +
    -
    - - - - - - - - - - - - - + + + + + + + +
    From 361f5093c7ea1d0a855ff1ccd7d2218e2bcc79e2 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Fri, 13 Jan 2017 14:42:36 +0100 Subject: [PATCH 230/599] align width with input fields --- .../src/less/components/umb-node-preview.less | 2 ++ src/Umbraco.Web.UI.Client/src/less/forms.less | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less index b69d4b4b22..3f51b09154 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -6,6 +6,7 @@ display: flex; align-items: center; max-width: 66.6%; + box-sizing: border-box; } .umb-node-preview--sortable { @@ -71,6 +72,7 @@ font-weight: bold; padding: 5px 15px; max-width: 66.6%; + box-sizing: border-box; } .umb-node-preview-add:hover { diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index d046ac7104..5b6e97ad71 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -190,7 +190,7 @@ input[type="tel"], input[type="color"], .uneditable-input { display: inline-block; - height: @baseLineHeight; + height: 30px; padding: 4px 6px; margin-bottom: @baseLineHeight / 2; font-size: @baseFontSize; @@ -198,6 +198,7 @@ input[type="color"], color: @gray; .border-radius(@inputBorderRadius); vertical-align: middle; + box-sizing: border-box; } input.-full-width-input { From a15bdba5d751c80ab756f1b2991c9b19c642366a Mon Sep 17 00:00:00 2001 From: jamiepollock Date: Sun, 15 Jan 2017 18:26:20 -0500 Subject: [PATCH 231/599] View fix for icons not appearing in the installed package list --- .../src/views/packager/views/installed.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/installed.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/installed.html index 5da3c33a76..a41a7e72c0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/installed.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/installed.html @@ -12,8 +12,8 @@
    - - + +
    From ede342febad2a6777185c64b300c03809490b5e0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Jan 2017 16:38:23 +1100 Subject: [PATCH 232/599] Finds all other instances where a reader would be executed inside another reader's loop and fixes --- src/Umbraco.Core/Cache/CacheKeys.cs | 3 +- src/Umbraco.Core/DatabaseContext.cs | 13 +- .../Cache/ContentTypeCacheRefresher.cs | 10 +- .../umbraco/controls/ContentTypeControl.cs | 3 + .../umbraco/users/EditUser.aspx.cs | 16 +- src/umbraco.cms/businesslogic/Tags/Tag.cs | 2 +- .../propertytype/propertytype.cs | 143 +++++++----------- src/umbraco.cms/businesslogic/web/Document.cs | 23 ++- .../PickerRelationsEventHandler.cs | 28 ++-- 9 files changed, 97 insertions(+), 144 deletions(-) diff --git a/src/Umbraco.Core/Cache/CacheKeys.cs b/src/Umbraco.Core/Cache/CacheKeys.cs index 0c1a202b66..3b9ee9c63a 100644 --- a/src/Umbraco.Core/Cache/CacheKeys.cs +++ b/src/Umbraco.Core/Cache/CacheKeys.cs @@ -64,7 +64,8 @@ namespace Umbraco.Core.Cache [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")] public const string ContentTypePropertiesCacheKey = "ContentType_PropertyTypes_Content:"; - [UmbracoWillObsolete("This cache key is only used for legacy business logic caching, remove in v8")] + [Obsolete("No longer used and will be removed in v8")] + [EditorBrowsable(EditorBrowsableState.Never)] public const string PropertyTypeCacheKey = "UmbracoPropertyTypeCache"; [Obsolete("This is no longer used and will be removed from the codebase in the future")] diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs index a6d6ca9e6e..d4a5a309af 100644 --- a/src/Umbraco.Core/DatabaseContext.cs +++ b/src/Umbraco.Core/DatabaseContext.cs @@ -203,15 +203,10 @@ namespace Umbraco.Core var path = Path.Combine(GlobalSettings.FullpathToRoot, "App_Data", "Umbraco.sdf"); if (File.Exists(path) == false) { - var engine = new SqlCeEngine(connectionString); - engine.CreateDatabase(); - - // SD: Pretty sure this should be in a using clause but i don't want to cause unknown side-effects here - // since it's been like this for quite some time - //using (var engine = new SqlCeEngine(connectionString)) - //{ - // engine.CreateDatabase(); - //} + using (var engine = new SqlCeEngine(connectionString)) + { + engine.CreateDatabase(); + } } Initialize(providerName); diff --git a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs index 246571d479..1340545621 100644 --- a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs @@ -131,9 +131,7 @@ namespace Umbraco.Web.Cache ClearAllIsolatedCacheByEntityType(); ClearAllIsolatedCacheByEntityType(); ClearAllIsolatedCacheByEntityType(); - - //all property type cache - ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.PropertyTypeCacheKey); + //all content type property cache ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.ContentTypePropertiesCacheKey); //all content type cache @@ -266,12 +264,6 @@ namespace Umbraco.Web.Cache /// private static void ClearContentTypeCache(JsonPayload payload) { - //clears the cache for each property type associated with the content type - foreach (var pid in payload.PropertyTypeIds) - { - ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(CacheKeys.PropertyTypeCacheKey + pid); - } - //clears the cache associated with the Content type itself ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.ContentTypeCacheKey, payload.Id)); //clears the cache associated with the content type properties collection diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControl.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControl.cs index 5a8b0b616e..c912db701d 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControl.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControl.cs @@ -2,6 +2,7 @@ using System; using System.Web.UI.HtmlControls; using System.Web.UI.WebControls; using System.Collections; +using System.ComponentModel; using System.IO; using Umbraco.Core.IO; using System.Linq; @@ -11,6 +12,8 @@ namespace umbraco.controls /// /// Summary description for ContentTypeControl. /// + [Obsolete("No longer used, will be removed in v8")] + [EditorBrowsable(EditorBrowsableState.Never)] public class ContentTypeControl : uicontrols.TabView { public event System.EventHandler OnSave; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs index 9734401d95..91a8677c81 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/users/EditUser.aspx.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Configuration.Provider; using System.Globalization; using System.IO; +using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; @@ -320,13 +321,14 @@ namespace umbraco.cms.presentation.user } // Populate dropdowns - foreach (DocumentType dt in DocumentType.GetAllAsList()) - cDocumentType.Items.Add( - new ListItem(dt.Text, dt.Alias) - ); + var allContentTypes = Services.ContentTypeService.GetAllContentTypes().ToList(); + foreach (var dt in allContentTypes) + { + cDocumentType.Items.Add(new ListItem(dt.Name, dt.Alias)); + } // populate fields - ArrayList fields = new ArrayList(); + var fields = new ArrayList(); cDescription.ID = "cDescription"; cCategories.ID = "cCategories"; cExcerpt.ID = "cExcerpt"; @@ -334,9 +336,9 @@ namespace umbraco.cms.presentation.user cCategories.Items.Add(new ListItem(ui.Text("choose"), "")); cExcerpt.Items.Add(new ListItem(ui.Text("choose"), "")); - foreach (PropertyType pt in PropertyType.GetAll()) + foreach (var pt in allContentTypes.SelectMany(x => x.PropertyTypes).OrderBy(x => x.Name)) { - if (!fields.Contains(pt.Alias)) + if (fields.Contains(pt.Alias) == false) { cDescription.Items.Add(new ListItem(string.Format("{0} ({1})", pt.Name, pt.Alias), pt.Alias)); cCategories.Items.Add(new ListItem(string.Format("{0} ({1})", pt.Name, pt.Alias), pt.Alias)); diff --git a/src/umbraco.cms/businesslogic/Tags/Tag.cs b/src/umbraco.cms/businesslogic/Tags/Tag.cs index 8534d02d26..d4e0c90d00 100644 --- a/src/umbraco.cms/businesslogic/Tags/Tag.cs +++ b/src/umbraco.cms/businesslogic/Tags/Tag.cs @@ -351,7 +351,7 @@ namespace umbraco.cms.businesslogic.Tags { Document cnode = new Document(rr.GetInt("nodeid")); - if (cnode != null && cnode.Published) + if (cnode.Published) docs.Add(cnode); } } diff --git a/src/umbraco.cms/businesslogic/propertytype/propertytype.cs b/src/umbraco.cms/businesslogic/propertytype/propertytype.cs index b9622d7c91..e041306ba3 100644 --- a/src/umbraco.cms/businesslogic/propertytype/propertytype.cs +++ b/src/umbraco.cms/businesslogic/propertytype/propertytype.cs @@ -54,32 +54,31 @@ namespace umbraco.cms.businesslogic.propertytype public PropertyType(int id) { - using (var sqlHelper = Application.SqlHelper) - using (IRecordsReader dr = sqlHelper.ExecuteReader( - "Select mandatory, DataTypeId, propertyTypeGroupId, ContentTypeId, sortOrder, alias, name, validationRegExp, description from cmsPropertyType where id=@id", - sqlHelper.CreateParameter("@id", id))) + var found = ApplicationContext.Current.DatabaseContext.Database + .SingleOrDefault( + "Select mandatory, DataTypeId, propertyTypeGroupId, contentTypeId, sortOrder, alias, name, validationRegExp, description from cmsPropertyType where id=@id", + new {id = id}); + + if (found == null) + throw new ArgumentException("Propertytype with id: " + id + " doesnt exist!"); + + _mandatory = found.mandatory; + _id = id; + + if (found.propertyTypeGroupId != null) { - if (!dr.Read()) - throw new ArgumentException("Propertytype with id: " + id + " doesnt exist!"); - - _mandatory = dr.GetBoolean("mandatory"); - _id = id; - - if (!dr.IsNull("propertyTypeGroupId")) - { - _propertyTypeGroup = dr.GetInt("propertyTypeGroupId"); - //TODO: Remove after refactoring! - _tabId = _propertyTypeGroup; - } - - _sortOrder = dr.GetInt("sortOrder"); - _alias = dr.GetString("alias"); - _name = dr.GetString("Name"); - _validationRegExp = dr.GetString("validationRegExp"); - _DataTypeId = dr.GetInt("DataTypeId"); - _contenttypeid = dr.GetInt("contentTypeId"); - _description = dr.GetString("description"); + _propertyTypeGroup = found.propertyTypeGroupId; + //TODO: Remove after refactoring! + _tabId = _propertyTypeGroup; } + + _sortOrder = found.sortOrder; + _alias = found.alias; + _name = found.name; + _validationRegExp = found.validationRegExp; + _DataTypeId = found.DataTypeId; + _contenttypeid = found.contentTypeId; + _description = found.description; } #endregion @@ -92,7 +91,6 @@ namespace umbraco.cms.businesslogic.propertytype set { _DataTypeId = value.Id; - InvalidateCache(); using (var sqlHelper = Application.SqlHelper) sqlHelper.ExecuteNonQuery( "Update cmsPropertyType set DataTypeId = " + value.Id + " where id=" + Id); @@ -119,7 +117,6 @@ namespace umbraco.cms.businesslogic.propertytype { _tabId = value; PropertyTypeGroup = value; - InvalidateCache(); } } @@ -148,7 +145,6 @@ namespace umbraco.cms.businesslogic.propertytype set { _mandatory = value; - InvalidateCache(); using (var sqlHelper = Application.SqlHelper) sqlHelper.ExecuteNonQuery("Update cmsPropertyType set mandatory = @mandatory where id = @id", sqlHelper.CreateParameter("@mandatory", value), @@ -162,7 +158,6 @@ namespace umbraco.cms.businesslogic.propertytype set { _validationRegExp = value; - InvalidateCache(); using (var sqlHelper = Application.SqlHelper) sqlHelper.ExecuteNonQuery("Update cmsPropertyType set validationRegExp = @validationRegExp where id = @id", sqlHelper.CreateParameter("@validationRegExp", value), sqlHelper.CreateParameter("@id", Id)); @@ -199,7 +194,6 @@ namespace umbraco.cms.businesslogic.propertytype set { _description = value; - InvalidateCache(); using (var sqlHelper = Application.SqlHelper) sqlHelper.ExecuteNonQuery("Update cmsPropertyType set description = @description where id = @id", sqlHelper.CreateParameter("@description", value), @@ -213,7 +207,6 @@ namespace umbraco.cms.businesslogic.propertytype set { _sortOrder = value; - InvalidateCache(); using (var sqlHelper = Application.SqlHelper) sqlHelper.ExecuteNonQuery("Update cmsPropertyType set sortOrder = @sortOrder where id = @id", sqlHelper.CreateParameter("@sortOrder", value), @@ -227,7 +220,6 @@ namespace umbraco.cms.businesslogic.propertytype set { _alias = value; - InvalidateCache(); using (var sqlHelper = Application.SqlHelper) sqlHelper.ExecuteNonQuery("Update cmsPropertyType set alias = @alias where id= @id", sqlHelper.CreateParameter("@alias", Casing.SafeAliasWithForcingCheck(_alias)), @@ -264,7 +256,6 @@ namespace umbraco.cms.businesslogic.propertytype set { _name = value; - InvalidateCache(); using (var sqlHelper = Application.SqlHelper) sqlHelper.ExecuteNonQuery( "UPDATE cmsPropertyType SET name=@name WHERE id=@id", @@ -331,17 +322,17 @@ namespace umbraco.cms.businesslogic.propertytype public static IEnumerable GetPropertyTypes() { var result = new List(); - using (var sqlHelper = Application.SqlHelper) - using (IRecordsReader dr = - sqlHelper.ExecuteReader("select id from cmsPropertyType order by Name")) + + var propertyTypeIds = ApplicationContext.Current.DatabaseContext.Database.Fetch( + "select id from cmsPropertyType order by Name"); + + foreach (var propertyTypeId in propertyTypeIds) { - while (dr.Read()) - { - PropertyType pt = GetPropertyType(dr.GetInt("id")); - if (pt != null) - result.Add(pt); - } + PropertyType pt = GetPropertyType(propertyTypeId); + if (pt != null) + result.Add(pt); } + return result; } @@ -353,18 +344,17 @@ namespace umbraco.cms.businesslogic.propertytype public static IEnumerable GetPropertyTypesByGroup(int groupId) { var result = new List(); - using (var sqlHelper = Application.SqlHelper) - using (IRecordsReader dr = - sqlHelper.ExecuteReader("SELECT id FROM cmsPropertyType WHERE propertyTypeGroupId = @groupId order by SortOrder", - sqlHelper.CreateParameter("@groupId", groupId))) + + var propertyTypeIds = ApplicationContext.Current.DatabaseContext.Database.Fetch( + "SELECT id FROM cmsPropertyType WHERE propertyTypeGroupId = @groupId order by SortOrder", new {groupId = groupId}); + + foreach (var propertyTypeId in propertyTypeIds) { - while (dr.Read()) - { - PropertyType pt = GetPropertyType(dr.GetInt("id")); - if (pt != null) - result.Add(pt); - } + PropertyType pt = GetPropertyType(propertyTypeId); + if (pt != null) + result.Add(pt); } + return result; } @@ -376,20 +366,18 @@ namespace umbraco.cms.businesslogic.propertytype public static IEnumerable GetByDataTypeDefinition(int dataTypeDefId) { var result = new List(); - using (var sqlHelper = Application.SqlHelper) - using (IRecordsReader dr = - sqlHelper.ExecuteReader( - "select id, Name from cmsPropertyType where dataTypeId=@dataTypeId order by Name", - sqlHelper.CreateParameter("@dataTypeId", dataTypeDefId))) + + var propertyTypeIds = ApplicationContext.Current.DatabaseContext.Database.Fetch( + "select id from cmsPropertyType where dataTypeId=@dataTypeId order by Name", new {dataTypeId = dataTypeDefId}); + + foreach (var propertyTypeId in propertyTypeIds) { - while (dr.Read()) - { - PropertyType pt = GetPropertyType(dr.GetInt("id")); - if (pt != null) - result.Add(pt); - } + PropertyType pt = GetPropertyType(propertyTypeId); + if (pt != null) + result.Add(pt); } - return result.ToList(); + + return result; } public void delete() @@ -411,7 +399,6 @@ namespace umbraco.cms.businesslogic.propertytype // delete cache from either master (via tabid) or current contentype FlushCacheBasedOnTab(); - InvalidateCache(); } public void FlushCacheBasedOnTab() @@ -478,8 +465,6 @@ namespace umbraco.cms.businesslogic.propertytype protected virtual void FlushCache() { - // clear local cache - ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(GetCacheKey(Id)); // clear cache in contentype ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(CacheKeys.ContentTypePropertiesCacheKey + _contenttypeid); @@ -496,31 +481,9 @@ namespace umbraco.cms.businesslogic.propertytype public static PropertyType GetPropertyType(int id) { - return ApplicationContext.Current.ApplicationCache.RuntimeCache.GetCacheItem( - GetCacheKey(id), - timeout: TimeSpan.FromMinutes(30), - getCacheItem: () => - { - try - { - return new PropertyType(id); - } - catch - { - return null; - } - }); - } - - private void InvalidateCache() - { - ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(GetCacheKey(Id)); - } - - private static string GetCacheKey(int id) - { - return CacheKeys.PropertyTypeCacheKey + id; + return new PropertyType(id); } + #endregion } diff --git a/src/umbraco.cms/businesslogic/web/Document.cs b/src/umbraco.cms/businesslogic/web/Document.cs index 70775f5c71..bd21517d80 100644 --- a/src/umbraco.cms/businesslogic/web/Document.cs +++ b/src/umbraco.cms/businesslogic/web/Document.cs @@ -433,21 +433,20 @@ namespace umbraco.cms.businesslogic.web { XmlDocument xd = new XmlDocument(); - using (var sqlHelper = Application.SqlHelper) - using (IRecordsReader dr = sqlHelper.ExecuteReader("select nodeId from cmsDocument")) + var nodeIds = ApplicationContext.Current.DatabaseContext.Database.Fetch( + "select nodeId from cmsDocument"); + + foreach (var nodeId in nodeIds) { - while (dr.Read()) + try { - try - { - new Document(dr.GetInt("nodeId")).SaveXmlPreview(xd); - } - catch (Exception ee) - { - LogHelper.Error("Error generating preview xml", ee); - } + new Document(nodeId).SaveXmlPreview(xd); } - } + catch (Exception ee) + { + LogHelper.Error("Error generating preview xml", ee); + } + } } /// diff --git a/src/umbraco.editorControls/PickerRelations/PickerRelationsEventHandler.cs b/src/umbraco.editorControls/PickerRelations/PickerRelationsEventHandler.cs index 13f786359b..844d9920e0 100644 --- a/src/umbraco.editorControls/PickerRelations/PickerRelationsEventHandler.cs +++ b/src/umbraco.editorControls/PickerRelations/PickerRelationsEventHandler.cs @@ -2,8 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; - -using umbraco.BusinessLogic; // ApplicationBase +// ApplicationBase using umbraco.businesslogic; using umbraco.cms.businesslogic; // SaveEventArgs using umbraco.cms.businesslogic.media; // Media @@ -12,6 +11,8 @@ using umbraco.cms.businesslogic.web; // Documentusing umbraco.cms.businesslogic. using umbraco.cms.businesslogic.property; using umbraco.cms.businesslogic.relation; using umbraco.DataLayer; +using Umbraco.Core; +using Application = umbraco.BusinessLogic.Application; namespace umbraco.editorControls.PickerRelations { @@ -212,7 +213,7 @@ namespace umbraco.editorControls.PickerRelations private static void DeleteRelations(RelationType relationType, int contentNodeId, bool reverseIndexing, string instanceIdentifier) { //if relationType is bi-directional or a reverse index then we can't get at the relations via the API, so using SQL - string getRelationsSql = "SELECT id FROM umbracoRelation WHERE relType = " + relationType.Id.ToString() + " AND "; + string getRelationsSql = "SELECT id FROM umbracoRelation WHERE relType = " + relationType.Id + " AND "; if (reverseIndexing || relationType.Dual) { @@ -229,19 +230,16 @@ namespace umbraco.editorControls.PickerRelations getRelationsSql += " AND comment = '" + instanceIdentifier + "'"; - using (var sqlHelper = Application.SqlHelper) - using (IRecordsReader relations = sqlHelper.ExecuteReader(getRelationsSql)) - { - //clear data - Relation relation; - while (relations.Read()) - { - relation = new Relation(relations.GetInt("id")); + var relationIds = ApplicationContext.Current.DatabaseContext.Database.Fetch( + getRelationsSql); + foreach (var relationId in relationIds) + { + var relation = new Relation(relationId); - // TODO: [HR] check to see if an instance identifier is used - relation.Delete(); - } - } + // TODO: [HR] check to see if an instance identifier is used + relation.Delete(); + } + } /// From dfe3798aab98295247f3168725c44e099d18e626 Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Mon, 16 Jan 2017 09:59:00 +0100 Subject: [PATCH 233/599] More merge --- src/Umbraco.Core/Constants-Applications.cs | 8 +++++++- src/Umbraco.Core/Services/MediaService.cs | 18 +----------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Core/Constants-Applications.cs b/src/Umbraco.Core/Constants-Applications.cs index fb1fb42044..fd9476a8ab 100644 --- a/src/Umbraco.Core/Constants-Applications.cs +++ b/src/Umbraco.Core/Constants-Applications.cs @@ -112,9 +112,15 @@ public const string Languages = "languages"; + public const string PartialViews = "partialViews"; + + public const string PartialViewMacros = "partialViewMacros"; + + public const string Scripts = "scripts"; + //TODO: Fill in the rest! } - } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index 357f23ce19..7c583dfe28 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -1378,23 +1378,7 @@ namespace Umbraco.Core.Services _mediaFileSystem.GenerateThumbnails(filestream, filepath, propertyType); } } - - /// - /// Hack: This is used to fix some data if an entity's properties are invalid/corrupt - /// - /// - private void QuickUpdate(IMedia media) - { - if (media == null) throw new ArgumentNullException("media"); - if (media.HasIdentity == false) throw new InvalidOperationException("Cannot update an entity without an Identity"); - - var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateMediaRepository(uow)) - { - repository.AddOrUpdate(media); - uow.Commit(); - } - } + #region Event Handlers From d2837b9649438f170244df2957516e5a3e878a93 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Jan 2017 20:03:59 +1100 Subject: [PATCH 234/599] Initial commit of all UDI related things and tests, now to import the remaining items tagged for Core --- src/Umbraco.Core/Constants-Conventions.cs | 131 +++++++- src/Umbraco.Core/Deploy/ArtifactDependency.cs | 40 +++ .../Deploy/ArtifactDependencyCollection.cs | 69 ++++ .../Deploy/ArtifactDependencyMode.cs | 18 ++ .../Deploy/ArtifactDeployState.cs | 50 +++ .../ArtifactDeployStateOfTArtifactTEntity.cs | 48 +++ src/Umbraco.Core/Deploy/Difference.cs | 28 ++ src/Umbraco.Core/Deploy/Direction.cs | 8 + src/Umbraco.Core/Deploy/IArtifact.cs | 8 + src/Umbraco.Core/Deploy/IArtifactSignature.cs | 41 +++ src/Umbraco.Core/Deploy/IDeployContext.cs | 42 +++ src/Umbraco.Core/Deploy/IFileSource.cs | 60 ++++ src/Umbraco.Core/Deploy/IServiceConnector.cs | 86 +++++ src/Umbraco.Core/GuidUdi.cs | 83 +++++ src/Umbraco.Core/NamedUdiRange.cs | 34 ++ .../Serialization/UdiJsonConverter.cs | 47 +++ src/Umbraco.Core/StringUdi.cs | 71 +++++ src/Umbraco.Core/Udi.cs | 266 ++++++++++++++++ src/Umbraco.Core/UdiDefinitionAttribute.cs | 20 ++ src/Umbraco.Core/UdiGetterExtensions.cs | 300 ++++++++++++++++++ src/Umbraco.Core/UdiRange.cs | 104 ++++++ src/Umbraco.Core/UdiType.cs | 12 + src/Umbraco.Core/Umbraco.Core.csproj | 21 ++ src/Umbraco.Tests/UdiTests.cs | 155 +++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + 25 files changed, 1742 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Core/Deploy/ArtifactDependency.cs create mode 100644 src/Umbraco.Core/Deploy/ArtifactDependencyCollection.cs create mode 100644 src/Umbraco.Core/Deploy/ArtifactDependencyMode.cs create mode 100644 src/Umbraco.Core/Deploy/ArtifactDeployState.cs create mode 100644 src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs create mode 100644 src/Umbraco.Core/Deploy/Difference.cs create mode 100644 src/Umbraco.Core/Deploy/Direction.cs create mode 100644 src/Umbraco.Core/Deploy/IArtifact.cs create mode 100644 src/Umbraco.Core/Deploy/IArtifactSignature.cs create mode 100644 src/Umbraco.Core/Deploy/IDeployContext.cs create mode 100644 src/Umbraco.Core/Deploy/IFileSource.cs create mode 100644 src/Umbraco.Core/Deploy/IServiceConnector.cs create mode 100644 src/Umbraco.Core/GuidUdi.cs create mode 100644 src/Umbraco.Core/NamedUdiRange.cs create mode 100644 src/Umbraco.Core/Serialization/UdiJsonConverter.cs create mode 100644 src/Umbraco.Core/StringUdi.cs create mode 100644 src/Umbraco.Core/Udi.cs create mode 100644 src/Umbraco.Core/UdiDefinitionAttribute.cs create mode 100644 src/Umbraco.Core/UdiGetterExtensions.cs create mode 100644 src/Umbraco.Core/UdiRange.cs create mode 100644 src/Umbraco.Core/UdiType.cs create mode 100644 src/Umbraco.Tests/UdiTests.cs diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index d7f4576137..69e0f7c68e 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -4,7 +4,136 @@ using Umbraco.Core.Models; namespace Umbraco.Core { - public static partial class Constants + /// + /// Contains the valid selector values. + /// + internal static class DeploySelector + { + public const string This = "this"; + public const string ThisAndChildren = "this-and-children"; + public const string ThisAndDescendants = "this-and-descendants"; + public const string ChildrenOfThis = "children"; + public const string DescendantsOfThis = "descendants"; + } + + /// + /// Defines well-known entity types. + /// + /// Well-known entity types are those that Deploy already knows about, + /// but entity types are strings and so can be extended beyond what is defined here. + internal static class DeployEntityType + { + // guid entity types + + public const string AnyGuid = "any-guid"; // that one is for tests + + public const string Document = "document"; + public const string Media = "media"; + public const string Member = "member"; + + public const string DictionaryItem = "dictionary-item"; + public const string Macro = "macro"; + public const string Template = "template"; + + public const string DocumentType = "document-type"; + public const string DocumentTypeContainer = "document-type-container"; + public const string MediaType = "media-type"; + public const string MediaTypeContainer = "media-type-container"; + public const string DataType = "data-type"; + public const string DataTypeContainer = "data-type-container"; + public const string MemberType = "member-type"; + public const string MemberGroup = "member-group"; + + public const string RelationType = "relation-type"; + + // string entity types + + public const string AnyString = "any-string"; // that one is for tests + + public const string MediaFile = "media-file"; + public const string TemplateFile = "template-file"; + public const string Script = "script"; + public const string Stylesheet = "stylesheet"; + public const string PartialView = "partial-view"; + public const string PartialViewMacro = "partial-view-macro"; + public const string Xslt = "xslt"; + + public static string FromUmbracoObjectType(UmbracoObjectTypes umbracoObjectType) + { + switch (umbracoObjectType) + { + case UmbracoObjectTypes.Document: + return Document; + case UmbracoObjectTypes.Media: + return Media; + case UmbracoObjectTypes.Member: + return Member; + case UmbracoObjectTypes.Template: + return Template; + case UmbracoObjectTypes.DocumentType: + return DocumentType; + case UmbracoObjectTypes.DocumentTypeContainer: + return DocumentTypeContainer; + case UmbracoObjectTypes.MediaType: + return MediaType; + case UmbracoObjectTypes.MediaTypeContainer: + return MediaTypeContainer; + case UmbracoObjectTypes.DataType: + return DataType; + case UmbracoObjectTypes.DataTypeContainer: + return DataTypeContainer; + case UmbracoObjectTypes.MemberType: + return MemberType; + case UmbracoObjectTypes.MemberGroup: + return MemberGroup; + case UmbracoObjectTypes.Stylesheet: + return Stylesheet; + case UmbracoObjectTypes.RelationType: + return RelationType; + } + throw new NotSupportedException(string.Format("UmbracoObjectType \"{0}\" does not have a matching EntityType.", umbracoObjectType)); + } + + public static UmbracoObjectTypes ToUmbracoObjectType(string entityType) + { + switch (entityType) + { + case Document: + return UmbracoObjectTypes.Document; + case Media: + return UmbracoObjectTypes.Media; + case Member: + return UmbracoObjectTypes.Member; + case Template: + return UmbracoObjectTypes.Template; + case DocumentType: + return UmbracoObjectTypes.DocumentType; + case DocumentTypeContainer: + return UmbracoObjectTypes.DocumentTypeContainer; + case MediaType: + return UmbracoObjectTypes.MediaType; + case MediaTypeContainer: + return UmbracoObjectTypes.MediaTypeContainer; + case DataType: + return UmbracoObjectTypes.DataType; + case DataTypeContainer: + return UmbracoObjectTypes.DataTypeContainer; + case MemberType: + return UmbracoObjectTypes.MemberType; + case MemberGroup: + return UmbracoObjectTypes.MemberGroup; + case Stylesheet: + return UmbracoObjectTypes.Stylesheet; + case RelationType: + return UmbracoObjectTypes.RelationType; + } + throw new NotSupportedException( + string.Format("EntityType \"{0}\" does not have a matching UmbracoObjectType.", entityType)); + } + } + + + public static partial class Constants { /// /// Defines the identifiers for property-type alias conventions that are used within the Umbraco core. diff --git a/src/Umbraco.Core/Deploy/ArtifactDependency.cs b/src/Umbraco.Core/Deploy/ArtifactDependency.cs new file mode 100644 index 0000000000..41e349d636 --- /dev/null +++ b/src/Umbraco.Core/Deploy/ArtifactDependency.cs @@ -0,0 +1,40 @@ +namespace Umbraco.Core.Deploy +{ + /// + /// Represents an artifact dependency. + /// + /// + /// Dependencies have an order property which indicates whether it must be respected when ordering artifacts. + /// Dependencies have a mode which can be Match or Exist depending on whether the checksum should match. + /// + public class ArtifactDependency + { + /// + /// Initializes a new instance of the ArtifactDependency class with an entity identifier and a mode. + /// + /// The entity identifier of the artifact that is a dependency. + /// A value indicating whether the dependency is ordering. + /// The dependency mode. + public ArtifactDependency(Udi udi, bool ordering, ArtifactDependencyMode mode) + { + Udi = udi; + Ordering = ordering; + Mode = mode; + } + + /// + /// Gets the entity id of the artifact that is a dependency. + /// + public Udi Udi { get; private set; } + + /// + /// Gets a value indicating whether the dependency is ordering. + /// + public bool Ordering { get; private set; } + + /// + /// Gets the dependency mode. + /// + public ArtifactDependencyMode Mode { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/ArtifactDependencyCollection.cs b/src/Umbraco.Core/Deploy/ArtifactDependencyCollection.cs new file mode 100644 index 0000000000..fd036f4628 --- /dev/null +++ b/src/Umbraco.Core/Deploy/ArtifactDependencyCollection.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Umbraco.Core.Deploy +{ + /// + /// Represents a collection of distinct . + /// + /// The collection cannot contain duplicates and modes are properly managed. + public class ArtifactDependencyCollection : ICollection + { + private readonly Dictionary _dependencies + = new Dictionary(); + + public IEnumerator GetEnumerator() + { + return _dependencies.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void Add(ArtifactDependency item) + { + if (_dependencies.ContainsKey(item.Udi)) + { + var exist = _dependencies[item.Udi]; + if (item.Mode == ArtifactDependencyMode.Exist || item.Mode == exist.Mode) + return; + } + + _dependencies[item.Udi] = item; + } + + public void Clear() + { + _dependencies.Clear(); + } + + public bool Contains(ArtifactDependency item) + { + return _dependencies.ContainsKey(item.Udi) && + (_dependencies[item.Udi].Mode == item.Mode || _dependencies[item.Udi].Mode == ArtifactDependencyMode.Match); + } + + public void CopyTo(ArtifactDependency[] array, int arrayIndex) + { + _dependencies.Values.CopyTo(array, arrayIndex); + } + + public bool Remove(ArtifactDependency item) + { + throw new NotSupportedException(); + } + + public int Count + { + get { return _dependencies.Count; } + } + + public bool IsReadOnly + { + get { return false; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/ArtifactDependencyMode.cs b/src/Umbraco.Core/Deploy/ArtifactDependencyMode.cs new file mode 100644 index 0000000000..7ee5c2f220 --- /dev/null +++ b/src/Umbraco.Core/Deploy/ArtifactDependencyMode.cs @@ -0,0 +1,18 @@ +namespace Umbraco.Core.Deploy +{ + /// + /// Indicates the mode of the dependency. + /// + public enum ArtifactDependencyMode + { + /// + /// The dependency must match exactly. + /// + Match, + + /// + /// The dependency must exist. + /// + Exist + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/ArtifactDeployState.cs b/src/Umbraco.Core/Deploy/ArtifactDeployState.cs new file mode 100644 index 0000000000..3723e483cb --- /dev/null +++ b/src/Umbraco.Core/Deploy/ArtifactDeployState.cs @@ -0,0 +1,50 @@ +namespace Umbraco.Core.Deploy +{ + /// + /// Represent the state of an artifact being deployed. + /// + public abstract class ArtifactDeployState + { + /// + /// Creates a new instance of the class from an artifact and an entity. + /// + /// The type of the artifact. + /// The type of the entity. + /// The artifact. + /// The entity. + /// The service connector deploying the artifact. + /// The next pass number. + /// A deploying artifact. + public static ArtifactDeployState Create(TArtifact art, TEntity entity, IServiceConnector connector, int nextPass) + where TArtifact : IArtifact + { + return new ArtifactDeployState(art, entity, connector, nextPass); + } + + /// + /// Gets the artifact. + /// + public IArtifact Artifact + { + get { return GetArtifactAsIArtifact(); } + } + + /// + /// Gets the artifact as an . + /// + /// The artifact, as an . + /// This is because classes that inherit from this class cannot override the Artifact property + /// with a property that specializes the return type, and so they need to 'new' the property. + protected abstract IArtifact GetArtifactAsIArtifact(); + + /// + /// Gets or sets the service connector in charge of deploying the artifact. + /// + public IServiceConnector Connector { get; set; } + + /// + /// Gets or sets the next pass number. + /// + public int NextPass { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs b/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs new file mode 100644 index 0000000000..b4d2be23cd --- /dev/null +++ b/src/Umbraco.Core/Deploy/ArtifactDeployStateOfTArtifactTEntity.cs @@ -0,0 +1,48 @@ +namespace Umbraco.Core.Deploy +{ + /// + /// Represent the state of an artifact being deployed. + /// + /// The type of the artifact. + /// The type of the entity. + public class ArtifactDeployState : ArtifactDeployState + where TArtifact : IArtifact + { + /// + /// Initializes a new instance of the class. + /// + public ArtifactDeployState() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The artifact. + /// The entity. + /// The service connector deploying the artifact. + /// The next pass number. + public ArtifactDeployState(TArtifact art, TEntity entity, IServiceConnector connector, int nextPass) + { + Artifact = art; + Entity = entity; + Connector = connector; + NextPass = nextPass; + } + + /// + /// Gets or sets the artifact. + /// + public new TArtifact Artifact { get; set; } + + /// + /// Gets or sets the entity. + /// + public TEntity Entity { get; set; } + + /// + protected sealed override IArtifact GetArtifactAsIArtifact() + { + return Artifact; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/Difference.cs b/src/Umbraco.Core/Deploy/Difference.cs new file mode 100644 index 0000000000..90329afdd6 --- /dev/null +++ b/src/Umbraco.Core/Deploy/Difference.cs @@ -0,0 +1,28 @@ +namespace Umbraco.Core.Deploy +{ + public class Difference + { + public Difference(string title, string text = null, string category = null) + { + Title = title; + Text = text; + Category = category; + } + + public string Title { get; set; } + public string Text { get; set; } + public string Category { get; set; } + + public override string ToString() + { + var s = Title; + if (!string.IsNullOrWhiteSpace(Category)) s += string.Format("[{0}]", Category); + if (!string.IsNullOrWhiteSpace(Text)) + { + if (s.Length > 0) s += ":"; + s += Text; + } + return s; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/Direction.cs b/src/Umbraco.Core/Deploy/Direction.cs new file mode 100644 index 0000000000..b0e1c1dc0a --- /dev/null +++ b/src/Umbraco.Core/Deploy/Direction.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Core.Deploy +{ + public enum Direction + { + ToArtifact, + FromArtifact + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/IArtifact.cs b/src/Umbraco.Core/Deploy/IArtifact.cs new file mode 100644 index 0000000000..f39b7b433f --- /dev/null +++ b/src/Umbraco.Core/Deploy/IArtifact.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Core.Deploy +{ + /// + /// Represents an artifact ie an object that can be transfered between environments. + /// + public interface IArtifact : IArtifactSignature + { } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/IArtifactSignature.cs b/src/Umbraco.Core/Deploy/IArtifactSignature.cs new file mode 100644 index 0000000000..83b112586b --- /dev/null +++ b/src/Umbraco.Core/Deploy/IArtifactSignature.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Deploy +{ + /// + /// Represents the signature of an artifact. + /// + public interface IArtifactSignature + { + /// + /// Gets the entity unique identifier of this artifact. + /// + /// + /// The project identifier is independent from the state of the artifact, its data + /// values, dependencies, anything. It never changes and fully identifies the artifact. + /// What an entity uses as a unique identifier will influence what we can transfer + /// between environments. Eg content type "Foo" on one environment is not necessarily the + /// same as "Foo" on another environment, if guids are used as unique identifiers. What is + /// used should be documented for each entity, along with the consequences of the choice. + /// + Udi Udi { get; } + + /// + /// Gets the checksum of this artifact. + /// + /// + /// The checksum depends on the artifact's properties, and on the identifiers of all its dependencies, + /// but not on their checksums. So the checksum changes when any of the artifact's properties changes, + /// or when the list of dependencies changes. But not if one of these dependencies change. + /// It is assumed that checksum collisions cannot happen ie that no two different artifact's + /// states will ever produce the same checksum, so that if two artifacts have the same checksum then + /// they are identical. + /// + string Checksum { get; } + + /// + /// Gets the dependencies of this artifact. + /// + IEnumerable Dependencies { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/IDeployContext.cs b/src/Umbraco.Core/Deploy/IDeployContext.cs new file mode 100644 index 0000000000..7d4066e015 --- /dev/null +++ b/src/Umbraco.Core/Deploy/IDeployContext.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Deploy +{ + /// + /// Represents a deployment context. + /// + public interface IDeployContext + { + /// + /// Gets the unique identifier of the deployment. + /// + Guid SessionId { get; } + + /// + /// Gets the file source. + /// + /// The file source is used to obtain files from the source environment. + IFileSource FileSource { get; } + + /// + /// Gets the next number in a numerical sequence. + /// + /// The next sequence number. + /// Can be used to uniquely number things during a deployment. + int NextSeq(); + + /// + /// Gets items. + /// + IDictionary Items { get; } + + /// + /// Gets item. + /// + /// The type of the item. + /// The key of the item. + /// The item with the specified key and type, if any, else null. + T Item(string key) where T : class; + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/IFileSource.cs b/src/Umbraco.Core/Deploy/IFileSource.cs new file mode 100644 index 0000000000..3baa9069f0 --- /dev/null +++ b/src/Umbraco.Core/Deploy/IFileSource.cs @@ -0,0 +1,60 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Umbraco.Core.Deploy +{ + /// + /// Represents a file source, ie a mean for a target environment involved in a + /// deployment to obtain the content of files being deployed. + /// + public interface IFileSource + { + /// + /// Gets the content of a file as a stream. + /// + /// A file entity identifier. + /// A stream with read access to the file content. + /// + /// Returns null if no content could be read. + /// The caller should ensure that the stream is properly closed/disposed. + /// + Stream GetFileStream(StringUdi udi); + + /// + /// Gets the content of a file as a stream. + /// + /// A file entity identifier. + /// A cancellation token. + /// A stream with read access to the file content. + /// + /// Returns null if no content could be read. + /// The caller should ensure that the stream is properly closed/disposed. + /// + Task GetFileStreamAsync(StringUdi udi, CancellationToken token); + + /// + /// Gets the content of a file as a string. + /// + /// A file entity identifier. + /// A string containing the file content. + /// Returns null if no content could be read. + string GetFileContent(StringUdi udi); + + /// + /// Gets the content of a file as a string. + /// + /// A file entity identifier. + /// A cancellation token. + /// A string containing the file content. + /// Returns null if no content could be read. + Task GetFileContentAsync(StringUdi udi, CancellationToken token); + + ///// + ///// Gets the content of a file as a bytes array. + ///// + ///// A file entity identifier. + ///// A byte array containing the file content. + //byte[] GetFileBytes(StringUdi Udi); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/IServiceConnector.cs b/src/Umbraco.Core/Deploy/IServiceConnector.cs new file mode 100644 index 0000000000..d1c0bf61de --- /dev/null +++ b/src/Umbraco.Core/Deploy/IServiceConnector.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Umbraco.Core; + +namespace Umbraco.Core.Deploy +{ + /// + /// Connects to an Umbraco service. + /// + public interface IServiceConnector + { + /// + /// Gets an artifact. + /// + /// The entity identifier of the artifact. + /// The corresponding artifact, or null. + IArtifact GetArtifact(Udi udi); + + /// + /// Gets an artifact. + /// + /// The entity. + /// The corresponding artifact. + IArtifact GetArtifact(object entity); + + /// + /// Initializes processing for an artifact. + /// + /// The artifact. + /// The deploy context. + /// The mapped artifact. + ArtifactDeployState ProcessInit(IArtifact art, IDeployContext context); + + /// + /// Processes an artifact. + /// + /// The mapped artifact. + /// The deploy context. + /// The processing pass number. + void Process(ArtifactDeployState dart, IDeployContext context, int pass); + + /// + /// Explodes a range into udis. + /// + /// The range. + /// The list of udis where to add the new udis. + /// Also, it's cool to have a method named Explode. Kaboom! + void Explode(UdiRange range, List udis); + + /// + /// Gets a named range for a specified udi and selector. + /// + /// The udi. + /// The selector. + /// The named range for the specified udi and selector. + NamedUdiRange GetRange(Udi udi, string selector); + + /// + /// Gets a named range for specified entity type, identifier and selector. + /// + /// The entity type. + /// The identifier. + /// The selector. + /// The named range for the specified entity type, identifier and selector. + /// + /// This is temporary. At least we thought it would be, in sept. 2016. What day is it now? + /// At the moment our UI has a hard time returning proper udis, mainly because Core's tree do + /// not manage guids but only ints... so we have to provide a way to support it. The string id here + /// can be either a real string (for string udis) or an "integer as a string", using the value "-1" to + /// indicate the "root" i.e. an open udi. + /// + NamedUdiRange GetRange(string entityType, string sid, string selector); + + /// + /// Compares two artifacts. + /// + /// The first artifact. + /// The second artifact. + /// A collection of differences to append to, if not null. + /// A boolean value indicating whether the artifacts are identical. + /// ServiceConnectorBase{TArtifact} provides a very basic default implementation. + bool Compare(IArtifact art1, IArtifact art2, ICollection differences = null); + } + +} diff --git a/src/Umbraco.Core/GuidUdi.cs b/src/Umbraco.Core/GuidUdi.cs new file mode 100644 index 0000000000..fabdf46c93 --- /dev/null +++ b/src/Umbraco.Core/GuidUdi.cs @@ -0,0 +1,83 @@ +using System; + +namespace Umbraco.Core +{ + /// + /// Represents a guid-based entity identifier. + /// + public class GuidUdi : Udi + { + /// + /// The guid part of the identifier. + /// + public Guid Guid { get; private set; } + + /// + /// Initializes a new instance of the GuidUdi class with an entity type and a guid. + /// + /// The entity type part of the udi. + /// The guid part of the udi. + public GuidUdi(string entityType, Guid guid) + : base(entityType, "umb://" + entityType + "/" + guid.ToString("N")) + { + Guid = guid; + } + + /// + /// Initializes a new instance of the GuidUdi class with an uri value. + /// + /// The uri value of the udi. + public GuidUdi(Uri uriValue) + : base(uriValue) + { + Guid = Guid.Parse(uriValue.AbsolutePath.TrimStart('/')); + } + + /// + /// Converts the string representation of an entity identifier into the equivalent GuidUdi instance. + /// + /// The string to convert. + /// A GuidUdi instance that contains the value that was parsed. + public new static GuidUdi Parse(string s) + { + var udi = Udi.Parse(s); + if (!(udi is GuidUdi)) + throw new FormatException("String \"" + s + "\" is not a guid entity id."); + return (GuidUdi)udi; + } + + public static bool TryParse(string s, out GuidUdi udi) + { + Udi tmp; + udi = null; + if (!TryParse(s, out tmp)) return false; + udi = tmp as GuidUdi; + return udi != null; + } + + public override bool Equals(object obj) + { + var other = obj as GuidUdi; + if (other == null) return false; + return EntityType == other.EntityType && Guid == other.Guid; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + /// + public override bool IsRoot + { + get { return Guid == Guid.Empty; } + } + + /// + public GuidUdi EnsureClosed() + { + base.EnsureNotRoot(); + return this; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/NamedUdiRange.cs b/src/Umbraco.Core/NamedUdiRange.cs new file mode 100644 index 0000000000..4e81631e03 --- /dev/null +++ b/src/Umbraco.Core/NamedUdiRange.cs @@ -0,0 +1,34 @@ +namespace Umbraco.Core +{ + /// + /// Represents a complemented with a name. + /// + public class NamedUdiRange : UdiRange + { + /// + /// Initializes a new instance of the class with a and an optional selector. + /// + /// A . + /// An optional selector. + public NamedUdiRange(Udi udi, string selector = DeploySelector.This) + : base(udi, selector) + { } + + /// + /// Initializes a new instance of the class with a , a name, and an optional selector. + /// + /// A . + /// A name. + /// An optional selector. + public NamedUdiRange(Udi udi, string name, string selector = DeploySelector.This) + : base(udi, selector) + { + Name = name; + } + + /// + /// Gets or sets the name of the range. + /// + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Serialization/UdiJsonConverter.cs b/src/Umbraco.Core/Serialization/UdiJsonConverter.cs new file mode 100644 index 0000000000..7d2db92e81 --- /dev/null +++ b/src/Umbraco.Core/Serialization/UdiJsonConverter.cs @@ -0,0 +1,47 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Umbraco.Core.Serialization +{ + public class UdiRangeJsonConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return typeof(UdiRange).IsAssignableFrom(objectType); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value.ToString()); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var jo = JToken.ReadFrom(reader); + var val = jo.ToObject(); + return val == null ? null : UdiRange.Parse(val); + } + } + + + public class UdiJsonConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return typeof(Udi).IsAssignableFrom(objectType); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value.ToString()); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var jo = JToken.ReadFrom(reader); + var val = jo.ToObject(); + return val == null ? null : Udi.Parse(val); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/StringUdi.cs b/src/Umbraco.Core/StringUdi.cs new file mode 100644 index 0000000000..2fcb53f263 --- /dev/null +++ b/src/Umbraco.Core/StringUdi.cs @@ -0,0 +1,71 @@ +using System; + +namespace Umbraco.Core +{ + /// + /// Represents a string-based entity identifier. + /// + public class StringUdi : Udi + { + /// + /// The string part of the identifier. + /// + public string Id { get; private set; } + + /// + /// Initializes a new instance of the StringUdi class with an entity type and a string id. + /// + /// The entity type part of the udi. + /// The string id part of the udi. + public StringUdi(string entityType, string id) + : base(entityType, "umb://" + entityType + "/" + id) + { + Id = id; + } + + /// + /// Initializes a new instance of the StringUdi class with a uri value. + /// + /// The uri value of the udi. + public StringUdi(Uri uriValue) + : base(uriValue) + { + Id = uriValue.AbsolutePath.TrimStart('/'); + } + + /// + /// Converts the string representation of an entity identifier into the equivalent StringUdi instance. + /// + /// The string to convert. + /// A StringUdi instance that contains the value that was parsed. + public new static StringUdi Parse(string s) + { + var udi = Udi.Parse(s); + if (!(udi is StringUdi)) + throw new FormatException("String \"" + s + "\" is not a string entity id."); + return (StringUdi)udi; + } + + public static bool TryParse(string s, out StringUdi udi) + { + udi = null; + Udi tmp; + if (!TryParse(s, out tmp) || !(tmp is StringUdi)) return false; + udi = (StringUdi)tmp; + return true; + } + + /// + public override bool IsRoot + { + get { return Id == string.Empty; } + } + + /// + public StringUdi EnsureClosed() + { + base.EnsureNotRoot(); + return this; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Udi.cs b/src/Umbraco.Core/Udi.cs new file mode 100644 index 0000000000..aef0fc9274 --- /dev/null +++ b/src/Umbraco.Core/Udi.cs @@ -0,0 +1,266 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Data.Metadata.Edm; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Deploy; +using EntityContainer = System.Data.Metadata.Edm.EntityContainer; + +namespace Umbraco.Core +{ + /// + /// Represents an entity identifier. + /// + /// An Udi can be fully qualified or "closed" eg umb://document/{guid} or "open" eg umb://document. + public abstract class Udi + { + private static readonly Dictionary UdiTypes = new Dictionary(); + private static readonly ConcurrentDictionary RootUdis = new ConcurrentDictionary(); + internal readonly Uri UriValue; // internal for UdiRange + + /// + /// Initializes a new instance of the Udi class. + /// + /// The entity type part of the identifier. + /// The string value of the identifier. + protected Udi(string entityType, string stringValue) + { + EntityType = entityType; + UriValue = new Uri(stringValue); + } + + /// + /// Initializes a new instance of the Udi class. + /// + /// The uri value of the identifier. + protected Udi(Uri uriValue) + { + EntityType = uriValue.Host; + UriValue = uriValue; + } + + static Udi() + { + // for tests etc. + UdiTypes[DeployEntityType.AnyGuid] = UdiType.GuidUdi; + UdiTypes[DeployEntityType.AnyString] = UdiType.StringUdi; + + // we don't have connectors for these... + UdiTypes[DeployEntityType.Member] = UdiType.GuidUdi; + UdiTypes[DeployEntityType.MemberGroup] = UdiType.GuidUdi; + + // fixme - or inject from...? + // there is no way we can get the "registered" service connectors, as registration + // happens in Deploy, not in Core, and the Udi class belongs to Core - therefore, we + // just pick every service connectors - just making sure that not two of them + // would register the same entity type, with different udi types (would not make + // much sense anyways). + var connectors = PluginManager.Current.ResolveTypes(); + foreach (var connector in connectors) + { + var attrs = connector.GetCustomAttributes(false); + foreach (var attr in attrs) + { + UdiType udiType; + if (UdiTypes.TryGetValue(attr.EntityType, out udiType) && udiType != attr.UdiType) + throw new Exception(string.Format("Entity type \"{0}\" is declared by more than one IServiceConnector, with different UdiTypes.", attr.EntityType)); + UdiTypes[attr.EntityType] = attr.UdiType; + } + } + } + + /// + /// Gets the entity type part of the identifier. + /// + public string EntityType { get; private set; } + + public override string ToString() + { + // UriValue is created in the ctor and is never null + return UriValue.ToString(); + } + + /// + /// Converts the string representation of an entity identifier into the equivalent Udi instance. + /// + /// The string to convert. + /// An Udi instance that contains the value that was parsed. + public static Udi Parse(string s) + { + Udi udi; + ParseInternal(s, false, out udi); + return udi; + } + + public static bool TryParse(string s, out Udi udi) + { + return ParseInternal(s, true, out udi); + } + + private static bool ParseInternal(string s, bool tryParse, out Udi udi) + { + udi = null; + Uri uri; + + if (!Uri.IsWellFormedUriString(s, UriKind.Absolute) + || !Uri.TryCreate(s, UriKind.Absolute, out uri)) + { + if (tryParse) return false; + throw new FormatException(string.Format("String \"{0}\" is not a valid udi.", s)); + } + + var entityType = uri.Host; + UdiType udiType; + if (!UdiTypes.TryGetValue(entityType, out udiType)) + { + if (tryParse) return false; + throw new FormatException(string.Format("Unknown entity type \"{0}\".", entityType)); + } + var path = uri.AbsolutePath.TrimStart('/'); + if (udiType == UdiType.GuidUdi) + { + if (path == string.Empty) + { + udi = GetRootUdi(uri.Host); + return true; + } + Guid guid; + if (!Guid.TryParse(path, out guid)) + { + if (tryParse) return false; + throw new FormatException(string.Format("String \"{0}\" is not a valid udi.", s)); + } + udi = new GuidUdi(uri.Host, guid); + return true; + } + if (udiType == UdiType.StringUdi) + { + udi = path == string.Empty ? GetRootUdi(uri.Host) : new StringUdi(uri.Host, path); + return true; + } + if (tryParse) return false; + throw new InvalidOperationException("Internal error."); + } + + private static Udi GetRootUdi(string entityType) + { + return RootUdis.GetOrAdd(entityType, x => + { + UdiType udiType; + if (!UdiTypes.TryGetValue(x, out udiType)) + throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType)); + return udiType == UdiType.StringUdi + ? (Udi)new StringUdi(entityType, string.Empty) + : new GuidUdi(entityType, Guid.Empty); + }); + } + + /// + /// Creates a root Udi for an entity type. + /// + /// The entity type. + /// The root Udi for the entity type. + public static Udi Create(string entityType) + { + return GetRootUdi(entityType); + } + + /// + /// Creates a string Udi. + /// + /// The entity type. + /// The identifier. + /// The string Udi for the entity type and identifier. + public static Udi Create(string entityType, string id) + { + UdiType udiType; + if (!UdiTypes.TryGetValue(entityType, out udiType)) + throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType"); + if (string.IsNullOrWhiteSpace(id)) + throw new ArgumentException("Value cannot be null or whitespace.", "id"); + if (udiType != UdiType.StringUdi) + throw new InvalidOperationException(string.Format("Entity type \"{0}\" does not have string udis.", entityType)); + + return new StringUdi(entityType, id); + } + + /// + /// Creates a Guid Udi. + /// + /// The entity type. + /// The identifier. + /// The Guid Udi for the entity type and identifier. + public static Udi Create(string entityType, Guid id) + { + UdiType udiType; + if (!UdiTypes.TryGetValue(entityType, out udiType)) + throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", entityType), "entityType"); + if (udiType != UdiType.GuidUdi) + throw new InvalidOperationException(string.Format("Entity type \"{0}\" does not have guid udis.", entityType)); + if (id == default(Guid)) + throw new ArgumentException("Cannot be an empty guid.", "id"); + return new GuidUdi(entityType, id); + } + + internal static Udi Create(Uri uri) + { + UdiType udiType; + if (!UdiTypes.TryGetValue(uri.Host, out udiType)) + throw new ArgumentException(string.Format("Unknown entity type \"{0}\".", uri.Host), "uri"); + if (udiType == UdiType.GuidUdi) + return new GuidUdi(uri); + if (udiType == UdiType.GuidUdi) + return new StringUdi(uri); + throw new ArgumentException(string.Format("Uri \"{0}\" is not a valid udi.", uri)); + } + + public void EnsureType(params string[] validTypes) + { + if (!validTypes.Contains(EntityType)) + throw new Exception(string.Format("Unexpected entity type \"{0}\".", EntityType)); + } + + /// + /// Gets a value indicating whether this Udi is a root Udi. + /// + /// A root Udi points to the "root of all things" for a given entity type, e.g. the content tree root. + public abstract bool IsRoot { get; } + + /// + /// Ensures that this Udi is not a root Udi. + /// + /// This Udi. + /// When this Udi is a Root Udi. + public Udi EnsureNotRoot() + { + if (IsRoot) throw new Exception("Root Udi."); + return this; + } + + public override bool Equals(object obj) + { + var other = obj as Udi; + return other != null && GetType() == other.GetType() && UriValue == other.UriValue; + } + + public override int GetHashCode() + { + return UriValue.GetHashCode(); + } + + public static bool operator ==(Udi udi1, Udi udi2) + { + if (ReferenceEquals(udi1, udi2)) return true; + if ((object)udi1 == null || (object)udi2 == null) return false; + return udi1.Equals(udi2); + } + + public static bool operator !=(Udi udi1, Udi udi2) + { + return !(udi1 == udi2); + } + } + +} diff --git a/src/Umbraco.Core/UdiDefinitionAttribute.cs b/src/Umbraco.Core/UdiDefinitionAttribute.cs new file mode 100644 index 0000000000..cc52bb6039 --- /dev/null +++ b/src/Umbraco.Core/UdiDefinitionAttribute.cs @@ -0,0 +1,20 @@ +using System; + +namespace Umbraco.Core +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] + public sealed class UdiDefinitionAttribute : Attribute + { + public UdiDefinitionAttribute(string entityType, UdiType udiType) + { + if (string.IsNullOrWhiteSpace(entityType)) throw new ArgumentNullException("entityType"); + if (udiType != UdiType.GuidUdi && udiType != UdiType.StringUdi) throw new ArgumentException("Invalid value.", "udiType"); + EntityType = entityType; + UdiType = udiType; + } + + public string EntityType { get; private set; } + + public UdiType UdiType { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/UdiGetterExtensions.cs b/src/Umbraco.Core/UdiGetterExtensions.cs new file mode 100644 index 0000000000..aebb8873a6 --- /dev/null +++ b/src/Umbraco.Core/UdiGetterExtensions.cs @@ -0,0 +1,300 @@ +using System; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; + +namespace Umbraco.Core +{ + /// + /// Provides extension methods that return udis for Umbraco entities. + /// + public static class UdiGetterExtensions + { + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this ITemplate entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new GuidUdi(DeployEntityType.Template, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this IContentType entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new GuidUdi(DeployEntityType.DocumentType, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this IMediaType entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new GuidUdi(DeployEntityType.MediaType, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this IMemberType entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new GuidUdi(DeployEntityType.MemberType, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this IMemberGroup entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new GuidUdi(DeployEntityType.MemberGroup, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this IContentTypeComposition entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + + string type; + if (entity is IContentType) type = DeployEntityType.DocumentType; + else if (entity is IMediaType) type = DeployEntityType.MediaType; + else if (entity is IMemberType) type = DeployEntityType.MemberType; + else throw new NotSupportedException(string.Format("Composition type {0} is not supported.", entity.GetType().FullName)); + return new GuidUdi(type, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this IDataTypeDefinition entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new GuidUdi(DeployEntityType.DataType, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this Umbraco.Core.Models.EntityContainer entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + + string entityType; + if (entity.ContainedObjectType == Constants.ObjectTypes.DataTypeGuid) + entityType = DeployEntityType.DataTypeContainer; + else if (entity.ContainedObjectType == Constants.ObjectTypes.DocumentTypeGuid) + entityType = DeployEntityType.DocumentTypeContainer; + else if (entity.ContainedObjectType == Constants.ObjectTypes.MediaTypeGuid) + entityType = DeployEntityType.MediaTypeContainer; + else + throw new NotSupportedException(string.Format("Contained object type {0} is not supported.", entity.ContainedObjectType)); + return new GuidUdi(entityType, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this IMedia entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new GuidUdi(DeployEntityType.Media, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this IContent entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new GuidUdi(DeployEntityType.Document, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this IMember entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new GuidUdi(DeployEntityType.Member, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static StringUdi GetUdi(this Stylesheet entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new StringUdi(DeployEntityType.Stylesheet, entity.Path.TrimStart('/')).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static StringUdi GetUdi(this Script entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new StringUdi(DeployEntityType.Script, entity.Path.TrimStart('/')).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this IDictionaryItem entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new GuidUdi(DeployEntityType.DictionaryItem, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this IMacro entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new GuidUdi(DeployEntityType.Macro, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static StringUdi GetUdi(this IPartialView entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new StringUdi(DeployEntityType.PartialView, entity.Path.TrimStart('/')).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static StringUdi GetUdi(this IXsltFile entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new StringUdi(DeployEntityType.Xslt, entity.Path.TrimStart('/')).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this IContentBase entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + + string type; + if (entity is IContent) type = DeployEntityType.Document; + else if (entity is IMedia) type = DeployEntityType.Media; + else if (entity is IMember) type = DeployEntityType.Member; + else throw new NotSupportedException(string.Format("ContentBase type {0} is not supported.", entity.GetType().FullName)); + return new GuidUdi(type, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static GuidUdi GetUdi(this IRelationType entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + return new GuidUdi(DeployEntityType.RelationType, entity.Key).EnsureClosed(); + } + + /// + /// Gets the entity identifier of the entity. + /// + /// The entity. + /// The entity identifier of the entity. + public static Udi GetUdi(this IEntity entity) + { + if (entity == null) throw new ArgumentNullException("entity"); + + // entity could eg be anything implementing IThing + // so we have to go through casts here + + var template = entity as ITemplate; + if (template != null) return template.GetUdi(); + + var contentType = entity as IContentType; + if (contentType != null) return contentType.GetUdi(); + + var mediaType = entity as IMediaType; + if (mediaType != null) return mediaType.GetUdi(); + + var memberType = entity as IMemberType; + if (memberType != null) return memberType.GetUdi(); + + var memberGroup = entity as IMemberGroup; + if (memberGroup != null) return memberGroup.GetUdi(); + + var contentTypeComposition = entity as IContentTypeComposition; + if (contentTypeComposition != null) return contentTypeComposition.GetUdi(); + + var dataTypeComposition = entity as IDataTypeDefinition; + if (dataTypeComposition != null) return dataTypeComposition.GetUdi(); + + var container = entity as Umbraco.Core.Models.EntityContainer; + if (container != null) return container.GetUdi(); + + var media = entity as IMedia; + if (media != null) return media.GetUdi(); + + var content = entity as IContent; + if (content != null) return content.GetUdi(); + + var member = entity as IMember; + if (member != null) return member.GetUdi(); + + var contentBase = entity as IContentBase; + if (contentBase != null) return contentBase.GetUdi(); + + var macro = entity as IMacro; + if (macro != null) return macro.GetUdi(); + + var relationType = entity as IRelationType; + if (relationType != null) return relationType.GetUdi(); + + throw new NotSupportedException(string.Format("Entity type {0} is not supported.", entity.GetType().FullName)); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/UdiRange.cs b/src/Umbraco.Core/UdiRange.cs new file mode 100644 index 0000000000..e1aa49e079 --- /dev/null +++ b/src/Umbraco.Core/UdiRange.cs @@ -0,0 +1,104 @@ +using System; + +namespace Umbraco.Core +{ + /// + /// Represents a range. + /// + /// + /// A Udi range is composed of a which represents the base of the range, + /// plus a selector that can be "." (the Udi), ".*" (the Udi and its children), ".**" (the udi and + /// its descendants, "*" (the children of the Udi), and "**" (the descendants of the Udi). + /// The Udi here can be a closed entity, or an open entity. + public class UdiRange + { + private readonly Uri _uriValue; + + /// + /// Initializes a new instance of the class with a and an optional selector. + /// + /// A . + /// An optional selector. + public UdiRange(Udi udi, string selector = DeploySelector.This) + { + Udi = udi; + switch (selector) + { + case DeploySelector.This: + Selector = selector; + _uriValue = udi.UriValue; + break; + case DeploySelector.ChildrenOfThis: + case DeploySelector.DescendantsOfThis: + case DeploySelector.ThisAndChildren: + case DeploySelector.ThisAndDescendants: + Selector = selector; + _uriValue = new Uri(Udi + "?" + selector); + break; + default: + throw new ArgumentException(string.Format("Invalid selector \"{0}\".", selector)); + } + } + + /// + /// Gets the for this range. + /// + public Udi Udi { get; private set; } + + /// + /// Gets or sets the selector for this range. + /// + public string Selector { get; private set; } + + /// + /// Gets the entity type of the for this range. + /// + public string EntityType + { + get { return Udi.EntityType; } + } + + public static UdiRange Parse(string s) + { + Uri uri; + + if (!Uri.IsWellFormedUriString(s, UriKind.Absolute) + || !Uri.TryCreate(s, UriKind.Absolute, out uri)) + { + //if (tryParse) return false; + throw new FormatException(string.Format("String \"{0}\" is not a valid udi range.", s)); + } + + var udiUri = uri.Query == string.Empty ? uri : new UriBuilder(uri) { Query = string.Empty }.Uri; + return new UdiRange(Udi.Create(udiUri), uri.Query.TrimStart('?')); + } + + public override string ToString() + { + return _uriValue.ToString(); + } + + public override bool Equals(object obj) + { + var other = obj as UdiRange; + return other != null && GetType() == other.GetType() && _uriValue == other._uriValue; + } + + public override int GetHashCode() + { + return _uriValue.GetHashCode(); + } + + public static bool operator ==(UdiRange range1, UdiRange range2) + { + if (ReferenceEquals(range1, range2)) return true; + if ((object)range1 == null || (object)range2 == null) return false; + return range1.Equals(range2); + } + + public static bool operator !=(UdiRange range1, UdiRange range2) + { + return !(range1 == range2); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/UdiType.cs b/src/Umbraco.Core/UdiType.cs new file mode 100644 index 0000000000..d15987033d --- /dev/null +++ b/src/Umbraco.Core/UdiType.cs @@ -0,0 +1,12 @@ +namespace Umbraco.Core +{ + /// + /// Defines Udi types. + /// + public enum UdiType + { + Unknown, + GuidUdi, + StringUdi + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 950a44502f..38f2920f7d 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -299,6 +299,18 @@ + + + + + + + + + + + + @@ -340,6 +352,7 @@ True Files.resx + @@ -410,6 +423,7 @@ + @@ -527,6 +541,7 @@ + @@ -1326,6 +1341,7 @@ + @@ -1370,6 +1386,11 @@ + + + + + diff --git a/src/Umbraco.Tests/UdiTests.cs b/src/Umbraco.Tests/UdiTests.cs new file mode 100644 index 0000000000..8654f4b458 --- /dev/null +++ b/src/Umbraco.Tests/UdiTests.cs @@ -0,0 +1,155 @@ +using System; +using System.Linq; +using Newtonsoft.Json; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Serialization; + +namespace Umbraco.Tests +{ + [TestFixture] + public class UdiTests + { + [Test] + public void StringEntityCtorTest() + { + var udi = new StringUdi(DeployEntityType.AnyString, "test-id"); + Assert.AreEqual(DeployEntityType.AnyString, udi.EntityType); + Assert.AreEqual("test-id", udi.Id); + Assert.AreEqual("umb://" + DeployEntityType.AnyString + "/test-id", udi.ToString()); + } + + [Test] + public void StringEntityParseTest() + { + var udi = Udi.Parse("umb://" + DeployEntityType.AnyString + "/test-id"); + Assert.AreEqual(DeployEntityType.AnyString, udi.EntityType); + Assert.IsInstanceOf(udi); + var stringEntityId = udi as StringUdi; + Assert.IsNotNull(stringEntityId); + Assert.AreEqual("test-id", stringEntityId.Id); + Assert.AreEqual("umb://" + DeployEntityType.AnyString + "/test-id", udi.ToString()); + } + + [Test] + public void GuidEntityCtorTest() + { + var guid = Guid.NewGuid(); + var udi = new GuidUdi(DeployEntityType.AnyGuid, guid); + Assert.AreEqual(DeployEntityType.AnyGuid, udi.EntityType); + Assert.AreEqual(guid, udi.Guid); + Assert.AreEqual("umb://" + DeployEntityType.AnyGuid + "/" + guid.ToString("N"), udi.ToString()); + } + + [Test] + public void GuidEntityParseTest() + { + var guid = Guid.NewGuid(); + var s = "umb://" + DeployEntityType.AnyGuid + "/" + guid.ToString("N"); + var udi = Udi.Parse(s); + Assert.AreEqual(DeployEntityType.AnyGuid, udi.EntityType); + Assert.IsInstanceOf(udi); + var gudi = udi as GuidUdi; + Assert.IsNotNull(gudi); + Assert.AreEqual(guid, gudi.Guid); + Assert.AreEqual(s, udi.ToString()); + } + + [Test] + public void EqualityTest() + { + var guid1 = Guid.NewGuid(); + var guid2 = Guid.NewGuid(); + + Assert.IsTrue(new GuidUdi("type", guid1).Equals(new GuidUdi("type", guid1))); + Assert.IsTrue(new GuidUdi("type", guid1) == new GuidUdi("type", guid1)); + + Assert.IsTrue(((Udi)new GuidUdi("type", guid1)).Equals((Udi)new GuidUdi("type", guid1))); + Assert.IsTrue((Udi)new GuidUdi("type", guid1) == (Udi)new GuidUdi("type", guid1)); + + Assert.IsFalse(new GuidUdi("type", guid1).Equals(new GuidUdi("typex", guid1))); + Assert.IsFalse(new GuidUdi("type", guid1) == new GuidUdi("typex", guid1)); + Assert.IsFalse(new GuidUdi("type", guid1).Equals(new GuidUdi("type", guid2))); + Assert.IsFalse(new GuidUdi("type", guid1) == new GuidUdi("type", guid2)); + + Assert.IsTrue(new GuidUdi("type", guid1).ToString() == new StringUdi("type", guid1.ToString("N")).ToString()); + Assert.IsFalse(new GuidUdi("type", guid1) == new StringUdi("type", guid1.ToString("N"))); + } + + [Test] + public void DistinctTest() + { + var guid1 = Guid.NewGuid(); + var entities = new[] + { + new GuidUdi(DeployEntityType.AnyGuid, guid1), + new GuidUdi(DeployEntityType.AnyGuid, guid1), + new GuidUdi(DeployEntityType.AnyGuid, guid1), + }; + Assert.AreEqual(1, entities.Distinct().Count()); + } + + [Test] + public void CreateTest() + { + var guid = Guid.NewGuid(); + var udi = Udi.Create(DeployEntityType.AnyGuid, guid); + Assert.AreEqual(DeployEntityType.AnyGuid, udi.EntityType); + Assert.AreEqual(guid, ((GuidUdi)udi).Guid); + + Assert.Throws(() => Udi.Create(DeployEntityType.AnyString, guid)); + Assert.Throws(() => Udi.Create(DeployEntityType.AnyGuid, "foo")); + Assert.Throws(() => Udi.Create("barf", "foo")); + } + + [Test] + public void RangeTest() + { + // can parse open string udi + const string stringUdiString = "umb://stylesheet"; + Udi stringUdi; + Assert.IsTrue(Udi.TryParse(stringUdiString, out stringUdi)); + Assert.AreEqual(string.Empty, ((StringUdi)stringUdi).Id); + + // can parse open guid udi + const string guidUdiString = "umb://document"; + Udi guidUdi; + Assert.IsTrue(Udi.TryParse(guidUdiString, out guidUdi)); + Assert.AreEqual(Guid.Empty, ((GuidUdi)guidUdi).Guid); + + // can create a range + var range = new UdiRange(stringUdi, DeploySelector.ChildrenOfThis); + + // cannot create invalid ranges + Assert.Throws(() => new UdiRange(guidUdi, "x")); + } + + [Test] + public void SerializationTest() + { + var settings = new JsonSerializerSettings + { + Converters = new JsonConverter[] { new UdiJsonConverter(), new UdiRangeJsonConverter() } + }; + + + var guid = Guid.NewGuid(); + var udi = new GuidUdi(DeployEntityType.AnyGuid, guid); + var json = JsonConvert.SerializeObject(udi, settings); + Assert.AreEqual(string.Format("\"umb://any-guid/{0:N}\"", guid), json); + + var dudi = JsonConvert.DeserializeObject(json, settings); + Assert.AreEqual(DeployEntityType.AnyGuid, dudi.EntityType); + Assert.AreEqual(guid, ((GuidUdi)dudi).Guid); + + var range = new UdiRange(udi, DeploySelector.ChildrenOfThis); + json = JsonConvert.SerializeObject(range, settings); + Assert.AreEqual(string.Format("\"umb://any-guid/{0:N}?children\"", guid), json); + + var drange = JsonConvert.DeserializeObject(json, settings); + Assert.AreEqual(udi, drange.Udi); + Assert.AreEqual(string.Format("umb://any-guid/{0:N}", guid), drange.Udi.UriValue.ToString()); + Assert.AreEqual(DeploySelector.ChildrenOfThis, drange.Selector); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 1f5e324142..0f0a7385c5 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -170,6 +170,7 @@ + From 198edec4f4f9f72bf3ebea1dbdbd4ebfc37ee282 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 10:09:53 +0100 Subject: [PATCH 235/599] add fallback icons --- .../contentpicker/contentpicker.controller.js | 21 ++++++++++++++++++- .../memberpicker/memberpicker.controller.js | 3 ++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index ccf9d865bc..e77e743f2b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -231,7 +231,26 @@ function contentPickerController($scope, dialogService, entityResource, contentR }); if (entity) { - entity.icon = iconHelper.convertFromLegacyIcon(entity.icon); + + // set icon + if(entity.icon) { + entity.icon = iconHelper.convertFromLegacyIcon(entity.icon); + } + + // set default icon + if (!entity.icon) { + switch (entityType) { + case "Document": + entity.icon = "icon-document"; + break; + case "Media": + entity.icon = "icon-picture"; + break; + case "Member": + entity.icon = "icon-user"; + break; + } + } var url = (entity.urls && entity.urls.length > 0) ? entity.urls[0] : ""; var path = ($scope.model.config.showPathOnHover) ? entity.path : ""; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js index 0d4b2817d0..0029332f64 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js @@ -97,7 +97,8 @@ function memberPickerController($scope, dialogService, entityResource, $log, ico var modelIds = $scope.model.value ? $scope.model.value.split(',') : []; entityResource.getByIds(modelIds, "Member").then(function (data) { _.each(data, function (item, i) { - item.icon = iconHelper.convertFromLegacyIcon(item.icon); + // set default icon if it's missing + item.icon = (item.icon) ? iconHelper.convertFromLegacyIcon(item.icon) : "icon-user"; $scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon }); }); }); From 7a0ac625ff9342d403e67e2279f71ad9a350261d Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Mon, 16 Jan 2017 10:13:32 +0100 Subject: [PATCH 236/599] Last remaining dk translation strings This needs a review by the dynamic writing duo vera and martin. --- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 3c52d7f1a1..e14a0f9109 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -1009,11 +1009,11 @@ Mange hilsner fra Umbraco robotten Rediger skabelong Sektioner - Insert content area - Insert content area placeholder + Indsæt indholdsområde + Indsæt indholdsområde placeholder Indsæt - Vælg hvad du vil indsætte + Hvad vil du indsætte ? Oversættelse Indsætter en oversætbar tekst som skifter efter det sprog som websitet vises i. @@ -1021,17 +1021,20 @@ Mange hilsner fra Umbraco robotten Makro En makro er et element som kan have forskellige indstillinger når det indsættes. - Det er glimrende som en genbrugelig del af dit design såsom gallerier, formularer + Brug det som en genbrugelig del af dit design såsom gallerier, formularer og lister. - Value - Displays the value of a named field from the current page, with options to modify the value or fallback to alternative values. + Side værdi + + Viser værdien af et felt fra den nuværende side. Kan indstilles til at bruge rekursive værdier eller + vise en standard værdi i tilfælde af at feltet er tomt. + Partial view - A partial view is a separate template file which can be rendered inside another - template, it's great for reusing markup or for separating complex templates into separate files. + Et Partial View er et skabelon element som kan indsættes i andre skabeloner og derved + genbruges og deles på tværs af side-skabelonerne. Master skabelon @@ -1081,8 +1084,6 @@ Mange hilsner fra Umbraco robotten filtre og - - Skabelon From d24541fce41699a75a99ea47f96a68a641fec946 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 16 Jan 2017 20:45:08 +1100 Subject: [PATCH 237/599] second commit of everything that we want to add to core for Deploy, have moved stuff to where they should go, probably need to review if the GridValue should be in models though or if we already have this. --- src/Umbraco.Core/Constants-Conventions.cs | 12 -- .../Constants-DatabaseProviders.cs | 12 ++ .../Constants-DeployEntityType.cs | 129 ++++++++++++++++++ src/Umbraco.Core/Constants-DeploySelector.cs | 21 +++ src/Umbraco.Core/Constants-Security.cs | 33 +++++ src/Umbraco.Core/Constants-System.cs | 8 +- src/Umbraco.Core/Constants-Web.cs | 26 +--- src/Umbraco.Core/Deploy/ArtifactBase.cs | 47 +++++++ src/Umbraco.Core/Deploy/ArtifactSignature.cs | 21 +++ src/Umbraco.Core/Deploy/GridValue.cs | 82 +++++++++++ .../Deploy/IGridCellValueConnector.cs | 41 ++++++ src/Umbraco.Core/Deploy/IImageSourceParser.cs | 27 ++++ src/Umbraco.Core/Deploy/ILocalLinkParser.cs | 27 ++++ src/Umbraco.Core/Deploy/IMacroParser.cs | 32 +++++ src/Umbraco.Core/Deploy/IPreValueConnector.cs | 32 +++++ src/Umbraco.Core/Deploy/IPrettyArtifact.cs | 8 ++ src/Umbraco.Core/Deploy/IValueConnector.cs | 35 +++++ src/Umbraco.Core/NamedUdiRange.cs | 4 +- .../Serialization/StreamResultExtensions.cs | 22 +++ .../Serialization/StreamedResult.cs | 18 --- .../Serialization/UdiJsonConverter.cs | 20 --- .../Serialization/UdiRangeJsonConverter.cs | 26 ++++ src/Umbraco.Core/UdiRange.cs | 12 +- src/Umbraco.Core/Umbraco.Core.csproj | 16 +++ src/Umbraco.Tests/UdiTests.cs | 6 +- 25 files changed, 624 insertions(+), 93 deletions(-) create mode 100644 src/Umbraco.Core/Constants-DatabaseProviders.cs create mode 100644 src/Umbraco.Core/Constants-DeployEntityType.cs create mode 100644 src/Umbraco.Core/Constants-DeploySelector.cs create mode 100644 src/Umbraco.Core/Constants-Security.cs create mode 100644 src/Umbraco.Core/Deploy/ArtifactBase.cs create mode 100644 src/Umbraco.Core/Deploy/ArtifactSignature.cs create mode 100644 src/Umbraco.Core/Deploy/GridValue.cs create mode 100644 src/Umbraco.Core/Deploy/IGridCellValueConnector.cs create mode 100644 src/Umbraco.Core/Deploy/IImageSourceParser.cs create mode 100644 src/Umbraco.Core/Deploy/ILocalLinkParser.cs create mode 100644 src/Umbraco.Core/Deploy/IMacroParser.cs create mode 100644 src/Umbraco.Core/Deploy/IPreValueConnector.cs create mode 100644 src/Umbraco.Core/Deploy/IPrettyArtifact.cs create mode 100644 src/Umbraco.Core/Deploy/IValueConnector.cs create mode 100644 src/Umbraco.Core/Serialization/StreamResultExtensions.cs create mode 100644 src/Umbraco.Core/Serialization/UdiRangeJsonConverter.cs diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index 69e0f7c68e..d6c4003fcf 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -4,18 +4,6 @@ using Umbraco.Core.Models; namespace Umbraco.Core { - /// - /// Contains the valid selector values. - /// - internal static class DeploySelector - { - public const string This = "this"; - public const string ThisAndChildren = "this-and-children"; - public const string ThisAndDescendants = "this-and-descendants"; - public const string ChildrenOfThis = "children"; - public const string DescendantsOfThis = "descendants"; - } - /// /// Defines well-known entity types. /// diff --git a/src/Umbraco.Core/Constants-DatabaseProviders.cs b/src/Umbraco.Core/Constants-DatabaseProviders.cs new file mode 100644 index 0000000000..2a64ade081 --- /dev/null +++ b/src/Umbraco.Core/Constants-DatabaseProviders.cs @@ -0,0 +1,12 @@ +namespace Umbraco.Core +{ + public static partial class Constants + { + public static class DatabaseProviders + { + public const string SqlCe = "System.Data.SqlServerCe.4.0"; + public const string SqlServer = "System.Data.SqlClient"; + public const string MySql = "MySql.Data.MySqlClient"; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Constants-DeployEntityType.cs b/src/Umbraco.Core/Constants-DeployEntityType.cs new file mode 100644 index 0000000000..2c0ea81be3 --- /dev/null +++ b/src/Umbraco.Core/Constants-DeployEntityType.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core +{ + + public static partial class Constants + { + + /// + /// Defines well-known entity types. + /// + /// Well-known entity types are those that Deploy already knows about, + /// but entity types are strings and so can be extended beyond what is defined here. + internal static class DeployEntityType + { + // guid entity types + + public const string AnyGuid = "any-guid"; // that one is for tests + + public const string Document = "document"; + public const string Media = "media"; + public const string Member = "member"; + + public const string DictionaryItem = "dictionary-item"; + public const string Macro = "macro"; + public const string Template = "template"; + + public const string DocumentType = "document-type"; + public const string DocumentTypeContainer = "document-type-container"; + public const string MediaType = "media-type"; + public const string MediaTypeContainer = "media-type-container"; + public const string DataType = "data-type"; + public const string DataTypeContainer = "data-type-container"; + public const string MemberType = "member-type"; + public const string MemberGroup = "member-group"; + + public const string RelationType = "relation-type"; + + // string entity types + + public const string AnyString = "any-string"; // that one is for tests + + public const string MediaFile = "media-file"; + public const string TemplateFile = "template-file"; + public const string Script = "script"; + public const string Stylesheet = "stylesheet"; + public const string PartialView = "partial-view"; + public const string PartialViewMacro = "partial-view-macro"; + public const string Xslt = "xslt"; + + public static string FromUmbracoObjectType(UmbracoObjectTypes umbracoObjectType) + { + switch (umbracoObjectType) + { + case UmbracoObjectTypes.Document: + return Document; + case UmbracoObjectTypes.Media: + return Media; + case UmbracoObjectTypes.Member: + return Member; + case UmbracoObjectTypes.Template: + return Template; + case UmbracoObjectTypes.DocumentType: + return DocumentType; + case UmbracoObjectTypes.DocumentTypeContainer: + return DocumentTypeContainer; + case UmbracoObjectTypes.MediaType: + return MediaType; + case UmbracoObjectTypes.MediaTypeContainer: + return MediaTypeContainer; + case UmbracoObjectTypes.DataType: + return DataType; + case UmbracoObjectTypes.DataTypeContainer: + return DataTypeContainer; + case UmbracoObjectTypes.MemberType: + return MemberType; + case UmbracoObjectTypes.MemberGroup: + return MemberGroup; + case UmbracoObjectTypes.Stylesheet: + return Stylesheet; + case UmbracoObjectTypes.RelationType: + return RelationType; + } + throw new NotSupportedException(string.Format("UmbracoObjectType \"{0}\" does not have a matching EntityType.", umbracoObjectType)); + } + + public static UmbracoObjectTypes ToUmbracoObjectType(string entityType) + { + switch (entityType) + { + case Document: + return UmbracoObjectTypes.Document; + case Media: + return UmbracoObjectTypes.Media; + case Member: + return UmbracoObjectTypes.Member; + case Template: + return UmbracoObjectTypes.Template; + case DocumentType: + return UmbracoObjectTypes.DocumentType; + case DocumentTypeContainer: + return UmbracoObjectTypes.DocumentTypeContainer; + case MediaType: + return UmbracoObjectTypes.MediaType; + case MediaTypeContainer: + return UmbracoObjectTypes.MediaTypeContainer; + case DataType: + return UmbracoObjectTypes.DataType; + case DataTypeContainer: + return UmbracoObjectTypes.DataTypeContainer; + case MemberType: + return UmbracoObjectTypes.MemberType; + case MemberGroup: + return UmbracoObjectTypes.MemberGroup; + case Stylesheet: + return UmbracoObjectTypes.Stylesheet; + case RelationType: + return UmbracoObjectTypes.RelationType; + } + throw new NotSupportedException( + string.Format("EntityType \"{0}\" does not have a matching UmbracoObjectType.", entityType)); + } + } + + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Constants-DeploySelector.cs b/src/Umbraco.Core/Constants-DeploySelector.cs new file mode 100644 index 0000000000..b2415fb37f --- /dev/null +++ b/src/Umbraco.Core/Constants-DeploySelector.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core +{ + public static partial class Constants + { + /// + /// Contains the valid selector values. + /// + internal static class DeploySelector + { + public const string This = "this"; + public const string ThisAndChildren = "this-and-children"; + public const string ThisAndDescendants = "this-and-descendants"; + public const string ChildrenOfThis = "children"; + public const string DescendantsOfThis = "descendants"; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Constants-Security.cs b/src/Umbraco.Core/Constants-Security.cs new file mode 100644 index 0000000000..bd2e1c5acf --- /dev/null +++ b/src/Umbraco.Core/Constants-Security.cs @@ -0,0 +1,33 @@ +using System; +using System.ComponentModel; + +namespace Umbraco.Core +{ + public static partial class Constants + { + public static class Security + { + + public const string BackOfficeAuthenticationType = "UmbracoBackOffice"; + public const string BackOfficeExternalAuthenticationType = "UmbracoExternalCookie"; + public const string BackOfficeExternalCookieName = "UMB_EXTLOGIN"; + public const string BackOfficeTokenAuthenticationType = "UmbracoBackOfficeToken"; + public const string BackOfficeTwoFactorAuthenticationType = "UmbracoTwoFactorCookie"; + + /// + /// The prefix used for external identity providers for their authentication type + /// + /// + /// By default we don't want to interfere with front-end external providers and their default setup, for back office the + /// providers need to be setup differently and each auth type for the back office will be prefixed with this value + /// + public const string BackOfficeExternalAuthenticationTypePrefix = "Umbraco."; + + public const string StartContentNodeIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/startcontentnode"; + public const string StartMediaNodeIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/startmedianode"; + public const string AllowedApplicationsClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/allowedapp"; + public const string SessionIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/sessionid"; + + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Constants-System.cs b/src/Umbraco.Core/Constants-System.cs index d1ac6fbb38..b38417c39b 100644 --- a/src/Umbraco.Core/Constants-System.cs +++ b/src/Umbraco.Core/Constants-System.cs @@ -29,12 +29,6 @@ public const string UmbracoConnectionName = "umbracoDbDSN"; public const string UmbracoMigrationName = "Umbraco"; } - - public static class DatabaseProviders - { - public const string SqlCe = "System.Data.SqlServerCe.4.0"; - public const string SqlServer = "System.Data.SqlClient"; - public const string MySql = "MySql.Data.MySqlClient"; - } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs index f596820506..67b5698610 100644 --- a/src/Umbraco.Core/Constants-Web.cs +++ b/src/Umbraco.Core/Constants-Web.cs @@ -29,30 +29,6 @@ namespace Umbraco.Core public const string AuthCookieName = "UMB_UCONTEXT"; } - - public static class Security - { - - public const string BackOfficeAuthenticationType = "UmbracoBackOffice"; - public const string BackOfficeExternalAuthenticationType = "UmbracoExternalCookie"; - public const string BackOfficeExternalCookieName = "UMB_EXTLOGIN"; - public const string BackOfficeTokenAuthenticationType = "UmbracoBackOfficeToken"; - public const string BackOfficeTwoFactorAuthenticationType = "UmbracoTwoFactorCookie"; - - /// - /// The prefix used for external identity providers for their authentication type - /// - /// - /// By default we don't want to interfere with front-end external providers and their default setup, for back office the - /// providers need to be setup differently and each auth type for the back office will be prefixed with this value - /// - public const string BackOfficeExternalAuthenticationTypePrefix = "Umbraco."; - - public const string StartContentNodeIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/startcontentnode"; - public const string StartMediaNodeIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/startmedianode"; - public const string AllowedApplicationsClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/allowedapp"; - public const string SessionIdClaimType = "http://umbraco.org/2015/02/identity/claims/backoffice/sessionid"; - - } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/ArtifactBase.cs b/src/Umbraco.Core/Deploy/ArtifactBase.cs new file mode 100644 index 0000000000..4d6bc687cb --- /dev/null +++ b/src/Umbraco.Core/Deploy/ArtifactBase.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; + +namespace Umbraco.Core.Deploy +{ + /// + /// Provides a base class to all artifacts. + /// + public abstract class ArtifactBase : IArtifact + where TUdi : Udi + { + protected ArtifactBase(TUdi udi, IEnumerable dependencies = null) + { + if (udi == null) + throw new ArgumentNullException("udi"); + Udi = udi; + + Dependencies = dependencies ?? Enumerable.Empty(); + _checksum = new Lazy(GetChecksum); + } + + private readonly Lazy _checksum; + + protected abstract string GetChecksum(); + + #region Abstract implementation of IArtifactSignature + + Udi IArtifactSignature.Udi + { + get { return Udi; } + } + + public TUdi Udi { get; set; } + + [JsonIgnore] + public string Checksum + { + get { return _checksum.Value; } + } + + public IEnumerable Dependencies { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/ArtifactSignature.cs b/src/Umbraco.Core/Deploy/ArtifactSignature.cs new file mode 100644 index 0000000000..4bea6a5ce8 --- /dev/null +++ b/src/Umbraco.Core/Deploy/ArtifactSignature.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Core.Deploy +{ + public sealed class ArtifactSignature : IArtifactSignature + { + public ArtifactSignature(Udi udi, string checksum, IEnumerable dependencies = null) + { + Udi = udi; + Checksum = checksum; + Dependencies = dependencies ?? Enumerable.Empty(); + } + + public Udi Udi { get; private set; } + + public string Checksum { get; private set; } + + public IEnumerable Dependencies { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/GridValue.cs b/src/Umbraco.Core/Deploy/GridValue.cs new file mode 100644 index 0000000000..39a38a665c --- /dev/null +++ b/src/Umbraco.Core/Deploy/GridValue.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Umbraco.Core.Deploy +{ + public class GridValue + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("sections")] + public IEnumerable
    Sections { get; set; } + + public class Section + { + [JsonProperty("grid")] + public string Grid { get; set; } + + [JsonProperty("rows")] + public IEnumerable Rows { get; set; } + } + + public class Row + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("id")] + public Guid Id { get; set; } + + [JsonProperty("areas")] + public IEnumerable Areas { get; set; } + + [JsonProperty("styles")] + public JToken Styles { get; set; } + + [JsonProperty("config")] + public JToken Config { get; set; } + } + + public class Area + { + [JsonProperty("grid")] + public string Grid { get; set; } + + [JsonProperty("controls")] + public IEnumerable Controls { get; set; } + + [JsonProperty("styles")] + public JToken Styles { get; set; } + + [JsonProperty("config")] + public JToken Config { get; set; } + } + + public class Control + { + [JsonProperty("value")] + public JToken Value { get; set; } + + [JsonProperty("editor")] + public Editor Editor { get; set; } + + [JsonProperty("styles")] + public JToken Styles { get; set; } + + [JsonProperty("config")] + public JToken Config { get; set; } + } + + public class Editor + { + [JsonProperty("alias")] + public string Alias { get; set; } + + [JsonProperty("view")] + public string View { get; set; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/IGridCellValueConnector.cs b/src/Umbraco.Core/Deploy/IGridCellValueConnector.cs new file mode 100644 index 0000000000..f95a4e7cf1 --- /dev/null +++ b/src/Umbraco.Core/Deploy/IGridCellValueConnector.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Deploy +{ + /// + /// Defines methods that can convert a grid cell value to / from an environment-agnostic string. + /// + /// Grid cell values may contain values such as content identifiers, that would be local + /// to one environment, and need to be converted in order to be deployed. + public interface IGridCellValueConnector + { + /// + /// Gets a value indicating whether the connector supports a specified grid editor alias. + /// + /// The grid editor alias. + /// A value indicating whether the connector supports the grid editor alias. + /// Note that can be string.Empty to indicate the "default" connector. + bool IsConnector(string alias); + + /// + /// Gets the value to be deployed from the control value as a string. + /// + /// The control containing the value. + /// The property where the control is located. Do not modify - only used for context + /// The dependencies of the property. + /// The grid cell value to be deployed. + /// Note that + string GetValue(GridValue.Control control, Property property, ICollection dependencies); + + /// + /// Allows you to modify the value of a control being deployed. + /// + /// The control being deployed. + /// The property where the is located. Do not modify - only used for context. + /// Follows the pattern of the property value connectors (). The SetValue method is used to modify the value of the . + /// Note that only the value should be modified - not the . + /// The should only be used to assist with context data relevant when setting the value. + void SetValue(GridValue.Control control, Property property); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/IImageSourceParser.cs b/src/Umbraco.Core/Deploy/IImageSourceParser.cs new file mode 100644 index 0000000000..d8e8d860ac --- /dev/null +++ b/src/Umbraco.Core/Deploy/IImageSourceParser.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Deploy +{ + /// + /// Provides methods to parse image tag sources in property values. + /// + public interface IImageSourceParser + { + /// + /// Parses an Umbraco property value and produces an artifact property value. + /// + /// The property value. + /// A list of dependencies. + /// The parsed value. + /// Turns src="/media/..." into src="umb://media/..." and adds the corresponding udi to the dependencies. + string ToArtifact(string value, ICollection dependencies); + + /// + /// Parses an artifact property value and produces an Umbraco property value. + /// + /// The artifact property value. + /// The parsed value. + /// Turns umb://media/... into /media/.... + string FromArtifact(string value); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/ILocalLinkParser.cs b/src/Umbraco.Core/Deploy/ILocalLinkParser.cs new file mode 100644 index 0000000000..c5906c2060 --- /dev/null +++ b/src/Umbraco.Core/Deploy/ILocalLinkParser.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Deploy +{ + /// + /// Provides methods to parse local link tags in property values. + /// + public interface ILocalLinkParser + { + /// + /// Parses an Umbraco property value and produces an artifact property value. + /// + /// The property value. + /// A list of dependencies. + /// The parsed value. + /// Turns {{localLink:1234}} into {{localLink:umb://{type}/{id}}} and adds the corresponding udi to the dependencies. + string ToArtifact(string value, ICollection dependencies); + + /// + /// Parses an artifact property value and produces an Umbraco property value. + /// + /// The artifact property value. + /// The parsed value. + /// Turns {{localLink:umb://{type}/{id}}} into {{localLink:1234}}. + string FromArtifact(string value); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/IMacroParser.cs b/src/Umbraco.Core/Deploy/IMacroParser.cs new file mode 100644 index 0000000000..9cde8ef8b6 --- /dev/null +++ b/src/Umbraco.Core/Deploy/IMacroParser.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Deploy +{ + public interface IMacroParser + { + /// + /// Parses an Umbraco property value and produces an artifact property value. + /// + /// Property value. + /// A list of dependencies. + /// Parsed value. + string ToArtifact(string value, ICollection dependencies); + + /// + /// Parses an artifact property value and produces an Umbraco property value. + /// + /// Artifact property value. + /// Parsed value. + string FromArtifact(string value); + + /// + /// Tries to replace the value of the attribute/parameter with a value containing a converted identifier. + /// + /// Value to attempt to convert + /// Alias of the editor used for the parameter + /// Collection to add dependencies to when performing ToArtifact + /// Indicates which action is being performed (to or from artifact) + /// Value with converted identifiers + string ReplaceAttributeValue(string value, string editorAlias, ICollection dependencies, Direction direction); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/IPreValueConnector.cs b/src/Umbraco.Core/Deploy/IPreValueConnector.cs new file mode 100644 index 0000000000..4ef898cc74 --- /dev/null +++ b/src/Umbraco.Core/Deploy/IPreValueConnector.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.Deploy +{ + /// + /// Defines methods that can convert a preValue to / from an environment-agnostic string. + /// + /// PreValues may contain values such as content identifiers, that would be local + /// to one environment, and need to be converted in order to be deployed. + public interface IPreValueConnector + { + /// + /// Gets the property editor aliases that the value converter supports by default. + /// + IEnumerable PropertyEditorAliases { get; } + + /// + /// Gets the environment-agnostic preValues corresponding to environment-specific preValues. + /// + /// The environment-specific preValues. + /// The dependencies. + /// + IDictionary ConvertToDeploy(IDictionary preValues, ICollection dependencies); + + /// + /// Gets the environment-specific preValues corresponding to environment-agnostic preValues. + /// + /// The environment-agnostic preValues. + /// + IDictionary ConvertToLocalEnvironment(IDictionary preValues); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/IPrettyArtifact.cs b/src/Umbraco.Core/Deploy/IPrettyArtifact.cs new file mode 100644 index 0000000000..38c0ddee4c --- /dev/null +++ b/src/Umbraco.Core/Deploy/IPrettyArtifact.cs @@ -0,0 +1,8 @@ +namespace Umbraco.Core.Deploy +{ + public interface IPrettyArtifact + { + string Name { get; } + string Alias { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/IValueConnector.cs b/src/Umbraco.Core/Deploy/IValueConnector.cs new file mode 100644 index 0000000000..a93e5a05a4 --- /dev/null +++ b/src/Umbraco.Core/Deploy/IValueConnector.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Deploy +{ + /// + /// Defines methods that can convert a property value to / from an environment-agnostic string. + /// + /// Property values may contain values such as content identifiers, that would be local + /// to one environment, and need to be converted in order to be deployed. Connectors also deal + /// with serializing to / from string. + public interface IValueConnector + { + /// + /// Gets the property editor aliases that the value converter supports by default. + /// + IEnumerable PropertyEditorAliases { get; } + + /// + /// Gets the deploy property corresponding to a content property. + /// + /// The content property. + /// The content dependencies. + /// The deploy property value. + string GetValue(Property property, ICollection dependencies); + + /// + /// Sets a content property value using a deploy property. + /// + /// The content item. + /// The property alias. + /// The deploy property value. + void SetValue(IContentBase content, string alias, string value); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/NamedUdiRange.cs b/src/Umbraco.Core/NamedUdiRange.cs index 4e81631e03..c87e5db82e 100644 --- a/src/Umbraco.Core/NamedUdiRange.cs +++ b/src/Umbraco.Core/NamedUdiRange.cs @@ -10,7 +10,7 @@ namespace Umbraco.Core ///
    /// A . /// An optional selector. - public NamedUdiRange(Udi udi, string selector = DeploySelector.This) + public NamedUdiRange(Udi udi, string selector = Constants.DeploySelector.This) : base(udi, selector) { } @@ -20,7 +20,7 @@ namespace Umbraco.Core /// A . /// A name. /// An optional selector. - public NamedUdiRange(Udi udi, string name, string selector = DeploySelector.This) + public NamedUdiRange(Udi udi, string name, string selector = Constants.DeploySelector.This) : base(udi, selector) { Name = name; diff --git a/src/Umbraco.Core/Serialization/StreamResultExtensions.cs b/src/Umbraco.Core/Serialization/StreamResultExtensions.cs new file mode 100644 index 0000000000..96490a933c --- /dev/null +++ b/src/Umbraco.Core/Serialization/StreamResultExtensions.cs @@ -0,0 +1,22 @@ +using System.IO; +using System.Text; +using System.Xml.Linq; + +namespace Umbraco.Core.Serialization +{ + public static class StreamResultExtensions + { + public static string ToJsonString(this Stream stream) + { + byte[] bytes = new byte[stream.Length]; + stream.Position = 0; + stream.Read(bytes, 0, (int)stream.Length); + return Encoding.UTF8.GetString(bytes); + } + + public static XDocument ToXDoc(this Stream stream) + { + return XDocument.Load(stream); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Serialization/StreamedResult.cs b/src/Umbraco.Core/Serialization/StreamedResult.cs index 9b73fd06bf..0a50751229 100644 --- a/src/Umbraco.Core/Serialization/StreamedResult.cs +++ b/src/Umbraco.Core/Serialization/StreamedResult.cs @@ -1,6 +1,4 @@ using System.IO; -using System.Text; -using System.Xml.Linq; namespace Umbraco.Core.Serialization { @@ -20,20 +18,4 @@ namespace Umbraco.Core.Serialization #endregion } - - public static class StreamResultExtensions - { - public static string ToJsonString(this Stream stream) - { - byte[] bytes = new byte[stream.Length]; - stream.Position = 0; - stream.Read(bytes, 0, (int)stream.Length); - return Encoding.UTF8.GetString(bytes); - } - - public static XDocument ToXDoc(this Stream stream) - { - return XDocument.Load(stream); - } - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Serialization/UdiJsonConverter.cs b/src/Umbraco.Core/Serialization/UdiJsonConverter.cs index 7d2db92e81..ff62535825 100644 --- a/src/Umbraco.Core/Serialization/UdiJsonConverter.cs +++ b/src/Umbraco.Core/Serialization/UdiJsonConverter.cs @@ -4,26 +4,6 @@ using Newtonsoft.Json.Linq; namespace Umbraco.Core.Serialization { - public class UdiRangeJsonConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return typeof(UdiRange).IsAssignableFrom(objectType); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - writer.WriteValue(value.ToString()); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var jo = JToken.ReadFrom(reader); - var val = jo.ToObject(); - return val == null ? null : UdiRange.Parse(val); - } - } - public class UdiJsonConverter : JsonConverter { diff --git a/src/Umbraco.Core/Serialization/UdiRangeJsonConverter.cs b/src/Umbraco.Core/Serialization/UdiRangeJsonConverter.cs new file mode 100644 index 0000000000..099c46f29d --- /dev/null +++ b/src/Umbraco.Core/Serialization/UdiRangeJsonConverter.cs @@ -0,0 +1,26 @@ +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Umbraco.Core.Serialization +{ + public class UdiRangeJsonConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return typeof(UdiRange).IsAssignableFrom(objectType); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value.ToString()); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var jo = JToken.ReadFrom(reader); + var val = jo.ToObject(); + return val == null ? null : UdiRange.Parse(val); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/UdiRange.cs b/src/Umbraco.Core/UdiRange.cs index e1aa49e079..a708220066 100644 --- a/src/Umbraco.Core/UdiRange.cs +++ b/src/Umbraco.Core/UdiRange.cs @@ -19,19 +19,19 @@ namespace Umbraco.Core ///
    /// A . /// An optional selector. - public UdiRange(Udi udi, string selector = DeploySelector.This) + public UdiRange(Udi udi, string selector = Constants.DeploySelector.This) { Udi = udi; switch (selector) { - case DeploySelector.This: + case Constants.DeploySelector.This: Selector = selector; _uriValue = udi.UriValue; break; - case DeploySelector.ChildrenOfThis: - case DeploySelector.DescendantsOfThis: - case DeploySelector.ThisAndChildren: - case DeploySelector.ThisAndDescendants: + case Constants.DeploySelector.ChildrenOfThis: + case Constants.DeploySelector.DescendantsOfThis: + case Constants.DeploySelector.ThisAndChildren: + case Constants.DeploySelector.ThisAndDescendants: Selector = selector; _uriValue = new Uri(Udi + "?" + selector); break; diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 38f2920f7d..d06547ff19 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -290,27 +290,41 @@ + + + + + + + + + + + + + + @@ -541,7 +555,9 @@ + + diff --git a/src/Umbraco.Tests/UdiTests.cs b/src/Umbraco.Tests/UdiTests.cs index 8654f4b458..2587e245aa 100644 --- a/src/Umbraco.Tests/UdiTests.cs +++ b/src/Umbraco.Tests/UdiTests.cs @@ -118,7 +118,7 @@ namespace Umbraco.Tests Assert.AreEqual(Guid.Empty, ((GuidUdi)guidUdi).Guid); // can create a range - var range = new UdiRange(stringUdi, DeploySelector.ChildrenOfThis); + var range = new UdiRange(stringUdi, Constants.DeploySelector.ChildrenOfThis); // cannot create invalid ranges Assert.Throws(() => new UdiRange(guidUdi, "x")); @@ -142,14 +142,14 @@ namespace Umbraco.Tests Assert.AreEqual(DeployEntityType.AnyGuid, dudi.EntityType); Assert.AreEqual(guid, ((GuidUdi)dudi).Guid); - var range = new UdiRange(udi, DeploySelector.ChildrenOfThis); + var range = new UdiRange(udi, Constants.DeploySelector.ChildrenOfThis); json = JsonConvert.SerializeObject(range, settings); Assert.AreEqual(string.Format("\"umb://any-guid/{0:N}?children\"", guid), json); var drange = JsonConvert.DeserializeObject(json, settings); Assert.AreEqual(udi, drange.Udi); Assert.AreEqual(string.Format("umb://any-guid/{0:N}", guid), drange.Udi.UriValue.ToString()); - Assert.AreEqual(DeploySelector.ChildrenOfThis, drange.Selector); + Assert.AreEqual(Constants.DeploySelector.ChildrenOfThis, drange.Selector); } } } \ No newline at end of file From b7d37af933f615deb96cfb1dbdca4b9ae678b884 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 16 Jan 2017 11:04:28 +0100 Subject: [PATCH 238/599] Bumps version --- build/UmbracoVersion.txt | 2 +- src/SolutionInfo.cs | 4 ++-- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index 78653d30ea..7b0909cd23 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,2 +1,2 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) -7.5.7 \ No newline at end of file +7.5.8 \ No newline at end of file diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 7ed0a29d79..9889522e34 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -11,5 +11,5 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyFileVersion("7.5.7")] -[assembly: AssemblyInformationalVersion("7.5.7")] \ No newline at end of file +[assembly: AssemblyFileVersion("7.5.8")] +[assembly: AssemblyInformationalVersion("7.5.8")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 86c64872ff..0bc8eee404 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration { public class UmbracoVersion { - private static readonly Version Version = new Version("7.5.7"); + private static readonly Version Version = new Version("7.5.8"); /// /// Gets the current version of Umbraco. diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 7b2dd06c89..c4134d6850 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -2421,9 +2421,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\" True True - 7570 + 7580 / - http://localhost:7570 + http://localhost:7580 False False From 3bdc2e63abda90cb03f1d62d0bc935a78119a0a3 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 14:45:50 +0100 Subject: [PATCH 239/599] get url for items + set published/unpublished state --- .../components/umbnodepreview.directive.js | 1 + .../src/common/resources/entity.resource.js | 35 +++++- .../src/less/components/umb-node-preview.less | 8 ++ .../views/components/umb-node-preview.html | 2 +- .../contentpicker/contentpicker.controller.js | 100 ++++++++++++------ .../contentpicker/contentpicker.html | 1 + src/Umbraco.Web/Editors/EntityController.cs | 31 ++++++ 7 files changed, 140 insertions(+), 38 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js index 62a8bc766e..8d63623efb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js @@ -15,6 +15,7 @@ icon: "=?", name: "=", description: "=?", + published: "=?", sortable: "=?", allowOpen: "=?", allowRemove: "=?", diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js index 914b601249..5e0f5deada 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js @@ -56,7 +56,7 @@ function entityResource($q, $http, umbRequestHelper) { * * ##usage *
    -         * entityResource.getPath(id)
    +         * entityResource.getPath(id, type)
              *    .then(function(pathArray) {
              *        alert('its here!');
              *    });
    @@ -77,6 +77,37 @@ function entityResource($q, $http, umbRequestHelper) {
                    'Failed to retrieve path for id:' + id);
             },
     
    +        /**
    +         * @ngdoc method
    +         * @name umbraco.resources.entityResource#getUrl
    +         * @methodOf umbraco.resources.entityResource
    +         *
    +         * @description
    +         * Returns a url, given a node ID and type
    +         *
    +         * ##usage
    +         * 
    +         * entityResource.getUrl(id, type)
    +         *    .then(function(url) {
    +         *        alert('its here!');
    +         *    });
    +         * 
    + * + * @param {Int} id Id of node to return the public url to + * @param {string} type Object type name + * @returns {Promise} resourcePromise object containing the url. + * + */ + getUrl: function(id, type) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "entityApiBaseUrl", + "GetUrl", + [{ id: id }, {type: type }])), + 'Failed to retrieve url for id:' + id); + }, + /** * @ngdoc method * @name umbraco.resources.entityResource#getById @@ -140,7 +171,7 @@ function entityResource($q, $http, umbRequestHelper) { query += "ids=" + item + "&"; }); - // if ids array is empty we need a empty variable in the querystring otherwise the service returns a error + // if ids array is empty we need a empty variable in the querystring otherwise the service returns a error if (ids.length === 0) { query += "ids=&"; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less index 3f51b09154..5ef9cc7e74 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -17,6 +17,14 @@ border-color: #d9d9d9; } +.umb-node-preview--unpublished { + .umb-node-preview__icon, + .umb-node-preview__name, + .umb-node-preview__description { + opacity: 0.6; + } +} + .umb-node-preview__icon { display: flex; width: 25px; diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html index b811ae8eec..f31f595a88 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-node-preview.html @@ -1,4 +1,4 @@ -
    +
    {{ name }}
    diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index e77e743f2b..f97aa2bf62 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -194,8 +194,19 @@ function contentPickerController($scope, dialogService, entityResource, contentR }); if (currIds.indexOf(item.id) < 0) { - item.icon = iconHelper.convertFromLegacyIcon(item.icon); - $scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon, path: item.path }); + + // get url for content and media items + if(entityType !== "Member") { + entityResource.getUrl(item.id, entityType).then(function(data){ + // update url + item.url = data.url; + // push item to render model + addSelectedItem(item); + }); + } else { + addSelectedItem(item); + } + } }; @@ -221,43 +232,28 @@ function contentPickerController($scope, dialogService, entityResource, contentR //load current data var modelIds = $scope.model.value ? $scope.model.value.split(',') : []; - var nodePromise = (entityType === "Document") ? contentResource.getByIds(modelIds) : entityResource.getByIds(modelIds, entityType); + var nodePromise = entityResource.getByIds(modelIds, entityType); nodePromise.then(function (data) { - _.each(modelIds, function (id, i) { - var entity = _.find(data, function (d) { - return d.id == id; - }); - - if (entity) { - - // set icon - if(entity.icon) { - entity.icon = iconHelper.convertFromLegacyIcon(entity.icon); - } - - // set default icon - if (!entity.icon) { - switch (entityType) { - case "Document": - entity.icon = "icon-document"; - break; - case "Media": - entity.icon = "icon-picture"; - break; - case "Member": - entity.icon = "icon-user"; - break; - } - } - - var url = (entity.urls && entity.urls.length > 0) ? entity.urls[0] : ""; - var path = ($scope.model.config.showPathOnHover) ? entity.path : ""; - - $scope.renderModel.push({ name: entity.name, id: entity.id, icon: entity.icon, path: path, url: url }); + _.each(modelIds, function (id, i) { + var entity = _.find(data, function (d) { + return d.id == id; + }); + + if (entity) { + // get url for content and media items + if(entityType !== "Member") { + entityResource.getUrl(entity.id, entityType).then(function(data){ + // update url + entity.url = data.url; + // push item to render model + addSelectedItem(entity); + }); + } else { + addSelectedItem(entity); } - + } }); @@ -266,6 +262,40 @@ function contentPickerController($scope, dialogService, entityResource, contentR }); + function addSelectedItem(item) { + + // set icon + if(item.icon) { + item.icon = iconHelper.convertFromLegacyIcon(item.icon); + } + + // set default icon + if (!item.icon) { + switch (entityType) { + case "Document": + item.icon = "icon-document"; + break; + case "Media": + item.icon = "icon-picture"; + break; + case "Member": + item.icon = "icon-user"; + break; + } + } + + $scope.renderModel.push({ + "name": item.name, + "id": item.id, + "icon": item.icon, + "path": item.path, + "url": item.url, + "published": (item.metaData.IsPublished === false && entityType === "Document") ? false : true + // only content supports published/unpublished content so we set everything else to published so the UI looks correct + }); + + } + } angular.module('umbraco').controller("Umbraco.PropertyEditors.ContentPickerController", contentPickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index abcb7f20c0..a8076ff987 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -8,6 +8,7 @@ ng-repeat="node in renderModel" icon="node.icon" name="node.name" + published="node.published" description="node.url" sortable="sortable" allow-remove="allowRemoveButton" diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 596e27e3a5..cfc04a7f20 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -144,6 +144,37 @@ namespace Umbraco.Web.Editors return foundContent.Path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse); } + + + + public dynamic GetUrl(int id, UmbracoEntityTypes type) + { + dynamic result = new System.Dynamic.ExpandoObject(); + + + if(type == UmbracoEntityTypes.Document) + { + var foundUrl = Umbraco.Url(id); + if (string.IsNullOrEmpty(foundUrl) == false && foundUrl != "#") + { + result.url = foundUrl; + return result; + } + } + + var ancestors = GetAncestors(id, type); + + //if content, skip the first node for replicating NiceUrl defaults + if(type == UmbracoEntityTypes.Document) { + ancestors = ancestors.Skip(1); + } + + result.url = "/" + string.Join("/", ancestors.Select(x => x.Name) ); + + + return result; + } + /// /// Gets an entity by it's unique id if the entity supports that /// From ace111aefb268e7c472a3082de65f88b6e912015 Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Mon, 16 Jan 2017 16:50:22 +0100 Subject: [PATCH 240/599] Adding support for get, save and rename to new Partial View editor --- .../src/common/resources/codefile.resource.js | 168 ++++++++++++ .../src/views/partialviews/edit.controller.js | 10 +- .../src/views/partialviews/edit.html | 5 +- src/Umbraco.Web/Editors/CodeFileController.cs | 239 ++++++++++++------ .../Models/ContentEditing/CodeFileDisplay.cs | 18 +- .../Models/Mapping/CodeFileDisplayMapper.cs | 4 +- 6 files changed, 352 insertions(+), 92 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js new file mode 100644 index 0000000000..cb0f821abd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js @@ -0,0 +1,168 @@ +/** + * @ngdoc service + * @name umbraco.resources.codefileResource + * @description Loads in data for files that contain code such as js scripts, partial views and partial view macros + **/ +function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) { + + return { + + /** + * @ngdoc method + * @name umbraco.resources.codefileResource#getByPath + * @methodOf umbraco.resources.codefileResource + * + * @description + * Gets a codefile item with a given path + * + * ##usage + *
    +         * codefileResource.getByPath('partialView', 'oooh-la-la')
    +         *    .then(function(codefile) {
    +         *        alert('its here!');
    +         *    });
    +         * 
    + * + * @param {type} the type of script (partialView, partialViewMacro, script) + * @param {virtualpath} the virtual path of the script + * @returns {Promise} resourcePromise object. + * + */ + getByPath: function (type, virtualpath) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "codeFileApiBaseUrl", + "GetByPath", + [{ type: type }, {virtualPath: virtualpath }])), + "Failed to retrieve data for " + type + " from virtual path " + virtualpath); + }, + + /** + * @ngdoc method + * @name umbraco.resources.templateResource#getByAlias + * @methodOf umbraco.resources.templateResource + * + * @description + * Gets a template item with a given alias + * + * ##usage + *
    +         * templateResource.getByAlias("upload")
    +         *    .then(function(template) {
    +         *        alert('its here!');
    +         *    });
    +         * 
    + * + * @param {String} alias Alias of template to retrieve + * @returns {Promise} resourcePromise object. + * + */ + getByAlias: function (alias) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "templateApiBaseUrl", + "GetByAlias", + [{ alias: alias }])), + "Failed to retrieve data for template with alias: " + alias); + }, + + + /** + * @ngdoc method + * @name umbraco.resources.templateResource#getScaffold + * @methodOf umbraco.resources.templateResource + * + * @description + * Returns a scaffold of an empty template item + * + * The scaffold is used to build editors for templates that has not yet been populated with data. + * + * ##usage + *
    +         * templateResource.getScaffold()
    +         *    .then(function(template) {
    +         *        alert('its here!');
    +         *    });
    +         * 
    + * + * @returns {Promise} resourcePromise object containing the template scaffold. + * + */ + getScaffold: function (id) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "templateApiBaseUrl", + "GetScaffold", + [{ id: id }])), + "Failed to retrieve data for empty template"); + }, + + /** + * @ngdoc method + * @name umbraco.resources.templateResource#deleteById + * @methodOf umbraco.resources.templateResource + * + * @description + * Deletes a template with a given id + * + * ##usage + *
    +         * templateResource.deleteById(1234)
    +         *    .then(function() {
    +         *        alert('its gone!');
    +         *    });
    +         * 
    + * + * @param {Int} id id of template to delete + * @returns {Promise} resourcePromise object. + * + */ + deleteByPath: function (type, virtualpath) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "codeFileApiBaseUrl", + "Delete", + [{ type: type }, { virtualPath: virtualpath}])), + "Failed to delete item " + id); + }, + + /** + * @ngdoc method + * @name umbraco.resources.templateResource#save + * @methodOf umbraco.resources.templateResource + * + * @description + * Saves or update a template + * + * ##usage + *
    +         * templateResource.save(template)
    +         *    .then(function(template) {
    +         *        alert('its saved!');
    +         *    });
    +         * 
    + * + * @param {Object} template object to save + * @returns {Promise} resourcePromise object. + * + */ + save: function (partialView) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "codeFileApiBaseUrl", + "PostSave"), + partialView), + "Failed to save data for partialView " + partialView.virtualPath); + } + }; +} + +angular.module("umbraco.resources").factory("codefileResource", codefileResource); diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js index 45065a8e89..cf377259af 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function PartialViewsEditController($scope, $routeParams, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, angularHelper, $timeout, contentEditingHelper, localizationService, templateHelper) { + function PartialViewsEditController($scope, $routeParams, codefileResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, angularHelper, $timeout, contentEditingHelper, localizationService, templateHelper) { var vm = this; var localizeSaving = localizationService.localize("general_saving"); @@ -33,7 +33,7 @@ contentEditingHelper.contentEditorPerformSave({ statusMessage: localizeSaving, - saveMethod: templateResource.save, + saveMethod: codefileResource.save, scope: $scope, content: vm.partialView, //We do not redirect on failure for stylesheets - this is because it is not possible to actually save the doc @@ -229,11 +229,11 @@ //we need to load this somewhere, for now its here. assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css"); if ($routeParams.create) { - templateResource.getScaffold().then(function (partialView) { + codefileResource.getScaffold().then(function (partialView) { ready(partialView); }); } else { - templateResource.getById($routeParams.id).then(function (partialView) { + codefileResource.getByPath('partialViews', $routeParams.id).then(function (partialView) { ready(partialView); }); } @@ -246,7 +246,7 @@ //sync state editorState.set(vm.partialView); - navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) { + navigationService.syncTree({ tree: "partialViews", path: vm.partialView.virtualPath, forceReload: true }).then(function (syncArgs) { vm.page.menu.currentNode = syncArgs.node; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html index 647844e3d5..fe5f061141 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html @@ -13,7 +13,8 @@ name="vm.partialView.name" hide-alias="true" description="vm.partialView.virtualPath" - menu="vm.page.menu" + menu="vm.page.menu" + description-locked="true" hide-icon="true"> @@ -58,7 +59,7 @@
    + model="vm.partialView.content">
    diff --git a/src/Umbraco.Web/Editors/CodeFileController.cs b/src/Umbraco.Web/Editors/CodeFileController.cs index 7c1e1d2430..647d49c8c9 100644 --- a/src/Umbraco.Web/Editors/CodeFileController.cs +++ b/src/Umbraco.Web/Editors/CodeFileController.cs @@ -8,145 +8,222 @@ using System.Text; using System.Threading.Tasks; using System.Web.Http; using Umbraco.Core.Models; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; +using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Editors { [PluginController("UmbracoApi")] - public class CodeFileController : UmbracoAuthorizedJsonController + [PrefixlessBodyModelValidator] + [UmbracoApplicationAuthorizeAttribute(Core.Constants.Applications.Settings)] + public class CodeFileController : BackOfficeNotificationsController { + [ValidationFilter] public HttpResponseMessage PostCreate(string type, CodeFileDisplay display) { switch (type) { - case "partialView": + case Core.Constants.Trees.PartialViews: var view = new PartialView(display.VirtualPath); - var result = Services.FileService.CreatePartialView(view, display.Snippet, Security.CurrentUser.Id); - return result.Success == true ? Request.CreateResponse(HttpStatusCode.OK, result.Result) : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); - case "partialViewMacro": + var result = Services.FileService.CreatePartialView(view, display.Snippet, Security.CurrentUser.Id); + return result.Success == true ? Request.CreateResponse(HttpStatusCode.OK) : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); + case Core.Constants.Trees.PartialViewMacros: var viewMacro = new PartialView(display.VirtualPath); var resultMacro = Services.FileService.CreatePartialViewMacro(viewMacro, display.Snippet, Security.CurrentUser.Id); - return resultMacro.Success == true ? Request.CreateResponse(HttpStatusCode.OK, resultMacro.Result) : Request.CreateNotificationValidationErrorResponse(resultMacro.Exception.Message); - case "script": + return resultMacro.Success == true ? Request.CreateResponse(HttpStatusCode.OK) : Request.CreateNotificationValidationErrorResponse(resultMacro.Exception.Message); + case Core.Constants.Trees.Scripts: var script = new Script(display.VirtualPath); Services.FileService.SaveScript(script, Security.CurrentUser.Id); return Request.CreateResponse(HttpStatusCode.OK); default: - throw new ArgumentException("File Type not supported", "type"); + return Request.CreateResponse(HttpStatusCode.NotFound); } } public CodeFileDisplay GetByPath(string type, string virtualPath) { - switch (type) + if (string.IsNullOrWhiteSpace(type) == false && string.IsNullOrWhiteSpace(virtualPath) == false) { - case "partialView": + if (type == Core.Constants.Trees.PartialViews) + { var view = Services.FileService.GetPartialView(virtualPath); - return view == null ? null : Mapper.Map(view); - case "partialViewMacro": + if (view != null) + { + var display = Mapper.Map(view); + display.FileType = Core.Constants.Trees.PartialViews; + return display; + } + else + { + return null; + } + } + else if (type == Core.Constants.Trees.PartialViewMacros) + { var viewMacro = Services.FileService.GetPartialViewMacro(virtualPath); - return viewMacro == null ? null : Mapper.Map(viewMacro); - case "script": + if (viewMacro != null) + { + var display = Mapper.Map(viewMacro); + display.FileType = Core.Constants.Trees.PartialViewMacros; + return display; + } + else + { + return null; + } + } + else if (type == Core.Constants.Trees.Scripts) + { var script = Services.FileService.GetScriptByName(virtualPath); - return script == null ? null : Mapper.Map(script); - default: - throw new ArgumentException("File Type not supported", "type"); + if (script != null) + { + var display = Mapper.Map(script); + display.FileType = Core.Constants.Trees.Scripts; + return display; + } + else + { + return null; + } + + } + else + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + } + else + { + throw new HttpResponseException(HttpStatusCode.NotFound); } } [HttpDelete] + [HttpPost] public HttpResponseMessage Delete(string type, string virtualPath) { - switch (type) - { - case "partialView": - if (Services.FileService.DeletePartialView(virtualPath, Security.CurrentUser.Id)) - { - return Request.CreateResponse(HttpStatusCode.OK); - } - else - { - return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View found with the specified path"); - } - break; - case "partialViewMacro": - if (Services.FileService.DeletePartialViewMacro(virtualPath, Security.CurrentUser.Id)) - { - return Request.CreateResponse(HttpStatusCode.OK); - } - else - { - return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro found with the specified path"); - } - break; - case "script": - if (Services.FileService.GetScriptByName(virtualPath) != null) - { - Services.FileService.DeleteScript(virtualPath, Security.CurrentUser.Id); - return Request.CreateResponse(HttpStatusCode.OK); - } - else - { - return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script found with the specified path"); - } - break; - default: - throw new ArgumentException("File Type not supported", "type"); - } - } - - public CodeFileDisplay PostSave(string type, CodeFileDisplay display) - { - if (display == null) - { - throw new ArgumentNullException("No file object has been passed"); - } - else + if (string.IsNullOrWhiteSpace(type) == false && string.IsNullOrWhiteSpace(virtualPath) == false) { switch (type) { - case "partialView": - var view = Services.FileService.GetPartialView(display.VirtualPath); - if (view != null) + case Core.Constants.Trees.PartialViews: + if (Services.FileService.DeletePartialView(virtualPath, Security.CurrentUser.Id)) { - view.Content = display.Content; - Services.FileService.SavePartialView(view, Security.CurrentUser.Id); + return Request.CreateResponse(HttpStatusCode.OK); } else { - throw new ArgumentNullException($"File doesn't exist - {display.VirtualPath}"); + return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View found with the specified path"); } break; - case "partialViewMacro": - var viewMacro = Services.FileService.GetPartialViewMacro(display.VirtualPath); - if (viewMacro != null) + case Core.Constants.Trees.PartialViewMacros: + if (Services.FileService.DeletePartialViewMacro(virtualPath, Security.CurrentUser.Id)) { - viewMacro.Content = display.Content; - Services.FileService.SavePartialViewMacro(viewMacro, Security.CurrentUser.Id); + return Request.CreateResponse(HttpStatusCode.OK); } else { - throw new ArgumentNullException($"File doesn't exist - {display.VirtualPath}"); + return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro found with the specified path"); } break; - case "script": - var script = Services.FileService.GetScriptByName(display.VirtualPath); - if (script != null) + case Core.Constants.Trees.Scripts: + if (Services.FileService.GetScriptByName(virtualPath) != null) { - script.Content = display.Content; - Services.FileService.SaveScript(script, Security.CurrentUser.Id); + Services.FileService.DeleteScript(virtualPath, Security.CurrentUser.Id); + return Request.CreateResponse(HttpStatusCode.OK); } else { - throw new ArgumentNullException($"File doesn't exist - {display.VirtualPath}"); + return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script found with the specified path"); } break; default: - throw new ArgumentException("File Type not supported", "type"); + return Request.CreateResponse(HttpStatusCode.NotFound); + } + } + else + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + } + + public CodeFileDisplay PostSave(CodeFileDisplay display) + { + if (ModelState.IsValid == false) + { + throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); + } + + if (display == null && string.IsNullOrWhiteSpace(display.FileType) == true) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + else + { + if (display.FileType == Core.Constants.Trees.PartialViews) + { + var view = Services.FileService.GetPartialView(display.VirtualPath); + if (view != null) + { + view.Content = display.Content; + view.Path = display.Name; + var result = Services.FileService.SavePartialView(view, Security.CurrentUser.Id); + if (result.Success == true) + { + return Mapper.Map(view, display); + } else + { + display.AddErrorNotification( + Services.TextService.Localize("speechBubbles/partialViewErrorHeader"), + Services.TextService.Localize("speechBubbles/partialViewErrorText")); + } + } + else + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + } + else if (display.FileType == Core.Constants.Trees.PartialViewMacros) + { + var viewMacro = Services.FileService.GetPartialViewMacro(display.VirtualPath); + if (viewMacro != null) + { + viewMacro.Content = display.Content; + var result = Services.FileService.SavePartialViewMacro(viewMacro, Security.CurrentUser.Id); + if (result.Success == false) + { + display.AddErrorNotification( + Services.TextService.Localize("speechBubbles/partialViewErrorHeader"), + Services.TextService.Localize("speechBubbles/partialViewErrorText")); + } + } + else + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + } + else if (display.FileType == Core.Constants.Trees.Scripts) + { + var script = Services.FileService.GetScriptByName(display.VirtualPath); + if (script != null) + { + script.Content = display.Content; + Services.FileService.SaveScript(script, Security.CurrentUser.Id); + + } + else + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + } + else + { + throw new HttpResponseException(HttpStatusCode.NotFound); } return display; } diff --git a/src/Umbraco.Web/Models/ContentEditing/CodeFileDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/CodeFileDisplay.cs index 1ab5f1ddb4..c63e2b98c0 100644 --- a/src/Umbraco.Web/Models/ContentEditing/CodeFileDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/CodeFileDisplay.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Runtime.Serialization; using System.Text; @@ -8,13 +9,24 @@ using System.Threading.Tasks; namespace Umbraco.Web.Models.ContentEditing { [DataContract(Name = "scriptFile", Namespace = "")] - public class CodeFileDisplay + public class CodeFileDisplay : INotificationModel { - [DataMember(Name = "virtualPath")] + [DataMember(Name = "virtualPath", IsRequired = true)] public string VirtualPath { get; set; } - [DataMember(Name = "content")] + + [DataMember(Name = "name", IsRequired = true)] + public string Name { get; set; } + + [DataMember(Name = "content", IsRequired = true)] public string Content { get; set; } + + [DataMember(Name = "fileType", IsRequired = true)] + public string FileType { get; set; } + [DataMember(Name = "snippet")] + [ReadOnly(true)] public string Snippet { get; set; } + + public List Notifications { get; private set; } } } diff --git a/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs b/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs index d777d27c43..d81d78e6d3 100644 --- a/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs @@ -16,9 +16,11 @@ namespace Umbraco.Web.Models.Mapping public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) { config.CreateMap() - .ForMember(x => x.Snippet, exp => exp.Ignore()); + .ForMember(x => x.Notifications, exp => exp.Ignore()) + .ForMember(x => x.Snippet, exp => exp.Ignore()); config.CreateMap() + .ForMember(x => x.Notifications, exp => exp.Ignore()) .ForMember(x => x.Snippet, exp => exp.Ignore()); config.CreateMap() From c0a2a78f3d28cf56838e33a0d23699fd1b78f596 Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Mon, 16 Jan 2017 19:13:05 +0100 Subject: [PATCH 241/599] Fixes issue with entities not being returned from Keys (Guids) --- src/Umbraco.Web/Editors/EntityController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 596e27e3a5..fa3e21a1eb 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -614,7 +614,7 @@ namespace Umbraco.Web.Editors .Select(Mapper.Map); // entities are in "some" order, put them back in order - var xref = entities.ToDictionary(x => x.Id); + var xref = entities.ToDictionary(x => x.Key); var result = keysArray.Select(x => xref.ContainsKey(x) ? xref[x] : null).Where(x => x != null); return result; From a162f21a70e1f370d82e7a63b417a34d973c486d Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Mon, 16 Jan 2017 19:49:57 +0100 Subject: [PATCH 242/599] Fixing missing mapping - let the green build lights appear --- src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs b/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs index d81d78e6d3..60edb87105 100644 --- a/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/CodeFileDisplayMapper.cs @@ -16,14 +16,17 @@ namespace Umbraco.Web.Models.Mapping public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext) { config.CreateMap() - .ForMember(x => x.Notifications, exp => exp.Ignore()) - .ForMember(x => x.Snippet, exp => exp.Ignore()); + .ForMember(x => x.FileType, exp => exp.Ignore()) + .ForMember(x => x.Notifications, exp => exp.Ignore()) + .ForMember(x => x.Snippet, exp => exp.Ignore()); config.CreateMap() + .ForMember(x => x.FileType, exp => exp.Ignore()) .ForMember(x => x.Notifications, exp => exp.Ignore()) .ForMember(x => x.Snippet, exp => exp.Ignore()); config.CreateMap() + .ForMember(x => x.Id, exp => exp.Ignore()) .ForMember(x => x.Key, exp => exp.Ignore()) .ForMember(x => x.Path, exp => exp.Ignore()) .ForMember(x => x.CreateDate, exp => exp.Ignore()) @@ -35,6 +38,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(x => x.HasIdentity, exp => exp.Ignore()); config.CreateMap() + .ForMember(x => x.Id, exp => exp.Ignore()) .ForMember(x => x.Key, exp => exp.Ignore()) .ForMember(x => x.Path, exp => exp.Ignore()) .ForMember(x => x.CreateDate, exp => exp.Ignore()) From 1261f6b159db18d1b3e36f134ea0352a3f2d627a Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 16 Jan 2017 17:41:46 +0100 Subject: [PATCH 243/599] U4-9322 - getting tests to run --- src/SQLCE4Umbraco/SqlCeApplicationBlock.cs | 2 +- src/Umbraco.Core/ApplicationContext.cs | 9 +- src/Umbraco.Core/DatabaseContext.cs | 5 +- .../Persistence/DefaultDatabaseFactory.cs | 7 +- src/Umbraco.Core/Persistence/PetaPoco.cs | 12 +- .../Repositories/NotificationsRepository.cs | 7 +- .../Persistence/UmbracoDatabase.cs | 14 + .../UnitOfWork/PetaPocoUnitOfWork.cs | 80 ++-- src/Umbraco.Core/Scoping/IScope.cs | 13 + src/Umbraco.Core/Scoping/IScopeProvider.cs | 37 ++ .../Scoping/IScopeProviderInternal.cs | 24 +- src/Umbraco.Core/Scoping/NoScope.cs | 43 +- src/Umbraco.Core/Scoping/Scope.cs | 79 +++- src/Umbraco.Core/Scoping/ScopeProvider.cs | 65 ++- src/Umbraco.Tests/ApplicationContextTests.cs | 11 +- .../PublishedMediaCacheTests.cs | 32 +- .../NotificationsRepositoryTest.cs | 167 ++++---- src/Umbraco.Tests/Scoping/ScopeTests.cs | 391 ++++++++++++++++++ .../Security/BackOfficeCookieManagerTests.cs | 5 +- .../Services/ContentServiceTests.cs | 26 +- .../TestHelpers/BaseDatabaseFactoryTest.cs | 6 + src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + src/umbraco.cms/businesslogic/Content.cs | 2 +- src/umbraco.datalayer/SqlHelper.cs | 37 +- .../SqlHelpers/MySql/MySqlHelper.cs | 6 +- 25 files changed, 844 insertions(+), 237 deletions(-) create mode 100644 src/Umbraco.Tests/Scoping/ScopeTests.cs diff --git a/src/SQLCE4Umbraco/SqlCeApplicationBlock.cs b/src/SQLCE4Umbraco/SqlCeApplicationBlock.cs index fd778bbfb3..2dd0f26e90 100644 --- a/src/SQLCE4Umbraco/SqlCeApplicationBlock.cs +++ b/src/SQLCE4Umbraco/SqlCeApplicationBlock.cs @@ -240,7 +240,7 @@ namespace SqlCE4Umbraco { var cmd = trx == null ? new SqlCeCommand(commandText, conn) : new SqlCeCommand(commandText, conn, trx); AttachParameters(cmd, commandParameters); - return cmd.ExecuteReader(CommandBehavior.CloseConnection); + return cmd.ExecuteReader(); } catch { diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index 88c2d65926..4be0787a31 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -422,10 +422,17 @@ namespace Umbraco.Core this.ApplicationCache = null; if (_databaseContext != null) //need to check the internal field here { + if (_databaseContext.ScopeProvider.AmbientScope != null) + { + var scope = _databaseContext.ScopeProvider.AmbientScope; + scope.Dispose(); + } + /* if (DatabaseContext.IsDatabaseConfigured && DatabaseContext.Database != null) { DatabaseContext.Database.Dispose(); - } + } + */ } this.DatabaseContext = null; this.Services = null; diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs index ec5607f641..43a755a780 100644 --- a/src/Umbraco.Core/DatabaseContext.cs +++ b/src/Umbraco.Core/DatabaseContext.cs @@ -113,8 +113,9 @@ namespace Umbraco.Core { get { - var scope = ScopeProvider.AmbientScope; - return scope != null ? scope.Database : ScopeProvider.CreateNoScope().Database; + return ScopeProvider.AmbientOrNoScope.Database; + //var scope = ScopeProvider.AmbientScope; + //return scope != null ? scope.Database : ScopeProvider.CreateNoScope().Database; } } diff --git a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs index 69026962ac..46a2defd66 100644 --- a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core.Persistence //private static readonly object Locker = new object(); // bwc imposes a weird x-dependency between database factory and scope provider... - public ScopeProvider ScopeProvider { get; set; } + public IScopeProviderInternal ScopeProvider { get; set; } /// /// Constructor accepting custom connection string @@ -61,8 +61,9 @@ namespace Umbraco.Core.Persistence public UmbracoDatabase CreateDatabase() { - var scope = ScopeProvider.AmbientScope; - return scope != null ? scope.Database : ScopeProvider.CreateNoScope().Database; + return ScopeProvider.AmbientOrNoScope.Database; + //var scope = ScopeProvider.AmbientScope; + //return scope != null ? scope.Database : ScopeProvider.CreateNoScope().Database; /* UmbracoDatabase database; diff --git a/src/Umbraco.Core/Persistence/PetaPoco.cs b/src/Umbraco.Core/Persistence/PetaPoco.cs index b569b1c45d..ecd5280012 100644 --- a/src/Umbraco.Core/Persistence/PetaPoco.cs +++ b/src/Umbraco.Core/Persistence/PetaPoco.cs @@ -332,8 +332,16 @@ namespace Umbraco.Core.Persistence if (_transactionDepth == 1) { OpenSharedConnection(); - _transaction = _sharedConnection.BeginTransaction(isolationLevel); - _transactionCancelled = false; + try + { + _transaction = _sharedConnection.BeginTransaction(isolationLevel); + } + + catch (Exception e) + { + throw; + } + _transactionCancelled = false; OnBeginTransaction(); } else if (isolationLevel > _transaction.IsolationLevel) diff --git a/src/Umbraco.Core/Persistence/Repositories/NotificationsRepository.cs b/src/Umbraco.Core/Persistence/Repositories/NotificationsRepository.cs index 9e6e3cf47c..aa7ebeacc8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/NotificationsRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/NotificationsRepository.cs @@ -9,7 +9,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { - internal class NotificationsRepository + internal class NotificationsRepository : IDisposable { private readonly IDatabaseUnitOfWork _unitOfWork; @@ -102,5 +102,10 @@ namespace Umbraco.Core.Persistence.Repositories _unitOfWork.Database.Insert(dto); return new Notification(dto.NodeId, dto.UserId, dto.Action, nodeType); } + + public void Dispose() + { + _unitOfWork.Dispose(); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs index 44a3db9424..46284b1a2d 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs @@ -57,6 +57,20 @@ namespace Umbraco.Core.Persistence /// internal bool EnableSqlTrace { get; set; } + public bool InTransaction { get; private set; } + + public override void OnBeginTransaction() + { + base.OnBeginTransaction(); + InTransaction = true; + } + + public override void OnEndTransaction() + { + base.OnEndTransaction(); + InTransaction = false; + } + #if DEBUG_DATABASES private const bool EnableSqlTraceDefault = true; #else diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs index 6c71614c1a..6499ea29fe 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs @@ -10,15 +10,16 @@ namespace Umbraco.Core.Persistence.UnitOfWork ///
    internal class PetaPocoUnitOfWork : DisposableObject, IDatabaseUnitOfWork { - private readonly IScope _scope; - - /// - /// Used for testing - /// - internal Guid InstanceId { get; private set; } - - private Guid _key; private readonly Queue _operations = new Queue(); + private readonly IScopeProvider _scopeProvider; + private bool _completeScope = true; // scope is completed by default + private IScope _scope; + private Guid _key; + + /// + /// Used for testing + /// + internal Guid InstanceId { get; private set; } /// /// Creates a new unit of work instance @@ -29,7 +30,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// internal PetaPocoUnitOfWork(IScopeProvider scopeProvider) { - _scope = scopeProvider.CreateScope(); + _scopeProvider = scopeProvider; _key = Guid.NewGuid(); InstanceId = Guid.NewGuid(); } @@ -102,34 +103,34 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// internal void Commit(Action transactionCompleting) { - using (var transaction = Database.GetTransaction()) + // this happens in a scope-managed transaction + + // in case anything goes wrong + _completeScope = false; + + while (_operations.Count > 0) { - while (_operations.Count > 0) + var operation = _operations.Dequeue(); + switch (operation.Type) { - var operation = _operations.Dequeue(); - switch (operation.Type) - { - case TransactionType.Insert: - operation.Repository.PersistNewItem(operation.Entity); - break; - case TransactionType.Delete: - operation.Repository.PersistDeletedItem(operation.Entity); - break; - case TransactionType.Update: - operation.Repository.PersistUpdatedItem(operation.Entity); - break; - } + case TransactionType.Insert: + operation.Repository.PersistNewItem(operation.Entity); + break; + case TransactionType.Delete: + operation.Repository.PersistDeletedItem(operation.Entity); + break; + case TransactionType.Update: + operation.Repository.PersistUpdatedItem(operation.Entity); + break; } - - //Execute the callback if there is one - if (transactionCompleting != null) - { - transactionCompleting(Database); - } - - transaction.Complete(); } + if (transactionCompleting != null) + transactionCompleting(Database); + + // all is ok + _completeScope = true; + // Clear everything _operations.Clear(); _key = Guid.NewGuid(); @@ -140,7 +141,16 @@ namespace Umbraco.Core.Persistence.UnitOfWork get { return _key; } } - public UmbracoDatabase Database {get { return _scope.Database; } } + public UmbracoDatabase Database + { + get + { + if (_scope == null) + //throw new InvalidOperationException("Out-of-scope UnitOfWork."); + _scope = _scopeProvider.CreateScope(); + return _scope.Database; + } + } #region Operation @@ -179,7 +189,11 @@ namespace Umbraco.Core.Persistence.UnitOfWork protected override void DisposeResources() { _operations.Clear(); + if (_scope == null) return; + + if (_completeScope) _scope.Complete(); _scope.Dispose(); + _scope = null; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Scoping/IScope.cs b/src/Umbraco.Core/Scoping/IScope.cs index f56fcdd018..e379273f58 100644 --- a/src/Umbraco.Core/Scoping/IScope.cs +++ b/src/Umbraco.Core/Scoping/IScope.cs @@ -4,11 +4,24 @@ using Umbraco.Core.Persistence; namespace Umbraco.Core.Scoping { + /// + /// Represents a scope. + /// public interface IScope : IDisposeOnRequestEnd // implies IDisposable { + /// + /// Gets the scope database. + /// UmbracoDatabase Database { get; } + + /// + /// Gets the scope event messages. + /// IList Messages { get; } + /// + /// Completes the scope. + /// void Complete(); } } diff --git a/src/Umbraco.Core/Scoping/IScopeProvider.cs b/src/Umbraco.Core/Scoping/IScopeProvider.cs index 112a467cc3..e6237b3af9 100644 --- a/src/Umbraco.Core/Scoping/IScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/IScopeProvider.cs @@ -1,10 +1,47 @@ namespace Umbraco.Core.Scoping { + /// + /// Provides scopes. + /// public interface IScopeProvider { + /// + /// Creates an ambient scope. + /// + /// The created ambient scope. + /// + /// The created scope becomes the ambient scope. + /// If an ambient scope already exists, it becomes the parent of the created scope. + /// When the created scope is disposed, the parent scope becomes the ambient scope again. + /// IScope CreateScope(); + + /// + /// Creates a detached scope. + /// + /// A detached scope. + /// + /// A detached scope is not ambient and has no parent. + /// It is meant to be attached by . + /// IScope CreateDetachedScope(); + + /// + /// Attaches a scope. + /// + /// The scope to attach. + /// + /// Only a scope created by can be attached. + /// void AttachScope(IScope scope); + + /// + /// Detaches a scope. + /// + /// The detached scope. + /// + /// Only a scope previously attached by can be detached. + /// IScope DetachScope(); } } diff --git a/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs b/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs index 8726efbfb6..856d30a4da 100644 --- a/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs +++ b/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs @@ -1,8 +1,28 @@ namespace Umbraco.Core.Scoping { + /// + /// Provices scopes. + /// + /// Extends with internal features. internal interface IScopeProviderInternal : IScopeProvider { - IScope AmbientScope { get; set; } - IScope CreateNoScope(); + /// + /// Gets the ambient scope. + /// + IScope AmbientScope { get; } + + // fixme + IScope AmbientOrNoScope { get; } + + /// + /// Creates a instance. + /// + /// The created ambient scope. + /// + /// The created scope becomes the ambient scope. + /// If an ambient scope already exists, throws. + /// The instance can be eventually replaced by a real instance. + /// + //IScope CreateNoScope(); } } diff --git a/src/Umbraco.Core/Scoping/NoScope.cs b/src/Umbraco.Core/Scoping/NoScope.cs index d72d201060..5f67c28d94 100644 --- a/src/Umbraco.Core/Scoping/NoScope.cs +++ b/src/Umbraco.Core/Scoping/NoScope.cs @@ -8,6 +8,7 @@ namespace Umbraco.Core.Scoping internal class NoScope : IScope { private readonly ScopeProvider _scopeProvider; + private bool _disposed; private UmbracoDatabase _database; private IList _messages; @@ -17,18 +18,44 @@ namespace Umbraco.Core.Scoping _scopeProvider = scopeProvider; } - public bool HasDatabase { get { return _database != null; } } + //public bool HasDatabase { get { return _database != null; } } public UmbracoDatabase Database { - get { return _database ?? (_database = _scopeProvider.DatabaseFactory.CreateNewDatabase()); } + get + { + EnsureNotDisposed(); + return _database ?? (_database = _scopeProvider.DatabaseFactory.CreateNewDatabase()); + } } - public bool HasMessages { get { return _messages != null; } } + public UmbracoDatabase DatabaseOrNull + { + get + { + EnsureNotDisposed(); + return _database; + } + } + + //public bool HasMessages { get { return _messages != null; } } public IList Messages { - get { return _messages ?? (_messages = new List()); } + get + { + EnsureNotDisposed(); + return _messages ?? (_messages = new List()); + } + } + + public IList MessagesOrNull + { + get + { + EnsureNotDisposed(); + return _messages; + } } public void Complete() @@ -36,9 +63,17 @@ namespace Umbraco.Core.Scoping throw new NotImplementedException(); } + private void EnsureNotDisposed() + { + if (_disposed) + throw new ObjectDisposedException("this"); + } + public void Dispose() { + EnsureNotDisposed(); _scopeProvider.Disposing(this); + _disposed = true; GC.SuppressFinalize(this); } } diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index 327adec8ca..8956688faa 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -5,77 +5,100 @@ using Umbraco.Core.Persistence; namespace Umbraco.Core.Scoping { + // note - scope is not thread-safe obviously + internal class Scope : IScope { private readonly ScopeProvider _scopeProvider; + private bool _disposed; private bool? _completed; private UmbracoDatabase _database; private IList _messages; + // initializes a new scope public Scope(ScopeProvider scopeProvider, bool detachable = false) { _scopeProvider = scopeProvider; Detachable = detachable; } + // initializes a new scope in a nested scopes chain, with its parent public Scope(ScopeProvider scopeProvider, Scope parent) : this(scopeProvider) { ParentScope = parent; } + // initializes a new scope, replacing a NoScope instance public Scope(ScopeProvider scopeProvider, NoScope noScope) : this(scopeProvider) { - // stealing everything from NoScope - _database = noScope.HasDatabase ? noScope.Database : null; - _messages = noScope.HasMessages ? noScope.Messages : null; - if (_database != null) - { - // must not be in a transaction - if (_database.Connection != null) - throw new Exception(); - } + // steal everything from NoScope + _database = noScope.DatabaseOrNull; + _messages = noScope.MessagesOrNull; + + // make sure the NoScope can be replaced ie not in a transaction + if (_database != null && _database.InTransaction) + throw new Exception("NoScope instance is not free."); } + // a value indicating whether the scope is detachable + // ie whether it was created by CreateDetachedScope public bool Detachable { get; private set; } + // the parent scope (in a nested scopes chain) public Scope ParentScope { get; set; } + // the original scope (when attaching a detachable scope) public Scope OrigScope { get; set; } - public bool HasDatabase - { - get { return ParentScope == null ? _database != null : ParentScope.HasDatabase; } - } + //public bool HasDatabase + //{ + // get { return ParentScope == null ? _database != null : ParentScope.HasDatabase; } + //} + /// public UmbracoDatabase Database { get { + EnsureNotDisposed(); if (ParentScope != null) return ParentScope.Database; if (_database != null) { - if (_database.Connection == null) // stolen from noScope + if (_database.InTransaction == false) // stolen from noScope _database.BeginTransaction(); // a scope implies a transaction, always + // fixme - what-if exception? return _database; } - _database = _scopeProvider.DatabaseFactory.CreateNewDatabase(); - _database.BeginTransaction(); // a scope implies a transaction, always - return _database; + var database = _scopeProvider.DatabaseFactory.CreateNewDatabase(); + database.BeginTransaction(); // a scope implies a transaction, always + // fixme - should dispose db on exception? + return _database = database; } } - public bool HasMessages + public UmbracoDatabase DatabaseOrNull { - get { return ParentScope == null ? _messages != null : ParentScope.HasMessages; } + get + { + EnsureNotDisposed(); + return ParentScope == null ? _database : ParentScope.DatabaseOrNull; + } } + //public bool HasMessages + //{ + // get { return ParentScope == null ? _messages != null : ParentScope.HasMessages; } + //} + + /// public IList Messages { get { + EnsureNotDisposed(); if (ParentScope != null) return ParentScope.Messages; if (_messages == null) _messages = new List(); @@ -83,6 +106,16 @@ namespace Umbraco.Core.Scoping } } + public IList MessagesOrNull + { + get + { + EnsureNotDisposed(); + return ParentScope == null ? _messages : ParentScope.MessagesOrNull; + } + } + + /// public void Complete() { if (_completed.HasValue == false) @@ -111,9 +144,17 @@ namespace Umbraco.Core.Scoping } } + private void EnsureNotDisposed() + { + if (_disposed) + throw new ObjectDisposedException("this"); + } + public void Dispose() { + EnsureNotDisposed(); _scopeProvider.Disposing(this, _completed); + _disposed = true; GC.SuppressFinalize(this); } } diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs index f3b139827c..c2b918f812 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -66,31 +66,29 @@ namespace Umbraco.Core.Scoping } } + /// public IScope AmbientScope { get { return StaticAmbientScope; } set { StaticAmbientScope = value; } } - // fixme should we do... - // using (var s = scopeProvider.AttachScope(other)) - // { - // } - // can't because disposing => detach or commit? cannot tell! - // var scope = scopeProvider.CreateScope(); - // scope = scopeProvider.Detach(); - // scope.Detach(); - // scopeProvider.Attach(scope); - // ... do things ... - // scopeProvider.Detach(); - // scopeProvider.Attach(scope); - // scope.Dispose(); + /// + public IScope AmbientOrNoScope + { + get + { + return AmbientScope ?? (AmbientScope = new NoScope(this)); + } + } + /// public IScope CreateDetachedScope() { return new Scope(this, true); } + /// public void AttachScope(IScope other) { var otherScope = other as Scope; @@ -119,6 +117,7 @@ namespace Umbraco.Core.Scoping AmbientScope = otherScope; } + /// public IScope DetachScope() { var ambient = AmbientScope; @@ -141,17 +140,20 @@ namespace Umbraco.Core.Scoping return scope; } + /// public IScope CreateScope() { var ambient = AmbientScope; if (ambient == null) return AmbientScope = new Scope(this); + // replace noScope with a real one var noScope = ambient as NoScope; if (noScope != null) { // peta poco nulls the shared connection after each command unless there's a trx - if (noScope.HasDatabase && noScope.Database.Connection != null) + var database = noScope.DatabaseOrNull; + if (database != null && database.InTransaction) throw new Exception(); return AmbientScope = new Scope(this, noScope); } @@ -162,13 +164,6 @@ namespace Umbraco.Core.Scoping return AmbientScope = new Scope(this, scope); } - public IScope CreateNoScope() - { - var ambient = AmbientScope; - if (ambient != null) throw new Exception(); - return AmbientScope = new NoScope(this); - } - public void Disposing(IScope disposing, bool? completed = null) { if (disposing != AmbientScope) @@ -178,7 +173,13 @@ namespace Umbraco.Core.Scoping if (noScope != null) { // fixme - kinda legacy - if (noScope.HasDatabase) noScope.Database.Dispose(); + var noScopeDatabase = noScope.DatabaseOrNull; + if (noScopeDatabase != null) + { + if (noScopeDatabase.InTransaction) + throw new Exception(); + noScopeDatabase.Dispose(); + } AmbientScope = null; return; } @@ -198,27 +199,19 @@ namespace Umbraco.Core.Scoping // fixme - a scope is in a transaction only if ... there is a db transaction, or always? // what shall we do with events if not in a transaction? + // fixme - when completing... the db should be released, no need to dispose the db? // note - messages // at the moment we are totally not filtering the messages based on completion // status, so whether the scope is committed or rolled back makes no difference + var database = scope.DatabaseOrNull; + if (database == null) return; + if (completed.HasValue && completed.Value) - { - var database = scope.HasDatabase ? scope.Database : null; - if (database != null) - { - database.CompleteTransaction(); - } - } + database.CompleteTransaction(); else - { - var database = scope.HasDatabase ? scope.Database : null; - if (database != null) - { - database.AbortTransaction(); - } - } + database.AbortTransaction(); } } } diff --git a/src/Umbraco.Tests/ApplicationContextTests.cs b/src/Umbraco.Tests/ApplicationContextTests.cs index 61dabf6e3b..255093f325 100644 --- a/src/Umbraco.Tests/ApplicationContextTests.cs +++ b/src/Umbraco.Tests/ApplicationContextTests.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Profiling; +using Umbraco.Core.Scoping; using Umbraco.Core.Services; namespace Umbraco.Tests @@ -26,7 +27,7 @@ namespace Umbraco.Tests migrationEntryService.Setup(x => x.FindEntry(It.IsAny(), It.IsAny())) .Returns(Mock.Of()); - var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); + var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(true); dbCtx.Setup(x => x.CanConnect).Returns(true); @@ -48,7 +49,7 @@ namespace Umbraco.Tests migrationEntryService.Setup(x => x.FindEntry(It.IsAny(), It.IsAny())) .Returns((IMigrationEntry)null); - var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); + var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(true); dbCtx.Setup(x => x.CanConnect).Returns(true); @@ -68,7 +69,7 @@ namespace Umbraco.Tests var migrationEntryService = new Mock(); - var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); + var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(true); dbCtx.Setup(x => x.CanConnect).Returns(true); @@ -88,7 +89,7 @@ namespace Umbraco.Tests var migrationEntryService = new Mock(); - var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); + var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(false); dbCtx.Setup(x => x.CanConnect).Returns(true); @@ -108,7 +109,7 @@ namespace Umbraco.Tests var migrationEntryService = new Mock(); - var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); + var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(true); dbCtx.Setup(x => x.CanConnect).Returns(false); diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index 32bbd34a77..f3af12279c 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -25,7 +25,7 @@ namespace Umbraco.Tests.Cache.PublishedCache PublishedContentModelFactoryResolver.Current = new PublishedContentModelFactoryResolver(); base.FreezeResolution(); } - + [Test] public void Get_Root_Docs() { @@ -35,7 +35,7 @@ namespace Umbraco.Tests.Cache.PublishedCache var mRoot2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("MediaRoot2", mType, user, -1); var mChild1 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child1", mType, user, mRoot1.Id); var mChild2 = global::umbraco.cms.businesslogic.media.Media.MakeNew("Child2", mType, user, mRoot2.Id); - + var ctx = GetUmbracoContext("/test", 1234); var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application), ctx); var roots = cache.GetAtRoot(); @@ -125,7 +125,7 @@ namespace Umbraco.Tests.Cache.PublishedCache { child1, child2 }); - + Assert.AreEqual(2, dicDoc.Children.Count()); Assert.AreEqual(222333, dicDoc.Children.ElementAt(0).Id); Assert.AreEqual(444555, dicDoc.Children.ElementAt(1).Id); @@ -187,7 +187,7 @@ namespace Umbraco.Tests.Cache.PublishedCache private XmlDocument GetMediaXml() { var xml = @" - @@ -195,12 +195,12 @@ namespace Umbraco.Tests.Cache.PublishedCache ]> - + - + - + "; @@ -210,8 +210,8 @@ namespace Umbraco.Tests.Cache.PublishedCache return xmlDoc; } - private Dictionary GetDictionary( - int id, + private Dictionary GetDictionary( + int id, Guid key, int parentId, string idKey, @@ -241,13 +241,13 @@ namespace Umbraco.Tests.Cache.PublishedCache {"parentID", parentId.ToString()} }; } - + private PublishedMediaCache.DictionaryPublishedContent GetDictionaryDocument( string idKey = "id", string templateKey = "template", string nodeNameKey = "nodeName", string nodeTypeAliasKey = "nodeTypeAlias", - string pathKey = "path", + string pathKey = "path", int idVal = 1234, Guid keyVal = default(Guid), int parentIdVal = 321, @@ -265,12 +265,12 @@ namespace Umbraco.Tests.Cache.PublishedCache a => null, //we're not going to test this so ignore (dd, n) => new List(), - (dd, a) => dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(a)), + (dd, a) => dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(a)), null, false), //callback to get the children (dd, n) => children, - (dd, a) => dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(a)), + (dd, a) => dd.Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(a)), null, false); return dicDoc; @@ -300,7 +300,7 @@ namespace Umbraco.Tests.Cache.PublishedCache if (!updateDateVal.HasValue) updateDateVal = DateTime.Parse("2012-01-03"); - DoAssert((IPublishedContent)dicDoc, idVal, keyVal, templateIdVal, sortOrderVal, urlNameVal, nodeTypeAliasVal, nodeTypeIdVal, writerNameVal, + DoAssert((IPublishedContent)dicDoc, idVal, keyVal, templateIdVal, sortOrderVal, urlNameVal, nodeTypeAliasVal, nodeTypeIdVal, writerNameVal, creatorNameVal, writerIdVal, creatorIdVal, pathVal, createDateVal, updateDateVal, levelVal); //now validate the parentId that has been parsed, this doesn't exist on the IPublishedContent @@ -345,9 +345,9 @@ namespace Umbraco.Tests.Cache.PublishedCache Assert.AreEqual(createDateVal.Value, doc.CreateDate); Assert.AreEqual(updateDateVal.Value, doc.UpdateDate); Assert.AreEqual(levelVal, doc.Level); - + } - + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Persistence/Repositories/NotificationsRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/NotificationsRepositoryTest.cs index 54387a03f9..f2aa367ffd 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/NotificationsRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/NotificationsRepositoryTest.cs @@ -22,19 +22,20 @@ namespace Umbraco.Tests.Persistence.Repositories { var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); - var repo = new NotificationsRepository(unitOfWork); + using (var repo = new NotificationsRepository(unitOfWork)) + { + var node = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,123", SortOrder = 1, Text = "hello", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; + var result = unitOfWork.Database.Insert(node); + var entity = Mock.Of(e => e.Id == node.NodeId); + var user = Mock.Of(e => e.Id == node.UserId); - var node = new NodeDto {CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,123", SortOrder = 1, Text = "hello", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0}; - var result = unitOfWork.Database.Insert(node); - var entity = Mock.Of(e => e.Id == node.NodeId); - var user = Mock.Of(e => e.Id == node.UserId); + var notification = repo.CreateNotification(user, entity, "A"); - var notification = repo.CreateNotification(user, entity, "A"); - - Assert.AreEqual("A", notification.Action); - Assert.AreEqual(node.NodeId, notification.EntityId); - Assert.AreEqual(node.NodeObjectType, notification.EntityType); - Assert.AreEqual(node.UserId, notification.UserId); + Assert.AreEqual("A", notification.Action); + Assert.AreEqual(node.NodeId, notification.EntityId); + Assert.AreEqual(node.NodeObjectType, notification.EntityType); + Assert.AreEqual(node.UserId, notification.UserId); + } } [Test] @@ -42,25 +43,26 @@ namespace Umbraco.Tests.Persistence.Repositories { var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); - var repo = new NotificationsRepository(unitOfWork); - - var userDto = new UserDto { ContentStartId = -1, Email = "test" , Login = "test" , MediaStartId = -1, Password = "test" , Type = 1, UserName = "test" , UserLanguage = "en" }; - unitOfWork.Database.Insert(userDto); - - var userNew = Mock.Of(e => e.Id == userDto.Id); - var userAdmin = Mock.Of(e => e.Id == 0); - - for (var i = 0; i < 10; i++) + using (var repo = new NotificationsRepository(unitOfWork)) { - var node = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1," + i, SortOrder = 1, Text = "hello" + i, Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; - var result = unitOfWork.Database.Insert(node); - var entity = Mock.Of(e => e.Id == node.NodeId); - var notification = repo.CreateNotification((i % 2 == 0) ? userAdmin : userNew, entity, i.ToString(CultureInfo.InvariantCulture)); + var userDto = new UserDto { ContentStartId = -1, Email = "test", Login = "test", MediaStartId = -1, Password = "test", Type = 1, UserName = "test", UserLanguage = "en" }; + unitOfWork.Database.Insert(userDto); + + var userNew = Mock.Of(e => e.Id == userDto.Id); + var userAdmin = Mock.Of(e => e.Id == 0); + + for (var i = 0; i < 10; i++) + { + var node = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1," + i, SortOrder = 1, Text = "hello" + i, Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; + var result = unitOfWork.Database.Insert(node); + var entity = Mock.Of(e => e.Id == node.NodeId); + var notification = repo.CreateNotification((i % 2 == 0) ? userAdmin : userNew, entity, i.ToString(CultureInfo.InvariantCulture)); + } + + var notifications = repo.GetUserNotifications(userAdmin); + + Assert.AreEqual(5, notifications.Count()); } - - var notifications = repo.GetUserNotifications(userAdmin); - - Assert.AreEqual(5, notifications.Count()); } [Test] @@ -68,26 +70,27 @@ namespace Umbraco.Tests.Persistence.Repositories { var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); - var repo = new NotificationsRepository(unitOfWork); - - var node1 = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,1", SortOrder = 1, Text = "hello1", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; - unitOfWork.Database.Insert(node1); - var entity1 = Mock.Of(e => e.Id == node1.NodeId); - var node2 = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,2", SortOrder = 1, Text = "hello2", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; - unitOfWork.Database.Insert(node2); - var entity2 = Mock.Of(e => e.Id == node2.NodeId); - - for (var i = 0; i < 10; i++) + using (var repo = new NotificationsRepository(unitOfWork)) { - var userDto = new UserDto { ContentStartId = -1, Email = "test" + i, Login = "test" + i, MediaStartId = -1, Password = "test", Type = 1, UserName = "test" + i, UserLanguage = "en" }; - unitOfWork.Database.Insert(userDto); - var userNew = Mock.Of(e => e.Id == userDto.Id); - var notification = repo.CreateNotification(userNew, (i % 2 == 0) ? entity1 : entity2, i.ToString(CultureInfo.InvariantCulture)); + var node1 = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,1", SortOrder = 1, Text = "hello1", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; + unitOfWork.Database.Insert(node1); + var entity1 = Mock.Of(e => e.Id == node1.NodeId); + var node2 = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,2", SortOrder = 1, Text = "hello2", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; + unitOfWork.Database.Insert(node2); + var entity2 = Mock.Of(e => e.Id == node2.NodeId); + + for (var i = 0; i < 10; i++) + { + var userDto = new UserDto { ContentStartId = -1, Email = "test" + i, Login = "test" + i, MediaStartId = -1, Password = "test", Type = 1, UserName = "test" + i, UserLanguage = "en" }; + unitOfWork.Database.Insert(userDto); + var userNew = Mock.Of(e => e.Id == userDto.Id); + var notification = repo.CreateNotification(userNew, (i % 2 == 0) ? entity1 : entity2, i.ToString(CultureInfo.InvariantCulture)); + } + + var notifications = repo.GetEntityNotifications(entity1); + + Assert.AreEqual(5, notifications.Count()); } - - var notifications = repo.GetEntityNotifications(entity1); - - Assert.AreEqual(5, notifications.Count()); } [Test] @@ -95,26 +98,27 @@ namespace Umbraco.Tests.Persistence.Repositories { var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); - var repo = new NotificationsRepository(unitOfWork); - - var node1 = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,1", SortOrder = 1, Text = "hello1", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; - unitOfWork.Database.Insert(node1); - var entity1 = Mock.Of(e => e.Id == node1.NodeId); - var node2 = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,2", SortOrder = 1, Text = "hello2", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; - unitOfWork.Database.Insert(node2); - var entity2 = Mock.Of(e => e.Id == node2.NodeId); - - for (var i = 0; i < 10; i++) + using (var repo = new NotificationsRepository(unitOfWork)) { - var userDto = new UserDto { ContentStartId = -1, Email = "test" + i, Login = "test" + i, MediaStartId = -1, Password = "test", Type = 1, UserName = "test" + i, UserLanguage = "en" }; - unitOfWork.Database.Insert(userDto); - var userNew = Mock.Of(e => e.Id == userDto.Id); - var notification = repo.CreateNotification(userNew, (i % 2 == 0) ? entity1 : entity2, i.ToString(CultureInfo.InvariantCulture)); + var node1 = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,1", SortOrder = 1, Text = "hello1", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; + unitOfWork.Database.Insert(node1); + var entity1 = Mock.Of(e => e.Id == node1.NodeId); + var node2 = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,2", SortOrder = 1, Text = "hello2", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; + unitOfWork.Database.Insert(node2); + var entity2 = Mock.Of(e => e.Id == node2.NodeId); + + for (var i = 0; i < 10; i++) + { + var userDto = new UserDto { ContentStartId = -1, Email = "test" + i, Login = "test" + i, MediaStartId = -1, Password = "test", Type = 1, UserName = "test" + i, UserLanguage = "en" }; + unitOfWork.Database.Insert(userDto); + var userNew = Mock.Of(e => e.Id == userDto.Id); + var notification = repo.CreateNotification(userNew, (i % 2 == 0) ? entity1 : entity2, i.ToString(CultureInfo.InvariantCulture)); + } + + var delCount = repo.DeleteNotifications(entity1); + + Assert.AreEqual(5, delCount); } - - var delCount = repo.DeleteNotifications(entity1); - - Assert.AreEqual(5, delCount); } [Test] @@ -122,25 +126,26 @@ namespace Umbraco.Tests.Persistence.Repositories { var provider = new PetaPocoUnitOfWorkProvider(Logger); var unitOfWork = provider.GetUnitOfWork(); - var repo = new NotificationsRepository(unitOfWork); - - var userDto = new UserDto { ContentStartId = -1, Email = "test", Login = "test", MediaStartId = -1, Password = "test", Type = 1, UserName = "test", UserLanguage = "en" }; - unitOfWork.Database.Insert(userDto); - - var userNew = Mock.Of(e => e.Id == userDto.Id); - var userAdmin = Mock.Of(e => e.Id == 0); - - for (var i = 0; i < 10; i++) + using (var repo = new NotificationsRepository(unitOfWork)) { - var node = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1," + i, SortOrder = 1, Text = "hello" + i, Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; - var result = unitOfWork.Database.Insert(node); - var entity = Mock.Of(e => e.Id == node.NodeId); - var notification = repo.CreateNotification((i % 2 == 0) ? userAdmin : userNew, entity, i.ToString(CultureInfo.InvariantCulture)); + var userDto = new UserDto { ContentStartId = -1, Email = "test", Login = "test", MediaStartId = -1, Password = "test", Type = 1, UserName = "test", UserLanguage = "en" }; + unitOfWork.Database.Insert(userDto); + + var userNew = Mock.Of(e => e.Id == userDto.Id); + var userAdmin = Mock.Of(e => e.Id == 0); + + for (var i = 0; i < 10; i++) + { + var node = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1," + i, SortOrder = 1, Text = "hello" + i, Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; + var result = unitOfWork.Database.Insert(node); + var entity = Mock.Of(e => e.Id == node.NodeId); + var notification = repo.CreateNotification((i % 2 == 0) ? userAdmin : userNew, entity, i.ToString(CultureInfo.InvariantCulture)); + } + + var delCount = repo.DeleteNotifications(userAdmin); + + Assert.AreEqual(5, delCount); } - - var delCount = repo.DeleteNotifications(userAdmin); - - Assert.AreEqual(5, delCount); } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Scoping/ScopeTests.cs b/src/Umbraco.Tests/Scoping/ScopeTests.cs new file mode 100644 index 0000000000..d84b4fc554 --- /dev/null +++ b/src/Umbraco.Tests/Scoping/ScopeTests.cs @@ -0,0 +1,391 @@ +using System; +using NUnit.Framework; +using Umbraco.Core.Persistence; +using Umbraco.Core.Scoping; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Scoping +{ + [TestFixture] + [DatabaseTestBehavior(DatabaseBehavior.EmptyDbFilePerTest)] + public class ScopeTests : BaseDatabaseFactoryTest + { + // setup + public override void Initialize() + { + base.Initialize(); + + // initialization leaves a NoScope around, remove it + var scope = DatabaseContext.ScopeProvider.AmbientScope; + Assert.IsNotNull(scope); + Assert.IsInstanceOf(scope); + scope.Dispose(); + Assert.IsNull(DatabaseContext.ScopeProvider.AmbientScope); // gone + } + + [Test] + public void SimpleCreateScope() + { + var scopeProvider = DatabaseContext.ScopeProvider; + + Assert.IsNull(scopeProvider.AmbientScope); + using (var scope = scopeProvider.CreateScope()) + { + Assert.IsInstanceOf(scope); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + } + Assert.IsNull(scopeProvider.AmbientScope); + } + + [Test] + public void SimpleCreateScopeDatabase() + { + var scopeProvider = DatabaseContext.ScopeProvider; + + UmbracoDatabase database; + + Assert.IsNull(scopeProvider.AmbientScope); + using (var scope = scopeProvider.CreateScope()) + { + Assert.IsInstanceOf(scope); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + database = scope.Database; // populates scope's database + Assert.IsNotNull(database); + Assert.IsNotNull(database.Connection); // in a transaction + } + Assert.IsNull(scopeProvider.AmbientScope); + Assert.IsNull(database.Connection); // poof gone + } + + [Test] + public void NestedCreateScope() + { + var scopeProvider = DatabaseContext.ScopeProvider; + + Assert.IsNull(scopeProvider.AmbientScope); + using (var scope = scopeProvider.CreateScope()) + { + Assert.IsInstanceOf(scope); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + using (var nested = scopeProvider.CreateScope()) + { + Assert.IsInstanceOf(nested); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(nested, scopeProvider.AmbientScope); + Assert.AreSame(scope, ((Scope) nested).ParentScope); + } + } + Assert.IsNull(scopeProvider.AmbientScope); + } + + [Test] + public void NestedCreateScopeDatabase() + { + var scopeProvider = DatabaseContext.ScopeProvider; + + UmbracoDatabase database; + + Assert.IsNull(scopeProvider.AmbientScope); + using (var scope = scopeProvider.CreateScope()) + { + Assert.IsInstanceOf(scope); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + database = scope.Database; // populates scope's database + Assert.IsNotNull(database); + Assert.IsNotNull(database.Connection); // in a transaction + using (var nested = scopeProvider.CreateScope()) + { + Assert.IsInstanceOf(nested); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(nested, scopeProvider.AmbientScope); + Assert.AreSame(scope, ((Scope)nested).ParentScope); + Assert.AreSame(database, nested.Database); + } + Assert.IsNotNull(database.Connection); // still + } + Assert.IsNull(scopeProvider.AmbientScope); + Assert.IsNull(database.Connection); // poof gone + } + + [Test] + public void SimpleNoScope() + { + var scopeProvider = DatabaseContext.ScopeProvider; + + Assert.IsNull(scopeProvider.AmbientScope); + using (var scope = scopeProvider.AmbientOrNoScope) + { + Assert.IsInstanceOf(scope); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + } + Assert.IsNull(scopeProvider.AmbientScope); + } + + [Test] + public void SimpleNoScopeDatabase() + { + var scopeProvider = DatabaseContext.ScopeProvider; + + UmbracoDatabase database; + + Assert.IsNull(scopeProvider.AmbientScope); + using (var scope = scopeProvider.AmbientOrNoScope) + { + Assert.IsInstanceOf(scope); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + database = scope.Database; // populates scope's database + Assert.IsNotNull(database); + Assert.IsNull(database.Connection); // no transaction + } + Assert.IsNull(scopeProvider.AmbientScope); + Assert.IsNull(database.Connection); // still + } + + [Test] + public void NestedNoScope() + { + var scopeProvider = DatabaseContext.ScopeProvider; + + Assert.IsNull(scopeProvider.AmbientScope); + var scope = scopeProvider.AmbientOrNoScope; + Assert.IsInstanceOf(scope); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + + using (var nested = scopeProvider.CreateScope()) + { + Assert.IsInstanceOf(nested); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(nested, scopeProvider.AmbientScope); + + // nested does not have a parent + Assert.IsNull(((Scope) nested).ParentScope); + } + + // and when nested is gone, scope is gone + Assert.IsNull(scopeProvider.AmbientScope); + } + + [Test] + public void NestedNoScopeDatabase() + { + var scopeProvider = DatabaseContext.ScopeProvider; + + Assert.IsNull(scopeProvider.AmbientScope); + var scope = scopeProvider.AmbientOrNoScope; + Assert.IsInstanceOf(scope); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + var database = scope.Database; + Assert.IsNotNull(database); + Assert.IsNull(database.Connection); // no transaction + + using (var nested = scopeProvider.CreateScope()) + { + Assert.IsInstanceOf(nested); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(nested, scopeProvider.AmbientScope); + var nestedDatabase = nested.Database; // causes transaction + Assert.AreSame(database, nestedDatabase); // stolen + Assert.IsNotNull(database.Connection); // no more + + // nested does not have a parent + Assert.IsNull(((Scope)nested).ParentScope); + } + + // and when nested is gone, scope is gone + Assert.IsNull(scopeProvider.AmbientScope); + Assert.IsNull(database.Connection); // poof gone + } + + [Test] + public void NestedNoScopeFail() + { + var scopeProvider = DatabaseContext.ScopeProvider; + + Assert.IsNull(scopeProvider.AmbientScope); + var scope = scopeProvider.AmbientOrNoScope; + Assert.IsInstanceOf(scope); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + var database = scope.Database; + Assert.IsNotNull(database); + Assert.IsNull(database.Connection); // no transaction + database.BeginTransaction(); + Assert.IsNotNull(database.Connection); // now there is one + + Assert.Throws(() => + { + // could not steal the database + /*var nested =*/ scopeProvider.CreateScope(); + }); + + // cleanup + database.CompleteTransaction(); + } + + [Test] + public void NoScopeNested() + { + var scopeProvider = DatabaseContext.ScopeProvider; + + Assert.IsNull(scopeProvider.AmbientScope); + using (var scope = scopeProvider.CreateScope()) + { + Assert.IsInstanceOf(scope); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + + // AmbientOrNoScope returns the ambient scope + Assert.AreSame(scope, scopeProvider.AmbientOrNoScope); + } + } + + [Test] + public void Transaction() + { + var scopeProvider = DatabaseContext.ScopeProvider; + var noScope = scopeProvider.AmbientOrNoScope; + var database = noScope.Database; + database.Execute("CREATE TABLE tmp (id INT, name NVARCHAR(64))"); + + using (var scope = scopeProvider.CreateScope()) + { + scope.Database.Execute("INSERT INTO tmp (id, name) VALUES (1, 'a')"); + var n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=1"); + Assert.AreEqual("a", n); + } + + using (var scope = scopeProvider.CreateScope()) + { + var n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=1"); + Assert.IsNull(n); + } + + using (var scope = scopeProvider.CreateScope()) + { + scope.Database.Execute("INSERT INTO tmp (id, name) VALUES (1, 'a')"); + scope.Complete(); + } + + using (var scope = scopeProvider.CreateScope()) + { + var n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=1"); + Assert.AreEqual("a", n); + } + } + + [Test] + public void NestedTransactionInnerFail() + { + var scopeProvider = DatabaseContext.ScopeProvider; + var noScope = scopeProvider.AmbientOrNoScope; + var database = noScope.Database; + database.Execute("CREATE TABLE tmp (id INT, name NVARCHAR(64))"); + + using (var scope = scopeProvider.CreateScope()) + { + scope.Database.Execute("INSERT INTO tmp (id, name) VALUES (1, 'a')"); + var n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=1"); + Assert.AreEqual("a", n); + + using (var nested = scopeProvider.CreateScope()) + { + nested.Database.Execute("INSERT INTO tmp (id, name) VALUES (2, 'b')"); + var nn = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); + Assert.AreEqual("b", nn); + } + + n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); + Assert.AreEqual("b", n); + + scope.Complete(); + } + + using (var scope = scopeProvider.CreateScope()) + { + var n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=1"); + Assert.IsNull(n); + n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); + Assert.IsNull(n); + } + } + + [Test] + public void NestedTransactionOuterFail() + { + var scopeProvider = DatabaseContext.ScopeProvider; + var noScope = scopeProvider.AmbientOrNoScope; + var database = noScope.Database; + database.Execute("CREATE TABLE tmp (id INT, name NVARCHAR(64))"); + + using (var scope = scopeProvider.CreateScope()) + { + scope.Database.Execute("INSERT INTO tmp (id, name) VALUES (1, 'a')"); + var n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=1"); + Assert.AreEqual("a", n); + + using (var nested = scopeProvider.CreateScope()) + { + nested.Database.Execute("INSERT INTO tmp (id, name) VALUES (2, 'b')"); + var nn = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); + Assert.AreEqual("b", nn); + nested.Complete(); + } + + n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); + Assert.AreEqual("b", n); + } + + using (var scope = scopeProvider.CreateScope()) + { + var n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=1"); + Assert.IsNull(n); + n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); + Assert.IsNull(n); + } + } + + [Test] + public void NestedTransactionComplete() + { + var scopeProvider = DatabaseContext.ScopeProvider; + var noScope = scopeProvider.AmbientOrNoScope; + var database = noScope.Database; + database.Execute("CREATE TABLE tmp (id INT, name NVARCHAR(64))"); + + using (var scope = scopeProvider.CreateScope()) + { + scope.Database.Execute("INSERT INTO tmp (id, name) VALUES (1, 'a')"); + var n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=1"); + Assert.AreEqual("a", n); + + using (var nested = scopeProvider.CreateScope()) + { + nested.Database.Execute("INSERT INTO tmp (id, name) VALUES (2, 'b')"); + var nn = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); + Assert.AreEqual("b", nn); + nested.Complete(); + } + + n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); + Assert.AreEqual("b", n); + scope.Complete(); + } + + using (var scope = scopeProvider.CreateScope()) + { + var n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=1"); + Assert.AreEqual("a", n); + n = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); + Assert.AreEqual("b", n); + } + } + } +} diff --git a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs index 320ab400d3..92250487b1 100644 --- a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs +++ b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Profiling; +using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Web; using Umbraco.Web.Routing; @@ -28,7 +29,7 @@ namespace Umbraco.Tests.Security //should force app ctx to show not-configured ConfigurationManager.AppSettings.Set("umbracoConfigurationStatus", ""); - var dbCtx = new Mock(Mock.Of(), Mock.Of(), Mock.Of(), "test"); + var dbCtx = new Mock(Mock.Of(), Mock.Of(), Mock.Of(), "test"); dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(false); var appCtx = new ApplicationContext( @@ -53,7 +54,7 @@ namespace Umbraco.Tests.Security [Test] public void ShouldAuthenticateRequest_When_Configured() { - var dbCtx = new Mock(Mock.Of(), Mock.Of(), Mock.Of(), "test"); + var dbCtx = new Mock(Mock.Of(), Mock.Of(), Mock.Of(), "test"); dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(true); var appCtx = new ApplicationContext( diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 72a58861e2..46af227ecd 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -81,7 +81,7 @@ namespace Umbraco.Tests.Services Assert.IsTrue(moveResult.Success); - //re-get with the fixed/moved path + //re-get with the fixed/moved path content = contentService.GetById(content.Id); Assert.AreEqual("-1,-20," + content.Id, content.Path); @@ -270,7 +270,7 @@ namespace Umbraco.Tests.Services content2.SetTags("tags", new[] { "hello", "world", "some", "tags" }, true); contentService.Publish(content2); - // Act + // Act contentService.MoveToRecycleBin(content1); // Assert @@ -306,7 +306,7 @@ namespace Umbraco.Tests.Services content2.SetTags("tags", new[] { "hello", "world", "some", "tags" }, true); contentService.Publish(content2); - // Act + // Act contentService.MoveToRecycleBin(content1); contentService.MoveToRecycleBin(content2); @@ -339,7 +339,7 @@ namespace Umbraco.Tests.Services content2.SetTags("tags", new[] { "hello", "world", "some", "tags" }, true); contentService.Publish(content2); - // Act + // Act contentService.UnPublish(content1); contentService.UnPublish(content2); @@ -375,7 +375,7 @@ namespace Umbraco.Tests.Services contentService.UnPublish(content1); contentService.UnPublish(content2); - // Act + // Act contentService.Publish(content1); // Assert @@ -411,7 +411,7 @@ namespace Umbraco.Tests.Services contentService.MoveToRecycleBin(content1); contentService.MoveToRecycleBin(content2); - // Act + // Act contentService.Move(content1, -1); contentService.Publish(content1); @@ -839,8 +839,10 @@ namespace Umbraco.Tests.Services bool published = contentService.Publish(content, 0); var provider = new PetaPocoUnitOfWorkProvider(Logger); - var uow = provider.GetUnitOfWork(); - Assert.IsTrue(uow.Database.Exists(content.Id)); + using (var uow = provider.GetUnitOfWork()) + { + Assert.IsTrue(uow.Database.Exists(content.Id)); + } // Act bool unpublished = contentService.UnPublish(content, 0); @@ -850,8 +852,10 @@ namespace Umbraco.Tests.Services Assert.That(unpublished, Is.True); Assert.That(content.Published, Is.False); - uow = provider.GetUnitOfWork(); - Assert.IsFalse(uow.Database.Exists(content.Id)); + using (var uow = provider.GetUnitOfWork()) + { + Assert.IsFalse(uow.Database.Exists(content.Id)); + } } /// @@ -947,7 +951,7 @@ namespace Umbraco.Tests.Services // Act contentService.RePublishAll(new int[] { allContent.Last().ContentTypeId }); - // Assert + // Assert using (var uow = provider.GetUnitOfWork()) { Assert.AreEqual(allContent.Count(), uow.Database.ExecuteScalar("select count(*) from cmsContentXml")); diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index 6bd44c2bdd..dc3a0e83b6 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -72,6 +72,12 @@ namespace Umbraco.Tests.TestHelpers GetDbProviderName(), Logger); + // fixme - bah + var scopeProvider = new ScopeProvider(null); + if (scopeProvider.AmbientScope != null) + scopeProvider.AmbientScope.Dispose(); + scopeProvider.AmbientScope = null; + base.Initialize(); using (ProfilingLogger.TraceDuration("init")) diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 1f5e324142..cd7010310c 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -168,6 +168,7 @@ + diff --git a/src/umbraco.cms/businesslogic/Content.cs b/src/umbraco.cms/businesslogic/Content.cs index 4b942d453f..b64e4cf678 100644 --- a/src/umbraco.cms/businesslogic/Content.cs +++ b/src/umbraco.cms/businesslogic/Content.cs @@ -149,7 +149,7 @@ namespace umbraco.cms.businesslogic { _contentType = new ContentType(contentTypeId); } - catch + catch (Exception e) { return null; } diff --git a/src/umbraco.datalayer/SqlHelper.cs b/src/umbraco.datalayer/SqlHelper.cs index 4420e8bde0..10d1183abb 100644 --- a/src/umbraco.datalayer/SqlHelper.cs +++ b/src/umbraco.datalayer/SqlHelper.cs @@ -359,11 +359,12 @@ namespace umbraco.DataLayer internal class CurrentConnectionUsing : IDisposable { - public static MethodInfo OpenMethod { get; private set; } - public static MethodInfo CloseMethod { get; private set; } + private static MethodInfo OpenMethod { get; set; } + //private static MethodInfo CloseMethod { get; set; } - private static readonly object Factory; - private static readonly MethodInfo CreateMethod; + private static readonly object ScopeProvider; + private static readonly PropertyInfo ScopeProviderAmbientOrNoScopeProperty; + private static readonly PropertyInfo ScopeDatabaseProperty; private static readonly PropertyInfo ConnectionProperty; private static readonly FieldInfo TransactionField; private static readonly PropertyInfo InnerConnectionProperty; @@ -378,23 +379,30 @@ namespace umbraco.DataLayer var applicationContextType = coreAssembly.GetType("Umbraco.Core.ApplicationContext"); var databaseContextType = coreAssembly.GetType("Umbraco.Core.DatabaseContext"); - var defaultDatabaseFactoryType = coreAssembly.GetType("Umbraco.Core.Persistence.DefaultDatabaseFactory"); var umbracoDatabaseType = coreAssembly.GetType("Umbraco.Core.Persistence.UmbracoDatabase"); var databaseType = coreAssembly.GetType("Umbraco.Core.Persistence.Database"); + var scopeProviderType = coreAssembly.GetType("Umbraco.Core.Scoping.IScopeProviderInternal"); + var scopeType = coreAssembly.GetType("Umbraco.Core.Scoping.IScope"); - var currentProperty = applicationContextType.GetProperty("Current", BindingFlags.Static | BindingFlags.Public); - var applicationContext = currentProperty.GetValue(null, NoArgs); + var applicationContextCurrentProperty = applicationContextType.GetProperty("Current", BindingFlags.Static | BindingFlags.Public); + if (applicationContextCurrentProperty == null) throw new Exception("oops: applicationContextCurrentProperty."); + var applicationContext = applicationContextCurrentProperty.GetValue(null, NoArgs); - var databaseContextProperty = applicationContextType.GetProperty("DatabaseContext", BindingFlags.Instance | BindingFlags.Public); - var databaseContext = databaseContextProperty.GetValue(applicationContext, NoArgs); + var applicationContextDatabaseContextProperty = applicationContextType.GetProperty("DatabaseContext", BindingFlags.Instance | BindingFlags.Public); + if (applicationContextDatabaseContextProperty == null) throw new Exception("oops: applicationContextDatabaseContextProperty."); + var databaseContext = applicationContextDatabaseContextProperty.GetValue(applicationContext, NoArgs); - var factoryField = databaseContextType.GetField("_factory", BindingFlags.Instance | BindingFlags.NonPublic); - Factory = factoryField.GetValue(databaseContext); + var databaseContextScopeProviderField = databaseContextType.GetField("ScopeProvider", BindingFlags.Instance | BindingFlags.NonPublic); + if (databaseContextScopeProviderField == null) throw new Exception("oops: databaseContextScopeProviderField."); + ScopeProvider = databaseContextScopeProviderField.GetValue(databaseContext); - CreateMethod = defaultDatabaseFactoryType.GetMethod("CreateDatabase", BindingFlags.Instance | BindingFlags.Public); + ScopeProviderAmbientOrNoScopeProperty = scopeProviderType.GetProperty("AmbientOrNoScope", BindingFlags.Instance | BindingFlags.Public); + if (ScopeProviderAmbientOrNoScopeProperty == null) throw new Exception("oops: ScopeProviderAmbientOrNoScopeProperty."); + ScopeDatabaseProperty = scopeType.GetProperty("Database", BindingFlags.Instance | BindingFlags.Public); + if (ScopeDatabaseProperty == null) throw new Exception("oops: ScopeDatabaseProperty."); OpenMethod = databaseType.GetMethod("OpenSharedConnection", BindingFlags.Instance | BindingFlags.Public); - CloseMethod = databaseType.GetMethod("CloseSharedConnection", BindingFlags.Instance | BindingFlags.Public); + //CloseMethod = databaseType.GetMethod("CloseSharedConnection", BindingFlags.Instance | BindingFlags.Public); ConnectionProperty = umbracoDatabaseType.GetProperty("Connection", BindingFlags.Instance | BindingFlags.Public); TransactionField = databaseType.GetField("_transaction", BindingFlags.Instance | BindingFlags.NonPublic); @@ -408,7 +416,8 @@ namespace umbraco.DataLayer public CurrentConnectionUsing() { - _database = CreateMethod.Invoke(Factory, NoArgs); + var scope = ScopeProviderAmbientOrNoScopeProperty.GetValue(ScopeProvider); + _database = ScopeDatabaseProperty.GetValue(scope); var connection = ConnectionProperty.GetValue(_database, NoArgs); // we have to open to make sure that we *do* have a connection diff --git a/src/umbraco.datalayer/SqlHelpers/MySql/MySqlHelper.cs b/src/umbraco.datalayer/SqlHelpers/MySql/MySqlHelper.cs index 2e76941c21..c1a530f8f4 100644 --- a/src/umbraco.datalayer/SqlHelpers/MySql/MySqlHelper.cs +++ b/src/umbraco.datalayer/SqlHelpers/MySql/MySqlHelper.cs @@ -122,12 +122,12 @@ namespace umbraco.DataLayer.SqlHelpers.MySql { using (var cc = UseCurrentConnection) { - return new MySqlDataReader(ExecuteReader((MSC.MySqlConnection) cc.Connection, (MSC.MySqlTransaction) cc.Transaction, commandText, parameters, true)); + return new MySqlDataReader(ExecuteReader((MSC.MySqlConnection) cc.Connection, (MSC.MySqlTransaction) cc.Transaction, commandText, parameters)); } } // copied & adapted from MySqlHelper - private static MSC.MySqlDataReader ExecuteReader(MSC.MySqlConnection connection, MSC.MySqlTransaction trx, string commandText, MSC.MySqlParameter[] commandParameters, bool externalConn) + private static MSC.MySqlDataReader ExecuteReader(MSC.MySqlConnection connection, MSC.MySqlTransaction trx, string commandText, MSC.MySqlParameter[] commandParameters) { MSC.MySqlCommand mySqlCommand = new MSC.MySqlCommand(); mySqlCommand.Connection = connection; @@ -139,7 +139,7 @@ namespace umbraco.DataLayer.SqlHelpers.MySql foreach (var commandParameter in commandParameters) mySqlCommand.Parameters.Add(commandParameter); } - MSC.MySqlDataReader mySqlDataReader = !externalConn ? mySqlCommand.ExecuteReader(CommandBehavior.CloseConnection) : mySqlCommand.ExecuteReader(); + MSC.MySqlDataReader mySqlDataReader = mySqlCommand.ExecuteReader(); mySqlCommand.Parameters.Clear(); return mySqlDataReader; } From bbe68cc22436917df70de0549045e45051888cfc Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 20:13:17 +0100 Subject: [PATCH 244/599] better styling of mini editor --- .../components/tabs/umbtabsnav.directive.js | 3 +- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../src/less/components/umb-mini-editor.less | 33 ++++++++++++++++ .../src/less/components/umb-node-preview.less | 10 +++++ .../views/common/dialogs/content/edit.html | 39 ++++++++++++++----- .../views/components/tabs/umb-tabs-nav.html | 2 +- 6 files changed, 76 insertions(+), 12 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtabsnav.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtabsnav.directive.js index 890acf3d6f..50c4775ef9 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtabsnav.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tabs/umbtabsnav.directive.js @@ -43,7 +43,8 @@ templateUrl: "views/components/tabs/umb-tabs-nav.html", scope: { model: "=", - tabdrop: "=" + tabdrop: "=", + idSuffix: "@" }, link: link }; diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 49a98ee219..79a3ecdf8f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -123,6 +123,7 @@ @import "components/notifications/umb-notifications.less"; @import "components/umb-file-dropzone.less"; @import "components/umb-node-preview.less"; +@import "components/umb-mini-editor.less"; // Utilities @import "utilities/_flexbox.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less new file mode 100644 index 0000000000..de10805aef --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less @@ -0,0 +1,33 @@ +.umb-modal .umb-mini-editor { + + .umb-panel-header { + padding: 20px 10px; + background: @grayLighter; + border-bottom: 1px solid @grayLight; + height: 59px; + + .umb-headline { + margin-left: 0; + margin-right: 0; + margin-bottom: 0; + margin-top: 3px; + } + } + + .umb-panel-body { + padding-left: 0; + padding-right: 0; + } + + .umb-panel-body.with-footer { + bottom: 52px; + } + + .umb-panel-footer { + background: @grayLighter; + border-top: 1px solid @grayLight; + height: 51px; + padding: 0 20px; + } + +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less index 5ef9cc7e74..5b96492b48 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -85,4 +85,14 @@ .umb-node-preview-add:hover { color: @blue; +} + +.umb-overlay, +.umb-modal { + .umb-node-preview { + max-width: none; + } + .umb-node-preview-add { + max-width: none; + } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.html index 55e0cad2e7..eef28aefa1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/content/edit.html @@ -2,7 +2,8 @@ ng-controller="Umbraco.Dialogs.Content.EditController" ng-show="loaded" ng-submit="save()" - val-form-manager> + val-form-manager + class="umb-mini-editor">
    @@ -12,15 +13,33 @@
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/tabs/umb-tabs-nav.html b/src/Umbraco.Web.UI.Client/src/views/components/tabs/umb-tabs-nav.html index 8522bc3b6a..808822e138 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/tabs/umb-tabs-nav.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/tabs/umb-tabs-nav.html @@ -1,5 +1,5 @@ From cca0f4c293fdf95420e9b74cf024468531ca5c74 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 20:13:43 +0100 Subject: [PATCH 245/599] clean up --- .../propertyeditors/contentpicker/contentpicker.controller.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index f97aa2bf62..b587231220 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -232,9 +232,8 @@ function contentPickerController($scope, dialogService, entityResource, contentR //load current data var modelIds = $scope.model.value ? $scope.model.value.split(',') : []; - var nodePromise = entityResource.getByIds(modelIds, entityType); - nodePromise.then(function (data) { + entityResource.getByIds(modelIds, entityType).then(function (data) { _.each(modelIds, function (id, i) { var entity = _.find(data, function (d) { From 7b616cf719ce8eedfc78cc182de5e78174052b82 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 20:50:56 +0100 Subject: [PATCH 246/599] add a bit padding on the sides --- .../src/less/components/umb-mini-editor.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less index de10805aef..53e0dcd6b3 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-editor.less @@ -1,7 +1,7 @@ .umb-modal .umb-mini-editor { .umb-panel-header { - padding: 20px 10px; + padding: 20px; background: @grayLighter; border-bottom: 1px solid @grayLight; height: 59px; From c58957dc7b1a7e8e885562a5b134f333e7b32de2 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 20:52:07 +0100 Subject: [PATCH 247/599] fix unit tests --- .../common/mocks/resources/entity.mocks.js | 17 ++++++++ .../contentpicker/contentpicker.controller.js | 2 +- .../content-picker-controller.spec.js | 40 +++++++++++++------ 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js index fc60781375..a98240111e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js @@ -33,6 +33,19 @@ angular.module('umbraco.mocks'). return [200, nodes, null]; } + function returnEntityUrl() { + + if (!mocksUtils.checkAuth()) { + return [401, null, null]; + } + + var urlOrbject = { + "url": "url" + }; + + return [200, urlOrbject, null]; + + } return { register: function () { @@ -48,6 +61,10 @@ angular.module('umbraco.mocks'). $httpBackend .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetById?')) .respond(returnEntitybyId); + + $httpBackend + .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetUrl?')) + .respond(returnEntityUrl); } }; }]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index b587231220..e5852c29fd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -289,7 +289,7 @@ function contentPickerController($scope, dialogService, entityResource, contentR "icon": item.icon, "path": item.path, "url": item.url, - "published": (item.metaData.IsPublished === false && entityType === "Document") ? false : true + "published": (item.metaData && item.metaData.IsPublished === false && entityType === "Document") ? false : true // only content supports published/unpublished content so we set everything else to published so the UI looks correct }); diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js index e6d1312109..f34f8088df 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js @@ -17,7 +17,11 @@ describe('Content picker controller tests', function () { value:"1233,1231,23121", label: "My content picker", description: "desc", - config: {} + config: { + startNode: { + type: "content" + } + } }; //this controller requires an angular form controller applied to it @@ -47,6 +51,12 @@ describe('Content picker controller tests', function () { })); describe('content edit controller save and publish', function () { + + var item = { + name: "meh", + id: 666, + icon: "woop" + }; it('should define the default properties on construction', function () { expect(scope.model.value).toNotBe(undefined); @@ -65,7 +75,6 @@ describe('Content picker controller tests', function () { }); it("Removing an item should update renderModel, ids and model.value", function(){ - scope.remove(1); scope.$apply(); expect(scope.renderModel.length).toBe(2); @@ -73,24 +82,29 @@ describe('Content picker controller tests', function () { }); it("Adding an item should update renderModel, ids and model.value", function(){ - - scope.add({name: "meh", id: 666, icon: "woop"}); + scope.add(item); scope.$apply(); - expect(scope.renderModel.length).toBe(4); - expect(scope.model.value).toBe("1233,1231,23121,666"); + setTimeout(function(){ + expect(scope.renderModel.length).toBe(4); + expect(scope.model.value).toBe("1233,1231,23121,666"); + }, 1000); }); it("Adding a dublicate item should note update renderModel, ids and model.value", function(){ - - scope.add({ name: "meh", id: 666, icon: "woop" }); + scope.add(item); scope.$apply(); - expect(scope.renderModel.length).toBe(4); - expect(scope.model.value).toBe("1233,1231,23121,666"); + setTimeout(function(){ + expect(scope.renderModel.length).toBe(4); + expect(scope.model.value).toBe("1233,1231,23121,666"); + }, 1000); - scope.add({ name: "meh 2", id: 666, icon: "woop 2" }); + scope.add(item); scope.$apply(); - expect(scope.renderModel.length).toBe(4); - expect(scope.model.value).toBe("1233,1231,23121,666"); + setTimeout(function(){ + expect(scope.renderModel.length).toBe(4); + expect(scope.model.value).toBe("1233,1231,23121,666"); + }, 1000); + }); }); }); \ No newline at end of file From bf4ad25cadaa97b5783ef178187e07b8987d4b91 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 16 Jan 2017 21:16:31 +0100 Subject: [PATCH 248/599] remove ng-title --- .../src/views/propertyeditors/contentpicker/contentpicker.html | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index a8076ff987..ca6f9ac826 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -4,7 +4,6 @@
    Date: Tue, 17 Jan 2017 12:38:54 +1100 Subject: [PATCH 249/599] Moves GridValue to Models --- .../Deploy/IGridCellValueConnector.cs | 16 ++++++------ .../{Deploy => Models}/GridValue.cs | 25 +++++++++++-------- src/Umbraco.Core/Umbraco.Core.csproj | 2 +- .../PropertyEditors/GridPropertyEditor.cs | 2 ++ 4 files changed, 25 insertions(+), 20 deletions(-) rename src/Umbraco.Core/{Deploy => Models}/GridValue.cs (72%) diff --git a/src/Umbraco.Core/Deploy/IGridCellValueConnector.cs b/src/Umbraco.Core/Deploy/IGridCellValueConnector.cs index f95a4e7cf1..65fde1ec0b 100644 --- a/src/Umbraco.Core/Deploy/IGridCellValueConnector.cs +++ b/src/Umbraco.Core/Deploy/IGridCellValueConnector.cs @@ -21,21 +21,21 @@ namespace Umbraco.Core.Deploy /// /// Gets the value to be deployed from the control value as a string. /// - /// The control containing the value. + /// The control containing the value. /// The property where the control is located. Do not modify - only used for context /// The dependencies of the property. /// The grid cell value to be deployed. /// Note that - string GetValue(GridValue.Control control, Property property, ICollection dependencies); + string GetValue(GridValue.GridControl gridControl, Property property, ICollection dependencies); /// /// Allows you to modify the value of a control being deployed. /// - /// The control being deployed. - /// The property where the is located. Do not modify - only used for context. - /// Follows the pattern of the property value connectors (). The SetValue method is used to modify the value of the . - /// Note that only the value should be modified - not the . - /// The should only be used to assist with context data relevant when setting the value. - void SetValue(GridValue.Control control, Property property); + /// The control being deployed. + /// The property where the is located. Do not modify - only used for context. + /// Follows the pattern of the property value connectors (). The SetValue method is used to modify the value of the . + /// Note that only the value should be modified - not the . + /// The should only be used to assist with context data relevant when setting the value. + void SetValue(GridValue.GridControl gridControl, Property property); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Deploy/GridValue.cs b/src/Umbraco.Core/Models/GridValue.cs similarity index 72% rename from src/Umbraco.Core/Deploy/GridValue.cs rename to src/Umbraco.Core/Models/GridValue.cs index 39a38a665c..87f6146af6 100644 --- a/src/Umbraco.Core/Deploy/GridValue.cs +++ b/src/Umbraco.Core/Models/GridValue.cs @@ -3,26 +3,29 @@ using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -namespace Umbraco.Core.Deploy +namespace Umbraco.Core.Models { + /// + /// A model representing the value saved for the grid + /// public class GridValue { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("sections")] - public IEnumerable
    Sections { get; set; } + public IEnumerable Sections { get; set; } - public class Section + public class GridSection { [JsonProperty("grid")] public string Grid { get; set; } [JsonProperty("rows")] - public IEnumerable Rows { get; set; } + public IEnumerable Rows { get; set; } } - public class Row + public class GridRow { [JsonProperty("name")] public string Name { get; set; } @@ -31,7 +34,7 @@ namespace Umbraco.Core.Deploy public Guid Id { get; set; } [JsonProperty("areas")] - public IEnumerable Areas { get; set; } + public IEnumerable Areas { get; set; } [JsonProperty("styles")] public JToken Styles { get; set; } @@ -40,13 +43,13 @@ namespace Umbraco.Core.Deploy public JToken Config { get; set; } } - public class Area + public class GridArea { [JsonProperty("grid")] public string Grid { get; set; } [JsonProperty("controls")] - public IEnumerable Controls { get; set; } + public IEnumerable Controls { get; set; } [JsonProperty("styles")] public JToken Styles { get; set; } @@ -55,13 +58,13 @@ namespace Umbraco.Core.Deploy public JToken Config { get; set; } } - public class Control + public class GridControl { [JsonProperty("value")] public JToken Value { get; set; } [JsonProperty("editor")] - public Editor Editor { get; set; } + public GridEditor Editor { get; set; } [JsonProperty("styles")] public JToken Styles { get; set; } @@ -70,7 +73,7 @@ namespace Umbraco.Core.Deploy public JToken Config { get; set; } } - public class Editor + public class GridEditor { [JsonProperty("alias")] public string Alias { get; set; } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index d06547ff19..e17a142376 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -312,7 +312,7 @@ - + diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index b40cf3bbf3..e22748096e 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -31,6 +31,8 @@ namespace Umbraco.Web.PropertyEditors { try { + //TODO: We should deserialize this to Umbraco.Core.Models.GridValue instead of doing the below + var json = JsonConvert.DeserializeObject(e.Fields[field.Name]); //check if this is formatted for grid json From 038b155aeabb28896f61467eb190eca49a9f3c1d Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 17 Jan 2017 14:42:30 +1100 Subject: [PATCH 250/599] Fixes constants and internalsvisibleto --- src/Umbraco.Core/Constants-Conventions.cs | 118 +----------------- .../Constants-DeployEntityType.cs | 2 +- src/Umbraco.Core/Constants-DeploySelector.cs | 2 +- src/Umbraco.Core/Properties/AssemblyInfo.cs | 7 +- src/Umbraco.Core/Udi.cs | 8 +- src/Umbraco.Core/UdiGetterExtensions.cs | 50 ++++---- src/Umbraco.Tests/UdiTests.cs | 40 +++--- src/Umbraco.Web/Properties/AssemblyInfo.cs | 3 + 8 files changed, 60 insertions(+), 170 deletions(-) diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index d6c4003fcf..2e3d652d7e 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -4,123 +4,7 @@ using Umbraco.Core.Models; namespace Umbraco.Core { - /// - /// Defines well-known entity types. - /// - /// Well-known entity types are those that Deploy already knows about, - /// but entity types are strings and so can be extended beyond what is defined here. - internal static class DeployEntityType - { - // guid entity types - - public const string AnyGuid = "any-guid"; // that one is for tests - - public const string Document = "document"; - public const string Media = "media"; - public const string Member = "member"; - - public const string DictionaryItem = "dictionary-item"; - public const string Macro = "macro"; - public const string Template = "template"; - - public const string DocumentType = "document-type"; - public const string DocumentTypeContainer = "document-type-container"; - public const string MediaType = "media-type"; - public const string MediaTypeContainer = "media-type-container"; - public const string DataType = "data-type"; - public const string DataTypeContainer = "data-type-container"; - public const string MemberType = "member-type"; - public const string MemberGroup = "member-group"; - - public const string RelationType = "relation-type"; - - // string entity types - - public const string AnyString = "any-string"; // that one is for tests - - public const string MediaFile = "media-file"; - public const string TemplateFile = "template-file"; - public const string Script = "script"; - public const string Stylesheet = "stylesheet"; - public const string PartialView = "partial-view"; - public const string PartialViewMacro = "partial-view-macro"; - public const string Xslt = "xslt"; - - public static string FromUmbracoObjectType(UmbracoObjectTypes umbracoObjectType) - { - switch (umbracoObjectType) - { - case UmbracoObjectTypes.Document: - return Document; - case UmbracoObjectTypes.Media: - return Media; - case UmbracoObjectTypes.Member: - return Member; - case UmbracoObjectTypes.Template: - return Template; - case UmbracoObjectTypes.DocumentType: - return DocumentType; - case UmbracoObjectTypes.DocumentTypeContainer: - return DocumentTypeContainer; - case UmbracoObjectTypes.MediaType: - return MediaType; - case UmbracoObjectTypes.MediaTypeContainer: - return MediaTypeContainer; - case UmbracoObjectTypes.DataType: - return DataType; - case UmbracoObjectTypes.DataTypeContainer: - return DataTypeContainer; - case UmbracoObjectTypes.MemberType: - return MemberType; - case UmbracoObjectTypes.MemberGroup: - return MemberGroup; - case UmbracoObjectTypes.Stylesheet: - return Stylesheet; - case UmbracoObjectTypes.RelationType: - return RelationType; - } - throw new NotSupportedException(string.Format("UmbracoObjectType \"{0}\" does not have a matching EntityType.", umbracoObjectType)); - } - - public static UmbracoObjectTypes ToUmbracoObjectType(string entityType) - { - switch (entityType) - { - case Document: - return UmbracoObjectTypes.Document; - case Media: - return UmbracoObjectTypes.Media; - case Member: - return UmbracoObjectTypes.Member; - case Template: - return UmbracoObjectTypes.Template; - case DocumentType: - return UmbracoObjectTypes.DocumentType; - case DocumentTypeContainer: - return UmbracoObjectTypes.DocumentTypeContainer; - case MediaType: - return UmbracoObjectTypes.MediaType; - case MediaTypeContainer: - return UmbracoObjectTypes.MediaTypeContainer; - case DataType: - return UmbracoObjectTypes.DataType; - case DataTypeContainer: - return UmbracoObjectTypes.DataTypeContainer; - case MemberType: - return UmbracoObjectTypes.MemberType; - case MemberGroup: - return UmbracoObjectTypes.MemberGroup; - case Stylesheet: - return UmbracoObjectTypes.Stylesheet; - case RelationType: - return UmbracoObjectTypes.RelationType; - } - throw new NotSupportedException( - string.Format("EntityType \"{0}\" does not have a matching UmbracoObjectType.", entityType)); - } - } - - + public static partial class Constants { /// diff --git a/src/Umbraco.Core/Constants-DeployEntityType.cs b/src/Umbraco.Core/Constants-DeployEntityType.cs index 2c0ea81be3..f622661dff 100644 --- a/src/Umbraco.Core/Constants-DeployEntityType.cs +++ b/src/Umbraco.Core/Constants-DeployEntityType.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core /// /// Well-known entity types are those that Deploy already knows about, /// but entity types are strings and so can be extended beyond what is defined here. - internal static class DeployEntityType + public static class DeployEntityType { // guid entity types diff --git a/src/Umbraco.Core/Constants-DeploySelector.cs b/src/Umbraco.Core/Constants-DeploySelector.cs index b2415fb37f..b7b45fd666 100644 --- a/src/Umbraco.Core/Constants-DeploySelector.cs +++ b/src/Umbraco.Core/Constants-DeploySelector.cs @@ -9,7 +9,7 @@ namespace Umbraco.Core /// /// Contains the valid selector values. /// - internal static class DeploySelector + public static class DeploySelector { public const string This = "this"; public const string ThisAndChildren = "this-and-children"; diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index dbc1ab6c93..a74ebb8fec 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -29,7 +29,7 @@ using System.Security.Permissions; [assembly: InternalsVisibleTo("umbraco.webservices")] [assembly: InternalsVisibleTo("umbraco.datalayer")] [assembly: InternalsVisibleTo("umbraco.MacroEngines")] - +[assembly: InternalsVisibleTo("umbraco.providers")] [assembly: InternalsVisibleTo("umbraco.editorControls")] [assembly: InternalsVisibleTo("Umbraco.Tests")] [assembly: InternalsVisibleTo("Umbraco.Tests.Benchmarks")] @@ -42,7 +42,10 @@ using System.Security.Permissions; [assembly: InternalsVisibleTo("Umbraco.VisualStudio")] [assembly: InternalsVisibleTo("Umbraco.Courier.Core")] [assembly: InternalsVisibleTo("Umbraco.Courier.Persistence")] -[assembly: InternalsVisibleTo("umbraco.providers")] + +[assembly: InternalsVisibleTo("Umbraco.Deploy")] +[assembly: InternalsVisibleTo("Umbraco.Deploy.UI")] +[assembly: InternalsVisibleTo("Umbraco.Deploy.Cloud")] //allow this to be mocked in our unit tests [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] \ No newline at end of file diff --git a/src/Umbraco.Core/Udi.cs b/src/Umbraco.Core/Udi.cs index aef0fc9274..95bd9f90b1 100644 --- a/src/Umbraco.Core/Udi.cs +++ b/src/Umbraco.Core/Udi.cs @@ -44,12 +44,12 @@ namespace Umbraco.Core static Udi() { // for tests etc. - UdiTypes[DeployEntityType.AnyGuid] = UdiType.GuidUdi; - UdiTypes[DeployEntityType.AnyString] = UdiType.StringUdi; + UdiTypes[Constants.DeployEntityType.AnyGuid] = UdiType.GuidUdi; + UdiTypes[Constants.DeployEntityType.AnyString] = UdiType.StringUdi; // we don't have connectors for these... - UdiTypes[DeployEntityType.Member] = UdiType.GuidUdi; - UdiTypes[DeployEntityType.MemberGroup] = UdiType.GuidUdi; + UdiTypes[Constants.DeployEntityType.Member] = UdiType.GuidUdi; + UdiTypes[Constants.DeployEntityType.MemberGroup] = UdiType.GuidUdi; // fixme - or inject from...? // there is no way we can get the "registered" service connectors, as registration diff --git a/src/Umbraco.Core/UdiGetterExtensions.cs b/src/Umbraco.Core/UdiGetterExtensions.cs index aebb8873a6..d10f730aef 100644 --- a/src/Umbraco.Core/UdiGetterExtensions.cs +++ b/src/Umbraco.Core/UdiGetterExtensions.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core public static GuidUdi GetUdi(this ITemplate entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new GuidUdi(DeployEntityType.Template, entity.Key).EnsureClosed(); + return new GuidUdi(Constants.DeployEntityType.Template, entity.Key).EnsureClosed(); } /// @@ -28,7 +28,7 @@ namespace Umbraco.Core public static GuidUdi GetUdi(this IContentType entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new GuidUdi(DeployEntityType.DocumentType, entity.Key).EnsureClosed(); + return new GuidUdi(Constants.DeployEntityType.DocumentType, entity.Key).EnsureClosed(); } /// @@ -39,7 +39,7 @@ namespace Umbraco.Core public static GuidUdi GetUdi(this IMediaType entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new GuidUdi(DeployEntityType.MediaType, entity.Key).EnsureClosed(); + return new GuidUdi(Constants.DeployEntityType.MediaType, entity.Key).EnsureClosed(); } /// @@ -50,7 +50,7 @@ namespace Umbraco.Core public static GuidUdi GetUdi(this IMemberType entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new GuidUdi(DeployEntityType.MemberType, entity.Key).EnsureClosed(); + return new GuidUdi(Constants.DeployEntityType.MemberType, entity.Key).EnsureClosed(); } /// @@ -61,7 +61,7 @@ namespace Umbraco.Core public static GuidUdi GetUdi(this IMemberGroup entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new GuidUdi(DeployEntityType.MemberGroup, entity.Key).EnsureClosed(); + return new GuidUdi(Constants.DeployEntityType.MemberGroup, entity.Key).EnsureClosed(); } /// @@ -74,9 +74,9 @@ namespace Umbraco.Core if (entity == null) throw new ArgumentNullException("entity"); string type; - if (entity is IContentType) type = DeployEntityType.DocumentType; - else if (entity is IMediaType) type = DeployEntityType.MediaType; - else if (entity is IMemberType) type = DeployEntityType.MemberType; + if (entity is IContentType) type = Constants.DeployEntityType.DocumentType; + else if (entity is IMediaType) type = Constants.DeployEntityType.MediaType; + else if (entity is IMemberType) type = Constants.DeployEntityType.MemberType; else throw new NotSupportedException(string.Format("Composition type {0} is not supported.", entity.GetType().FullName)); return new GuidUdi(type, entity.Key).EnsureClosed(); } @@ -89,7 +89,7 @@ namespace Umbraco.Core public static GuidUdi GetUdi(this IDataTypeDefinition entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new GuidUdi(DeployEntityType.DataType, entity.Key).EnsureClosed(); + return new GuidUdi(Constants.DeployEntityType.DataType, entity.Key).EnsureClosed(); } /// @@ -103,11 +103,11 @@ namespace Umbraco.Core string entityType; if (entity.ContainedObjectType == Constants.ObjectTypes.DataTypeGuid) - entityType = DeployEntityType.DataTypeContainer; + entityType = Constants.DeployEntityType.DataTypeContainer; else if (entity.ContainedObjectType == Constants.ObjectTypes.DocumentTypeGuid) - entityType = DeployEntityType.DocumentTypeContainer; + entityType = Constants.DeployEntityType.DocumentTypeContainer; else if (entity.ContainedObjectType == Constants.ObjectTypes.MediaTypeGuid) - entityType = DeployEntityType.MediaTypeContainer; + entityType = Constants.DeployEntityType.MediaTypeContainer; else throw new NotSupportedException(string.Format("Contained object type {0} is not supported.", entity.ContainedObjectType)); return new GuidUdi(entityType, entity.Key).EnsureClosed(); @@ -121,7 +121,7 @@ namespace Umbraco.Core public static GuidUdi GetUdi(this IMedia entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new GuidUdi(DeployEntityType.Media, entity.Key).EnsureClosed(); + return new GuidUdi(Constants.DeployEntityType.Media, entity.Key).EnsureClosed(); } /// @@ -132,7 +132,7 @@ namespace Umbraco.Core public static GuidUdi GetUdi(this IContent entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new GuidUdi(DeployEntityType.Document, entity.Key).EnsureClosed(); + return new GuidUdi(Constants.DeployEntityType.Document, entity.Key).EnsureClosed(); } /// @@ -143,7 +143,7 @@ namespace Umbraco.Core public static GuidUdi GetUdi(this IMember entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new GuidUdi(DeployEntityType.Member, entity.Key).EnsureClosed(); + return new GuidUdi(Constants.DeployEntityType.Member, entity.Key).EnsureClosed(); } /// @@ -154,7 +154,7 @@ namespace Umbraco.Core public static StringUdi GetUdi(this Stylesheet entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new StringUdi(DeployEntityType.Stylesheet, entity.Path.TrimStart('/')).EnsureClosed(); + return new StringUdi(Constants.DeployEntityType.Stylesheet, entity.Path.TrimStart('/')).EnsureClosed(); } /// @@ -165,7 +165,7 @@ namespace Umbraco.Core public static StringUdi GetUdi(this Script entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new StringUdi(DeployEntityType.Script, entity.Path.TrimStart('/')).EnsureClosed(); + return new StringUdi(Constants.DeployEntityType.Script, entity.Path.TrimStart('/')).EnsureClosed(); } /// @@ -176,7 +176,7 @@ namespace Umbraco.Core public static GuidUdi GetUdi(this IDictionaryItem entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new GuidUdi(DeployEntityType.DictionaryItem, entity.Key).EnsureClosed(); + return new GuidUdi(Constants.DeployEntityType.DictionaryItem, entity.Key).EnsureClosed(); } /// @@ -187,7 +187,7 @@ namespace Umbraco.Core public static GuidUdi GetUdi(this IMacro entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new GuidUdi(DeployEntityType.Macro, entity.Key).EnsureClosed(); + return new GuidUdi(Constants.DeployEntityType.Macro, entity.Key).EnsureClosed(); } /// @@ -198,7 +198,7 @@ namespace Umbraco.Core public static StringUdi GetUdi(this IPartialView entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new StringUdi(DeployEntityType.PartialView, entity.Path.TrimStart('/')).EnsureClosed(); + return new StringUdi(Constants.DeployEntityType.PartialView, entity.Path.TrimStart('/')).EnsureClosed(); } /// @@ -209,7 +209,7 @@ namespace Umbraco.Core public static StringUdi GetUdi(this IXsltFile entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new StringUdi(DeployEntityType.Xslt, entity.Path.TrimStart('/')).EnsureClosed(); + return new StringUdi(Constants.DeployEntityType.Xslt, entity.Path.TrimStart('/')).EnsureClosed(); } /// @@ -222,9 +222,9 @@ namespace Umbraco.Core if (entity == null) throw new ArgumentNullException("entity"); string type; - if (entity is IContent) type = DeployEntityType.Document; - else if (entity is IMedia) type = DeployEntityType.Media; - else if (entity is IMember) type = DeployEntityType.Member; + if (entity is IContent) type = Constants.DeployEntityType.Document; + else if (entity is IMedia) type = Constants.DeployEntityType.Media; + else if (entity is IMember) type = Constants.DeployEntityType.Member; else throw new NotSupportedException(string.Format("ContentBase type {0} is not supported.", entity.GetType().FullName)); return new GuidUdi(type, entity.Key).EnsureClosed(); } @@ -237,7 +237,7 @@ namespace Umbraco.Core public static GuidUdi GetUdi(this IRelationType entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new GuidUdi(DeployEntityType.RelationType, entity.Key).EnsureClosed(); + return new GuidUdi(Constants.DeployEntityType.RelationType, entity.Key).EnsureClosed(); } /// diff --git a/src/Umbraco.Tests/UdiTests.cs b/src/Umbraco.Tests/UdiTests.cs index 2587e245aa..1a7b30a770 100644 --- a/src/Umbraco.Tests/UdiTests.cs +++ b/src/Umbraco.Tests/UdiTests.cs @@ -13,41 +13,41 @@ namespace Umbraco.Tests [Test] public void StringEntityCtorTest() { - var udi = new StringUdi(DeployEntityType.AnyString, "test-id"); - Assert.AreEqual(DeployEntityType.AnyString, udi.EntityType); + var udi = new StringUdi(Constants.DeployEntityType.AnyString, "test-id"); + Assert.AreEqual(Constants.DeployEntityType.AnyString, udi.EntityType); Assert.AreEqual("test-id", udi.Id); - Assert.AreEqual("umb://" + DeployEntityType.AnyString + "/test-id", udi.ToString()); + Assert.AreEqual("umb://" + Constants.DeployEntityType.AnyString + "/test-id", udi.ToString()); } [Test] public void StringEntityParseTest() { - var udi = Udi.Parse("umb://" + DeployEntityType.AnyString + "/test-id"); - Assert.AreEqual(DeployEntityType.AnyString, udi.EntityType); + var udi = Udi.Parse("umb://" + Constants.DeployEntityType.AnyString + "/test-id"); + Assert.AreEqual(Constants.DeployEntityType.AnyString, udi.EntityType); Assert.IsInstanceOf(udi); var stringEntityId = udi as StringUdi; Assert.IsNotNull(stringEntityId); Assert.AreEqual("test-id", stringEntityId.Id); - Assert.AreEqual("umb://" + DeployEntityType.AnyString + "/test-id", udi.ToString()); + Assert.AreEqual("umb://" + Constants.DeployEntityType.AnyString + "/test-id", udi.ToString()); } [Test] public void GuidEntityCtorTest() { var guid = Guid.NewGuid(); - var udi = new GuidUdi(DeployEntityType.AnyGuid, guid); - Assert.AreEqual(DeployEntityType.AnyGuid, udi.EntityType); + var udi = new GuidUdi(Constants.DeployEntityType.AnyGuid, guid); + Assert.AreEqual(Constants.DeployEntityType.AnyGuid, udi.EntityType); Assert.AreEqual(guid, udi.Guid); - Assert.AreEqual("umb://" + DeployEntityType.AnyGuid + "/" + guid.ToString("N"), udi.ToString()); + Assert.AreEqual("umb://" + Constants.DeployEntityType.AnyGuid + "/" + guid.ToString("N"), udi.ToString()); } [Test] public void GuidEntityParseTest() { var guid = Guid.NewGuid(); - var s = "umb://" + DeployEntityType.AnyGuid + "/" + guid.ToString("N"); + var s = "umb://" + Constants.DeployEntityType.AnyGuid + "/" + guid.ToString("N"); var udi = Udi.Parse(s); - Assert.AreEqual(DeployEntityType.AnyGuid, udi.EntityType); + Assert.AreEqual(Constants.DeployEntityType.AnyGuid, udi.EntityType); Assert.IsInstanceOf(udi); var gudi = udi as GuidUdi; Assert.IsNotNull(gudi); @@ -82,9 +82,9 @@ namespace Umbraco.Tests var guid1 = Guid.NewGuid(); var entities = new[] { - new GuidUdi(DeployEntityType.AnyGuid, guid1), - new GuidUdi(DeployEntityType.AnyGuid, guid1), - new GuidUdi(DeployEntityType.AnyGuid, guid1), + new GuidUdi(Constants.DeployEntityType.AnyGuid, guid1), + new GuidUdi(Constants.DeployEntityType.AnyGuid, guid1), + new GuidUdi(Constants.DeployEntityType.AnyGuid, guid1), }; Assert.AreEqual(1, entities.Distinct().Count()); } @@ -93,12 +93,12 @@ namespace Umbraco.Tests public void CreateTest() { var guid = Guid.NewGuid(); - var udi = Udi.Create(DeployEntityType.AnyGuid, guid); - Assert.AreEqual(DeployEntityType.AnyGuid, udi.EntityType); + var udi = Udi.Create(Constants.DeployEntityType.AnyGuid, guid); + Assert.AreEqual(Constants.DeployEntityType.AnyGuid, udi.EntityType); Assert.AreEqual(guid, ((GuidUdi)udi).Guid); - Assert.Throws(() => Udi.Create(DeployEntityType.AnyString, guid)); - Assert.Throws(() => Udi.Create(DeployEntityType.AnyGuid, "foo")); + Assert.Throws(() => Udi.Create(Constants.DeployEntityType.AnyString, guid)); + Assert.Throws(() => Udi.Create(Constants.DeployEntityType.AnyGuid, "foo")); Assert.Throws(() => Udi.Create("barf", "foo")); } @@ -134,12 +134,12 @@ namespace Umbraco.Tests var guid = Guid.NewGuid(); - var udi = new GuidUdi(DeployEntityType.AnyGuid, guid); + var udi = new GuidUdi(Constants.DeployEntityType.AnyGuid, guid); var json = JsonConvert.SerializeObject(udi, settings); Assert.AreEqual(string.Format("\"umb://any-guid/{0:N}\"", guid), json); var dudi = JsonConvert.DeserializeObject(json, settings); - Assert.AreEqual(DeployEntityType.AnyGuid, dudi.EntityType); + Assert.AreEqual(Constants.DeployEntityType.AnyGuid, dudi.EntityType); Assert.AreEqual(guid, ((GuidUdi)dudi).Guid); var range = new UdiRange(udi, Constants.DeploySelector.ChildrenOfThis); diff --git a/src/Umbraco.Web/Properties/AssemblyInfo.cs b/src/Umbraco.Web/Properties/AssemblyInfo.cs index 89499b833e..7b30b1f4b3 100644 --- a/src/Umbraco.Web/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Web/Properties/AssemblyInfo.cs @@ -34,6 +34,9 @@ using System.Security; [assembly: InternalsVisibleTo("Concorde.Sync")] [assembly: InternalsVisibleTo("Umbraco.Courier.Core")] [assembly: InternalsVisibleTo("Umbraco.Courier.Persistence")] +[assembly: InternalsVisibleTo("Umbraco.Deploy")] +[assembly: InternalsVisibleTo("Umbraco.Deploy.UI")] +[assembly: InternalsVisibleTo("Umbraco.Deploy.Cloud")] [assembly: InternalsVisibleTo("Umbraco.VisualStudio")] [assembly: InternalsVisibleTo("Umbraco.ModelsBuilder")] [assembly: InternalsVisibleTo("Umbraco.ModelsBuilder.AspNet")] From 256ebe7c14b5f06134df4d9bac5802c6e228beba Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 17 Jan 2017 15:29:58 +1100 Subject: [PATCH 251/599] bumps version for MyGet --- build/UmbracoVersion.txt | 2 +- src/SolutionInfo.cs | 2 +- src/Umbraco.Core/Configuration/UmbracoVersion.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index 1672ad53d0..62a06d8bcc 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,3 +1,3 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) 7.6.0 -alpha048 +alpha051 \ No newline at end of file diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index b092457e25..837339ea56 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -12,4 +12,4 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyFileVersion("7.6.0")] -[assembly: AssemblyInformationalVersion("7.6.0-alpha048")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("7.6.0-alpha051")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index cd759cba82..6cbbcd82a4 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration /// Gets the version comment (like beta or RC). /// /// The version comment. - public static string CurrentComment { get { return "alpha048"; } } + public static string CurrentComment { get { return "alpha051"; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx From 28debbda1b55e071e09911597beb5ffa27a3b1d0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 17 Jan 2017 16:21:43 +1100 Subject: [PATCH 252/599] adds more logging for when indexes are rebuilt --- src/UmbracoExamine/UmbracoContentIndexer.cs | 6 +++++- src/UmbracoExamine/UmbracoMemberIndexer.cs | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index efc3e4a214..37e1188612 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -371,6 +371,8 @@ namespace UmbracoExamine const int pageSize = 10000; var pageIndex = 0; + DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - Start data queries - {0}", type)); + switch (type) { case IndexTypes.Content: @@ -471,6 +473,8 @@ namespace UmbracoExamine break; } + + DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - End data queries - {0}", type)); } private IEnumerable GetSerializedContent(IEnumerable content, ISet notPublished) @@ -520,7 +524,7 @@ namespace UmbracoExamine public override void RebuildIndex() { - DataService.LogService.AddVerboseLog(-1, "Rebuilding index"); + DataService.LogService.AddInfoLog(-1, "Rebuilding index"); base.RebuildIndex(); } diff --git a/src/UmbracoExamine/UmbracoMemberIndexer.cs b/src/UmbracoExamine/UmbracoMemberIndexer.cs index 64a574822f..bc7dd2bda6 100644 --- a/src/UmbracoExamine/UmbracoMemberIndexer.cs +++ b/src/UmbracoExamine/UmbracoMemberIndexer.cs @@ -132,6 +132,8 @@ namespace UmbracoExamine const int pageSize = 1000; var pageIndex = 0; + DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - Start data queries - {0}", type)); + IMember[] members; if (IndexerData.IncludeNodeTypes.Any()) @@ -163,6 +165,8 @@ namespace UmbracoExamine pageIndex++; } while (members.Length == pageSize); } + + DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - End data queries - {0}", type)); } private IEnumerable GetSerializedMembers(IEnumerable members) From b2acb97639ada0a8f18ccdcb5df4d2ce61c8823d Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 17 Jan 2017 16:29:30 +1100 Subject: [PATCH 253/599] adds stopwatch timer to logging --- src/UmbracoExamine/UmbracoContentIndexer.cs | 6 +++++- src/UmbracoExamine/UmbracoMemberIndexer.cs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index 37e1188612..d23f14cd60 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.Diagnostics; using System.IO; using System.Linq; using System.Xml.Linq; @@ -372,6 +373,8 @@ namespace UmbracoExamine var pageIndex = 0; DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - Start data queries - {0}", type)); + var stopwatch = new Stopwatch(); + stopwatch.Start(); switch (type) { @@ -474,7 +477,8 @@ namespace UmbracoExamine break; } - DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - End data queries - {0}", type)); + stopwatch.Stop(); + DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - End data queries - {0}, took {1}ms", type, stopwatch.ElapsedMilliseconds)); } private IEnumerable GetSerializedContent(IEnumerable content, ISet notPublished) diff --git a/src/UmbracoExamine/UmbracoMemberIndexer.cs b/src/UmbracoExamine/UmbracoMemberIndexer.cs index bc7dd2bda6..e3833da317 100644 --- a/src/UmbracoExamine/UmbracoMemberIndexer.cs +++ b/src/UmbracoExamine/UmbracoMemberIndexer.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Services; using UmbracoExamine.Config; using System.Collections.Generic; +using System.Diagnostics; using Examine; using System.IO; using UmbracoExamine.DataServices; @@ -133,6 +134,8 @@ namespace UmbracoExamine var pageIndex = 0; DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - Start data queries - {0}", type)); + var stopwatch = new Stopwatch(); + stopwatch.Start(); IMember[] members; @@ -166,7 +169,8 @@ namespace UmbracoExamine } while (members.Length == pageSize); } - DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - End data queries - {0}", type)); + stopwatch.Stop(); + DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - End data queries - {0}, took {1}ms", type, stopwatch.ElapsedMilliseconds)); } private IEnumerable GetSerializedMembers(IEnumerable members) From 24c7dee5d05be6d6370a55bc92682341e72babd3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 17 Jan 2017 17:57:49 +1100 Subject: [PATCH 254/599] U4-9395 When rebuilding content indexes that don't support unpublished content and member indexes, use the cmsContentXml table as the data source --- src/Umbraco.Core/Models/IMediaType.cs | 2 +- .../Interfaces/IContentRepository.cs | 1 + .../Interfaces/IMediaRepository.cs | 11 +- .../Interfaces/IMemberRepository.cs | 2 +- .../Interfaces/IRepositoryVersionable.cs | 10 + .../Repositories/MediaRepository.cs | 24 -- .../Repositories/MemberRepository.cs | 2 +- .../Repositories/VersionableRepositoryBase.cs | 25 ++ src/Umbraco.Core/Services/ContentService.cs | 21 ++ src/Umbraco.Core/Services/IContentService.cs | 14 + src/Umbraco.Core/Services/IMemberService.cs | 10 + src/Umbraco.Core/Services/MemberService.cs | 20 ++ src/UmbracoExamine/UmbracoContentIndexer.cs | 263 +++++++++++------- src/UmbracoExamine/UmbracoMemberIndexer.cs | 105 +++++-- 14 files changed, 351 insertions(+), 159 deletions(-) diff --git a/src/Umbraco.Core/Models/IMediaType.cs b/src/Umbraco.Core/Models/IMediaType.cs index 29e4b665ba..e8f7c16190 100644 --- a/src/Umbraco.Core/Models/IMediaType.cs +++ b/src/Umbraco.Core/Models/IMediaType.cs @@ -4,7 +4,7 @@ namespace Umbraco.Core.Models { /// /// Defines a ContentType, which Media is based on - /// public interface IMediaType : IContentTypeComposition { diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs index e2555995d2..28e4fbf199 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs @@ -96,5 +96,6 @@ namespace Umbraco.Core.Persistence.Repositories /// An Enumerable list of objects IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, bool orderBySystemField, IQuery filter = null); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs index 64989f9269..9e8890a37b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs @@ -38,15 +38,6 @@ namespace Umbraco.Core.Persistence.Repositories /// An Enumerable list of objects IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, bool orderBySystemField, string filter = ""); - - /// - /// Gets paged media descendants as XML by path - /// - /// Path starts with - /// Page number - /// Page size - /// Total records the query would return without paging - /// A paged enumerable of XML entries of media items - IEnumerable GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs index a24116f0e2..9528fd3d76 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMemberRepository.cs @@ -73,6 +73,6 @@ namespace Umbraco.Core.Persistence.Repositories /// /// void AddOrUpdatePreviewXml(IMember content, Func xml); - + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs index 3e05d1feaf..256ef36c05 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs @@ -67,5 +67,15 @@ namespace Umbraco.Core.Persistence.Repositories /// Id of the object to delete versions from /// Latest version date void DeleteVersions(int id, DateTime versionDate); + + /// + /// Gets paged content descendants as XML by path + /// + /// Path starts with + /// Page number + /// Page size + /// Total records the query would return without paging + /// A paged enumerable of XML entries of content items + IEnumerable GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 08c2f7e0be..2687848fa5 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -502,30 +502,6 @@ namespace Umbraco.Core.Persistence.Repositories } - /// - /// Gets paged media descendants as XML by path - /// - /// Path starts with - /// Page number - /// Page size - /// Total records the query would return without paging - /// A paged enumerable of XML entries of media items - public IEnumerable GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords) - { - Sql query; - if (path == "-1") - { - query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE nodeObjectType = @0)", Guid.Parse(Constants.ObjectTypes.Media)).OrderBy("nodeId"); - } - else - { - query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE path LIKE @0)", path.EnsureEndsWith(",%")).OrderBy("nodeId"); - } - var pagedResult = Database.Page(pageIndex+1, pageSize, query); - totalRecords = pagedResult.TotalItems; - return pagedResult.Items.Select(dto => XElement.Parse(dto.Xml)); - } - /// /// Private method to create a media object from a ContentDto /// diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index dcab898685..a92794775f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -381,7 +381,7 @@ namespace Umbraco.Core.Persistence.Repositories .Where(GetBaseWhereClause(), new { Id = id }) .OrderByDescending(x => x.VersionDate, SqlSyntax); return ProcessQuery(sql, true); - } + } public void RebuildXmlStructures(Func serializer, int groupSize = 200, IEnumerable contentTypeIds = null) { diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index ec1623d16a..df15ab049c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; +using System.Xml.Linq; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; @@ -137,6 +138,30 @@ namespace Umbraco.Core.Persistence.Repositories #endregion + /// + /// Gets paged content descendants as XML by path + /// + /// Path starts with + /// Page number + /// Page size + /// Total records the query would return without paging + /// A paged enumerable of XML entries of content items + public IEnumerable GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords) + { + Sql query; + if (path == "-1") + { + query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE nodeObjectType = @0)", NodeObjectTypeId).OrderBy("nodeId"); + } + else + { + query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE path LIKE (@0))", path.EnsureEndsWith(",%")).OrderBy("nodeId"); + } + var pagedResult = Database.Page(pageIndex + 1, pageSize, query); + totalRecords = pagedResult.TotalItems; + return pagedResult.Items.Select(dto => XElement.Parse(dto.Xml)); + } + public int CountDescendants(int parentId, string contentTypeAlias = null) { var pathMatch = parentId == -1 diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 0838a0eb85..fff0ac61c8 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -1764,6 +1764,27 @@ namespace Umbraco.Core.Services return true; } + /// + /// Gets paged content descendants as XML by path + /// + /// Path starts with + /// Page number + /// Page size + /// Total records the query would return without paging + /// A paged enumerable of XML entries of content items + public IEnumerable GetPagedXmlEntries(string path, long pageIndex, int pageSize, out long totalRecords) + { + Mandate.ParameterCondition(pageIndex >= 0, "pageIndex"); + Mandate.ParameterCondition(pageSize > 0, "pageSize"); + + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateContentRepository(uow)) + { + var contents = repository.GetPagedXmlEntriesByPath(path, pageIndex, pageSize, out totalRecords); + return contents; + } + } + /// /// This builds the Xml document used for the XML cache /// diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index ec5db8c4fb..7722bf9c65 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Xml; +using System.Xml.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.DatabaseModelDefinitions; @@ -95,6 +96,19 @@ namespace Umbraco.Core.Services /// public interface IContentService : IService { + /// + /// Gets all XML entries found in the cmsContentXml table based on the given path + /// + /// Path starts with + /// Page number + /// Page size + /// Total records the query would return without paging + /// A paged enumerable of XML entries of content items + /// + /// If -1 is passed, then this will return all content xml entries, otherwise will return all descendents from the path + /// + IEnumerable GetPagedXmlEntries(string path, long pageIndex, int pageSize, out long totalRecords); + /// /// This builds the Xml document used for the XML cache /// diff --git a/src/Umbraco.Core/Services/IMemberService.cs b/src/Umbraco.Core/Services/IMemberService.cs index a893d89feb..38f53f2d68 100644 --- a/src/Umbraco.Core/Services/IMemberService.cs +++ b/src/Umbraco.Core/Services/IMemberService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Xml.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.DatabaseModelDefinitions; @@ -13,6 +14,15 @@ namespace Umbraco.Core.Services /// public interface IMemberService : IMembershipMemberService { + /// + /// Gets all XML entries found in the cmsContentXml table + /// + /// Page number + /// Page size + /// Total records the query would return without paging + /// A paged enumerable of XML entries of content items + IEnumerable GetPagedXmlEntries(long pageIndex, int pageSize, out long totalRecords); + /// /// Rebuilds all xml content in the cmsContentXml table for all documents /// diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index 2ea4f1f023..cf833dcd28 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -612,6 +612,26 @@ namespace Umbraco.Core.Services Audit(AuditType.Publish, "MemberService.RebuildXmlStructures completed, the xml has been regenerated in the database", 0, -1); } + /// + /// Gets paged member descendants as XML by path + /// + /// Page number + /// Page size + /// Total records the query would return without paging + /// A paged enumerable of XML entries of member items + public IEnumerable GetPagedXmlEntries(long pageIndex, int pageSize, out long totalRecords) + { + Mandate.ParameterCondition(pageIndex >= 0, "pageIndex"); + Mandate.ParameterCondition(pageSize > 0, "pageSize"); + + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateMemberRepository(uow)) + { + var contents = repository.GetPagedXmlEntriesByPath("-1", pageIndex, pageSize, out totalRecords); + return contents; + } + } + #endregion #region IMembershipMemberService Implementation diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index 447fd62f4c..c2dbecf83d 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -4,6 +4,7 @@ using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; +using System.Xml; using System.Xml.Linq; using Examine; using Lucene.Net.Documents; @@ -230,7 +231,13 @@ namespace UmbracoExamine SupportProtectedContent = supportProtected; else SupportProtectedContent = false; - + + bool disableXmlDocLookup; + if (config["disableXmlDocLookup"] != null && bool.TryParse(config["disableXmlDocLookup"], out disableXmlDocLookup)) + DisableXmlDocumentLookup = disableXmlDocLookup; + else + DisableXmlDocumentLookup = false; + base.Initialize(name, config); } @@ -238,6 +245,11 @@ namespace UmbracoExamine #region Properties + /// + /// Whether to use the cmsContentXml data to re-index when possible (i.e. for published content, media and members) + /// + public bool DisableXmlDocumentLookup { get; private set; } + /// /// By default this is false, if set to true then the indexer will include indexing content that is flagged as publicly protected. /// This property is ignored if SupportUnpublishedContent is set to true. @@ -364,6 +376,9 @@ namespace UmbracoExamine protected override void PerformIndexAll(string type) { + if (SupportedTypes.Contains(type) == false) + return; + const int pageSize = 10000; var pageIndex = 0; @@ -371,111 +386,171 @@ namespace UmbracoExamine var stopwatch = new Stopwatch(); stopwatch.Start(); - switch (type) + try { - case IndexTypes.Content: - var contentParentId = -1; - if (IndexerData.ParentNodeId.HasValue && IndexerData.ParentNodeId.Value > 0) - { - contentParentId = IndexerData.ParentNodeId.Value; - } - IContent[] content; - - //used to track non-published entities so we can determine what items are implicitly not published - var notPublished = new HashSet(); - - do - { - long total; - - IEnumerable descendants; - if (SupportUnpublishedContent) + switch (type) + { + case IndexTypes.Content: + var contentParentId = -1; + if (IndexerData.ParentNodeId.HasValue && IndexerData.ParentNodeId.Value > 0) { - descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total); - } - else - { - //get all paged records but order by level ascending, we need to do this because we need to track which nodes are not published so that we can determine - // which descendent nodes are implicitly not published - descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "level", Direction.Ascending, true, (string)null); - } - - //if specific types are declared we need to post filter them - //TODO: Update the service layer to join the cmsContentType table so we can query by content type too - if (IndexerData.IncludeNodeTypes.Any()) - { - content = descendants.Where(x => IndexerData.IncludeNodeTypes.Contains(x.ContentType.Alias)).ToArray(); - } - else - { - content = descendants.ToArray(); - } - - AddNodesToIndex(GetSerializedContent( - SupportUnpublishedContent, - c => _serializer.Serialize(_contentService, _dataTypeService, _userService, c), - content, notPublished).WhereNotNull(), type); - - pageIndex++; - } while (content.Length == pageSize); - - notPublished.Clear(); - - break; - case IndexTypes.Media: - var mediaParentId = -1; - - if (IndexerData.ParentNodeId.HasValue && IndexerData.ParentNodeId.Value > 0) - { - mediaParentId = IndexerData.ParentNodeId.Value; - } - - XElement[] mediaXElements; - - var mediaTypes = _contentTypeService.GetAllMediaTypes().ToArray(); - var icons = mediaTypes.ToDictionary(x => x.Id, y => y.Icon); - - do - { - long total; - if (mediaParentId == -1) - { - mediaXElements = _mediaService.GetPagedXmlEntries("-1", pageIndex, pageSize, out total).ToArray(); - } - else - { - //Get the parent - var parent = _mediaService.GetById(mediaParentId); - if (parent == null) - mediaXElements = new XElement[0]; - else - mediaXElements = _mediaService.GetPagedXmlEntries(parent.Path, pageIndex, pageSize, out total).ToArray(); - } - - //if specific types are declared we need to post filter them - //TODO: Update the service layer to join the cmsContentType table so we can query by content type too - if (IndexerData.IncludeNodeTypes.Any()) - { - var includeNodeTypeIds = mediaTypes.Where(x => IndexerData.IncludeNodeTypes.Contains(x.Alias)).Select(x => x.Id); - mediaXElements = mediaXElements.Where(elm => includeNodeTypeIds.Contains(elm.AttributeValue("nodeType"))).ToArray(); + contentParentId = IndexerData.ParentNodeId.Value; } - foreach (var element in mediaXElements) + if (SupportUnpublishedContent == false && DisableXmlDocumentLookup == false) { - element.Add(new XAttribute("icon", icons[element.AttributeValue("nodeType")])); + ReindexWithXmlEntries(type, contentParentId, + () => _contentTypeService.GetAllContentTypes().ToArray(), + (path, pIndex, pSize) => + { + long totalContent; + var result = _contentService.GetPagedXmlEntries(path, pIndex, pSize, out totalContent).ToArray(); + return new Tuple(totalContent, result); + }, + i => _contentService.GetById(i)); + } + else + { + //used to track non-published entities so we can determine what items are implicitly not published + //currently this is not in use apart form in tests + var notPublished = new HashSet(); + + IContent[] content; + do + { + long total; + + IEnumerable descendants; + if (SupportUnpublishedContent) + { + descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total); + } + else + { + //get all paged records but order by level ascending, we need to do this because we need to track which nodes are not published so that we can determine + // which descendent nodes are implicitly not published + descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "level", Direction.Ascending, true, (string)null); + } + + //if specific types are declared we need to post filter them + //TODO: Update the service layer to join the cmsContentType table so we can query by content type too + if (IndexerData.IncludeNodeTypes.Any()) + { + content = descendants.Where(x => IndexerData.IncludeNodeTypes.Contains(x.ContentType.Alias)).ToArray(); + } + else + { + content = descendants.ToArray(); + } + + AddNodesToIndex(GetSerializedContent( + SupportUnpublishedContent, + c => _serializer.Serialize(_contentService, _dataTypeService, _userService, c), + content, notPublished).WhereNotNull(), type); + + pageIndex++; + } while (content.Length == pageSize); } - AddNodesToIndex(mediaXElements, type); - pageIndex++; - } while (mediaXElements.Length == pageSize); + break; + case IndexTypes.Media: + var mediaParentId = -1; - break; + if (IndexerData.ParentNodeId.HasValue && IndexerData.ParentNodeId.Value > 0) + { + mediaParentId = IndexerData.ParentNodeId.Value; + } + + ReindexWithXmlEntries(type, mediaParentId, + () => _contentTypeService.GetAllMediaTypes().ToArray(), + (path, pIndex, pSize) => + { + long totalMedia; + var result = _mediaService.GetPagedXmlEntries(path, pIndex, pSize, out totalMedia).ToArray(); + return new Tuple(totalMedia, result); + }, + i => _mediaService.GetById(i)); + + break; + } } - - stopwatch.Stop(); + finally + { + stopwatch.Stop(); + } + DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - End data queries - {0}, took {1}ms", type, stopwatch.ElapsedMilliseconds)); } + /// + /// Performs a reindex of a type based on looking up entries from the cmsContentXml table - but using callbacks to get this data since + /// we don't have a common underlying service interface for the media/content stuff + /// + /// + /// + /// + /// + /// + internal void ReindexWithXmlEntries( + string type, + int parentId, + Func getContentTypes, + Func> getPagedXmlEntries, + Func getContent) + where TContentType: IContentTypeComposition + { + const int pageSize = 10000; + var pageIndex = 0; + + XElement[] xElements; + + var contentTypes = getContentTypes(); + var icons = contentTypes.ToDictionary(x => x.Id, y => y.Icon); + + do + { + long total; + if (parentId == -1) + { + var pagedElements = getPagedXmlEntries("-1", pageIndex, pageSize); + total = pagedElements.Item1; + xElements = pagedElements.Item2; + } + else + { + //Get the parent + var parent = getContent(parentId); + if (parent == null) + xElements = new XElement[0]; + else + { + var pagedElements = getPagedXmlEntries(parent.Path, pageIndex, pageSize); + total = pagedElements.Item1; + xElements = pagedElements.Item2; + } + } + + //if specific types are declared we need to post filter them + //TODO: Update the service layer to join the cmsContentType table so we can query by content type too + if (IndexerData.IncludeNodeTypes.Any()) + { + var includeNodeTypeIds = contentTypes.Where(x => IndexerData.IncludeNodeTypes.Contains(x.Alias)).Select(x => x.Id); + xElements = xElements.Where(elm => includeNodeTypeIds.Contains(elm.AttributeValue("nodeType"))).ToArray(); + } + + foreach (var element in xElements) + { + if (element.Attribute("icon") == null) + { + element.Add(new XAttribute("icon", icons[element.AttributeValue("nodeType")])); + } + } + + AddNodesToIndex(xElements, type); + pageIndex++; + } while (xElements.Length == pageSize); + } + internal static IEnumerable GetSerializedContent( bool supportUnpublishdContent, Func serializer, diff --git a/src/UmbracoExamine/UmbracoMemberIndexer.cs b/src/UmbracoExamine/UmbracoMemberIndexer.cs index e3833da317..33000a9f19 100644 --- a/src/UmbracoExamine/UmbracoMemberIndexer.cs +++ b/src/UmbracoExamine/UmbracoMemberIndexer.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Services; using UmbracoExamine.Config; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using Examine; using System.IO; @@ -25,6 +26,7 @@ namespace UmbracoExamine { private readonly IMemberService _memberService; + private readonly IMemberTypeService _memberTypeService; private readonly IDataTypeService _dataTypeService; /// @@ -34,6 +36,7 @@ namespace UmbracoExamine { _dataTypeService = ApplicationContext.Current.Services.DataTypeService; _memberService = ApplicationContext.Current.Services.MemberService; + _memberTypeService = ApplicationContext.Current.Services.MemberTypeService; } /// @@ -49,6 +52,7 @@ namespace UmbracoExamine { _dataTypeService = ApplicationContext.Current.Services.DataTypeService; _memberService = ApplicationContext.Current.Services.MemberService; + _memberTypeService = ApplicationContext.Current.Services.MemberTypeService; } /// @@ -61,6 +65,8 @@ namespace UmbracoExamine /// /// /// + [Obsolete("Use the ctor specifying all dependencies instead")] + [EditorBrowsable(EditorBrowsableState.Never)] public UmbracoMemberIndexer(IIndexCriteria indexerData, DirectoryInfo indexPath, IDataService dataService, IDataTypeService dataTypeService, IMemberService memberService, @@ -69,9 +75,31 @@ namespace UmbracoExamine { _dataTypeService = dataTypeService; _memberService = memberService; + _memberTypeService = ApplicationContext.Current.Services.MemberTypeService; } - + /// + /// Constructor to allow for creating an indexer at runtime + /// + /// + /// + /// + /// + /// + /// + /// + /// + public UmbracoMemberIndexer(IIndexCriteria indexerData, DirectoryInfo indexPath, IDataService dataService, + IDataTypeService dataTypeService, + IMemberService memberService, + IMemberTypeService memberTypeService, + Analyzer analyzer, bool async) + : base(indexerData, indexPath, dataService, analyzer, async) + { + _dataTypeService = dataTypeService; + _memberService = memberService; + _memberTypeService = memberTypeService; + } /// /// Ensures that the'_searchEmail' is added to the user fields so that it is indexed - without having to modify the config @@ -130,46 +158,67 @@ namespace UmbracoExamine if (SupportedTypes.Contains(type) == false) return; - const int pageSize = 1000; - var pageIndex = 0; - DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - Start data queries - {0}", type)); var stopwatch = new Stopwatch(); stopwatch.Start(); - IMember[] members; - - if (IndexerData.IncludeNodeTypes.Any()) + try { - //if there are specific node types then just index those - foreach (var nodeType in IndexerData.IncludeNodeTypes) + if (DisableXmlDocumentLookup == false) { - do + ReindexWithXmlEntries(type, -1, + () => _memberTypeService.GetAll().ToArray(), + (path, pIndex, pSize) => + { + long totalContent; + var result = _memberService.GetPagedXmlEntries(pIndex, pSize, out totalContent).ToArray(); + return new Tuple(totalContent, result); + }, + i => _memberService.GetById(i)); + } + else + { + const int pageSize = 1000; + var pageIndex = 0; + + IMember[] members; + + if (IndexerData.IncludeNodeTypes.Any()) { - long total; - members = _memberService.GetAll(pageIndex, pageSize, out total, "LoginName", Direction.Ascending, true, null, nodeType).ToArray(); + //if there are specific node types then just index those + foreach (var nodeType in IndexerData.IncludeNodeTypes) + { + do + { + long total; + members = _memberService.GetAll(pageIndex, pageSize, out total, "LoginName", Direction.Ascending, true, null, nodeType).ToArray(); - AddNodesToIndex(GetSerializedMembers(members), type); + AddNodesToIndex(GetSerializedMembers(members), type); - pageIndex++; - } while (members.Length == pageSize); + pageIndex++; + } while (members.Length == pageSize); + } + } + else + { + //no node types specified, do all members + do + { + int total; + members = _memberService.GetAll(pageIndex, pageSize, out total).ToArray(); + + AddNodesToIndex(GetSerializedMembers(members), type); + + pageIndex++; + } while (members.Length == pageSize); + } } } - else + finally { - //no node types specified, do all members - do - { - int total; - members = _memberService.GetAll(pageIndex, pageSize, out total).ToArray(); - - AddNodesToIndex(GetSerializedMembers(members), type); - - pageIndex++; - } while (members.Length == pageSize); + stopwatch.Stop(); } - - stopwatch.Stop(); + DataService.LogService.AddInfoLog(-1, string.Format("PerformIndexAll - End data queries - {0}, took {1}ms", type, stopwatch.ElapsedMilliseconds)); } From a5af5ba1a957f05990710268a2ecdc459fd84590 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 17 Jan 2017 18:54:50 +1100 Subject: [PATCH 255/599] Adds new property to PropertyEditor's to mark them as deprecated so they don't show up in the data type list unless they are already selected --- .../UmbracoSettings/ContentElement.cs | 11 +++++++++++ .../UmbracoSettings/IContentSection.cs | 6 ++++++ src/Umbraco.Core/PropertyEditors/PropertyEditor.cs | 4 ++++ .../PropertyEditors/PropertyEditorAttribute.cs | 6 ++++++ .../Mapping/AvailablePropertyEditorsResolver.cs | 14 ++++++++++++++ .../Models/Mapping/DataTypeModelMapper.cs | 3 ++- .../PropertyEditors/ContentPickerPropertyEditor.cs | 2 +- .../PropertyEditors/FolderBrowserPropertyEditor.cs | 2 +- .../PropertyEditors/MediaPickerPropertyEditor.cs | 3 ++- .../MemberGroupPickerPropertyEditor.cs | 2 +- .../PropertyEditors/MemberPickerPropertyEditor.cs | 2 +- .../MultiNodeTreePickerPropertyEditor.cs | 2 +- .../MultipleMediaPickerPropertyEditor.cs | 2 +- .../PropertyEditors/UserPickerPropertyEditor.cs | 2 +- 14 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs index aed7f61a06..05189acc3e 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs @@ -159,6 +159,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings get { return GetOptionalTextElement("defaultDocumentTypeProperty", "Textstring"); } } + [ConfigurationProperty("showDeprecatedPropertyEditors")] + internal InnerTextConfigurationElement ShowDeprecatedPropertyEditors + { + get { return GetOptionalTextElement("showDeprecatedPropertyEditors", false); } + } + [ConfigurationProperty("EnableInheritedDocumentTypes")] internal InnerTextConfigurationElement EnableInheritedDocumentTypes { @@ -306,6 +312,11 @@ namespace Umbraco.Core.Configuration.UmbracoSettings get { return DefaultDocumentTypeProperty; } } + bool IContentSection.ShowDeprecatedPropertyEditors + { + get { return ShowDeprecatedPropertyEditors; } + } + bool IContentSection.EnableInheritedDocumentTypes { get { return EnableInheritedDocumentTypes; } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs index 3d5e4435b6..a388126948 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs @@ -60,6 +60,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings string DefaultDocumentTypeProperty { get; } + /// + /// The default for this is false but if you would like deprecated property editors displayed + /// in the data type editor you can enable this + /// + bool ShowDeprecatedPropertyEditors { get; } + bool EnableInheritedDocumentTypes { get; } bool EnableInheritedMediaTypes { get; } diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs index 6cecc9dff5..c2498ecc7a 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs @@ -38,6 +38,7 @@ namespace Umbraco.Core.PropertyEditors IsParameterEditor = _attribute.IsParameterEditor; Icon = _attribute.Icon; Group = _attribute.Group; + IsDeprecated = _attribute.IsDeprecated; } } @@ -90,6 +91,9 @@ namespace Umbraco.Core.PropertyEditors get { return CreateValueEditor(); } } + [JsonIgnore] + public bool IsDeprecated { get; internal set; } + [JsonIgnore] IValueEditor IParameterEditor.ValueEditor { diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditorAttribute.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorAttribute.cs index d120753185..41e4ccc74e 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditorAttribute.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditorAttribute.cs @@ -60,6 +60,12 @@ namespace Umbraco.Core.PropertyEditors public string ValueType { get; set; } public bool IsParameterEditor { get; set; } + /// + /// If set to true, this property editor will not show up in the DataType's drop down list + /// if there is not already one of them chosen for a DataType + /// + public bool IsDeprecated { get; set; } + /// /// If this is is true than the editor will be displayed full width without a label /// diff --git a/src/Umbraco.Web/Models/Mapping/AvailablePropertyEditorsResolver.cs b/src/Umbraco.Web/Models/Mapping/AvailablePropertyEditorsResolver.cs index 624c641d3b..2b9047c284 100644 --- a/src/Umbraco.Web/Models/Mapping/AvailablePropertyEditorsResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/AvailablePropertyEditorsResolver.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using AutoMapper; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Web.Models.ContentEditing; @@ -10,9 +11,22 @@ namespace Umbraco.Web.Models.Mapping { internal class AvailablePropertyEditorsResolver : ValueResolver> { + private readonly IContentSection _contentSection; + + public AvailablePropertyEditorsResolver(IContentSection contentSection) + { + _contentSection = contentSection; + } + protected override IEnumerable ResolveCore(IDataTypeDefinition source) { return PropertyEditorResolver.Current.PropertyEditors + .Where(x => + { + if (_contentSection.ShowDeprecatedPropertyEditors) + return true; + return source.PropertyEditorAlias == x.Alias || x.IsDeprecated == false; + }) .OrderBy(x => x.Name) .Select(Mapper.Map); } diff --git a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs b/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs index ac2faa82dc..d7dfdbf9d0 100644 --- a/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/DataTypeModelMapper.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using AutoMapper; using System.Linq; using Umbraco.Core; +using Umbraco.Core.Configuration; using Umbraco.Core.Models; using Umbraco.Core.Models.Mapping; using Umbraco.Core.PropertyEditors; @@ -61,7 +62,7 @@ namespace Umbraco.Web.Models.Mapping }); config.CreateMap() - .ForMember(display => display.AvailableEditors, expression => expression.ResolveUsing(new AvailablePropertyEditorsResolver())) + .ForMember(display => display.AvailableEditors, expression => expression.ResolveUsing(new AvailablePropertyEditorsResolver(UmbracoConfig.For.UmbracoSettings().Content))) .ForMember(display => display.PreValues, expression => expression.ResolveUsing( new PreValueDisplayResolver(lazyDataTypeService))) .ForMember(display => display.SelectedEditor, expression => expression.MapFrom( diff --git a/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs index 4d58060164..6a709845ff 100644 --- a/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs @@ -4,7 +4,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.ContentPickerAlias, "Content Picker", PropertyEditorValueTypes.Integer, "contentpicker", IsParameterEditor = true, Group = "Pickers")] + [PropertyEditor(Constants.PropertyEditors.ContentPickerAlias, "[Legacy] Content Picker", PropertyEditorValueTypes.Integer, "contentpicker", IsParameterEditor = true, Group = "Pickers", IsDeprecated = true)] public class ContentPickerPropertyEditor : PropertyEditor { diff --git a/src/Umbraco.Web/PropertyEditors/FolderBrowserPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/FolderBrowserPropertyEditor.cs index bfdbe368d1..5c89b868b8 100644 --- a/src/Umbraco.Web/PropertyEditors/FolderBrowserPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/FolderBrowserPropertyEditor.cs @@ -9,7 +9,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { [Obsolete("This is no longer used by default, use the ListViewPropertyEditor instead")] - [PropertyEditor(Constants.PropertyEditors.FolderBrowserAlias, "(Obsolete) Folder Browser", "folderbrowser", HideLabel=true, Icon="icon-folder", Group="media")] + [PropertyEditor(Constants.PropertyEditors.FolderBrowserAlias, "(Obsolete) Folder Browser", "folderbrowser", HideLabel=true, Icon="icon-folder", Group="media", IsDeprecated = true)] public class FolderBrowserPropertyEditor : PropertyEditor { diff --git a/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs index 7e966d31ad..718334f60b 100644 --- a/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MediaPickerPropertyEditor.cs @@ -9,7 +9,8 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.MediaPickerAlias, "Legacy Media Picker", PropertyEditorValueTypes.Integer, "mediapicker", Group="media", Icon="icon-picture")] + + [PropertyEditor(Constants.PropertyEditors.MediaPickerAlias, "Legacy Media Picker", PropertyEditorValueTypes.Integer, "mediapicker", Group="media", Icon="icon-picture", IsDeprecated = true)] public class MediaPickerPropertyEditor : PropertyEditor { public MediaPickerPropertyEditor() diff --git a/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs index 433199c536..d7051ba8da 100644 --- a/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MemberGroupPickerPropertyEditor.cs @@ -8,7 +8,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.MemberGroupPickerAlias, "Member Group Picker", "membergrouppicker", Group="People", Icon="icon-users")] + [PropertyEditor(Constants.PropertyEditors.MemberGroupPickerAlias, "Member Group Picker", "membergrouppicker", Group="People", Icon="icon-users", IsDeprecated = true)] public class MemberGroupPickerPropertyEditor : PropertyEditor { } diff --git a/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs index 7dadbfe24c..90e7562f3a 100644 --- a/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MemberPickerPropertyEditor.cs @@ -8,7 +8,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.MemberPickerAlias, "Member Picker", PropertyEditorValueTypes.Integer, "memberpicker", Group = "People", Icon = "icon-user")] + [PropertyEditor(Constants.PropertyEditors.MemberPickerAlias, "Member Picker", PropertyEditorValueTypes.Integer, "memberpicker", Group = "People", Icon = "icon-user", IsDeprecated = true)] public class MemberPickerPropertyEditor : PropertyEditor { } diff --git a/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs index b3d3f02448..1bdf0d45d9 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs @@ -5,7 +5,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.MultiNodeTreePickerAlias, "Multinode Treepicker", "contentpicker", Group="pickers", Icon="icon-page-add")] + [PropertyEditor(Constants.PropertyEditors.MultiNodeTreePickerAlias, "Multinode Treepicker", "contentpicker", Group="pickers", Icon="icon-page-add", IsDeprecated = true)] public class MultiNodeTreePickerPropertyEditor : PropertyEditor { public MultiNodeTreePickerPropertyEditor() diff --git a/src/Umbraco.Web/PropertyEditors/MultipleMediaPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultipleMediaPickerPropertyEditor.cs index 37587d1c54..18a1e564d6 100644 --- a/src/Umbraco.Web/PropertyEditors/MultipleMediaPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultipleMediaPickerPropertyEditor.cs @@ -3,7 +3,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.MultipleMediaPickerAlias, "Media Picker", "mediapicker", Group = "media", Icon = "icon-pictures-alt-2")] + [PropertyEditor(Constants.PropertyEditors.MultipleMediaPickerAlias, "Media Picker", "mediapicker", Group = "media", Icon = "icon-pictures-alt-2", IsDeprecated = true)] public class MultipleMediaPickerPropertyEditor : MediaPickerPropertyEditor { public MultipleMediaPickerPropertyEditor() diff --git a/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs index e4a070cb29..f824f25294 100644 --- a/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/UserPickerPropertyEditor.cs @@ -6,7 +6,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { - [PropertyEditor(Constants.PropertyEditors.UserPickerAlias, "User picker", PropertyEditorValueTypes.Integer, "entitypicker", Group="People", Icon="icon-user")] + [PropertyEditor(Constants.PropertyEditors.UserPickerAlias, "User picker", PropertyEditorValueTypes.Integer, "entitypicker", Group="People", Icon="icon-user", IsDeprecated = true)] public class UserPickerPropertyEditor : PropertyEditor { private IDictionary _defaultPreValues; From 907afa11ad9797470c4496bf110be5f5d592cb18 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 17 Jan 2017 11:11:20 +0100 Subject: [PATCH 256/599] combine "open" end "edit" prevalues into one setting (open in dialog) + remove "show path on hover" setting because it is not used anymore --- .../PropertyEditors/ContentPickerPropertyEditor.cs | 7 +------ .../PropertyEditors/MultiNodeTreePickerPropertyEditor.cs | 9 +-------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs index 4d58060164..be10cd83e2 100644 --- a/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ContentPickerPropertyEditor.cs @@ -33,17 +33,12 @@ namespace Umbraco.Web.PropertyEditors internal class ContentPickerPreValueEditor : PreValueEditor { - [PreValueField("showOpenButton", "Show open button", "boolean")] + [PreValueField("showOpenButton", "Show open button (this feature is in preview!)", "boolean", Description = " Opens the node in a dialog")] public string ShowOpenButton { get; set; } - - [PreValueField("showEditButton", "Show edit button (this feature is in preview!)", "boolean")] - public string ShowEditButton { get; set; } [PreValueField("startNodeId", "Start node", "treepicker")] public int StartNodeId { get; set; } - [PreValueField("showPathOnHover", "Show path when hovering items", "boolean")] - public bool ShowPathOnHover { get; set; } } } } \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs index b3d3f02448..82daf80e34 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultiNodeTreePickerPropertyEditor.cs @@ -45,16 +45,9 @@ namespace Umbraco.Web.PropertyEditors [PreValueField("maxNumber", "Maximum number of items", "number")] public string MaxNumber { get; set; } - - [PreValueField("showOpenButton", "Show open button", "boolean")] + [PreValueField("showOpenButton", "Show open button (this feature is in preview!)", "boolean", Description = " Opens the node in a dialog")] public string ShowOpenButton { get; set; } - [PreValueField("showEditButton", "Show edit button (this feature is in preview!)", "boolean")] - public string ShowEditButton { get; set; } - - [PreValueField("showPathOnHover", "Show path when hovering items", "boolean")] - public bool ShowPathOnHover { get; set; } - /// /// This ensures the multiPicker pre-val is set based on the maxNumber of nodes set /// From 01f50a243b3fcec4a3bc49dc6b1c86dfb97efa3b Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 17 Jan 2017 14:13:20 +0100 Subject: [PATCH 257/599] translate master template and no macros --- .../views/common/overlays/macropicker/macropicker.html | 8 ++++---- .../src/views/templates/edit.controller.js | 8 -------- src/Umbraco.Web.UI.Client/src/views/templates/edit.html | 6 +++++- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 2 ++ src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 2 ++ 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.html index 418e6b5ca6..6bcbaad50c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/macropicker/macropicker.html @@ -27,12 +27,12 @@ - - + + There are no macros available to insert -
    diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 97aa50cbbf..4cf55f4768 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -444,23 +444,15 @@ } function getMasterTemplateName(masterTemplateAlias, templates) { - if(masterTemplateAlias) { - var templateName = ""; - angular.forEach(templates, function(template){ if(template.alias === masterTemplateAlias) { templateName = template.name; } }); - return templateName; - - } else { - return "no master"; } - } function removeMasterTemplate() { diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html index 2e5593894c..bdfb9517f3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.html @@ -33,7 +33,11 @@ class="umb-era-button umb-button--s" ng-click="vm.openMasterTemplateOverlay()"> - Master template: {{ vm.getMasterTemplateName(vm.template.masterTemplateAlias, vm.templates) }} + Master template: + + {{ vm.getMasterTemplateName(vm.template.masterTemplateAlias, vm.templates) }} + No master + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index e14a0f9109..32b1ef780e 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -291,6 +291,7 @@ Vælg medlem Vælg medlemsgruppe Der er ingen parametre for denne makro + Der er ikke tilføjet nogle makroer Link dit Fjern link fra dit konto @@ -1039,6 +1040,7 @@ Mange hilsner fra Umbraco robotten Master skabelon Ingen master skabelon + Ingen master Indsæt en underliggende skabelon diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 3a7a0fda7c..7243a34490 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -305,6 +305,7 @@ Select member group No icons were found There are no parameters for this macro + There are no macros available to insert External login providers Exception Details Stacktrace @@ -1057,6 +1058,7 @@ To manage your website, simply open the Umbraco back office and start adding con Master template No master template + No master Render child template From 5f68f26d2f5901cacca02d3d803253c982aafe80 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 17 Jan 2017 16:32:40 +0100 Subject: [PATCH 258/599] U4-9322 - scope and database, getting tests to run --- .../Persistence/DefaultDatabaseFactory.cs | 137 +----------------- .../Persistence/UmbracoDatabase.cs | 15 +- .../UnitOfWork/PetaPocoUnitOfWork.cs | 13 +- src/Umbraco.Core/Scoping/IScope.cs | 5 +- .../Scoping/IScopeProviderInternal.cs | 18 +-- src/Umbraco.Core/Scoping/NoScope.cs | 20 ++- src/Umbraco.Core/Scoping/Scope.cs | 77 ++++++---- src/Umbraco.Core/Scoping/ScopeProvider.cs | 74 ++++------ src/Umbraco.Core/Scoping/ScopeReference.cs | 31 ++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + src/Umbraco.Tests/Scoping/LeakTests.cs | 90 ++++++++++++ src/Umbraco.Tests/Scoping/ScopeTests.cs | 54 +++++-- .../TestHelpers/BaseDatabaseFactoryTest.cs | 18 ++- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + 14 files changed, 287 insertions(+), 267 deletions(-) create mode 100644 src/Umbraco.Core/Scoping/ScopeReference.cs create mode 100644 src/Umbraco.Tests/Scoping/LeakTests.cs diff --git a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs index 46a2defd66..87a6263351 100644 --- a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs @@ -62,106 +62,8 @@ namespace Umbraco.Core.Persistence public UmbracoDatabase CreateDatabase() { return ScopeProvider.AmbientOrNoScope.Database; - //var scope = ScopeProvider.AmbientScope; - //return scope != null ? scope.Database : ScopeProvider.CreateNoScope().Database; - - /* - UmbracoDatabase database; - - // gets or creates a database, using either the call context (if no http context) or - // the current request context (http context) to store it. once done using the database, - // it should be disposed - which will remove it from whatever context it is currently - // stored in. this is automatic with http context because UmbracoDatabase implements - // IDisposeOnRequestEnd, but NOT with call context. - - if (HttpContext.Current == null) - { - database = NonContextValue; - if (database == null) - { - lock (Locker) - { - database = NonContextValue; - if (database == null) - { - database = CreateDatabaseInstance(ContextOwner.CallContext); - NonContextValue = database; - } -#if DEBUG_DATABASES - else - { - Log("Get lcc", database); - } -#endif - } - } -#if DEBUG_DATABASES - else - { - Log("Get lcc", database); - } -#endif - return database; - } - - if (HttpContext.Current.Items.Contains(typeof (DefaultDatabaseFactory)) == false) - { - database = CreateDatabaseInstance(ContextOwner.HttpContext); - HttpContext.Current.Items.Add(typeof (DefaultDatabaseFactory), database); - } - else - { - database = (UmbracoDatabase) HttpContext.Current.Items[typeof(DefaultDatabaseFactory)]; -#if DEBUG_DATABASES - Log("Get ctx", database); -#endif - } - - return database; - */ } - // called by UmbracoDatabase when disposed, so that the factory can de-list it from context - /* - internal void OnDispose(UmbracoDatabase disposing) - { - var value = disposing; - switch (disposing.ContextOwner) - { - case ContextOwner.CallContext: - value = NonContextValue; - break; - case ContextOwner.HttpContext: - value = (UmbracoDatabase) HttpContext.Current.Items[typeof (DefaultDatabaseFactory)]; - break; - } - - if (value != null && value.InstanceId != disposing.InstanceId) throw new Exception("panic: wrong db."); - - switch (disposing.ContextOwner) - { - case ContextOwner.CallContext: - NonContextValue = null; -#if DEBUG_DATABASES - Log("Clr lcc", disposing); -#endif - break; - case ContextOwner.HttpContext: - HttpContext.Current.Items.Remove(typeof(DefaultDatabaseFactory)); -#if DEBUG_DATABASES - Log("Clr ctx", disposing); -#endif - break; - } - - disposing.ContextOwner = ContextOwner.None; - -#if DEBUG_DATABASES - _databases.Remove(value); -#endif - } - */ - #if DEBUG_DATABASES // helps identifying when non-httpContext databases are created by logging the stack trace private void LogCallContextStack() @@ -188,58 +90,23 @@ namespace Umbraco.Core.Persistence } #endif - internal enum ContextOwner - { - None, - HttpContext, - CallContext - } - public UmbracoDatabase CreateNewDatabase() { - return CreateDatabaseInstance(ContextOwner.None); + return CreateDatabaseInstance(); } - internal UmbracoDatabase CreateDatabaseInstance(ContextOwner contextOwner) + internal UmbracoDatabase CreateDatabaseInstance() { var database = string.IsNullOrEmpty(ConnectionString) == false && string.IsNullOrEmpty(ProviderName) == false ? new UmbracoDatabase(ConnectionString, ProviderName, _logger) : new UmbracoDatabase(_connectionStringName, _logger); - database.ContextOwner = contextOwner; database.DatabaseFactory = this; - //database.EnableSqlTrace = true; -#if DEBUG_DATABASES - Log("Create " + contextOwner, database); - if (contextOwner == ContextOwner.CallContext) - LogCallContextStack(); - _databases.Add(database); -#endif return database; } protected override void DisposeResources() { - /* - UmbracoDatabase database; - - if (HttpContext.Current == null) - { - database = NonContextValue; -#if DEBUG_DATABASES - Log("Release lcc", database); -#endif - } - else - { - database = (UmbracoDatabase) HttpContext.Current.Items[typeof (DefaultDatabaseFactory)]; -#if DEBUG_DATABASES - Log("Release ctx", database); -#endif - } - - if (database != null) database.Dispose(); // removes it from call context - */ } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs index 46284b1a2d..c451117847 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs @@ -29,7 +29,6 @@ namespace Umbraco.Core.Persistence private int _spid = -1; #endif - internal DefaultDatabaseFactory.ContextOwner ContextOwner = DefaultDatabaseFactory.ContextOwner.None; internal DefaultDatabaseFactory DatabaseFactory = null; /// @@ -156,6 +155,8 @@ namespace Umbraco.Core.Persistence // propagate timeout if none yet #if DEBUG_DATABASES + // determines the database connection SPID for debugging + if (DatabaseType == DBType.MySql) { using (var command = connection.CreateCommand()) @@ -220,6 +221,7 @@ namespace Umbraco.Core.Persistence } #if DEBUG_DATABASES + // ensures the database does not have an open reader, for debugging DatabaseDebugHelper.SetCommand(cmd, InstanceSid + " [T" + Thread.CurrentThread.ManagedThreadId + "]"); var refsobj = DatabaseDebugHelper.GetReferencedObjects(cmd.Connection); if (refsobj != null) _logger.Debug("Oops!" + Environment.NewLine + refsobj); @@ -272,16 +274,5 @@ namespace Umbraco.Core.Persistence //use the defaults base.BuildSqlDbSpecificPagingQuery(databaseType, skip, take, sql, sqlSelectRemoved, sqlOrderBy, ref args, out sqlPage); } - - /* - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); -#if DEBUG_DATABASES - LogHelper.Debug("Dispose (" + InstanceSid + ")."); -#endif - if (DatabaseFactory != null) DatabaseFactory.OnDispose(this); - } - */ } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs index 6499ea29fe..5398a00095 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs @@ -141,15 +141,14 @@ namespace Umbraco.Core.Persistence.UnitOfWork get { return _key; } } + private IScope ThisScope + { + get { return _scope ?? (_scope = _scopeProvider.CreateScope()); } + } + public UmbracoDatabase Database { - get - { - if (_scope == null) - //throw new InvalidOperationException("Out-of-scope UnitOfWork."); - _scope = _scopeProvider.CreateScope(); - return _scope.Database; - } + get { return ThisScope.Database; } } #region Operation diff --git a/src/Umbraco.Core/Scoping/IScope.cs b/src/Umbraco.Core/Scoping/IScope.cs index e379273f58..bc23ad3868 100644 --- a/src/Umbraco.Core/Scoping/IScope.cs +++ b/src/Umbraco.Core/Scoping/IScope.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Core.Events; using Umbraco.Core.Persistence; @@ -7,7 +8,7 @@ namespace Umbraco.Core.Scoping /// /// Represents a scope. /// - public interface IScope : IDisposeOnRequestEnd // implies IDisposable + public interface IScope : IDisposable { /// /// Gets the scope database. diff --git a/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs b/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs index 856d30a4da..b99b18e1d0 100644 --- a/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs +++ b/src/Umbraco.Core/Scoping/IScopeProviderInternal.cs @@ -1,7 +1,7 @@ namespace Umbraco.Core.Scoping { /// - /// Provices scopes. + /// Provides scopes. /// /// Extends with internal features. internal interface IScopeProviderInternal : IScopeProvider @@ -11,18 +11,16 @@ /// IScope AmbientScope { get; } - // fixme + /// + /// Gets the ambient scope if any, else creates and returns a . + /// IScope AmbientOrNoScope { get; } /// - /// Creates a instance. + /// Resets the ambient scope. /// - /// The created ambient scope. - /// - /// The created scope becomes the ambient scope. - /// If an ambient scope already exists, throws. - /// The instance can be eventually replaced by a real instance. - /// - //IScope CreateNoScope(); + /// Resets the ambient scope (not completed anymore) and disposes the + /// entire scopes chain until there is no more scopes. + void Reset(); } } diff --git a/src/Umbraco.Core/Scoping/NoScope.cs b/src/Umbraco.Core/Scoping/NoScope.cs index 5f67c28d94..9a2ebec5fe 100644 --- a/src/Umbraco.Core/Scoping/NoScope.cs +++ b/src/Umbraco.Core/Scoping/NoScope.cs @@ -5,6 +5,9 @@ using Umbraco.Core.Persistence; namespace Umbraco.Core.Scoping { + /// + /// Implements when there is no scope. + /// internal class NoScope : IScope { private readonly ScopeProvider _scopeProvider; @@ -18,8 +21,7 @@ namespace Umbraco.Core.Scoping _scopeProvider = scopeProvider; } - //public bool HasDatabase { get { return _database != null; } } - + /// public UmbracoDatabase Database { get @@ -38,8 +40,7 @@ namespace Umbraco.Core.Scoping } } - //public bool HasMessages { get { return _messages != null; } } - + /// public IList Messages { get @@ -58,6 +59,7 @@ namespace Umbraco.Core.Scoping } } + /// public void Complete() { throw new NotImplementedException(); @@ -72,7 +74,15 @@ namespace Umbraco.Core.Scoping public void Dispose() { EnsureNotDisposed(); - _scopeProvider.Disposing(this); + + if (this != _scopeProvider.AmbientScope) + throw new InvalidOperationException("Not the ambient scope."); + + if (_database != null) + _database.Dispose(); + + _scopeProvider.AmbientScope = null; + _disposed = true; GC.SuppressFinalize(this); } diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index 8956688faa..cdfa24310a 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -5,8 +5,10 @@ using Umbraco.Core.Persistence; namespace Umbraco.Core.Scoping { - // note - scope is not thread-safe obviously - + /// + /// Implements . + /// + /// Not thread-safe obviously. internal class Scope : IScope { private readonly ScopeProvider _scopeProvider; @@ -53,11 +55,6 @@ namespace Umbraco.Core.Scoping // the original scope (when attaching a detachable scope) public Scope OrigScope { get; set; } - //public bool HasDatabase - //{ - // get { return ParentScope == null ? _database != null : ParentScope.HasDatabase; } - //} - /// public UmbracoDatabase Database { @@ -88,11 +85,6 @@ namespace Umbraco.Core.Scoping } } - //public bool HasMessages - //{ - // get { return ParentScope == null ? _messages != null : ParentScope.HasMessages; } - //} - /// public IList Messages { @@ -122,26 +114,16 @@ namespace Umbraco.Core.Scoping _completed = true; } - public void CompleteChild(bool? completed) + public void Reset() { - if (completed.HasValue) - { - if (completed.Value) - { - // child did complete - // nothing to do - } - else - { - // child did not complete, we cannot complete - _completed = false; - } - } - else - { - // child did not complete, we cannot complete + _completed = null; + } + + public void ChildCompleted(bool? completed) + { + // if child did not complete we cannot complete + if (completed.HasValue == false || completed.Value == false) _completed = false; - } } private void EnsureNotDisposed() @@ -153,9 +135,42 @@ namespace Umbraco.Core.Scoping public void Dispose() { EnsureNotDisposed(); - _scopeProvider.Disposing(this, _completed); + + if (this != _scopeProvider.AmbientScope) + throw new InvalidOperationException("Not the ambient scope."); + + var parent = ParentScope; + _scopeProvider.AmbientScope = parent; + + if (parent != null) + parent.ChildCompleted(_completed); + else + DisposeLastScope(); + _disposed = true; GC.SuppressFinalize(this); } + + private void DisposeLastScope() + { + // note - messages + // at the moment we are totally not filtering the messages based on completion + // status, so whether the scope is committed or rolled back makes no difference + + if (_database == null) return; + + try + { + if (_completed.HasValue && _completed.Value) + _database.CompleteTransaction(); + else + _database.AbortTransaction(); + } + finally + { + _database.Dispose(); + _database = null; + } + } } } diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs index c2b918f812..facd0f746c 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -5,6 +5,9 @@ using Umbraco.Core.Persistence; namespace Umbraco.Core.Scoping { + /// + /// Implements . + /// internal class ScopeProvider : IScopeProviderInternal { public ScopeProvider(IDatabaseFactory2 databaseFactory) @@ -26,13 +29,17 @@ namespace Umbraco.Core.Scoping var ambient = StaticAmbientScope; if (ambient != null) ambient.Dispose(); - StaticAmbientScope = (IScope) scope; + StaticAmbientScope = (IScope)scope; }); } public IDatabaseFactory2 DatabaseFactory { get; private set; } private const string ItemKey = "Umbraco.Core.Scoping.IScope"; + private const string ItemRefKey = "Umbraco.Core.Scoping.ScopeReference"; + + // only 1 instance which can be disposed and disposed again + private static readonly ScopeReference StaticScopeReference = new ScopeReference(new ScopeProvider(null)); private static IScope CallContextValue { @@ -49,8 +56,17 @@ namespace Umbraco.Core.Scoping get { return (IScope) HttpContext.Current.Items[ItemKey]; } set { - if (value == null) HttpContext.Current.Items.Remove(ItemKey); - else HttpContext.Current.Items[ItemKey] = value; + if (value == null) + { + HttpContext.Current.Items.Remove(ItemKey); + HttpContext.Current.Items.Remove(ItemRefKey); + } + else + { + HttpContext.Current.Items[ItemKey] = value; + if (HttpContext.Current.Items[ItemRefKey] == null) + HttpContext.Current.Items[ItemRefKey] = StaticScopeReference; + } } } @@ -164,54 +180,14 @@ namespace Umbraco.Core.Scoping return AmbientScope = new Scope(this, scope); } - public void Disposing(IScope disposing, bool? completed = null) + /// + public void Reset() { - if (disposing != AmbientScope) - throw new InvalidOperationException(); + var scope = AmbientScope as Scope; + if (scope != null) + scope.Reset(); - var noScope = disposing as NoScope; - if (noScope != null) - { - // fixme - kinda legacy - var noScopeDatabase = noScope.DatabaseOrNull; - if (noScopeDatabase != null) - { - if (noScopeDatabase.InTransaction) - throw new Exception(); - noScopeDatabase.Dispose(); - } - AmbientScope = null; - return; - } - - var scope = disposing as Scope; - if (scope == null) - throw new Exception(); - - var parent = scope.ParentScope; - AmbientScope = parent; - - if (parent != null) - { - parent.CompleteChild(completed); - return; - } - - // fixme - a scope is in a transaction only if ... there is a db transaction, or always? - // what shall we do with events if not in a transaction? - // fixme - when completing... the db should be released, no need to dispose the db? - - // note - messages - // at the moment we are totally not filtering the messages based on completion - // status, so whether the scope is committed or rolled back makes no difference - - var database = scope.DatabaseOrNull; - if (database == null) return; - - if (completed.HasValue && completed.Value) - database.CompleteTransaction(); - else - database.AbortTransaction(); + StaticScopeReference.Dispose(); } } } diff --git a/src/Umbraco.Core/Scoping/ScopeReference.cs b/src/Umbraco.Core/Scoping/ScopeReference.cs new file mode 100644 index 0000000000..74db47e9c4 --- /dev/null +++ b/src/Umbraco.Core/Scoping/ScopeReference.cs @@ -0,0 +1,31 @@ +namespace Umbraco.Core.Scoping +{ + /// + /// References a scope. + /// + /// Should go into HttpContext to indicate there is also an IScope in context + /// that needs to be disposed at the end of the request (the scope, and the entire scopes + /// chain). + internal class ScopeReference : IDisposeOnRequestEnd // implies IDisposable + { + private readonly IScopeProviderInternal _scopeProvider; + + public ScopeReference(IScopeProviderInternal scopeProvider) + { + _scopeProvider = scopeProvider; + } + + public void Dispose() + { + // dispose the entire chain (if any) + // reset (don't commit by default) + IScope scope; + while ((scope = _scopeProvider.AmbientScope) != null) + { + if (scope is Scope) + ((Scope) scope).Reset(); + scope.Dispose(); + } + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 3dc2cc31f4..afa85609d0 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -518,6 +518,7 @@ + diff --git a/src/Umbraco.Tests/Scoping/LeakTests.cs b/src/Umbraco.Tests/Scoping/LeakTests.cs new file mode 100644 index 0000000000..94d1f2a295 --- /dev/null +++ b/src/Umbraco.Tests/Scoping/LeakTests.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Runtime.Remoting.Messaging; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; +using Umbraco.Core.Persistence; +using Umbraco.Core.Scoping; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Scoping +{ + [TestFixture] + [DatabaseTestBehavior(DatabaseBehavior.EmptyDbFilePerTest)] + public class LeakTests : BaseDatabaseFactoryTest + { + private UmbracoDatabase _database; + private IDbConnection _connection; + + // setup + public override void Initialize() + { + base.Initialize(); + + //// initialization leaves a NoScope around, remove it + //var scope = DatabaseContext.ScopeProvider.AmbientScope; + //Assert.IsNotNull(scope); + //Assert.IsInstanceOf(scope); + //scope.Dispose(); + Assert.IsNull(DatabaseContext.ScopeProvider.AmbientScope); // gone + } + + // note: testing this with a test action is pointless as the + // AfterTest method runs *before* the TearDown methods which + // are the methods that should cleanup the call context... + + [Test] + public void LeakTest() + { + _database = DatabaseContext.Database; // creates a database + + _database.Execute("CREATE TABLE foo (id INT)"); // opens a connection + Assert.IsNull(_database.Connection); // is immediately closed + + _database.BeginTransaction(); // opens and maintains a connection + + // the test is leaking a scope with a non-null database + var contextScope = CallContext.LogicalGetData("Umbraco.Core.Scoping.IScope"); + Assert.IsNotNull(contextScope); + Assert.IsInstanceOf(CallContext.LogicalGetData("Umbraco.Core.Scoping.IScope")); + Assert.IsNotNull(((NoScope) contextScope).DatabaseOrNull); + Assert.AreSame(_database, ((NoScope)contextScope).DatabaseOrNull); + + // save the connection + _connection = _database.Connection; + Assert.IsInstanceOf(_connection); + _connection = ((StackExchange.Profiling.Data.ProfiledDbConnection) _connection).InnerConnection; + + // the connection is open + Assert.IsNotNull(_connection); + Assert.AreEqual(ConnectionState.Open, _connection.State); + } + + // need to explicitely do it in every test which kinda defeats + // the purposes of having an automated check? give me v8! + + private static void AssertSafeCallContext() + { + var scope = CallContext.LogicalGetData("Umbraco.Core.Scoping.IScope"); + if (scope != null) throw new Exception("Leaked call context scope."); + } + + [TearDown] + public override void TearDown() + { + base.TearDown(); + + // the leaked scope should be gone + AssertSafeCallContext(); + + // its database should have been disposed + Assert.IsNull(_database.Connection); + + // the underlying connection should have been closed + Assert.AreEqual(ConnectionState.Closed, _connection.State); + } + } +} diff --git a/src/Umbraco.Tests/Scoping/ScopeTests.cs b/src/Umbraco.Tests/Scoping/ScopeTests.cs index d84b4fc554..6de011c28c 100644 --- a/src/Umbraco.Tests/Scoping/ScopeTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeTests.cs @@ -1,5 +1,6 @@ using System; using NUnit.Framework; +using Umbraco.Core; using Umbraco.Core.Persistence; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; @@ -15,11 +16,11 @@ namespace Umbraco.Tests.Scoping { base.Initialize(); - // initialization leaves a NoScope around, remove it - var scope = DatabaseContext.ScopeProvider.AmbientScope; - Assert.IsNotNull(scope); - Assert.IsInstanceOf(scope); - scope.Dispose(); + //// initialization leaves a NoScope around, remove it + //var scope = DatabaseContext.ScopeProvider.AmbientScope; + //Assert.IsNotNull(scope); + //Assert.IsInstanceOf(scope); + //scope.Dispose(); Assert.IsNull(DatabaseContext.ScopeProvider.AmbientScope); // gone } @@ -102,7 +103,7 @@ namespace Umbraco.Tests.Scoping Assert.IsInstanceOf(nested); Assert.IsNotNull(scopeProvider.AmbientScope); Assert.AreSame(nested, scopeProvider.AmbientScope); - Assert.AreSame(scope, ((Scope)nested).ParentScope); + Assert.AreSame(scope, ((Scope) nested).ParentScope); Assert.AreSame(database, nested.Database); } Assert.IsNotNull(database.Connection); // still @@ -196,7 +197,7 @@ namespace Umbraco.Tests.Scoping Assert.IsNotNull(database.Connection); // no more // nested does not have a parent - Assert.IsNull(((Scope)nested).ParentScope); + Assert.IsNull(((Scope) nested).ParentScope); } // and when nested is gone, scope is gone @@ -223,7 +224,8 @@ namespace Umbraco.Tests.Scoping Assert.Throws(() => { // could not steal the database - /*var nested =*/ scopeProvider.CreateScope(); + /*var nested =*/ + scopeProvider.CreateScope(); }); // cleanup @@ -387,5 +389,39 @@ namespace Umbraco.Tests.Scoping Assert.AreEqual("b", n); } } + + [Test] + public void CallContextScope() + { + var scopeProvider = DatabaseContext.ScopeProvider; + var scope = scopeProvider.CreateScope(); + Assert.IsNotNull(scopeProvider.AmbientScope); + using (new SafeCallContext()) + { + Assert.IsNull(scopeProvider.AmbientScope); + } + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + } + + [Test] + public void ScopeReference() + { + var scopeProvider = DatabaseContext.ScopeProvider; + var scope = scopeProvider.CreateScope(); + var nested = scopeProvider.CreateScope(); + Assert.IsNotNull(scopeProvider.AmbientScope); + var scopeRef = new ScopeReference(scopeProvider); + scopeRef.Dispose(); + Assert.IsNull(scopeProvider.AmbientScope); + Assert.Throws(() => + { + var db = scope.Database; + }); + Assert.Throws(() => + { + var db = nested.Database; + }); + } } -} +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index dc3a0e83b6..792e5a80ed 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -280,9 +280,9 @@ namespace Umbraco.Tests.TestHelpers _isFirstTestInFixture = false; //ensure this is false before anything! if (DatabaseTestBehavior == DatabaseBehavior.NewDbFileAndSchemaPerTest) - { - RemoveDatabaseFile(); - } + RemoveDatabaseFile(); // closes connections too + else + CloseDbConnections(); AppDomain.CurrentDomain.SetData("DataDirectory", null); @@ -294,10 +294,14 @@ namespace Umbraco.Tests.TestHelpers private void CloseDbConnections() { - //Ensure that any database connections from a previous test is disposed. - //This is really just double safety as its also done in the TearDown. - if (ApplicationContext != null && DatabaseContext != null && DatabaseContext.Database != null) - DatabaseContext.Database.Dispose(); + // just to be sure, although it's also done in TearDown + if (ApplicationContext != null + && ApplicationContext.DatabaseContext != null + && ApplicationContext.DatabaseContext.ScopeProvider != null) + { + ApplicationContext.DatabaseContext.ScopeProvider.Reset(); + } + SqlCeContextGuardian.CloseBackgroundConnection(); } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index cd7010310c..8681e2f80f 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -168,6 +168,7 @@ + From 7495cd3f2c35605d6abf13d0dfb89c9422818b96 Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 17 Jan 2017 16:46:34 +0100 Subject: [PATCH 259/599] U4-9322 - cleanup --- src/Umbraco.Core/DatabaseContext.cs | 12 ---- .../Persistence/DefaultDatabaseFactory.cs | 26 -------- src/Umbraco.Core/Scoping/Scope.cs | 2 +- src/Umbraco.Core/Scoping/ScopeProvider.cs | 65 ++++++++++++------- 4 files changed, 42 insertions(+), 63 deletions(-) diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs index 43a755a780..54e3ad796a 100644 --- a/src/Umbraco.Core/DatabaseContext.cs +++ b/src/Umbraco.Core/DatabaseContext.cs @@ -86,18 +86,6 @@ namespace Umbraco.Core _configured = true; } -#if DEBUG_DATABASES - public List Databases - { - get - { - var factory = _factory as DefaultDatabaseFactory; - if (factory == null) throw new NotSupportedException(); - return factory.Databases; - } - } -#endif - public ISqlSyntaxProvider SqlSyntax { get; private set; } /// diff --git a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs index 87a6263351..891c4dd295 100644 --- a/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/DefaultDatabaseFactory.cs @@ -64,32 +64,6 @@ namespace Umbraco.Core.Persistence return ScopeProvider.AmbientOrNoScope.Database; } -#if DEBUG_DATABASES - // helps identifying when non-httpContext databases are created by logging the stack trace - private void LogCallContextStack() - { - var trace = Environment.StackTrace; - if (trace.IndexOf("ScheduledPublishing") > 0) - LogHelper.Debug("CallContext: Scheduled Publishing"); - else if (trace.IndexOf("TouchServerTask") > 0) - LogHelper.Debug("CallContext: Server Registration"); - else if (trace.IndexOf("LogScrubber") > 0) - LogHelper.Debug("CallContext: Log Scrubber"); - else - LogHelper.Debug("CallContext: " + Environment.StackTrace); - } - - private readonly List _databases = new List(); - - // helps identifying database leaks by keeping track of all instances - public List Databases { get { return _databases; } } - - private static void Log(string message, UmbracoDatabase database) - { - LogHelper.Debug(message + " (" + (database == null ? "" : database.InstanceSid) + ")."); - } -#endif - public UmbracoDatabase CreateNewDatabase() { return CreateDatabaseInstance(); diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index cdfa24310a..36df7e546b 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -53,7 +53,7 @@ namespace Umbraco.Core.Scoping public Scope ParentScope { get; set; } // the original scope (when attaching a detachable scope) - public Scope OrigScope { get; set; } + public IScope OrigScope { get; set; } /// public UmbracoDatabase Database diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs index facd0f746c..9a1b5d18cd 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -109,27 +109,12 @@ namespace Umbraco.Core.Scoping { var otherScope = other as Scope; if (otherScope == null) - throw new ArgumentException(); + throw new ArgumentException("Not a Scope instance."); if (otherScope.Detachable == false) - throw new ArgumentException(); + throw new ArgumentException("Not a detachable scope."); - var ambient = AmbientScope; - if (ambient == null) - { - AmbientScope = other; - return; - } - - var noScope = ambient as NoScope; - if (noScope != null) - throw new InvalidOperationException(); - - var scope = ambient as Scope; - if (ambient != null && scope == null) - throw new Exception(); - - otherScope.OrigScope = scope; + otherScope.OrigScope = AmbientScope; AmbientScope = otherScope; } @@ -138,18 +123,18 @@ namespace Umbraco.Core.Scoping { var ambient = AmbientScope; if (ambient == null) - throw new InvalidOperationException(); + throw new InvalidOperationException("There is no ambient scope."); var noScope = ambient as NoScope; if (noScope != null) - throw new InvalidOperationException(); + throw new InvalidOperationException("Cannot detach NoScope."); var scope = ambient as Scope; if (scope == null) - throw new Exception(); + throw new Exception("Ambient scope is not a Scope instance."); if (scope.Detachable == false) - throw new InvalidOperationException(); + throw new InvalidOperationException("Ambient scope is not detachable."); AmbientScope = scope.OrigScope; scope.OrigScope = null; @@ -170,12 +155,12 @@ namespace Umbraco.Core.Scoping // peta poco nulls the shared connection after each command unless there's a trx var database = noScope.DatabaseOrNull; if (database != null && database.InTransaction) - throw new Exception(); + throw new Exception("NoScope is in a transaction."); return AmbientScope = new Scope(this, noScope); } var scope = ambient as Scope; - if (scope == null) throw new Exception(); + if (scope == null) throw new Exception("Ambient scope is not a Scope instance."); return AmbientScope = new Scope(this, scope); } @@ -189,5 +174,37 @@ namespace Umbraco.Core.Scoping StaticScopeReference.Dispose(); } + +#if DEBUG_SCOPES + // this code needs TLC + // the idea is to keep in a list all the scopes that have been created + // and remove them when they are disposed + // so we can track leaks + + // helps identifying when non-httpContext scopes are created by logging the stack trace + private void LogCallContextStack() + { + var trace = Environment.StackTrace; + if (trace.IndexOf("ScheduledPublishing") > 0) + LogHelper.Debug("CallContext: Scheduled Publishing"); + else if (trace.IndexOf("TouchServerTask") > 0) + LogHelper.Debug("CallContext: Server Registration"); + else if (trace.IndexOf("LogScrubber") > 0) + LogHelper.Debug("CallContext: Log Scrubber"); + else + LogHelper.Debug("CallContext: " + Environment.StackTrace); + } + + private readonly List _scopes = new List(); + + // helps identifying scope leaks by keeping track of all instances + public List Scopes { get { return _scopes; } } + + private static void Log(string message, UmbracoDatabase database) + { + LogHelper.Debug(message + " (" + (database == null ? "" : database.InstanceSid) + ")."); + } +#endif + } } From 931db6f0b177cd381d78dd9005b13c971dffc2a3 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 17 Jan 2017 16:47:54 +0100 Subject: [PATCH 260/599] U4-9395 - Handle errors with non-existing fields and invalide JSON data --- src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs | 5 +++++ src/UmbracoExamine/UmbracoMemberIndexer.cs | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index b40cf3bbf3..db9792572f 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -92,7 +92,12 @@ namespace Umbraco.Web.PropertyEditors //swallow...on purpose, there's a chance that this isn't json and we don't want that to affect // the website. } + catch (ArgumentException) + { + //swallow on purpose to prevent this error: + // Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject. + } } } } diff --git a/src/UmbracoExamine/UmbracoMemberIndexer.cs b/src/UmbracoExamine/UmbracoMemberIndexer.cs index 33000a9f19..2e48dff64e 100644 --- a/src/UmbracoExamine/UmbracoMemberIndexer.cs +++ b/src/UmbracoExamine/UmbracoMemberIndexer.cs @@ -238,7 +238,9 @@ namespace UmbracoExamine var fields = base.GetSpecialFieldsToIndex(allValuesForIndexing); //adds the special path property to the index - fields.Add("__key", allValuesForIndexing["__key"]); + string valuesForIndexing; + if (allValuesForIndexing.TryGetValue("__key", out valuesForIndexing)) + fields.Add("__key", valuesForIndexing); return fields; From a54f4aeffdf139533f356f541ab53ea1bb8427cd Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 17 Jan 2017 19:10:22 +0100 Subject: [PATCH 261/599] added documentation for umb-node-preview component --- .../components/umbnodepreview.directive.js | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js index 8d63623efb..77f6f06a30 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbnodepreview.directive.js @@ -1,3 +1,91 @@ +/** +@ngdoc directive +@name umbraco.directives.directive:umbNodePreview +@restrict E +@scope + +@description +Added in Umbraco v. 7.6: Use this directive to render a node preview. + +

    Markup example

    +
    +    
    + +
    + + +
    + +
    +
    + +

    Controller example

    +
    +    (function () {
    +        "use strict";
    +    
    +        function Controller() {
    +    
    +            var vm = this;
    +    
    +            vm.allowRemove = true;
    +            vm.allowOpen = true;
    +            vm.sortable = true;
    +    
    +            vm.nodes = [
    +                {
    +                    "icon": "icon-document",
    +                    "name": "My node 1",
    +                    "published": true,
    +                    "description": "A short description of my node"
    +                },
    +                {
    +                    "icon": "icon-document",
    +                    "name": "My node 2",
    +                    "published": true,
    +                    "description": "A short description of my node"
    +                }
    +            ];
    +    
    +            vm.remove = remove;
    +            vm.open = open;
    +    
    +            function remove(index, nodes) {
    +                alert("remove node");
    +            }
    +    
    +            function open(node) {
    +                alert("open node");
    +            }
    +    
    +        }
    +    
    +        angular.module("umbraco").controller("My.NodePreviewController", Controller);
    +    
    +    })();
    +
    + +@param {string} icon (binding): The node icon. +@param {string} name (binding): The node name. +@param {boolean} published (binding): The node pusblished state. +@param {string} description (binding): A short description. +@param {boolean} sortable (binding): Will add a move cursor on the node preview. Can used in combination with ui-sortable. +@param {boolean} allowRemove (binding): Show/Hide the remove button. +@param {boolean} allowOpen (binding): Show/Hide the open button. +@param {function} onRemove (expression): Callback function when the remove button is clicked. +@param {function} onOpen (expression): Callback function when the open button is clicked. +**/ + (function () { 'use strict'; From 4fe12d893044478a4b2f275b50ad15542e7fa16c Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 17 Jan 2017 19:26:00 +0100 Subject: [PATCH 262/599] clean up dependencies in content picker controller --- .../propertyeditors/contentpicker/contentpicker.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index e5852c29fd..1ebfe81905 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -1,7 +1,7 @@ //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it -function contentPickerController($scope, dialogService, entityResource, contentResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location, $timeout, miniEditorHelper) { +function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, miniEditorHelper) { function trim(str, chr) { var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g'); From 4612183b13f8a15c6154e8c470137306e1a6f44f Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 17 Jan 2017 19:27:39 +0100 Subject: [PATCH 263/599] fix unit test typo --- .../unit/app/propertyeditors/content-picker-controller.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js index f34f8088df..d5e3a7a496 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/app/propertyeditors/content-picker-controller.spec.js @@ -90,7 +90,7 @@ describe('Content picker controller tests', function () { }, 1000); }); - it("Adding a dublicate item should note update renderModel, ids and model.value", function(){ + it("Adding a duplicate item should note update renderModel, ids and model.value", function(){ scope.add(item); scope.$apply(); setTimeout(function(){ From 2964f9a1c7596ecc4796d715dd0ef2257018e67a Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 17 Jan 2017 20:48:00 +0100 Subject: [PATCH 264/599] clean up redundant code --- .../contentpicker/contentpicker.controller.js | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 1ebfe81905..c191ee5518 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -194,19 +194,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper }); if (currIds.indexOf(item.id) < 0) { - - // get url for content and media items - if(entityType !== "Member") { - entityResource.getUrl(item.id, entityType).then(function(data){ - // update url - item.url = data.url; - // push item to render model - addSelectedItem(item); - }); - } else { - addSelectedItem(item); - } - + setEntityUrl(item); } }; @@ -241,17 +229,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper }); if (entity) { - // get url for content and media items - if(entityType !== "Member") { - entityResource.getUrl(entity.id, entityType).then(function(data){ - // update url - entity.url = data.url; - // push item to render model - addSelectedItem(entity); - }); - } else { - addSelectedItem(entity); - } + setEntityUrl(entity); } }); @@ -261,6 +239,20 @@ function contentPickerController($scope, entityResource, editorState, iconHelper }); + function setEntityUrl(entity) { + // get url for content and media items + if(entityType !== "Member") { + entityResource.getUrl(entity.id, entityType).then(function(data){ + // update url + entity.url = data.url; + // push item to render model + addSelectedItem(entity); + }); + } else { + addSelectedItem(entity); + } + } + function addSelectedItem(item) { // set icon From c6ed4eaff695a3ad20a198e4229a7e07c78c993d Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 17 Jan 2017 21:27:24 +0100 Subject: [PATCH 265/599] only sort lists with more than one item --- .../contentpicker/contentpicker.controller.js | 21 ++++++++++++++++++- .../contentpicker/contentpicker.html | 4 ++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index c191ee5518..3a238e141f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -39,6 +39,9 @@ function contentPickerController($scope, entityResource, editorState, iconHelper else { $scope.contentPickerForm.maxCount.$setValidity("maxCount", true); } + + setSortingState($scope.renderModel); + }); } @@ -59,6 +62,14 @@ function contentPickerController($scope, entityResource, editorState, iconHelper } }; + // sortable options + $scope.sortableOptions = { + distance: 10, + tolerance: "pointer", + scroll: true, + zIndex: 6000 + }; + if ($scope.model.config) { //merge the server config on top of the default config, then set the server config to use the result $scope.model.config = angular.extend(defaultConfig, $scope.model.config); @@ -78,7 +89,6 @@ function contentPickerController($scope, entityResource, editorState, iconHelper $scope.allowOpenButton = entityType === "Document" || entityType === "Media"; $scope.allowEditButton = entityType === "Document"; $scope.allowRemoveButton = true; - $scope.sortable = true; //the dialog options for the picker var dialogOptions = { @@ -287,6 +297,15 @@ function contentPickerController($scope, entityResource, editorState, iconHelper } + function setSortingState(items) { + // disable sorting if the list only consist of one item + if(items.length > 1) { + $scope.sortableOptions.disabled = false; + } else { + $scope.sortableOptions.disabled = true; + } + } + } angular.module('umbraco').controller("Umbraco.PropertyEditors.ContentPickerController", contentPickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html index ca6f9ac826..900cf6b416 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.html @@ -2,14 +2,14 @@ -
    +
    Date: Tue, 17 Jan 2017 23:01:40 +0100 Subject: [PATCH 266/599] remove option to open media items in mini editor --- .../propertyeditors/contentpicker/contentpicker.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 3a238e141f..22312511ce 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -86,7 +86,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper : $scope.model.config.startNode.type === "media" ? "Media" : "Document"; - $scope.allowOpenButton = entityType === "Document" || entityType === "Media"; + $scope.allowOpenButton = entityType === "Document"; $scope.allowEditButton = entityType === "Document"; $scope.allowRemoveButton = true; From fd1da492cc3e49d698230cf5edd6df07de38b545 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 18 Jan 2017 19:18:19 +1100 Subject: [PATCH 267/599] fixes range test --- src/Umbraco.Tests/UdiTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests/UdiTests.cs b/src/Umbraco.Tests/UdiTests.cs index 1a7b30a770..36242bab13 100644 --- a/src/Umbraco.Tests/UdiTests.cs +++ b/src/Umbraco.Tests/UdiTests.cs @@ -106,13 +106,13 @@ namespace Umbraco.Tests public void RangeTest() { // can parse open string udi - const string stringUdiString = "umb://stylesheet"; + var stringUdiString = "umb://" + Constants.DeployEntityType.AnyString; Udi stringUdi; Assert.IsTrue(Udi.TryParse(stringUdiString, out stringUdi)); Assert.AreEqual(string.Empty, ((StringUdi)stringUdi).Id); // can parse open guid udi - const string guidUdiString = "umb://document"; + var guidUdiString = "umb://" + Constants.DeployEntityType.AnyGuid; Udi guidUdi; Assert.IsTrue(Udi.TryParse(guidUdiString, out guidUdi)); Assert.AreEqual(Guid.Empty, ((GuidUdi)guidUdi).Guid); From 1f98b76da0cf944c9e6f610c35e5a580b0400822 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 18 Jan 2017 09:34:02 +0000 Subject: [PATCH 268/599] On app.authenticated JS event - we clear out LocalStorage values in case a different user is logging in --- src/Umbraco.Web.UI.Client/src/init.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/init.js b/src/Umbraco.Web.UI.Client/src/init.js index ded5ba08e4..3233974cee 100644 --- a/src/Umbraco.Web.UI.Client/src/init.js +++ b/src/Umbraco.Web.UI.Client/src/init.js @@ -1,6 +1,6 @@ /** Executed when the application starts, binds to events and set global state */ -app.run(['userService', '$log', '$rootScope', '$location', 'navigationService', 'appState', 'editorState', 'fileManager', 'assetsService', 'eventsService', '$cookies', '$templateCache', - function (userService, $log, $rootScope, $location, navigationService, appState, editorState, fileManager, assetsService, eventsService, $cookies, $templateCache) { +app.run(['userService', '$log', '$rootScope', '$location', 'navigationService', 'appState', 'editorState', 'fileManager', 'assetsService', 'eventsService', '$cookies', '$templateCache', 'localStorageService', + function (userService, $log, $rootScope, $location, navigationService, appState, editorState, fileManager, assetsService, eventsService, $cookies, $templateCache, localStorageService) { //This sets the default jquery ajax headers to include our csrf token, we // need to user the beforeSend method because our token changes per user/login so @@ -13,6 +13,11 @@ app.run(['userService', '$log', '$rootScope', '$location', 'navigationService', /** Listens for authentication and checks if our required assets are loaded, if/once they are we'll broadcast a ready event */ eventsService.on("app.authenticated", function(evt, data) { + + //Removes all stored LocalStorage browser items - that may contain sensitive data + //So if a machine or computer is shared and a new user logs in, we clear out the previous persons localStorage items + localStorageService.clearAll(); + assetsService._loadInitAssets().then(function() { appState.setGlobalState("isReady", true); From 1f636d05d11259aca8e98cee745c0ee3a33bbab8 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 18 Jan 2017 10:36:11 +0100 Subject: [PATCH 269/599] U4-9322 - fix scope leaks --- .../Persistence/LockingRepository.cs | 22 +++- src/Umbraco.Core/Scoping/IScope.cs | 4 + src/Umbraco.Core/Scoping/IScopeProvider.cs | 8 +- src/Umbraco.Core/Scoping/NoScope.cs | 12 ++ src/Umbraco.Core/Scoping/Scope.cs | 12 ++ src/Umbraco.Core/Scoping/ScopeProvider.cs | 106 +++++++++++++++--- src/Umbraco.Core/UmbracoApplicationBase.cs | 9 +- 7 files changed, 146 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Core/Persistence/LockingRepository.cs b/src/Umbraco.Core/Persistence/LockingRepository.cs index f513073e71..88df2c492e 100644 --- a/src/Umbraco.Core/Persistence/LockingRepository.cs +++ b/src/Umbraco.Core/Persistence/LockingRepository.cs @@ -46,7 +46,7 @@ namespace Umbraco.Core.Persistence public TResult WithReadLocked(Func, TResult> func, bool autoCommit = true) { - var uow = _uowProvider.GetUnitOfWork(); + using (var uow = _uowProvider.GetUnitOfWork()) using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) { foreach (var lockId in _readLockIds) @@ -65,7 +65,9 @@ namespace Umbraco.Core.Persistence public void WithWriteLocked(Action> action, bool autoCommit = true) { - var uow = _uowProvider.GetUnitOfWork(); + // must use the uow to ensure it's disposed if GetTransaction fails! + + using (var uow = _uowProvider.GetUnitOfWork()) using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) { foreach (var lockId in _writeLockIds) @@ -79,11 +81,25 @@ namespace Umbraco.Core.Persistence transaction.Complete(); } } + // fixme + // the change above ensures that scopes are properly disposed + // TODO apply to all methods + // now how can we manage the isolation level? + + //uow.ScopeIsolationLevel = IsolationLevel.RepeatableRead; // might throw when creating the scope + // getTransaction here throws because of different levels + // but the exception gets eaten because on the way out, disposing the _outer_ scope fails + // so... how shall we figure it out?! + // - we should be able to set the isolation level on the uow scope + // - should we be able to dispose parent scopes and then what happens? NO! + // + // if we can create scopes with isolation levels what happens when we nest scopes? + // note: this is a *very* special use case } public TResult WithWriteLocked(Func, TResult> func, bool autoCommit = true) { - var uow = _uowProvider.GetUnitOfWork(); + using (var uow = _uowProvider.GetUnitOfWork()) using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) { foreach (var lockId in _writeLockIds) diff --git a/src/Umbraco.Core/Scoping/IScope.cs b/src/Umbraco.Core/Scoping/IScope.cs index bc23ad3868..4960350575 100644 --- a/src/Umbraco.Core/Scoping/IScope.cs +++ b/src/Umbraco.Core/Scoping/IScope.cs @@ -24,5 +24,9 @@ namespace Umbraco.Core.Scoping /// Completes the scope. ///
    void Complete(); + +#if DEBUG_SCOPES + Guid InstanceId { get; } +#endif } } diff --git a/src/Umbraco.Core/Scoping/IScopeProvider.cs b/src/Umbraco.Core/Scoping/IScopeProvider.cs index e6237b3af9..6622b1a417 100644 --- a/src/Umbraco.Core/Scoping/IScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/IScopeProvider.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Core.Scoping +using System.Collections.Generic; + +namespace Umbraco.Core.Scoping { ///
    /// Provides scopes. @@ -43,5 +45,9 @@ /// Only a scope previously attached by can be detached. /// IScope DetachScope(); + +#if DEBUG_SCOPES + IEnumerable ScopeInfos { get; } +#endif } } diff --git a/src/Umbraco.Core/Scoping/NoScope.cs b/src/Umbraco.Core/Scoping/NoScope.cs index 9a2ebec5fe..5c53e89047 100644 --- a/src/Umbraco.Core/Scoping/NoScope.cs +++ b/src/Umbraco.Core/Scoping/NoScope.cs @@ -19,8 +19,16 @@ namespace Umbraco.Core.Scoping public NoScope(ScopeProvider scopeProvider) { _scopeProvider = scopeProvider; +#if DEBUG_SCOPES + _scopeProvider.Register(this); +#endif } +#if DEBUG_SCOPES + private readonly Guid _instanceId = Guid.NewGuid(); + public Guid InstanceId { get { return _instanceId; } } +#endif + /// public UmbracoDatabase Database { @@ -78,6 +86,10 @@ namespace Umbraco.Core.Scoping if (this != _scopeProvider.AmbientScope) throw new InvalidOperationException("Not the ambient scope."); +#if DEBUG_SCOPES + _scopeProvider.Disposed(this); +#endif + if (_database != null) _database.Dispose(); diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index 36df7e546b..5b220fe433 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -23,6 +23,9 @@ namespace Umbraco.Core.Scoping { _scopeProvider = scopeProvider; Detachable = detachable; +#if DEBUG_SCOPES + _scopeProvider.Register(this); +#endif } // initializes a new scope in a nested scopes chain, with its parent @@ -45,6 +48,11 @@ namespace Umbraco.Core.Scoping throw new Exception("NoScope instance is not free."); } +#if DEBUG_SCOPES + private readonly Guid _instanceId = Guid.NewGuid(); + public Guid InstanceId { get { return _instanceId; } } +#endif + // a value indicating whether the scope is detachable // ie whether it was created by CreateDetachedScope public bool Detachable { get; private set; } @@ -139,6 +147,10 @@ namespace Umbraco.Core.Scoping if (this != _scopeProvider.AmbientScope) throw new InvalidOperationException("Not the ambient scope."); +#if DEBUG_SCOPES + _scopeProvider.Disposed(this); +#endif + var parent = ParentScope; _scopeProvider.AmbientScope = parent; diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs index 9a1b5d18cd..7f43b7208e 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Runtime.Remoting.Messaging; using System.Web; using Umbraco.Core.Persistence; @@ -46,6 +48,12 @@ namespace Umbraco.Core.Scoping get { return (IScope) CallContext.LogicalGetData(ItemKey); } set { +#if DEBUG_SCOPES + var ambient = (IScope)CallContext.LogicalGetData(ItemKey); + if (ambient != null) RegisterContext(ambient, null); + if (value != null) + RegisterContext(value, "lcc"); +#endif if (value == null) CallContext.FreeNamedDataSlot(ItemKey); else CallContext.LogicalSetData(ItemKey, value); } @@ -56,6 +64,12 @@ namespace Umbraco.Core.Scoping get { return (IScope) HttpContext.Current.Items[ItemKey]; } set { +#if DEBUG_SCOPES + var ambient = (IScope) HttpContext.Current.Items[ItemKey]; + if (ambient != null) RegisterContext(ambient, null); + if (value != null) + RegisterContext(value, "http"); +#endif if (value == null) { HttpContext.Current.Items.Remove(ItemKey); @@ -152,6 +166,9 @@ namespace Umbraco.Core.Scoping var noScope = ambient as NoScope; if (noScope != null) { +#if DEBUG_SCOPES + Disposed(noScope); +#endif // peta poco nulls the shared connection after each command unless there's a trx var database = noScope.DatabaseOrNull; if (database != null && database.InTransaction) @@ -182,29 +199,82 @@ namespace Umbraco.Core.Scoping // so we can track leaks // helps identifying when non-httpContext scopes are created by logging the stack trace - private void LogCallContextStack() - { - var trace = Environment.StackTrace; - if (trace.IndexOf("ScheduledPublishing") > 0) - LogHelper.Debug("CallContext: Scheduled Publishing"); - else if (trace.IndexOf("TouchServerTask") > 0) - LogHelper.Debug("CallContext: Server Registration"); - else if (trace.IndexOf("LogScrubber") > 0) - LogHelper.Debug("CallContext: Log Scrubber"); - else - LogHelper.Debug("CallContext: " + Environment.StackTrace); - } - - private readonly List _scopes = new List(); + //private void LogCallContextStack() + //{ + // var trace = Environment.StackTrace; + // if (trace.IndexOf("ScheduledPublishing") > 0) + // LogHelper.Debug("CallContext: Scheduled Publishing"); + // else if (trace.IndexOf("TouchServerTask") > 0) + // LogHelper.Debug("CallContext: Server Registration"); + // else if (trace.IndexOf("LogScrubber") > 0) + // LogHelper.Debug("CallContext: Log Scrubber"); + // else + // LogHelper.Debug("CallContext: " + Environment.StackTrace); + //} // helps identifying scope leaks by keeping track of all instances - public List Scopes { get { return _scopes; } } + public static readonly List StaticScopeInfos = new List(); - private static void Log(string message, UmbracoDatabase database) + public IEnumerable ScopeInfos { get { return StaticScopeInfos; } } + + //private static void Log(string message, UmbracoDatabase database) + //{ + // LogHelper.Debug(message + " (" + (database == null ? "" : database.InstanceSid) + ")."); + //} + + public void Register(IScope scope) { - LogHelper.Debug(message + " (" + (database == null ? "" : database.InstanceSid) + ")."); + if (StaticScopeInfos.Any(x => x.Scope == scope)) throw new Exception(); + StaticScopeInfos.Add(new ScopeInfo(scope, Environment.StackTrace)); + } + + public static void RegisterContext(IScope scope, string context) + { + var info = StaticScopeInfos.FirstOrDefault(x => x.Scope == scope); + if (info == null) + { + if (context == null) return; + throw new Exception(); + } + if (context == null) info.NullStack = Environment.StackTrace; + info.Context = context; + } + + public void Disposed(IScope scope) + { + var info = StaticScopeInfos.FirstOrDefault(x => x.Scope == scope); + if (info != null) + { + // enable this by default + StaticScopeInfos.Remove(info); + + // instead, enable this to keep *all* scopes + // beware, there can be a lot of scopes! + //info.Disposed = true; + //info.DisposedStack = Environment.StackTrace; + } } #endif - } + +#if DEBUG_SCOPES + public class ScopeInfo + { + public ScopeInfo(IScope scope, string ctorStack) + { + Scope = scope; + Created = DateTime.Now; + CtorStack = ctorStack; + } + + public IScope Scope { get; private set; } + public Guid Parent { get { return (Scope is NoScope || ((Scope) Scope).ParentScope == null) ? Guid.Empty : ((Scope) Scope).ParentScope.InstanceId; } } + public DateTime Created { get; private set; } + public bool Disposed { get; set; } + public string Context { get; set; } + public string CtorStack { get; private set; } // the stacktrace of the scope ctor + public string DisposedStack { get; set; } // the stacktrace when disposed + public string NullStack { get; set; } // the stacktrace when context went null + } +#endif } diff --git a/src/Umbraco.Core/UmbracoApplicationBase.cs b/src/Umbraco.Core/UmbracoApplicationBase.cs index 1bf90fb154..02f50710a9 100644 --- a/src/Umbraco.Core/UmbracoApplicationBase.cs +++ b/src/Umbraco.Core/UmbracoApplicationBase.cs @@ -8,6 +8,7 @@ using System.Web.Hosting; using log4net; using Umbraco.Core.Logging; using Umbraco.Core.ObjectResolution; +using Umbraco.Core.Scoping; namespace Umbraco.Core { @@ -60,12 +61,10 @@ namespace Umbraco.Core //And now we can dispose of our startup handlers - save some memory ApplicationEventsResolver.Current.Dispose(); - // after Umbraco has started there is a database in "context" and that context is + // after Umbraco has started there is a scope in "context" and that context is // going to stay there and never get destroyed nor reused, so we have to ensure that - // the database is disposed (which will auto-remove it from context). - var database = ApplicationContext.Current.DatabaseContext.Database; - if (database != null) // never to happen... unless in weird tests - ApplicationContext.Current.DatabaseContext.Database.Dispose(); + // the scope is disposed (along with database etc) - reset it all entirely + ((ScopeProvider) ApplicationContext.Current.ScopeProvider).Reset(); } /// From 6710ac8c85f075b447bdfb03727bfe39780ec0c5 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 18 Jan 2017 09:43:21 +0000 Subject: [PATCH 270/599] Fix up camelCase method name to GetChildren --- src/Umbraco.Web/Editors/MediaController.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 5dd0c3073f..391931cb91 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -191,7 +191,7 @@ namespace Umbraco.Web.Editors var entity = Services.EntityService.GetByKey(idGuid); if (entity != null) { - return getChildren(entity.Id, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter); + return GetChildren(entity.Id, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter); } else { @@ -200,13 +200,13 @@ namespace Umbraco.Web.Editors } else if (int.TryParse(id, out idInt)) { - return getChildren(idInt, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter); + return GetChildren(idInt, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter); } throw new HttpResponseException(HttpStatusCode.NotFound); } - private PagedResult> getChildren(int id, + private PagedResult> GetChildren(int id, int pageNumber = 0, int pageSize = 0, string orderBy = "SortOrder", From ba64768d8d4a6a939d8d04a9a3035ef99ba149e4 Mon Sep 17 00:00:00 2001 From: Aaron Sturmfels Date: Wed, 18 Jan 2017 21:12:30 +1100 Subject: [PATCH 271/599] Fixed UmbracoContentIndexer's PerformIndexAll SupportUnpublishedContent = true paging breaking out of the loop early if filtering by node types. --- src/UmbracoExamine/UmbracoContentIndexer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index c2dbecf83d..70d4f8242a 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -416,6 +416,7 @@ namespace UmbracoExamine var notPublished = new HashSet(); IContent[] content; + int currentPageSize; do { long total; @@ -432,6 +433,9 @@ namespace UmbracoExamine descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "level", Direction.Ascending, true, (string)null); } + // need to store decendants count before filtering, in order for loop to work correctly + currentPageSize = descendants.Count(); + //if specific types are declared we need to post filter them //TODO: Update the service layer to join the cmsContentType table so we can query by content type too if (IndexerData.IncludeNodeTypes.Any()) @@ -449,7 +453,7 @@ namespace UmbracoExamine content, notPublished).WhereNotNull(), type); pageIndex++; - } while (content.Length == pageSize); + } while (currentPageSize == pageSize); } break; From 54e460cf5bc06f640b7e8166fc2f9cadf75af35e Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 18 Jan 2017 11:47:23 +0100 Subject: [PATCH 272/599] U4-9392 Upgrade ImageProcessor with cache header and high CPU fix U4-9393 Upgrade to Examine 0.1.80 --- build/NuSpecs/UmbracoCms.Core.nuspec | 4 ++-- src/Umbraco.Tests/Umbraco.Tests.csproj | 5 +++-- src/Umbraco.Tests/packages.config | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 9 +++++---- src/Umbraco.Web.UI/packages.config | 4 ++-- src/Umbraco.Web/Umbraco.Web.csproj | 5 +++-- src/Umbraco.Web/packages.config | 2 +- src/UmbracoExamine/UmbracoExamine.csproj | 5 +++-- src/UmbracoExamine/packages.config | 2 +- src/umbraco.MacroEngines/packages.config | 2 +- src/umbraco.MacroEngines/umbraco.MacroEngines.csproj | 5 +++-- 11 files changed, 25 insertions(+), 20 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 219437d5e0..3c65f6f145 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -32,9 +32,9 @@ - + - + diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index dbb877ee64..6e20740bcb 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -57,8 +57,9 @@ ..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll - - ..\packages\Examine.0.1.70.0\lib\Examine.dll + + ..\packages\Examine.0.1.80\lib\net45\Examine.dll + True ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll diff --git a/src/Umbraco.Tests/packages.config b/src/Umbraco.Tests/packages.config index 6428eebadf..b71ba7a170 100644 --- a/src/Umbraco.Tests/packages.config +++ b/src/Umbraco.Tests/packages.config @@ -2,7 +2,7 @@ - + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index c4134d6850..af11140701 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -127,8 +127,8 @@ False ..\packages\dotless.1.4.1.0\lib\dotless.Core.dll - - ..\packages\Examine.0.1.70.0\lib\Examine.dll + + ..\packages\Examine.0.1.80\lib\net45\Examine.dll True @@ -138,8 +138,9 @@ ..\packages\ImageProcessor.2.5.1\lib\net45\ImageProcessor.dll - - ..\packages\ImageProcessor.Web.4.7.2\lib\net45\ImageProcessor.Web.dll + + ..\packages\ImageProcessor.Web.4.8.0\lib\net45\ImageProcessor.Web.dll + True False diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 6919e10f64..629438762f 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -4,9 +4,9 @@ - + - + diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 1db9416f86..31aa38b1bf 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -112,8 +112,9 @@ ..\packages\dotless.1.4.1.0\lib\dotless.Core.dll - - ..\packages\Examine.0.1.70.0\lib\Examine.dll + + ..\packages\Examine.0.1.80\lib\net45\Examine.dll + True ..\packages\HtmlAgilityPack.1.4.9\lib\Net45\HtmlAgilityPack.dll diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index f8d737194e..9490486e9a 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -3,7 +3,7 @@ - + diff --git a/src/UmbracoExamine/UmbracoExamine.csproj b/src/UmbracoExamine/UmbracoExamine.csproj index 1bc438edd7..8720849744 100644 --- a/src/UmbracoExamine/UmbracoExamine.csproj +++ b/src/UmbracoExamine/UmbracoExamine.csproj @@ -82,8 +82,9 @@ ..\Solution Items\TheFARM-Public.snk - - ..\packages\Examine.0.1.70.0\lib\Examine.dll + + ..\packages\Examine.0.1.80\lib\net45\Examine.dll + True ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll diff --git a/src/UmbracoExamine/packages.config b/src/UmbracoExamine/packages.config index 04734b9fb8..0c85a9c3ca 100644 --- a/src/UmbracoExamine/packages.config +++ b/src/UmbracoExamine/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/umbraco.MacroEngines/packages.config b/src/umbraco.MacroEngines/packages.config index 9a785e35c3..930b7adbb0 100644 --- a/src/umbraco.MacroEngines/packages.config +++ b/src/umbraco.MacroEngines/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj index c9ee70e52c..198867a5d6 100644 --- a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj +++ b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj @@ -45,8 +45,9 @@ false - - ..\packages\Examine.0.1.70.0\lib\Examine.dll + + ..\packages\Examine.0.1.80\lib\net45\Examine.dll + True ..\packages\HtmlAgilityPack.1.4.9\lib\Net45\HtmlAgilityPack.dll From fc5bf81991fa79f10fd8753da22de8bec7f3abf6 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 18 Jan 2017 12:33:43 +0000 Subject: [PATCH 273/599] Returns a string as opposed to a Dynamic object - however cannot return a string object from this method as the result is wrapped in quotes hence we use a HttpResponseMessage & StringContent to return a raw string from the API endpoint --- src/Umbraco.Web/Editors/EntityController.cs | 31 ++++++++++++++------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index cfc04a7f20..9f8b15449c 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using System.Linq; +using System.Net.Http; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models; using Umbraco.Web.WebApi.Filters; @@ -146,19 +147,27 @@ namespace Umbraco.Web.Editors - - public dynamic GetUrl(int id, UmbracoEntityTypes type) + /// + /// Gets the url of an entity + /// + /// Int id of the entity to fetch URL for + /// The tpye of entity such as Document, Media, Member + /// The URL or path to the item + public HttpResponseMessage GetUrl(int id, UmbracoEntityTypes type) { - dynamic result = new System.Dynamic.ExpandoObject(); - + var returnUrl = string.Empty; - if(type == UmbracoEntityTypes.Document) + if (type == UmbracoEntityTypes.Document) { var foundUrl = Umbraco.Url(id); if (string.IsNullOrEmpty(foundUrl) == false && foundUrl != "#") { - result.url = foundUrl; - return result; + returnUrl = foundUrl; + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(returnUrl) + }; } } @@ -169,10 +178,12 @@ namespace Umbraco.Web.Editors ancestors = ancestors.Skip(1); } - result.url = "/" + string.Join("/", ancestors.Select(x => x.Name) ); + returnUrl = "/" + string.Join("/", ancestors.Select(x => x.Name)); - - return result; + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(returnUrl) + }; } /// From 40ea54007fab4e08e540cbf3444064da703a22d1 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Wed, 18 Jan 2017 12:34:58 +0000 Subject: [PATCH 274/599] Update the usage of GetUrl - to just use the raw string value as opposed to a JSON object. Updated related JS object mock as well --- .../src/common/mocks/resources/entity.mocks.js | 8 ++------ .../contentpicker/contentpicker.controller.js | 3 ++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js index a98240111e..8c03679183 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/entity.mocks.js @@ -38,12 +38,8 @@ angular.module('umbraco.mocks'). if (!mocksUtils.checkAuth()) { return [401, null, null]; } - - var urlOrbject = { - "url": "url" - }; - return [200, urlOrbject, null]; + return [200, "url", null]; } @@ -61,7 +57,7 @@ angular.module('umbraco.mocks'). $httpBackend .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetById?')) .respond(returnEntitybyId); - + $httpBackend .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetUrl?')) .respond(returnEntityUrl); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 22312511ce..d39027e014 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -254,7 +254,8 @@ function contentPickerController($scope, entityResource, editorState, iconHelper if(entityType !== "Member") { entityResource.getUrl(entity.id, entityType).then(function(data){ // update url - entity.url = data.url; + entity.url = data; + // push item to render model addSelectedItem(entity); }); From 1fd98dba30aff6183ec6c82613a7a2ac8b2f72ea Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 18 Jan 2017 14:50:51 +0100 Subject: [PATCH 275/599] make launch mini editor service return a promise so we can update a node when it is saved --- .../src/common/services/minieditorhelper.service.js | 12 +++++++++++- .../contentpicker/contentpicker.controller.js | 11 ++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js index 9c974f9ca9..693457c7e8 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/minieditorhelper.service.js @@ -1,12 +1,14 @@ (function () { 'use strict'; - function miniEditorHelper(dialogService, editorState, fileManager, contentEditingHelper) { + function miniEditorHelper(dialogService, editorState, fileManager, contentEditingHelper, $q) { var launched = false; function launchMiniEditor(node) { + var deferred = $q.defer(); + launched = true; //We need to store the current files selected in the file manager locally because the fileManager @@ -47,6 +49,9 @@ } launched = false; + + deferred.resolve(data); + }, closeCallback: function () { //reset the fileManager to what it was @@ -59,9 +64,14 @@ editorState.set(currEditorState); launched = false; + + deferred.reject(); + } }); + return deferred.promise; + } var service = { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index d39027e014..ba29b92477 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -213,7 +213,16 @@ function contentPickerController($scope, entityResource, editorState, iconHelper }; $scope.openMiniEditor = function(node) { - miniEditorHelper.launchMiniEditor(node); + miniEditorHelper.launchMiniEditor(node).then(function(updatedNode){ + // update the node + node.name = updatedNode.name; + node.published = updatedNode.hasPublishedVersion; + if(entityType !== "Member") { + entityResource.getUrl(updatedNode.id, entityType).then(function(data){ + node.url = data; + }); + } + }); }; var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { From ca777b26de8d2eca9fb519ddc1b434c56b78233d Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 18 Jan 2017 14:53:24 +0100 Subject: [PATCH 276/599] U4-9322 - manage isolation levels, cleanup tests --- .../Persistence/LockedRepository.cs | 22 +- .../Persistence/LockingRepository.cs | 74 +++-- .../UnitOfWork/IDatabaseUnitOfWorkProvider.cs | 2 +- .../UnitOfWork/PetaPocoUnitOfWork.cs | 7 +- .../UnitOfWork/PetaPocoUnitOfWorkProvider.cs | 6 + src/Umbraco.Core/Scoping/IScopeProvider.cs | 5 +- src/Umbraco.Core/Scoping/Scope.cs | 64 +++- src/Umbraco.Core/Scoping/ScopeProvider.cs | 13 +- src/Umbraco.Tests/Persistence/LocksTests.cs | 284 ++++++++---------- .../Repositories/LockedRepositoryTests.cs | 149 +++++++++ src/Umbraco.Tests/Scoping/LeakTests.cs | 5 - src/Umbraco.Tests/Scoping/ScopeTests.cs | 5 - .../Services/ThreadSafetyServiceTest.cs | 159 +++------- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + src/Umbraco.Web/Scheduling/LogScrubber.cs | 3 +- .../Scheduling/ScheduledPublishing.cs | 1 + .../ServerRegistrationEventHandler.cs | 8 +- 17 files changed, 434 insertions(+), 374 deletions(-) create mode 100644 src/Umbraco.Tests/Persistence/Repositories/LockedRepositoryTests.cs diff --git a/src/Umbraco.Core/Persistence/LockedRepository.cs b/src/Umbraco.Core/Persistence/LockedRepository.cs index b5d2d672f2..c092a3329f 100644 --- a/src/Umbraco.Core/Persistence/LockedRepository.cs +++ b/src/Umbraco.Core/Persistence/LockedRepository.cs @@ -7,21 +7,27 @@ namespace Umbraco.Core.Persistence internal class LockedRepository where TRepository : IDisposable, IRepository { - public LockedRepository(Transaction transaction, IDatabaseUnitOfWork unitOfWork, TRepository repository) + public LockedRepository(IDatabaseUnitOfWork unitOfWork, TRepository repository) { - Transaction = transaction; UnitOfWork = unitOfWork; Repository = repository; } - public Transaction Transaction { get; private set; } + public LockedRepository(Transaction transaction, IDatabaseUnitOfWork unitOfWork, TRepository repository) + { + //Transaction = transaction; + UnitOfWork = unitOfWork; + Repository = repository; + } + + //public Transaction Transaction { get; private set; } public IDatabaseUnitOfWork UnitOfWork { get; private set; } public TRepository Repository { get; private set; } - public void Commit() - { - UnitOfWork.Commit(); - Transaction.Complete(); - } + //public void Commit() + //{ + // UnitOfWork.Commit(); + // Transaction.Complete(); + //} } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/LockingRepository.cs b/src/Umbraco.Core/Persistence/LockingRepository.cs index 88df2c492e..09195f8ce6 100644 --- a/src/Umbraco.Core/Persistence/LockingRepository.cs +++ b/src/Umbraco.Core/Persistence/LockingRepository.cs @@ -28,92 +28,88 @@ namespace Umbraco.Core.Persistence public void WithReadLocked(Action> action, bool autoCommit = true) { - var uow = _uowProvider.GetUnitOfWork(); - using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) + using (var uow = ((PetaPocoUnitOfWorkProvider) _uowProvider).GetUnitOfWork(IsolationLevel.RepeatableRead)) { + // getting the database creates a scope and a transaction + // the scope is IsolationLevel.RepeatableRead (because UnitOfWork is) + // and will throw if outer scope (if any) has a lower isolation level + foreach (var lockId in _readLockIds) uow.Database.AcquireLockNodeReadLock(lockId); using (var repository = _repositoryFactory(uow)) { - action(new LockedRepository(transaction, uow, repository)); + action(new LockedRepository(uow, repository)); if (autoCommit == false) return; uow.Commit(); - transaction.Complete(); - } - } + + } // dispose repository => dispose uow => complete (or not) scope + } // dispose uow again => nothing } public TResult WithReadLocked(Func, TResult> func, bool autoCommit = true) { - using (var uow = _uowProvider.GetUnitOfWork()) - using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) + using (var uow = ((PetaPocoUnitOfWorkProvider) _uowProvider).GetUnitOfWork(IsolationLevel.RepeatableRead)) { + // getting the database creates a scope and a transaction + // the scope is IsolationLevel.RepeatableRead (because UnitOfWork is) + // and will throw if outer scope (if any) has a lower isolation level + foreach (var lockId in _readLockIds) uow.Database.AcquireLockNodeReadLock(lockId); using (var repository = _repositoryFactory(uow)) { - var ret = func(new LockedRepository(transaction, uow, repository)); + var ret = func(new LockedRepository(uow, repository)); if (autoCommit == false) return ret; uow.Commit(); - transaction.Complete(); return ret; - } - } + + } // dispose repository => dispose uow => complete (or not) scope + } // dispose uow again => nothing } public void WithWriteLocked(Action> action, bool autoCommit = true) { - // must use the uow to ensure it's disposed if GetTransaction fails! - - using (var uow = _uowProvider.GetUnitOfWork()) - using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) + using (var uow = ((PetaPocoUnitOfWorkProvider) _uowProvider).GetUnitOfWork(IsolationLevel.RepeatableRead)) { + // getting the database creates a scope and a transaction + // the scope is IsolationLevel.RepeatableRead (because UnitOfWork is) + // and will throw if outer scope (if any) has a lower isolation level + foreach (var lockId in _writeLockIds) uow.Database.AcquireLockNodeWriteLock(lockId); using (var repository = _repositoryFactory(uow)) { - action(new LockedRepository(transaction, uow, repository)); + action(new LockedRepository(uow, repository)); if (autoCommit == false) return; uow.Commit(); - transaction.Complete(); - } - } - // fixme - // the change above ensures that scopes are properly disposed - // TODO apply to all methods - // now how can we manage the isolation level? - //uow.ScopeIsolationLevel = IsolationLevel.RepeatableRead; // might throw when creating the scope - // getTransaction here throws because of different levels - // but the exception gets eaten because on the way out, disposing the _outer_ scope fails - // so... how shall we figure it out?! - // - we should be able to set the isolation level on the uow scope - // - should we be able to dispose parent scopes and then what happens? NO! - // - // if we can create scopes with isolation levels what happens when we nest scopes? - // note: this is a *very* special use case + } // dispose repository => dispose uow => complete (or not) scope + } // dispose uow again => nothing } public TResult WithWriteLocked(Func, TResult> func, bool autoCommit = true) { - using (var uow = _uowProvider.GetUnitOfWork()) - using (var transaction = uow.Database.GetTransaction(IsolationLevel.RepeatableRead)) + using (var uow = ((PetaPocoUnitOfWorkProvider) _uowProvider).GetUnitOfWork(IsolationLevel.RepeatableRead)) { + // getting the database creates a scope and a transaction + // the scope is IsolationLevel.RepeatableRead (because UnitOfWork is) + // and will throw if outer scope (if any) has a lower isolation level + foreach (var lockId in _writeLockIds) uow.Database.AcquireLockNodeReadLock(lockId); using (var repository = _repositoryFactory(uow)) { - var ret = func(new LockedRepository(transaction, uow, repository)); + var ret = func(new LockedRepository(uow, repository)); if (autoCommit == false) return ret; uow.Commit(); - transaction.Complete(); return ret; - } - } + + } // dispose repository => dispose uow => complete (or not) scope + } // dispose uow again => nothing } } } diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWorkProvider.cs index c18c08d8bb..a63299d0a6 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWorkProvider.cs @@ -6,5 +6,5 @@ namespace Umbraco.Core.Persistence.UnitOfWork public interface IDatabaseUnitOfWorkProvider { IDatabaseUnitOfWork GetUnitOfWork(); - } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs index 5398a00095..9caf787ddc 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Scoping; @@ -11,6 +12,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork internal class PetaPocoUnitOfWork : DisposableObject, IDatabaseUnitOfWork { private readonly Queue _operations = new Queue(); + private readonly IsolationLevel _isolationLevel; private readonly IScopeProvider _scopeProvider; private bool _completeScope = true; // scope is completed by default private IScope _scope; @@ -28,9 +30,10 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// /// This should normally not be used directly and should be created with the UnitOfWorkProvider /// - internal PetaPocoUnitOfWork(IScopeProvider scopeProvider) + internal PetaPocoUnitOfWork(IScopeProvider scopeProvider, IsolationLevel isolationLevel = IsolationLevel.Unspecified) { _scopeProvider = scopeProvider; + _isolationLevel = isolationLevel; _key = Guid.NewGuid(); InstanceId = Guid.NewGuid(); } @@ -143,7 +146,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork private IScope ThisScope { - get { return _scope ?? (_scope = _scopeProvider.CreateScope()); } + get { return _scope ?? (_scope = _scopeProvider.CreateScope(_isolationLevel)); } } public UmbracoDatabase Database diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs index 6b901d34f7..4d1b638016 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Data; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Scoping; @@ -69,6 +70,11 @@ namespace Umbraco.Core.Persistence.UnitOfWork return new PetaPocoUnitOfWork(_scopeProvider); } + public IDatabaseUnitOfWork GetUnitOfWork(IsolationLevel isolationLevel) + { + return new PetaPocoUnitOfWork(_scopeProvider, isolationLevel); + } + #endregion /// diff --git a/src/Umbraco.Core/Scoping/IScopeProvider.cs b/src/Umbraco.Core/Scoping/IScopeProvider.cs index 6622b1a417..9a3e935a78 100644 --- a/src/Umbraco.Core/Scoping/IScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/IScopeProvider.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Data; namespace Umbraco.Core.Scoping { @@ -16,7 +17,7 @@ namespace Umbraco.Core.Scoping /// If an ambient scope already exists, it becomes the parent of the created scope. /// When the created scope is disposed, the parent scope becomes the ambient scope again. /// - IScope CreateScope(); + IScope CreateScope(IsolationLevel isolationLevel = IsolationLevel.Unspecified); /// /// Creates a detached scope. @@ -26,7 +27,7 @@ namespace Umbraco.Core.Scoping /// A detached scope is not ambient and has no parent. /// It is meant to be attached by . /// - IScope CreateDetachedScope(); + IScope CreateDetachedScope(IsolationLevel isolationLevel = IsolationLevel.Unspecified); /// /// Attaches a scope. diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index 5b220fe433..14d7fdd8d9 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data; using Umbraco.Core.Events; using Umbraco.Core.Persistence; @@ -12,16 +13,21 @@ namespace Umbraco.Core.Scoping internal class Scope : IScope { private readonly ScopeProvider _scopeProvider; + private readonly IsolationLevel _isolationLevel; private bool _disposed; private bool? _completed; private UmbracoDatabase _database; private IList _messages; + // this is v7, in v8 this has to change to RepeatableRead + private const IsolationLevel DefaultIsolationLevel = IsolationLevel.ReadCommitted; + // initializes a new scope - public Scope(ScopeProvider scopeProvider, bool detachable = false) + public Scope(ScopeProvider scopeProvider, IsolationLevel isolationLevel = IsolationLevel.Unspecified, bool detachable = false) { _scopeProvider = scopeProvider; + _isolationLevel = isolationLevel; Detachable = detachable; #if DEBUG_SCOPES _scopeProvider.Register(this); @@ -29,15 +35,15 @@ namespace Umbraco.Core.Scoping } // initializes a new scope in a nested scopes chain, with its parent - public Scope(ScopeProvider scopeProvider, Scope parent) - : this(scopeProvider) + public Scope(ScopeProvider scopeProvider, Scope parent, IsolationLevel isolationLevel = IsolationLevel.Unspecified) + : this(scopeProvider, isolationLevel) { ParentScope = parent; } // initializes a new scope, replacing a NoScope instance - public Scope(ScopeProvider scopeProvider, NoScope noScope) - : this(scopeProvider) + public Scope(ScopeProvider scopeProvider, NoScope noScope, IsolationLevel isolationLevel = IsolationLevel.Unspecified) + : this(scopeProvider, isolationLevel) { // steal everything from NoScope _database = noScope.DatabaseOrNull; @@ -63,24 +69,56 @@ namespace Umbraco.Core.Scoping // the original scope (when attaching a detachable scope) public IScope OrigScope { get; set; } + private IsolationLevel IsolationLevel + { + get + { + if (_isolationLevel != IsolationLevel.Unspecified) return _isolationLevel; + if (ParentScope != null) return ParentScope.IsolationLevel; + return DefaultIsolationLevel; + } + } + /// public UmbracoDatabase Database { get { EnsureNotDisposed(); - if (ParentScope != null) return ParentScope.Database; + if (ParentScope != null) + { + var database = ParentScope.Database; + if (_isolationLevel > IsolationLevel.Unspecified && database.CurrentTransactionIsolationLevel < _isolationLevel) + throw new Exception("Scope requires isolation level " + _isolationLevel + ", but got " + database.CurrentTransactionIsolationLevel + " from parent."); + } + if (_database != null) { - if (_database.InTransaction == false) // stolen from noScope - _database.BeginTransaction(); // a scope implies a transaction, always - // fixme - what-if exception? + // if the database has been created by a Scope instance it has to be + // in a transaction, however it can be a database that was stolen from + // a NoScope instance, in which case we need to enter a transaction, as + // a scope implies a transaction, always + if (_database.InTransaction) + return _database; + } + else + { + // create a new database + _database = _scopeProvider.DatabaseFactory.CreateNewDatabase(); + } + + // enter a transaction, as a scope implies a transaction, always + try + { + _database.BeginTransaction(IsolationLevel); return _database; } - var database = _scopeProvider.DatabaseFactory.CreateNewDatabase(); - database.BeginTransaction(); // a scope implies a transaction, always - // fixme - should dispose db on exception? - return _database = database; + catch + { + _database.Dispose(); + _database = null; + throw; + } } } diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs index 7f43b7208e..7843b4f92d 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data; using System.Linq; using System.Runtime.Remoting.Messaging; using System.Web; @@ -113,9 +114,9 @@ namespace Umbraco.Core.Scoping } /// - public IScope CreateDetachedScope() + public IScope CreateDetachedScope(IsolationLevel isolationLevel = IsolationLevel.Unspecified) { - return new Scope(this, true); + return new Scope(this, isolationLevel, true); } /// @@ -156,11 +157,11 @@ namespace Umbraco.Core.Scoping } /// - public IScope CreateScope() + public IScope CreateScope(IsolationLevel isolationLevel = IsolationLevel.Unspecified) { var ambient = AmbientScope; if (ambient == null) - return AmbientScope = new Scope(this); + return AmbientScope = new Scope(this, isolationLevel); // replace noScope with a real one var noScope = ambient as NoScope; @@ -173,13 +174,13 @@ namespace Umbraco.Core.Scoping var database = noScope.DatabaseOrNull; if (database != null && database.InTransaction) throw new Exception("NoScope is in a transaction."); - return AmbientScope = new Scope(this, noScope); + return AmbientScope = new Scope(this, noScope, isolationLevel); } var scope = ambient as Scope; if (scope == null) throw new Exception("Ambient scope is not a Scope instance."); - return AmbientScope = new Scope(this, scope); + return AmbientScope = new Scope(this, scope, isolationLevel); } /// diff --git a/src/Umbraco.Tests/Persistence/LocksTests.cs b/src/Umbraco.Tests/Persistence/LocksTests.cs index ae008e1e63..358e98e797 100644 --- a/src/Umbraco.Tests/Persistence/LocksTests.cs +++ b/src/Umbraco.Tests/Persistence/LocksTests.cs @@ -5,14 +5,8 @@ using System.Data.SqlServerCe; using System.Threading; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Events; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Core.Publishing; -using Umbraco.Core.Scoping; -using Umbraco.Core.Services; using Umbraco.Tests.Services; using Umbraco.Tests.TestHelpers; using Ignore = NUnit.Framework.IgnoreAttribute; @@ -24,81 +18,32 @@ namespace Umbraco.Tests.Persistence [Ignore("Takes too much time.")] public class LocksTests : BaseDatabaseFactoryTest { - private ThreadSafetyServiceTest.PerThreadPetaPocoUnitOfWorkProvider _uowProvider; - private ThreadSafetyServiceTest.PerThreadDatabaseFactory _dbFactory; - [SetUp] public override void Initialize() { base.Initialize(); - //we need to use our own custom IDatabaseFactory for the DatabaseContext because we MUST ensure that - //a Database instance is created per thread, whereas the default implementation which will work in an HttpContext - //threading environment, or a single apartment threading environment will not work for this test because - //it is multi-threaded. - _dbFactory = new ThreadSafetyServiceTest.PerThreadDatabaseFactory(Logger); - var scopeProvider = new ScopeProvider(_dbFactory); - //overwrite the local object - ApplicationContext.DatabaseContext = new DatabaseContext(scopeProvider, Logger, new SqlCeSyntaxProvider(), Constants.DatabaseProviders.SqlCe); - - //disable cache - var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); - - //here we are going to override the ServiceContext because normally with our test cases we use a - //global Database object but this is NOT how it should work in the web world or in any multi threaded scenario. - //we need a new Database object for each thread. - var repositoryFactory = new RepositoryFactory(cacheHelper, Logger, SqlSyntax, SettingsForTests.GenerateMockSettings()); - _uowProvider = new ThreadSafetyServiceTest.PerThreadPetaPocoUnitOfWorkProvider(_dbFactory); - var evtMsgs = new TransientMessagesFactory(); - ApplicationContext.Services = new ServiceContext( - repositoryFactory, - _uowProvider, - new FileUnitOfWorkProvider(), - new PublishingStrategy(evtMsgs, Logger), - cacheHelper, - Logger, - evtMsgs); - // create a few lock objects - var database = DatabaseContext.Database; - database.BeginTransaction(IsolationLevel.RepeatableRead); - try + using (var scope = ApplicationContext.ScopeProvider.CreateScope(IsolationLevel.RepeatableRead)) { + var database = scope.Database; database.Execute("SET IDENTITY_INSERT umbracoLock ON"); database.Insert("umbracoLock", "id", false, new LockDto { Id = 1, Name = "Lock.1" }); database.Insert("umbracoLock", "id", false, new LockDto { Id = 2, Name = "Lock.2" }); database.Insert("umbracoLock", "id", false, new LockDto { Id = 3, Name = "Lock.3" }); database.Execute("SET IDENTITY_INSERT umbracoLock OFF"); - database.CompleteTransaction(); + scope.Complete(); } - catch - { - database.AbortTransaction(); - } - } - - [TearDown] - public override void TearDown() - { - //dispose! - _dbFactory.Dispose(); - _uowProvider.Dispose(); - - base.TearDown(); } [Test] public void Test() { - var database = DatabaseContext.Database; - database.BeginTransaction(IsolationLevel.RepeatableRead); - try + using (var scope = ApplicationContext.ScopeProvider.CreateScope(IsolationLevel.RepeatableRead)) { + var database = scope.Database; database.AcquireLockNodeReadLock(Constants.Locks.Servers); - } - finally - { - database.CompleteTransaction(); + scope.Complete(); } } @@ -108,40 +53,39 @@ namespace Umbraco.Tests.Persistence var threads = new List(); var locker = new object(); var acquired = 0; - var maxAcquired = 0; + var m2 = new ManualResetEventSlim(false); + var m1 = new ManualResetEventSlim(false); for (var i = 0; i < 5; i++) { threads.Add(new Thread(() => { - var database = DatabaseContext.Database; - database.BeginTransaction(IsolationLevel.RepeatableRead); - try + // each thread gets its own scope, because it has its own LCC, hence its own database + using (var scope = ApplicationContext.ScopeProvider.CreateScope(IsolationLevel.RepeatableRead)) { + var database = scope.Database; database.AcquireLockNodeReadLock(Constants.Locks.Servers); lock (locker) { acquired++; + if (acquired == 5) m2.Set(); } - Thread.Sleep(500); - lock (locker) - { - if (maxAcquired < acquired) maxAcquired = acquired; - } - Thread.Sleep(500); + m1.Wait(); lock (locker) { acquired--; } - } - finally - { - database.CompleteTransaction(); + scope.Complete(); } })); } foreach (var thread in threads) thread.Start(); + m2.Wait(); + // all threads have locked in parallel + var maxAcquired = acquired; + m1.Set(); foreach (var thread in threads) thread.Join(); Assert.AreEqual(5, maxAcquired); + Assert.AreEqual(0, acquired); } [Test] @@ -149,41 +93,51 @@ namespace Umbraco.Tests.Persistence { var threads = new List(); var locker = new object(); + var entered = 0; + var ms = new AutoResetEvent[5]; + for (var i = 0; i < 5; i++) ms[i] = new AutoResetEvent(false); + var m1 = new ManualResetEventSlim(false); var acquired = 0; - var maxAcquired = 0; for (var i = 0; i < 5; i++) { + var ic = i; threads.Add(new Thread(() => { - var database = DatabaseContext.Database; - database.BeginTransaction(IsolationLevel.RepeatableRead); - try + // each thread gets its own scope, because it has its own LCC, hence its own database + using (var scope = ApplicationContext.ScopeProvider.CreateScope(IsolationLevel.RepeatableRead)) { + var database = scope.Database; + lock (locker) + { + entered++; + if (entered == 5) m1.Set(); + } + ms[ic].WaitOne(); database.AcquireLockNodeWriteLock(Constants.Locks.Servers); lock (locker) { acquired++; } - Thread.Sleep(500); - lock (locker) - { - if (maxAcquired < acquired) maxAcquired = acquired; - } - Thread.Sleep(500); + ms[ic].WaitOne(); lock (locker) { acquired--; } - } - finally - { - database.CompleteTransaction(); + scope.Complete(); } })); } foreach (var thread in threads) thread.Start(); + m1.Wait(); + // all threads have entered + ms[0].Set(); // let 0 go + Thread.Sleep(100); + for (var i = 1; i < 5; i++) ms[i].Set(); // let others go + Thread.Sleep(500); + // only 1 thread has locked + Assert.AreEqual(1, acquired); + for (var i = 0; i < 5; i++) ms[i].Set(); // let all go foreach (var thread in threads) thread.Join(); - Assert.AreEqual(1, maxAcquired); } [Test] @@ -193,52 +147,56 @@ namespace Umbraco.Tests.Persistence var thread1 = new Thread(() => { - var database = DatabaseContext.Database; - database.BeginTransaction(IsolationLevel.RepeatableRead); - try + // each thread gets its own scope, because it has its own LCC, hence its own database + using (var scope = ApplicationContext.ScopeProvider.CreateScope(IsolationLevel.RepeatableRead)) { - database.AcquireLockNodeWriteLock(1); - Thread.Sleep(1000); - database.AcquireLockNodeWriteLock(2); - Thread.Sleep(1000); - } - catch (Exception e) - { - e1 = e; - } - finally - { - database.CompleteTransaction(); + var database = scope.Database; + Console.WriteLine("Thread 1 db " + database.InstanceSid); + try + { + database.AcquireLockNodeWriteLock(1); + Thread.Sleep(100); + database.AcquireLockNodeWriteLock(2); + Thread.Sleep(1000); + } + catch (Exception e) + { + e1 = e; + } + scope.Complete(); } }); var thread2 = new Thread(() => { - var database = DatabaseContext.Database; - database.BeginTransaction(IsolationLevel.RepeatableRead); - try + // each thread gets its own scope, because it has its own LCC, hence its own database + using (var scope = ApplicationContext.ScopeProvider.CreateScope(IsolationLevel.RepeatableRead)) { - database.AcquireLockNodeWriteLock(2); - Thread.Sleep(1000); - database.AcquireLockNodeWriteLock(1); - Thread.Sleep(1000); - } - catch (Exception e) - { - e2 = e; - } - finally - { - database.CompleteTransaction(); + var database = scope.Database; + Console.WriteLine("Thread 2 db " + database.InstanceSid); + try + { + database.AcquireLockNodeWriteLock(2); + Thread.Sleep(100); + database.AcquireLockNodeWriteLock(1); + Thread.Sleep(1000); + } + catch (Exception e) + { + e2 = e; + } + scope.Complete(); } }); thread1.Start(); thread2.Start(); thread1.Join(); thread2.Join(); - Assert.IsNotNull(e1); - Assert.IsNotNull(e2); - Assert.IsInstanceOf(e1); - Assert.IsInstanceOf(e2); + var oneIsNotNull = e1 != null || e2 != null; + Assert.IsTrue(oneIsNotNull); + if (e1 != null) + Assert.IsInstanceOf(e1); + if (e2 != null) + Assert.IsInstanceOf(e2); } [Test] @@ -248,51 +206,51 @@ namespace Umbraco.Tests.Persistence var thread1 = new Thread(() => { - var database = DatabaseContext.Database; - database.BeginTransaction(IsolationLevel.RepeatableRead); - try + // each thread gets its own scope, because it has its own LCC, hence its own database + using (var scope = ApplicationContext.ScopeProvider.CreateScope(IsolationLevel.RepeatableRead)) { - database.AcquireLockNodeWriteLock(1); - var info = database.Query("SELECT * FROM sys.lock_information;"); - Console.WriteLine("LOCKS:"); - foreach (var row in info) - Console.WriteLine(string.Format("> {0} {1} {2} {3} {4} {5} {6}", row.request_spid, - row.resource_type, row.resource_description, row.request_mode, row.resource_table, - row.resource_table_id, row.request_status)); - Thread.Sleep(6000); - } - catch (Exception e) - { - e1 = e; - } - finally - { - database.CompleteTransaction(); + var database = scope.Database; + try + { + database.AcquireLockNodeWriteLock(1); + var info = database.Query("SELECT * FROM sys.lock_information;"); + Console.WriteLine("LOCKS:"); + foreach (var row in info) + Console.WriteLine(string.Format("> {0} {1} {2} {3} {4} {5} {6}", row.request_spid, + row.resource_type, row.resource_description, row.request_mode, row.resource_table, + row.resource_table_id, row.request_status)); + Thread.Sleep(6000); + } + catch (Exception e) + { + e1 = e; + } + scope.Complete(); } }); var thread2 = new Thread(() => { - var database = DatabaseContext.Database; - database.BeginTransaction(IsolationLevel.RepeatableRead); - try + // each thread gets its own scope, because it has its own LCC, hence its own database + using (var scope = ApplicationContext.ScopeProvider.CreateScope(IsolationLevel.RepeatableRead)) { - Thread.Sleep(1000); - database.AcquireLockNodeWriteLock(2); - var info = database.Query("SELECT * FROM sys.lock_information;"); - Console.WriteLine("LOCKS:"); - foreach (var row in info) - Console.WriteLine(string.Format("> {0} {1} {2} {3} {4} {5} {6}", row.request_spid, - row.resource_type, row.resource_description, row.request_mode, row.resource_table, - row.resource_table_id, row.request_status)); - Thread.Sleep(1000); - } - catch (Exception e) - { - e2 = e; - } - finally - { - database.CompleteTransaction(); + var database = scope.Database; + try + { + Thread.Sleep(1000); + database.AcquireLockNodeWriteLock(2); + var info = database.Query("SELECT * FROM sys.lock_information;"); + Console.WriteLine("LOCKS:"); + foreach (var row in info) + Console.WriteLine(string.Format("> {0} {1} {2} {3} {4} {5} {6}", row.request_spid, + row.resource_type, row.resource_description, row.request_mode, row.resource_table, + row.resource_table_id, row.request_status)); + Thread.Sleep(1000); + } + catch (Exception e) + { + e2 = e; + } + scope.Complete(); } }); thread1.Start(); diff --git a/src/Umbraco.Tests/Persistence/Repositories/LockedRepositoryTests.cs b/src/Umbraco.Tests/Persistence/Repositories/LockedRepositoryTests.cs new file mode 100644 index 0000000000..7307be9d52 --- /dev/null +++ b/src/Umbraco.Tests/Persistence/Repositories/LockedRepositoryTests.cs @@ -0,0 +1,149 @@ +using System; +using System.Data; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Scoping; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Persistence.Repositories +{ + [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] + [TestFixture] + public class LockedRepositoryTests : BaseDatabaseFactoryTest + { + private static IServerRegistrationRepository CreateRepository(IDatabaseUnitOfWork uow, ILogger logger, CacheHelper cacheHelper, ISqlSyntaxProvider sqlSyntax) + { + return new ServerRegistrationRepository( + uow, + cacheHelper.StaticCache, + logger, sqlSyntax); + } + + protected override CacheHelper CreateCacheHelper() + { + // ServerRegistrationRepository wants a real cache else weird things happen + //return CacheHelper.CreateDisabledCacheHelper(); + return new CacheHelper( + new ObjectCacheRuntimeCacheProvider(), + new StaticCacheProvider(), + new NullCacheProvider(), + new IsolatedRuntimeCache(type => new ObjectCacheRuntimeCacheProvider())); + } + + [Test] + public void NoOuterScopeJustWorks() + { + var uowProvider = new PetaPocoUnitOfWorkProvider(Logger); + var sqlSyntax = ApplicationContext.DatabaseContext.SqlSyntax; + + var lrepo = new LockingRepository(uowProvider, + x => CreateRepository(x, Logger, CacheHelper, sqlSyntax), + new[] { Constants.Locks.Servers }, new[] { Constants.Locks.Servers }); + + IServerRegistration reg = null; + + lrepo.WithWriteLocked(xrepo => + { + xrepo.Repository.AddOrUpdate(reg = new ServerRegistration("a1234", "i1234", DateTime.Now)); + + // no need - autocommit by default + //xrepo.UnitOfWork.Commit(); + }); + + Assert.IsNull(((ScopeProvider) ApplicationContext.ScopeProvider).AmbientScope); + + Assert.AreNotEqual(0, reg.Id); + + // that's cheating somehow because it will not really hit the DB because of the cache + var reg2 = lrepo.WithReadLocked(xrepo => + { + return xrepo.Repository.Get(reg.Id); + }); + + Assert.IsNull(((ScopeProvider) ApplicationContext.ScopeProvider).AmbientScope); + + Assert.IsNotNull(reg2); + Assert.AreEqual("a1234", reg2.ServerAddress); + Assert.AreEqual("i1234", reg2.ServerIdentity); + + // this really makes sure there's something in database + using (var scope = ApplicationContext.ScopeProvider.CreateScope()) + { + var reg3 = scope.Database.Fetch("SELECT * FROM umbracoServer WHERE id=@id", new { id = reg2.Id }).FirstOrDefault(); + Assert.IsNotNull(reg3); + Assert.AreEqual("a1234", reg3.address); + Assert.AreEqual("i1234", reg3.computerName); + } + + Assert.IsNull(((ScopeProvider) ApplicationContext.ScopeProvider).AmbientScope); + } + + [Test] + public void OuterScopeBadIsolationLevel() + { + var uowProvider = new PetaPocoUnitOfWorkProvider(Logger); + var sqlSyntax = ApplicationContext.DatabaseContext.SqlSyntax; + + var lrepo = new LockingRepository(uowProvider, + x => CreateRepository(x, Logger, CacheHelper, sqlSyntax), + new[] { Constants.Locks.Servers }, new[] { Constants.Locks.Servers }); + + // this creates a IsolationLevel.Unspecified scope + using (var scope = ApplicationContext.ScopeProvider.CreateScope()) + { + // so outer scope creates IsolationLevel.ReadCommitted (default) transaction + // then WithReadLocked creates a IsolationLevel.RepeatableRead scope, which + // fails - levels conflict + + try + { + lrepo.WithReadLocked(xrepo => + { + xrepo.Repository.DeactiveStaleServers(TimeSpan.Zero); + }); + Assert.Fail("Expected: Exception."); + } + catch (Exception e) + { + Assert.AreEqual("Scope requires isolation level RepeatableRead, but got ReadCommitted from parent.", e.Message); + } + + scope.Complete(); + } + } + + [Test] + public void OuterScopeGoodIsolationLevel() + { + var uowProvider = new PetaPocoUnitOfWorkProvider(Logger); + var sqlSyntax = ApplicationContext.DatabaseContext.SqlSyntax; + + var lrepo = new LockingRepository(uowProvider, + x => CreateRepository(x, Logger, CacheHelper, sqlSyntax), + new[] { Constants.Locks.Servers }, new[] { Constants.Locks.Servers }); + + // this creates a IsolationLevel.RepeatableRead scope + using (var scope = ApplicationContext.ScopeProvider.CreateScope(IsolationLevel.RepeatableRead)) + { + // so outer scope creates IsolationLevel.RepeatableRead transaction + // then WithReadLocked creates a IsolationLevel.RepeatableRead scope, which + // suceeds - no level conflict + + lrepo.WithReadLocked(xrepo => + { + xrepo.Repository.DeactiveStaleServers(TimeSpan.Zero); + }); + + scope.Complete(); + } + } + } +} diff --git a/src/Umbraco.Tests/Scoping/LeakTests.cs b/src/Umbraco.Tests/Scoping/LeakTests.cs index 94d1f2a295..ae8da89b06 100644 --- a/src/Umbraco.Tests/Scoping/LeakTests.cs +++ b/src/Umbraco.Tests/Scoping/LeakTests.cs @@ -24,11 +24,6 @@ namespace Umbraco.Tests.Scoping { base.Initialize(); - //// initialization leaves a NoScope around, remove it - //var scope = DatabaseContext.ScopeProvider.AmbientScope; - //Assert.IsNotNull(scope); - //Assert.IsInstanceOf(scope); - //scope.Dispose(); Assert.IsNull(DatabaseContext.ScopeProvider.AmbientScope); // gone } diff --git a/src/Umbraco.Tests/Scoping/ScopeTests.cs b/src/Umbraco.Tests/Scoping/ScopeTests.cs index 6de011c28c..249ecd2081 100644 --- a/src/Umbraco.Tests/Scoping/ScopeTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeTests.cs @@ -16,11 +16,6 @@ namespace Umbraco.Tests.Scoping { base.Initialize(); - //// initialization leaves a NoScope around, remove it - //var scope = DatabaseContext.ScopeProvider.AmbientScope; - //Assert.IsNotNull(scope); - //Assert.IsInstanceOf(scope); - //scope.Dispose(); Assert.IsNull(DatabaseContext.ScopeProvider.AmbientScope); // gone } diff --git a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs index 2a67a8194c..bd6a9df19b 100644 --- a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs +++ b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs @@ -1,25 +1,14 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using NUnit.Framework; using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Core.Publishing; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; -using umbraco.editorControls.tinyMCE3; -using umbraco.interfaces; -using Umbraco.Core.Events; -using Umbraco.Core.Scoping; namespace Umbraco.Tests.Services { @@ -27,40 +16,10 @@ namespace Umbraco.Tests.Services [TestFixture, RequiresSTA] public class ThreadSafetyServiceTest : BaseDatabaseFactoryTest { - private PerThreadPetaPocoUnitOfWorkProvider _uowProvider; - private PerThreadDatabaseFactory _dbFactory; - [SetUp] public override void Initialize() - { + { base.Initialize(); - - //we need to use our own custom IDatabaseFactory for the DatabaseContext because we MUST ensure that - //a Database instance is created per thread, whereas the default implementation which will work in an HttpContext - //threading environment, or a single apartment threading environment will not work for this test because - //it is multi-threaded. - _dbFactory = new PerThreadDatabaseFactory(Logger); - var scopeProvider = new ScopeProvider(_dbFactory); - //overwrite the local object - ApplicationContext.DatabaseContext = new DatabaseContext(scopeProvider, Logger, new SqlCeSyntaxProvider(), Constants.DatabaseProviders.SqlCe); - - //disable cache - var cacheHelper = CacheHelper.CreateDisabledCacheHelper(); - - //here we are going to override the ServiceContext because normally with our test cases we use a - //global Database object but this is NOT how it should work in the web world or in any multi threaded scenario. - //we need a new Database object for each thread. - var repositoryFactory = new RepositoryFactory(cacheHelper, Logger, SqlSyntax, SettingsForTests.GenerateMockSettings()); - _uowProvider = new PerThreadPetaPocoUnitOfWorkProvider(_dbFactory); - var evtMsgs = new TransientMessagesFactory(); - ApplicationContext.Services = new ServiceContext( - repositoryFactory, - _uowProvider, - new FileUnitOfWorkProvider(), - new PublishingStrategy(evtMsgs, Logger), - cacheHelper, - Logger, - evtMsgs); CreateTestData(); } @@ -70,10 +29,6 @@ namespace Umbraco.Tests.Services { _error = null; - //dispose! - _dbFactory.Dispose(); - _uowProvider.Dispose(); - base.TearDown(); } @@ -89,10 +44,10 @@ namespace Umbraco.Tests.Services { //we will mimick the ServiceContext in that each repository in a service (i.e. ContentService) is a singleton var contentService = (ContentService)ServiceContext.ContentService; - + var threads = new List(); - - Debug.WriteLine("Starting test..."); + + Debug.WriteLine("Starting..."); for (var i = 0; i < MaxThreadCount; i++) { @@ -100,27 +55,33 @@ namespace Umbraco.Tests.Services { try { - Debug.WriteLine("Created content on thread: " + Thread.CurrentThread.ManagedThreadId); - - //create 2 content items + Debug.WriteLine("[{0}] Running...", Thread.CurrentThread.ManagedThreadId); + + using (var scope = ApplicationContext.Current.ScopeProvider.CreateScope()) + { + var database = scope.Database; + Debug.WriteLine("[{0}] Database {1}.", Thread.CurrentThread.ManagedThreadId, database.InstanceSid); + } + + //create 2 content items string name1 = "test" + Guid.NewGuid(); var content1 = contentService.CreateContent(name1, -1, "umbTextpage", 0); - - Debug.WriteLine("Saving content1 on thread: " + Thread.CurrentThread.ManagedThreadId); + + Debug.WriteLine("[{0}] Saving content #1.", Thread.CurrentThread.ManagedThreadId); contentService.Save(content1); Thread.Sleep(100); //quick pause for maximum overlap! string name2 = "test" + Guid.NewGuid(); var content2 = contentService.CreateContent(name2, -1, "umbTextpage", 0); - Debug.WriteLine("Saving content2 on thread: " + Thread.CurrentThread.ManagedThreadId); - contentService.Save(content2); + Debug.WriteLine("[{0}] Saving content #2.", Thread.CurrentThread.ManagedThreadId); + contentService.Save(content2); } catch(Exception e) - { + { _error = e; - } + } }); threads.Add(t); } @@ -144,7 +105,7 @@ namespace Umbraco.Tests.Services { throw new Exception("Error!", _error); } - + } [Test] @@ -155,7 +116,7 @@ namespace Umbraco.Tests.Services var threads = new List(); - Debug.WriteLine("Starting test..."); + Debug.WriteLine("Starting..."); for (var i = 0; i < MaxThreadCount; i++) { @@ -163,21 +124,27 @@ namespace Umbraco.Tests.Services { try { - Debug.WriteLine("Created content on thread: " + Thread.CurrentThread.ManagedThreadId); + Debug.WriteLine("[{0}] Running...", Thread.CurrentThread.ManagedThreadId); - //create 2 content items + using (var scope = ApplicationContext.Current.ScopeProvider.CreateScope()) + { + var database = scope.Database; + Debug.WriteLine("[{0}] Database {1}.", Thread.CurrentThread.ManagedThreadId, database.InstanceSid); + } + + //create 2 content items string name1 = "test" + Guid.NewGuid(); var folder1 = mediaService.CreateMedia(name1, -1, Constants.Conventions.MediaTypes.Folder, 0); - Debug.WriteLine("Saving folder1 on thread: " + Thread.CurrentThread.ManagedThreadId); - mediaService.Save(folder1, 0); + Debug.WriteLine("[{0}] Saving content #1.", Thread.CurrentThread.ManagedThreadId); + mediaService.Save(folder1, 0); Thread.Sleep(100); //quick pause for maximum overlap! string name = "test" + Guid.NewGuid(); var folder2 = mediaService.CreateMedia(name, -1, Constants.Conventions.MediaTypes.Folder, 0); - Debug.WriteLine("Saving folder2 on thread: " + Thread.CurrentThread.ManagedThreadId); - mediaService.Save(folder2, 0); + Debug.WriteLine("[{0}] Saving content #2.", Thread.CurrentThread.ManagedThreadId); + mediaService.Save(folder2, 0); } catch (Exception e) { @@ -208,69 +175,13 @@ namespace Umbraco.Tests.Services } } - + public void CreateTestData() { //Create and Save ContentType "umbTextpage" -> 1045 ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage"); contentType.Key = new Guid("1D3A8E6E-2EA9-4CC1-B229-1AEE19821522"); - ServiceContext.ContentTypeService.Save(contentType); - } - - /// - /// Creates a Database object per thread, this mimics the web context which is per HttpContext and is required for the multi-threaded test - /// - internal class PerThreadDatabaseFactory : DisposableObject, IDatabaseFactory2 - { - private readonly ILogger _logger; - - public PerThreadDatabaseFactory(ILogger logger) - { - _logger = logger; - } - - private readonly ConcurrentDictionary _databases = new ConcurrentDictionary(); - - public UmbracoDatabase CreateDatabase() - { - var db = _databases.GetOrAdd( - Thread.CurrentThread.ManagedThreadId, - i => new UmbracoDatabase(Constants.System.UmbracoConnectionName, _logger)); - return db; - } - - public UmbracoDatabase CreateNewDatabase() - { - return new UmbracoDatabase(Constants.System.UmbracoConnectionName, _logger); - } - - protected override void DisposeResources() - { - //dispose the databases - _databases.ForEach(x => x.Value.Dispose()); - } - } - - /// - /// Creates a UOW with a Database object per thread - /// - internal class PerThreadPetaPocoUnitOfWorkProvider : DisposableObject, IDatabaseUnitOfWorkProvider - { - private readonly ScopeProvider _scopeProvider; - - public PerThreadPetaPocoUnitOfWorkProvider(PerThreadDatabaseFactory dbFactory) - { - _scopeProvider = new ScopeProvider(dbFactory); - } - - public IDatabaseUnitOfWork GetUnitOfWork() - { - //Create or get a database instance for this thread. - return new PetaPocoUnitOfWork(_scopeProvider); - } - - protected override void DisposeResources() - { } + ServiceContext.ContentTypeService.Save(contentType); } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 8681e2f80f..bdfcac3420 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -165,6 +165,7 @@ + diff --git a/src/Umbraco.Web/Scheduling/LogScrubber.cs b/src/Umbraco.Web/Scheduling/LogScrubber.cs index 187d0c55a2..f18f5ad603 100644 --- a/src/Umbraco.Web/Scheduling/LogScrubber.cs +++ b/src/Umbraco.Web/Scheduling/LogScrubber.cs @@ -77,7 +77,8 @@ namespace Umbraco.Web.Scheduling return false; // do NOT repeat, going down } - // running on a background task, requires its own (safe) scope + // running on a background task, and Log.CleanLogs uses the old SqlHelper, + // better wrap in a scope and ensure it's all cleaned up and nothing leaks using (ApplicationContext.Current.ScopeProvider.CreateScope()) using (DisposableTimer.DebugDuration("Log scrubbing executing", "Log scrubbing complete")) { diff --git a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs index 461abc984a..5ff7963f50 100644 --- a/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs +++ b/src/Umbraco.Web/Scheduling/ScheduledPublishing.cs @@ -84,6 +84,7 @@ namespace Umbraco.Web.Scheduling // running on a background task, requires its own (safe) scope // (GetAuthenticationHeaderValue uses UserService to load the current user, hence requires a database) + // (might not need a scope but we don't know really) using (ApplicationContext.Current.ScopeProvider.CreateScope()) { //pass custom the authorization header diff --git a/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs b/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs index 720dc0b20e..a25deac1d9 100644 --- a/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs +++ b/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs @@ -137,11 +137,9 @@ namespace Umbraco.Web.Strategies { try { - // running on a background task, requires its own (safe) scope - using (ApplicationContext.Current.ScopeProvider.CreateScope()) - { - _svc.TouchServer(_serverAddress, _svc.CurrentServerIdentity, _registrar.Options.StaleServerTimeout); - } + // TouchServer uses a proper unit of work etc underneath so even in a + // background task it is safe to call it without dealing with any scope + _svc.TouchServer(_serverAddress, _svc.CurrentServerIdentity, _registrar.Options.StaleServerTimeout); return true; // repeat } From ef9dd6d80383472a5012318d86ab0912a24053aa Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 18 Jan 2017 15:38:40 +0100 Subject: [PATCH 277/599] add scripts editor --- .../src/views/scripts/edit.controller.js | 132 ++++++++++++++++++ .../src/views/scripts/edit.html | 51 +++++++ 2 files changed, 183 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/scripts/edit.html diff --git a/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js new file mode 100644 index 0000000000..fe8837e50d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js @@ -0,0 +1,132 @@ +(function () { + "use strict"; + + function ScriptsEditController($scope, $routeParams, $timeout, appState, editorState, navigationService, assetsService, codefileResource, contentEditingHelper, notificationsService, localizationService) { + + var vm = this; + var currentPosition = null; + var localizeSaving = localizationService.localize("general_saving"); + + vm.page = {}; + vm.page.loading = true; + vm.page.menu = {}; + vm.page.menu.currentSection = appState.getSectionState("currentSection"); + vm.page.menu.currentNode = null; + vm.page.saveButtonState = "init"; + + vm.script = {}; + + // bind functions to view model + vm.save = save; + + /* Function bound to view model */ + + function save() { + + vm.page.saveButtonState = "busy"; + + vm.script.content = vm.editor.getValue(); + + contentEditingHelper.contentEditorPerformSave({ + statusMessage: localizeSaving, + saveMethod: codefileResource.save, + scope: $scope, + content: vm.script, + // We do not redirect on failure for scripts - this is because it is not possible to actually save the script + // when server side validation fails - as opposed to content where we are capable of saving the content + // item if server side validation fails + redirectOnFailure: false, + rebindCallback: function (orignal, saved) {} + }).then(function (saved) { + + notificationsService.success("Script saved"); + vm.page.saveButtonState = "success"; + vm.script = saved; + + //sync state + editorState.set(vm.script); + + // sync tree + navigationService.syncTree({ tree: "scripts", path: vm.script.virtualPath, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + + }, function (err) { + + vm.page.saveButtonState = "error"; + + localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_validationFailedMessage").then(function(msgValue) { + notificationsService.error(headerValue, msgValue); + }); + }); + + }); + + + } + + /* Local functions */ + + function init() { + + //we need to load this somewhere, for now its here. + assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css"); + + if ($routeParams.create) { + codefileResource.getScaffold().then(function (script) { + ready(script); + }); + } else { + codefileResource.getByPath('scripts', $routeParams.id).then(function (script) { + ready(script); + }); + } + + } + + function ready(script) { + + vm.page.loading = false; + + vm.script = script; + + //sync state + editorState.set(vm.script); + + navigationService.syncTree({ tree: "scripts", path: vm.script.virtualPath, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + + vm.aceOption = { + mode: "javascript", + theme: "chrome", + showPrintMargin: false, + advanced: { + fontSize: '14px' + }, + onLoad: function(_editor) { + + vm.editor = _editor; + + // initial cursor placement + // Keep cursor in name field if we are create a new script + // else set the cursor at the bottom of the code editor + if(!$routeParams.create) { + $timeout(function(){ + vm.editor.navigateFileEnd(); + vm.editor.focus(); + }); + } + + } + } + + } + + init(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.Scripts.EditController", ScriptsEditController); +})(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/scripts/edit.html b/src/Umbraco.Web.UI.Client/src/views/scripts/edit.html new file mode 100644 index 0000000000..18e5e8525c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/scripts/edit.html @@ -0,0 +1,51 @@ +
    + + + +
    + + + + + + + + +
    +
    + +
    + + + + + + + + + + + + +
    +
    + +
    \ No newline at end of file From 49d24dada774b9faa4cefb376c175213211c1e34 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Wed, 18 Jan 2017 15:39:08 +0100 Subject: [PATCH 278/599] add partial views macros editor --- .../partialviewmacros/edit.controller.js | 309 ++++++++++++++++++ .../src/views/partialviewmacros/edit.html | 123 +++++++ 2 files changed, 432 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.html diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.controller.js new file mode 100644 index 0000000000..d0a47aaa77 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.controller.js @@ -0,0 +1,309 @@ +(function () { + "use strict"; + + function partialViewMacrosEditController($scope, $routeParams, codefileResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, angularHelper, $timeout, contentEditingHelper, localizationService, templateHelper) { + + var vm = this; + var localizeSaving = localizationService.localize("general_saving"); + + vm.page = {}; + vm.page.loading = true; + vm.partialViewMacroFile = {}; + + //menu + vm.page.menu = {}; + vm.page.menu.currentSection = appState.getSectionState("currentSection"); + vm.page.menu.currentNode = null; + + // bind functions to view model + vm.save = save; + vm.openPageFieldOverlay = openPageFieldOverlay; + vm.openDictionaryItemOverlay = openDictionaryItemOverlay; + vm.openQueryBuilderOverlay = openQueryBuilderOverlay; + vm.openMacroOverlay = openMacroOverlay; + vm.openInsertOverlay = openInsertOverlay; + + /* Functions bound to view model */ + + function save() { + + vm.page.saveButtonState = "busy"; + vm.partialViewMacro.content = vm.editor.getValue(); + + contentEditingHelper.contentEditorPerformSave({ + statusMessage: localizeSaving, + saveMethod: codefileResource.save, + scope: $scope, + content: vm.partialViewMacro, + // We do not redirect on failure for partial view macros - this is because it is not possible to actually save the partial view + // when server side validation fails - as opposed to content where we are capable of saving the content + // item if server side validation fails + redirectOnFailure: false, + rebindCallback: function (orignal, saved) {} + }).then(function (saved) { + + notificationsService.success("Partial View Macro File saved"); + vm.page.saveButtonState = "success"; + vm.partialViewMacro = saved; + + //sync state + editorState.set(vm.partialViewMacro); + + // normal tree sync + navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.path, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + + // clear $dirty state on form + setFormState("pristine"); + + }, function (err) { + + vm.page.saveButtonState = "error"; + + localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_validationFailedMessage").then(function(msgValue) { + notificationsService.error(headerValue, msgValue); + }); + }); + + }); + + } + + function openInsertOverlay() { + + vm.insertOverlay = { + view: "insert", + allowedTypes: { + macro: true, + dictionary: true, + umbracoField: true + }, + hideSubmitButton: true, + show: true, + submit: function(model) { + + switch(model.insert.type) { + case "macro": + var macroObject = macroService.collectValueData(model.insert.selectedMacro, model.insert.macroParams, "Mvc"); + insert(macroObject.syntax); + break; + + case "dictionary": + var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name); + insert(code); + break; + + case "umbracoField": + insert(model.insert.umbracoField); + break; + } + + vm.insertOverlay.show = false; + vm.insertOverlay = null; + + }, + close: function(oldModel) { + // close the dialog + vm.insertOverlay.show = false; + vm.insertOverlay = null; + // focus editor + vm.editor.focus(); + } + }; + + } + + + function openMacroOverlay() { + + vm.macroPickerOverlay = { + view: "macropicker", + dialogData: {}, + show: true, + title: "Insert macro", + submit: function (model) { + + var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc"); + insert(macroObject.syntax); + + vm.macroPickerOverlay.show = false; + vm.macroPickerOverlay = null; + + }, + close: function(oldModel) { + // close the dialog + vm.macroPickerOverlay.show = false; + vm.macroPickerOverlay = null; + // focus editor + vm.editor.focus(); + } + }; + } + + + function openPageFieldOverlay() { + vm.pageFieldOverlay = { + submitButtonLabel: "Insert", + closeButtonlabel: "Cancel", + view: "insertfield", + show: true, + submit: function (model) { + insert(model.umbracoField); + vm.pageFieldOverlay.show = false; + vm.pageFieldOverlay = null; + }, + close: function (model) { + // close the dialog + vm.pageFieldOverlay.show = false; + vm.pageFieldOverlay = null; + // focus editor + vm.editor.focus(); + } + }; + } + + + function openDictionaryItemOverlay() { + vm.dictionaryItemOverlay = { + view: "treepicker", + section: "settings", + treeAlias: "dictionary", + entityType: "dictionary", + multiPicker: false, + show: true, + title: "Insert dictionary item", + select: function(node){ + + var code = templateHelper.getInsertDictionarySnippet(node.name); + insert(code); + + vm.dictionaryItemOverlay.show = false; + vm.dictionaryItemOverlay = null; + }, + close: function (model) { + // close dialog + vm.dictionaryItemOverlay.show = false; + vm.dictionaryItemOverlay = null; + // focus editor + vm.editor.focus(); + } + }; + } + + function openQueryBuilderOverlay() { + vm.queryBuilderOverlay = { + view: "querybuilder", + show: true, + title: "Query for content", + + submit: function (model) { + + var code = templateHelper.getQuerySnippet(model.result.queryExpression); + insert(code); + + vm.queryBuilderOverlay.show = false; + vm.queryBuilderOverlay = null; + }, + + close: function (model) { + // close dialog + vm.queryBuilderOverlay.show = false; + vm.queryBuilderOverlay = null; + // focus editor + vm.editor.focus(); + } + }; + } + + /* Local functions */ + + function init() { + //we need to load this somewhere, for now its here. + assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css"); + if ($routeParams.create) { + codefileResource.getScaffold().then(function (partialViewMacro) { + ready(partialViewMacro); + }); + } else { + codefileResource.getByPath('partialViewMacros', $routeParams.id).then(function (partialViewMacro) { + ready(partialViewMacro); + }); + } + } + + function ready(partialViewMacro) { + + vm.page.loading = false; + vm.partialViewMacro = partialViewMacro; + + //sync state + editorState.set(vm.partialViewMacro); + navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.virtualPath, forceReload: true }).then(function (syncArgs) { + vm.page.menu.currentNode = syncArgs.node; + }); + + // ace configuration + vm.aceOption = { + mode: "razor", + theme: "chrome", + showPrintMargin: false, + advanced: { + fontSize: '14px' + }, + onLoad: function(_editor) { + vm.editor = _editor; + + // initial cursor placement + // Keep cursor in name field if we are create a new template + // else set the cursor at the bottom of the code editor + if(!$routeParams.create) { + $timeout(function(){ + vm.editor.navigateFileEnd(); + vm.editor.focus(); + persistCurrentLocation(); + }); + } + + //change on blur, focus + vm.editor.on("blur", persistCurrentLocation); + vm.editor.on("focus", persistCurrentLocation); + } + } + + } + + function insert(str) { + vm.editor.moveCursorToPosition(vm.currentPosition); + vm.editor.insert(str); + vm.editor.focus(); + + // set form state to $dirty + setFormState("dirty"); + } + + function persistCurrentLocation() { + vm.currentPosition = vm.editor.getCursorPosition(); + } + + function setFormState(state) { + + // get the current form + var currentForm = angularHelper.getCurrentForm($scope); + + // set state + if(state === "dirty") { + currentForm.$setDirty(); + } else if(state === "pristine") { + currentForm.$setPristine(); + } + } + + + init(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.PartialViewMacros.EditController", partialViewMacrosEditController); +})(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.html b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.html new file mode 100644 index 0000000000..28e720c736 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.html @@ -0,0 +1,123 @@ +
    + + + +
    + + + + + + + + + + +
    +
    + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + +
    \ No newline at end of file From a020a02b2691fa66e657d2136ddd2d62b74fcc3f Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 18 Jan 2017 17:44:16 +0100 Subject: [PATCH 279/599] U4-9322 - more fixes --- src/Umbraco.Core/Scoping/Scope.cs | 1 + src/Umbraco.Core/UmbracoApplicationBase.cs | 4 +++- src/Umbraco.Tests/Scoping/ScopeTests.cs | 6 +++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index 14d7fdd8d9..4979aa6ae1 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -90,6 +90,7 @@ namespace Umbraco.Core.Scoping var database = ParentScope.Database; if (_isolationLevel > IsolationLevel.Unspecified && database.CurrentTransactionIsolationLevel < _isolationLevel) throw new Exception("Scope requires isolation level " + _isolationLevel + ", but got " + database.CurrentTransactionIsolationLevel + " from parent."); + _database = database; } if (_database != null) diff --git a/src/Umbraco.Core/UmbracoApplicationBase.cs b/src/Umbraco.Core/UmbracoApplicationBase.cs index 02f50710a9..662cbb12bb 100644 --- a/src/Umbraco.Core/UmbracoApplicationBase.cs +++ b/src/Umbraco.Core/UmbracoApplicationBase.cs @@ -64,7 +64,9 @@ namespace Umbraco.Core // after Umbraco has started there is a scope in "context" and that context is // going to stay there and never get destroyed nor reused, so we have to ensure that // the scope is disposed (along with database etc) - reset it all entirely - ((ScopeProvider) ApplicationContext.Current.ScopeProvider).Reset(); + var scopeProvider = ApplicationContext.Current.ScopeProvider as ScopeProvider; + if (scopeProvider != null) // can be mocked... + scopeProvider.Reset(); } /// diff --git a/src/Umbraco.Tests/Scoping/ScopeTests.cs b/src/Umbraco.Tests/Scoping/ScopeTests.cs index 249ecd2081..f6a9ab71df 100644 --- a/src/Umbraco.Tests/Scoping/ScopeTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeTests.cs @@ -295,7 +295,7 @@ namespace Umbraco.Tests.Scoping using (var nested = scopeProvider.CreateScope()) { nested.Database.Execute("INSERT INTO tmp (id, name) VALUES (2, 'b')"); - var nn = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); + var nn = nested.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); Assert.AreEqual("b", nn); } @@ -331,7 +331,7 @@ namespace Umbraco.Tests.Scoping using (var nested = scopeProvider.CreateScope()) { nested.Database.Execute("INSERT INTO tmp (id, name) VALUES (2, 'b')"); - var nn = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); + var nn = nested.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); Assert.AreEqual("b", nn); nested.Complete(); } @@ -366,7 +366,7 @@ namespace Umbraco.Tests.Scoping using (var nested = scopeProvider.CreateScope()) { nested.Database.Execute("INSERT INTO tmp (id, name) VALUES (2, 'b')"); - var nn = scope.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); + var nn = nested.Database.ExecuteScalar("SELECT name FROM tmp WHERE id=2"); Assert.AreEqual("b", nn); nested.Complete(); } From 4ab7f768fabe4b8ae27e89f35a179956c558082e Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 18 Jan 2017 18:48:13 +0100 Subject: [PATCH 280/599] U4-9322 - backport repository cache policy changes from v8 --- .../Cache/DefaultRepositoryCachePolicy.cs | 310 ++++++++---------- .../DefaultRepositoryCachePolicyFactory.cs | 27 -- .../Cache/FullDataSetRepositoryCachePolicy.cs | 269 ++++++--------- ...FullDataSetRepositoryCachePolicyFactory.cs | 33 -- .../Cache/IRepositoryCachePolicy.cs | 84 ++++- .../Cache/IRepositoryCachePolicyFactory.cs | 9 - ...SingleItemsRepositoryCachePolicyFactory.cs | 27 -- .../Cache/RepositoryCachePolicyBase.cs | 58 ++-- .../Cache/RepositoryCachePolicyOptions.cs | 3 + .../SingleItemsOnlyRepositoryCachePolicy.cs | 25 +- .../Repositories/ContentTypeRepository.cs | 16 +- .../Repositories/DictionaryRepository.cs | 85 ++--- .../Repositories/DomainRepository.cs | 13 +- .../Repositories/LanguageRepository.cs | 13 +- .../Repositories/MediaTypeRepository.cs | 14 +- .../Repositories/MemberTypeRepository.cs | 16 +- .../Repositories/PetaPocoRepositoryBase.cs | 1 - .../Repositories/PublicAccessRepository.cs | 13 +- .../Repositories/RelationTypeRepository.cs | 15 +- .../Repositories/RepositoryBase.cs | 79 ++--- .../Repositories/TemplateRepository.cs | 12 +- src/Umbraco.Core/Umbraco.Core.csproj | 4 - .../Cache/DefaultCachePolicyTests.cs | 50 +-- .../Cache/FullDataSetCachePolicyTests.cs | 72 ++-- .../Cache/SingleItemsOnlyCachePolicyTests.cs | 18 +- 25 files changed, 544 insertions(+), 722 deletions(-) delete mode 100644 src/Umbraco.Core/Cache/DefaultRepositoryCachePolicyFactory.cs delete mode 100644 src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicyFactory.cs delete mode 100644 src/Umbraco.Core/Cache/IRepositoryCachePolicyFactory.cs delete mode 100644 src/Umbraco.Core/Cache/OnlySingleItemsRepositoryCachePolicyFactory.cs diff --git a/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs index 1f51fc3ccc..656d532d8a 100644 --- a/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs @@ -1,23 +1,25 @@ using System; using System.Collections.Generic; using System.Linq; -using Umbraco.Core.Logging; using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Cache { /// - /// The default cache policy for retrieving a single entity + /// Represents the default cache policy. /// - /// - /// + /// The type of the entity. + /// The type of the identifier. /// - /// This cache policy uses sliding expiration and caches instances for 5 minutes. However if allow zero count is true, then we use the - /// default policy with no expiry. + /// The default cache policy caches entities with a 5 minutes sliding expiration. + /// Each entity is cached individually. + /// If options.GetAllCacheAllowZeroCount then a 'zero-count' array is cached when GetAll finds nothing. + /// If options.GetAllCacheValidateCount then we check against the db when getting many entities. /// internal class DefaultRepositoryCachePolicy : RepositoryCachePolicyBase where TEntity : class, IAggregateRoot { + private static readonly TEntity[] EmptyEntities = new TEntity[0]; // const private readonly RepositoryCachePolicyOptions _options; public DefaultRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options) @@ -27,242 +29,216 @@ namespace Umbraco.Core.Cache _options = options; } - protected string GetCacheIdKey(object id) + protected string GetEntityCacheKey(object id) { if (id == null) throw new ArgumentNullException("id"); - - return string.Format("{0}{1}", GetCacheTypeKey(), id); + return GetEntityTypeCacheKey() + id; } - protected string GetCacheTypeKey() + protected string GetEntityTypeCacheKey() { return string.Format("uRepo_{0}_", typeof(TEntity).Name); } - public override void CreateOrUpdate(TEntity entity, Action persistMethod) + protected virtual void InsertEntity(string cacheKey, TEntity entity) + { + Cache.InsertCacheItem(cacheKey, () => entity, TimeSpan.FromMinutes(5), true); + } + + protected virtual void InsertEntities(TId[] ids, TEntity[] entities) + { + if (ids.Length == 0 && entities.Length == 0 && _options.GetAllCacheAllowZeroCount) + { + // getting all of them, and finding nothing. + // if we can cache a zero count, cache an empty array, + // for as long as the cache is not cleared (no expiration) + Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => EmptyEntities); + } + else + { + // individually cache each item + foreach (var entity in entities) + { + var capture = entity; + Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true); + } + } + } + + /// + public override void Create(TEntity entity, Action persistNew) { if (entity == null) throw new ArgumentNullException("entity"); - if (persistMethod == null) throw new ArgumentNullException("persistMethod"); try { - persistMethod(entity); + persistNew(entity); - //set the disposal action - SetCacheAction(() => + // just to be safe, we cannot cache an item without an identity + if (entity.HasIdentity) { - //just to be safe, we cannot cache an item without an identity - if (entity.HasIdentity) - { - Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => entity, - timeout: TimeSpan.FromMinutes(5), - isSliding: true); - } - - //If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared - Cache.ClearCacheItem(GetCacheTypeKey()); - }); - + Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true); + } + + // if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared + Cache.ClearCacheItem(GetEntityTypeCacheKey()); } catch { - //set the disposal action - SetCacheAction(() => - { - //if an exception is thrown we need to remove the entry from cache, this is ONLY a work around because of the way - // that we cache entities: http://issues.umbraco.org/issue/U4-4259 - Cache.ClearCacheItem(GetCacheIdKey(entity.Id)); + // if an exception is thrown we need to remove the entry from cache, + // this is ONLY a work around because of the way + // that we cache entities: http://issues.umbraco.org/issue/U4-4259 + Cache.ClearCacheItem(GetEntityCacheKey(entity.Id)); + + // if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared + Cache.ClearCacheItem(GetEntityTypeCacheKey()); - //If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared - Cache.ClearCacheItem(GetCacheTypeKey()); - }); - throw; } } - public override void Remove(TEntity entity, Action persistMethod) + /// + public override void Update(TEntity entity, Action persistUpdated) { if (entity == null) throw new ArgumentNullException("entity"); - if (persistMethod == null) throw new ArgumentNullException("persistMethod"); try { - persistMethod(entity); - } - finally - { - //set the disposal action - var cacheKey = GetCacheIdKey(entity.Id); - SetCacheAction(() => + persistUpdated(entity); + + // just to be safe, we cannot cache an item without an identity + if (entity.HasIdentity) { - Cache.ClearCacheItem(cacheKey); - //If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared - Cache.ClearCacheItem(GetCacheTypeKey()); - }); + Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true); + } + + // if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared + Cache.ClearCacheItem(GetEntityTypeCacheKey()); + } + catch + { + // if an exception is thrown we need to remove the entry from cache, + // this is ONLY a work around because of the way + // that we cache entities: http://issues.umbraco.org/issue/U4-4259 + Cache.ClearCacheItem(GetEntityCacheKey(entity.Id)); + + // if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared + Cache.ClearCacheItem(GetEntityTypeCacheKey()); + + throw; } } - public override TEntity Get(TId id, Func getFromRepo) + /// + public override void Delete(TEntity entity, Action persistDeleted) { - if (getFromRepo == null) throw new ArgumentNullException("getFromRepo"); + if (entity == null) throw new ArgumentNullException("entity"); - var cacheKey = GetCacheIdKey(id); + try + { + persistDeleted(entity); + } + finally + { + // whatever happens, clear the cache + var cacheKey = GetEntityCacheKey(entity.Id); + Cache.ClearCacheItem(cacheKey); + // if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared + Cache.ClearCacheItem(GetEntityTypeCacheKey()); + } + } + + /// + public override TEntity Get(TId id, Func performGet, Func> performGetAll) + { + var cacheKey = GetEntityCacheKey(id); var fromCache = Cache.GetCacheItem(cacheKey); + + // if found in cache then return else fetch and cache if (fromCache != null) return fromCache; - - var entity = getFromRepo(id); + var entity = performGet(id); - //set the disposal action - SetCacheAction(cacheKey, entity); + if (entity != null && entity.HasIdentity) + InsertEntity(cacheKey, entity); return entity; } - public override TEntity Get(TId id) + /// + public override TEntity GetCached(TId id) { - var cacheKey = GetCacheIdKey(id); + var cacheKey = GetEntityCacheKey(id); return Cache.GetCacheItem(cacheKey); } - public override bool Exists(TId id, Func getFromRepo) + /// + public override bool Exists(TId id, Func performExists, Func> performGetAll) { - if (getFromRepo == null) throw new ArgumentNullException("getFromRepo"); - - var cacheKey = GetCacheIdKey(id); + // if found in cache the return else check + var cacheKey = GetEntityCacheKey(id); var fromCache = Cache.GetCacheItem(cacheKey); - return fromCache != null || getFromRepo(id); + return fromCache != null || performExists(id); } - public override TEntity[] GetAll(TId[] ids, Func> getFromRepo) + /// + public override TEntity[] GetAll(TId[] ids, Func> performGetAll) { - if (getFromRepo == null) throw new ArgumentNullException("getFromRepo"); - - if (ids.Any()) + if (ids.Length > 0) { - var entities = ids.Select(Get).ToArray(); - if (ids.Length.Equals(entities.Length) && entities.Any(x => x == null) == false) - return entities; + // try to get each entity from the cache + // if we can find all of them, return + var entities = ids.Select(GetCached).WhereNotNull().ToArray(); + if (ids.Length.Equals(entities.Length)) + return entities; // no need for null checks, we are not caching nulls } else { - var allEntities = GetAllFromCache(); - if (allEntities.Any()) + // get everything we have + var entities = Cache.GetCacheItemsByKeySearch(GetEntityTypeCacheKey()) + .ToArray(); // no need for null checks, we are not caching nulls + + if (entities.Length > 0) { + // if some of them were in the cache... if (_options.GetAllCacheValidateCount) { - //Get count of all entities of current type (TEntity) to ensure cached result is correct + // need to validate the count, get the actual count and return if ok var totalCount = _options.PerformCount(); - if (allEntities.Length == totalCount) - return allEntities; + if (entities.Length == totalCount) + return entities; } else { - return allEntities; + // no need to validate, just return what we have and assume it's all there is + return entities; } } else if (_options.GetAllCacheAllowZeroCount) { - //if the repository allows caching a zero count, then check the zero count cache - if (HasZeroCountCache()) - { - //there is a zero count cache so return an empty list - return new TEntity[] {}; - } + // if none of them were in the cache + // and we allow zero count - check for the special (empty) entry + var empty = Cache.GetCacheItem(GetEntityTypeCacheKey()); + if (empty != null) return empty; } } - //we need to do the lookup from the repo - var entityCollection = getFromRepo(ids) - //ensure we don't include any null refs in the returned collection! - .WhereNotNull() + // cache failed, get from repo and cache + var repoEntities = performGetAll(ids) + .WhereNotNull() // exclude nulls! + .Where(x => x.HasIdentity) // be safe, though would be weird... .ToArray(); - //set the disposal action - SetCacheAction(ids, entityCollection); + // note: if empty & allow zero count, will cache a special (empty) entry + InsertEntities(ids, repoEntities); - return entityCollection; + return repoEntities; } - /// - /// Looks up the zero count cache, must return null if it doesn't exist - /// - /// - protected bool HasZeroCountCache() + /// + public override void ClearAll() { - var zeroCount = Cache.GetCacheItem(GetCacheTypeKey()); - return (zeroCount != null && zeroCount.Any() == false); + Cache.ClearCacheByKeySearch(GetEntityTypeCacheKey()); } - - /// - /// Performs the lookup for all entities of this type from the cache - /// - /// - protected TEntity[] GetAllFromCache() - { - var allEntities = Cache.GetCacheItemsByKeySearch(GetCacheTypeKey()) - .WhereNotNull() - .ToArray(); - return allEntities.Any() ? allEntities : new TEntity[] {}; - } - - /// - /// Sets the action to execute on disposal for a single entity - /// - /// - /// - protected virtual void SetCacheAction(string cacheKey, TEntity entity) - { - if (entity == null) return; - - SetCacheAction(() => - { - //just to be safe, we cannot cache an item without an identity - if (entity.HasIdentity) - { - Cache.InsertCacheItem(cacheKey, () => entity, - timeout: TimeSpan.FromMinutes(5), - isSliding: true); - } - }); - } - - /// - /// Sets the action to execute on disposal for an entity collection - /// - /// - /// - protected virtual void SetCacheAction(TId[] ids, TEntity[] entityCollection) - { - SetCacheAction(() => - { - //This option cannot execute if we are looking up specific Ids - if (ids.Any() == false && entityCollection.Length == 0 && _options.GetAllCacheAllowZeroCount) - { - //there was nothing returned but we want to cache a zero count result so add an TEntity[] to the cache - // to signify that there is a zero count cache - //NOTE: Don't set expiry/sliding for a zero count - Cache.InsertCacheItem(GetCacheTypeKey(), () => new TEntity[] {}); - } - else - { - //This is the default behavior, we'll individually cache each item so that if/when these items are resolved - // by id, they are returned from the already existing cache. - foreach (var entity in entityCollection.WhereNotNull()) - { - var localCopy = entity; - //just to be safe, we cannot cache an item without an identity - if (localCopy.HasIdentity) - { - Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => localCopy, - timeout: TimeSpan.FromMinutes(5), - isSliding: true); - } - } - } - }); - } - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicyFactory.cs b/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicyFactory.cs deleted file mode 100644 index 5c02e41a48..0000000000 --- a/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicyFactory.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Cache -{ - /// - /// Creates cache policies - /// - /// - /// - internal class DefaultRepositoryCachePolicyFactory : IRepositoryCachePolicyFactory - where TEntity : class, IAggregateRoot - { - private readonly IRuntimeCacheProvider _runtimeCache; - private readonly RepositoryCachePolicyOptions _options; - - public DefaultRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, RepositoryCachePolicyOptions options) - { - _runtimeCache = runtimeCache; - _options = options; - } - - public virtual IRepositoryCachePolicy CreatePolicy() - { - return new DefaultRepositoryCachePolicy(_runtimeCache, _options); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs index 9b37d1861f..95ad7e2bf0 100644 --- a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs @@ -7,223 +7,168 @@ using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Cache { /// - /// A caching policy that caches an entire dataset as a single collection + /// Represents a caching policy that caches the entire entities set as a single collection. /// - /// - /// + /// The type of the entity. + /// The type of the identifier. + /// + /// Caches the entire set of entities as a single collection. + /// Used by Content-, Media- and MemberTypeRepository, DataTypeRepository, DomainRepository, + /// LanguageRepository, PublicAccessRepository, TemplateRepository... things that make sense to + /// keep as a whole in memory. + /// internal class FullDataSetRepositoryCachePolicy : RepositoryCachePolicyBase where TEntity : class, IAggregateRoot { - private readonly Func _getEntityId; - private readonly Func> _getAllFromRepo; + private readonly Func _entityGetId; private readonly bool _expires; - public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func getEntityId, Func> getAllFromRepo, bool expires) + public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, Func entityGetId, bool expires) : base(cache) { - _getEntityId = getEntityId; - _getAllFromRepo = getAllFromRepo; + _entityGetId = entityGetId; _expires = expires; } - private bool? _hasZeroCountCache; + protected static readonly TId[] EmptyIds = new TId[0]; // const - - protected string GetCacheTypeKey() + protected string GetEntityTypeCacheKey() { return string.Format("uRepo_{0}_", typeof(TEntity).Name); } - public override void CreateOrUpdate(TEntity entity, Action persistMethod) + protected void InsertEntities(TEntity[] entities) { - if (entity == null) throw new ArgumentNullException("entity"); - if (persistMethod == null) throw new ArgumentNullException("persistMethod"); + // cache is expected to be a deep-cloning cache ie it deep-clones whatever is + // IDeepCloneable when it goes in, and out. it also resets dirty properties, + // making sure that no 'dirty' entity is cached. + // + // this policy is caching the entire list of entities. to ensure that entities + // are properly deep-clones when cached, it uses a DeepCloneableList. however, + // we don't want to deep-clone *each* entity in the list when fetching it from + // cache as that would not be efficient for Get(id). so the DeepCloneableList is + // set to ListCloneBehavior.CloneOnce ie it will clone *once* when inserting, + // and then will *not* clone when retrieving. - try + if (_expires) { - persistMethod(entity); - - //set the disposal action - SetCacheAction(() => - { - //Clear all - Cache.ClearCacheItem(GetCacheTypeKey()); - }); + Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => new DeepCloneableList(entities), TimeSpan.FromMinutes(5), true); } - catch + else { - //set the disposal action - SetCacheAction(() => - { - //Clear all - Cache.ClearCacheItem(GetCacheTypeKey()); - }); - throw; + Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => new DeepCloneableList(entities)); } } - public override void Remove(TEntity entity, Action persistMethod) + /// + public override void Create(TEntity entity, Action persistNew) { if (entity == null) throw new ArgumentNullException("entity"); - if (persistMethod == null) throw new ArgumentNullException("persistMethod"); try { - persistMethod(entity); + persistNew(entity); } finally { - //set the disposal action - SetCacheAction(() => - { - //Clear all - Cache.ClearCacheItem(GetCacheTypeKey()); - }); + ClearAll(); } } - public override TEntity Get(TId id, Func getFromRepo) + /// + public override void Update(TEntity entity, Action persistUpdated) { - //Force get all with cache - var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull()); + if (entity == null) throw new ArgumentNullException("entity"); - //we don't have anything in cache (this should never happen), just return from the repo - if (found == null) return getFromRepo(id); - var entity = found.FirstOrDefault(x => _getEntityId(x).Equals(id)); - if (entity == null) return null; - - //We must ensure to deep clone each one out manually since the deep clone list only clones one way - return (TEntity)entity.DeepClone(); - } - - public override TEntity Get(TId id) - { - //Force get all with cache - var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull()); - - //we don't have anything in cache (this should never happen), just return null - if (found == null) return null; - var entity = found.FirstOrDefault(x => _getEntityId(x).Equals(id)); - if (entity == null) return null; - - //We must ensure to deep clone each one out manually since the deep clone list only clones one way - return (TEntity)entity.DeepClone(); - } - - public override bool Exists(TId id, Func getFromRepo) - { - //Force get all with cache - var found = GetAll(new TId[] { }, ids => _getAllFromRepo().WhereNotNull()); - - //we don't have anything in cache (this should never happen), just return from the repo - return found == null - ? getFromRepo(id) - : found.Any(x => _getEntityId(x).Equals(id)); - } - - public override TEntity[] GetAll(TId[] ids, Func> getFromRepo) - { - //process getting all including setting the cache callback - var result = PerformGetAll(getFromRepo); - - //now that the base result has been calculated, they will all be cached. - // Now we can just filter by ids if they have been supplied - - return (ids.Any() - ? result.Where(x => ids.Contains(_getEntityId(x))).ToArray() - : result) - //We must ensure to deep clone each one out manually since the deep clone list only clones one way - .Select(x => (TEntity)x.DeepClone()) - .ToArray(); - } - - private TEntity[] PerformGetAll(Func> getFromRepo) - { - var allEntities = GetAllFromCache(); - if (allEntities.Any()) + try { - return allEntities; + persistUpdated(entity); } - - //check the zero count cache - if (HasZeroCountCache()) + finally { - //there is a zero count cache so return an empty list - return new TEntity[] { }; + ClearAll(); } - - //we need to do the lookup from the repo - var entityCollection = getFromRepo(new TId[] { }) - //ensure we don't include any null refs in the returned collection! - .WhereNotNull() - .ToArray(); - - //set the disposal action - SetCacheAction(entityCollection); - - return entityCollection; } - /// - /// For this type of caching policy, we don't cache individual items - /// - /// - /// - protected void SetCacheAction(string cacheKey, TEntity entity) + /// + public override void Delete(TEntity entity, Action persistDeleted) { - //No-op - } + if (entity == null) throw new ArgumentNullException("entity"); - /// - /// Sets the action to execute on disposal for an entity collection - /// - /// - protected void SetCacheAction(TEntity[] entityCollection) - { - //set the disposal action - SetCacheAction(() => + try { - //We want to cache the result as a single collection - - if (_expires) - { - Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList(entityCollection), - timeout: TimeSpan.FromMinutes(5), - isSliding: true); - } - else - { - Cache.InsertCacheItem(GetCacheTypeKey(), () => new DeepCloneableList(entityCollection)); - } - }); + persistDeleted(entity); + } + finally + { + ClearAll(); + } } - /// - /// Looks up the zero count cache, must return null if it doesn't exist - /// - /// - protected bool HasZeroCountCache() + /// + public override TEntity Get(TId id, Func performGet, Func> performGetAll) { - if (_hasZeroCountCache.HasValue) - return _hasZeroCountCache.Value; + // get all from the cache, then look for the entity + var all = GetAllCached(performGetAll); + var entity = all.FirstOrDefault(x => _entityGetId(x).Equals(id)); - _hasZeroCountCache = Cache.GetCacheItem>(GetCacheTypeKey()) != null; - return _hasZeroCountCache.Value; + // see note in InsertEntities - what we get here is the original + // cached entity, not a clone, so we need to manually ensure it is deep-cloned. + return entity == null ? null : (TEntity) entity.DeepClone(); } - /// - /// This policy will cache the full data set as a single collection - /// - /// - protected TEntity[] GetAllFromCache() + /// + public override TEntity GetCached(TId id) { - var found = Cache.GetCacheItem>(GetCacheTypeKey()); + // get all from the cache -- and only the cache, then look for the entity + var all = Cache.GetCacheItem>(GetEntityTypeCacheKey()); + var entity = all == null ? null : all.FirstOrDefault(x => _entityGetId(x).Equals(id)); - //This method will get called before checking for zero count cache, so we'll just set the flag here - _hasZeroCountCache = found != null; - - return found == null ? new TEntity[] { } : found.WhereNotNull().ToArray(); + // see note in InsertEntities - what we get here is the original + // cached entity, not a clone, so we need to manually ensure it is deep-cloned. + return entity == null ? null : (TEntity)entity.DeepClone(); } + /// + public override bool Exists(TId id, Func performExits, Func> performGetAll) + { + // get all as one set, then look for the entity + var all = GetAllCached(performGetAll); + return all.Any(x => _entityGetId(x).Equals(id)); + } + + /// + public override TEntity[] GetAll(TId[] ids, Func> performGetAll) + { + // get all as one set, from cache if possible, else repo + var all = GetAllCached(performGetAll); + + // if ids have been specified, filter + if (ids.Length > 0) all = all.Where(x => ids.Contains(_entityGetId(x))); + + // and return + // see note in SetCacheActionToInsertEntities - what we get here is the original + // cached entities, not clones, so we need to manually ensure they are deep-cloned. + return all.Select(x => (TEntity) x.DeepClone()).ToArray(); + } + + // does NOT clone anything, so be nice with the returned values + private IEnumerable GetAllCached(Func> performGetAll) + { + // try the cache first + var all = Cache.GetCacheItem>(GetEntityTypeCacheKey()); + if (all != null) return all.ToArray(); + + // else get from repo and cache + var entities = performGetAll(EmptyIds).WhereNotNull().ToArray(); + InsertEntities(entities); // may be an empty array... + return entities; + } + + /// + public override void ClearAll() + { + Cache.ClearCacheItem(GetEntityTypeCacheKey()); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicyFactory.cs b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicyFactory.cs deleted file mode 100644 index e4addcf355..0000000000 --- a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicyFactory.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Cache -{ - /// - /// Creates cache policies - /// - /// - /// - internal class FullDataSetRepositoryCachePolicyFactory : IRepositoryCachePolicyFactory - where TEntity : class, IAggregateRoot - { - private readonly IRuntimeCacheProvider _runtimeCache; - private readonly Func _getEntityId; - private readonly Func> _getAllFromRepo; - private readonly bool _expires; - - public FullDataSetRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, Func getEntityId, Func> getAllFromRepo, bool expires) - { - _runtimeCache = runtimeCache; - _getEntityId = getEntityId; - _getAllFromRepo = getAllFromRepo; - _expires = expires; - } - - public virtual IRepositoryCachePolicy CreatePolicy() - { - return new FullDataSetRepositoryCachePolicy(_runtimeCache, _getEntityId, _getAllFromRepo, _expires); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/IRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/IRepositoryCachePolicy.cs index 215487c3be..11a3cf6bb9 100644 --- a/src/Umbraco.Core/Cache/IRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/IRepositoryCachePolicy.cs @@ -4,15 +4,83 @@ using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Cache { - internal interface IRepositoryCachePolicy : IDisposable + internal interface IRepositoryCachePolicy where TEntity : class, IAggregateRoot { - TEntity Get(TId id, Func getFromRepo); - TEntity Get(TId id); - bool Exists(TId id, Func getFromRepo); - - void CreateOrUpdate(TEntity entity, Action persistMethod); - void Remove(TEntity entity, Action persistMethod); - TEntity[] GetAll(TId[] ids, Func> getFromRepo); + // note: + // at the moment each repository instance creates its corresponding cache policy instance + // we could reduce allocations by using static cache policy instances but then we would need + // to modify all methods here to pass the repository and cache eg: + // + // TEntity Get(TRepository repository, IRuntimeCacheProvider cache, TId id); + // + // it is not *that* complicated but then RepositoryBase needs to have a TRepository generic + // type parameter and it all becomes convoluted - keeping it simple for the time being. + + /// + /// Gets an entity from the cache, else from the repository. + /// + /// The identifier. + /// The repository PerformGet method. + /// The repository PerformGetAll method. + /// The entity with the specified identifier, if it exits, else null. + /// First considers the cache then the repository. + TEntity Get(TId id, Func performGet, Func> performGetAll); + + /// + /// Gets an entity from the cache. + /// + /// The identifier. + /// The entity with the specified identifier, if it is in the cache already, else null. + /// Does not consider the repository at all. + TEntity GetCached(TId id); + + /// + /// Gets a value indicating whether an entity with a specified identifier exists. + /// + /// The identifier. + /// The repository PerformExists method. + /// The repository PerformGetAll method. + /// A value indicating whether an entity with the specified identifier exists. + /// First considers the cache then the repository. + bool Exists(TId id, Func performExists, Func> performGetAll); + + /// + /// Creates an entity. + /// + /// The entity. + /// The repository PersistNewItem method. + /// Creates the entity in the repository, and updates the cache accordingly. + void Create(TEntity entity, Action persistNew); + + /// + /// Updates an entity. + /// + /// The entity. + /// The reopsitory PersistUpdatedItem method. + /// Updates the entity in the repository, and updates the cache accordingly. + void Update(TEntity entity, Action persistUpdated); + + /// + /// Removes an entity. + /// + /// The entity. + /// The repository PersistDeletedItem method. + /// Removes the entity from the repository and clears the cache. + void Delete(TEntity entity, Action persistDeleted); + + /// + /// Gets entities. + /// + /// The identifiers. + /// The repository PerformGetAll method. + /// If is empty, all entities, else the entities with the specified identifiers. + /// Get all the entities. Either from the cache or the repository depending on the implementation. + TEntity[] GetAll(TId[] ids, Func> performGetAll); + + /// + /// Clears the entire cache. + /// + void ClearAll(); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/IRepositoryCachePolicyFactory.cs b/src/Umbraco.Core/Cache/IRepositoryCachePolicyFactory.cs deleted file mode 100644 index 2d69704b63..0000000000 --- a/src/Umbraco.Core/Cache/IRepositoryCachePolicyFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Cache -{ - internal interface IRepositoryCachePolicyFactory where TEntity : class, IAggregateRoot - { - IRepositoryCachePolicy CreatePolicy(); - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/OnlySingleItemsRepositoryCachePolicyFactory.cs b/src/Umbraco.Core/Cache/OnlySingleItemsRepositoryCachePolicyFactory.cs deleted file mode 100644 index b24838bc3b..0000000000 --- a/src/Umbraco.Core/Cache/OnlySingleItemsRepositoryCachePolicyFactory.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Umbraco.Core.Models.EntityBase; - -namespace Umbraco.Core.Cache -{ - /// - /// Creates cache policies - /// - /// - /// - internal class OnlySingleItemsRepositoryCachePolicyFactory : IRepositoryCachePolicyFactory - where TEntity : class, IAggregateRoot - { - private readonly IRuntimeCacheProvider _runtimeCache; - private readonly RepositoryCachePolicyOptions _options; - - public OnlySingleItemsRepositoryCachePolicyFactory(IRuntimeCacheProvider runtimeCache, RepositoryCachePolicyOptions options) - { - _runtimeCache = runtimeCache; - _options = options; - } - - public virtual IRepositoryCachePolicy CreatePolicy() - { - return new SingleItemsOnlyRepositoryCachePolicy(_runtimeCache, _options); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/RepositoryCachePolicyBase.cs b/src/Umbraco.Core/Cache/RepositoryCachePolicyBase.cs index b939cd14e6..ce366e3d0b 100644 --- a/src/Umbraco.Core/Cache/RepositoryCachePolicyBase.cs +++ b/src/Umbraco.Core/Cache/RepositoryCachePolicyBase.cs @@ -4,45 +4,45 @@ using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Cache { - internal abstract class RepositoryCachePolicyBase : DisposableObject, IRepositoryCachePolicy + /// + /// A base class for repository cache policies. + /// + /// The type of the entity. + /// The type of the identifier. + internal abstract class RepositoryCachePolicyBase : IRepositoryCachePolicy where TEntity : class, IAggregateRoot { - private Action _action; - protected RepositoryCachePolicyBase(IRuntimeCacheProvider cache) { - if (cache == null) throw new ArgumentNullException("cache"); - + if (cache == null) throw new ArgumentNullException("cache"); Cache = cache; } protected IRuntimeCacheProvider Cache { get; private set; } - /// - /// The disposal performs the caching - /// - protected override void DisposeResources() - { - if (_action != null) - { - _action(); - } - } + /// + public abstract TEntity Get(TId id, Func performGet, Func> performGetAll); - /// - /// Sets the action to execute on disposal - /// - /// - protected void SetCacheAction(Action action) - { - _action = action; - } + /// + public abstract TEntity GetCached(TId id); + + /// + public abstract bool Exists(TId id, Func performExists, Func> performGetAll); + + /// + public abstract void Create(TEntity entity, Action persistNew); + + /// + public abstract void Update(TEntity entity, Action persistUpdated); + + /// + public abstract void Delete(TEntity entity, Action persistDeleted); + + /// + public abstract TEntity[] GetAll(TId[] ids, Func> performGetAll); + + /// + public abstract void ClearAll(); - public abstract TEntity Get(TId id, Func getFromRepo); - public abstract TEntity Get(TId id); - public abstract bool Exists(TId id, Func getFromRepo); - public abstract void CreateOrUpdate(TEntity entity, Action persistMethod); - public abstract void Remove(TEntity entity, Action persistMethod); - public abstract TEntity[] GetAll(TId[] ids, Func> getFromRepo); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/RepositoryCachePolicyOptions.cs b/src/Umbraco.Core/Cache/RepositoryCachePolicyOptions.cs index e8c6ac02b0..14cef76db6 100644 --- a/src/Umbraco.Core/Cache/RepositoryCachePolicyOptions.cs +++ b/src/Umbraco.Core/Cache/RepositoryCachePolicyOptions.cs @@ -2,6 +2,9 @@ using System; namespace Umbraco.Core.Cache { + /// + /// Specifies how a repository cache policy should cache entities. + /// internal class RepositoryCachePolicyOptions { /// diff --git a/src/Umbraco.Core/Cache/SingleItemsOnlyRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/SingleItemsOnlyRepositoryCachePolicy.cs index 28ac4ee2d1..7ba7d445fe 100644 --- a/src/Umbraco.Core/Cache/SingleItemsOnlyRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/SingleItemsOnlyRepositoryCachePolicy.cs @@ -1,24 +1,27 @@ -using System.Linq; -using Umbraco.Core.Collections; using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Cache { /// - /// A caching policy that ignores all caches for GetAll - it will only cache calls for individual items + /// Represents a special policy that does not cache the result of GetAll. /// - /// - /// + /// The type of the entity. + /// The type of the identifier. + /// + /// Overrides the default repository cache policy and does not writes the result of GetAll + /// to cache, but only the result of individual Gets. It does read the cache for GetAll, though. + /// Used by DictionaryRepository. + /// internal class SingleItemsOnlyRepositoryCachePolicy : DefaultRepositoryCachePolicy where TEntity : class, IAggregateRoot { - public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options) : base(cache, options) + public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, RepositoryCachePolicyOptions options) + : base(cache, options) + { } + + protected override void InsertEntities(TId[] ids, TEntity[] entities) { - } - - protected override void SetCacheAction(TId[] ids, TEntity[] entityCollection) - { - //no-op + // nop } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs index b7b4ddd583..ca53b2e04e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs @@ -2,19 +2,13 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core.Cache; -using Umbraco.Core.Events; -using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Rdbms; - -using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.Relators; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Core.Services; namespace Umbraco.Core.Persistence.Repositories { @@ -24,6 +18,7 @@ namespace Umbraco.Core.Persistence.Repositories internal class ContentTypeRepository : ContentTypeBaseRepository, IContentTypeRepository { private readonly ITemplateRepository _templateRepository; + private IRepositoryCachePolicy _cachePolicy; public ContentTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, ITemplateRepository templateRepository) : base(work, cache, logger, sqlSyntax) @@ -31,16 +26,11 @@ namespace Umbraco.Core.Persistence.Repositories _templateRepository = templateRepository; } - private FullDataSetRepositoryCachePolicyFactory _cachePolicyFactory; - protected override IRepositoryCachePolicyFactory CachePolicyFactory + protected override IRepositoryCachePolicy CachePolicy { get { - //Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection - return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory( - RuntimeCache, GetEntityId, () => PerformGetAll(), - //allow this cache to expire - expires:true)); + return _cachePolicy ?? (_cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ true)); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs index 971efc4f2d..da6d4d94a8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs @@ -20,24 +20,27 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class DictionaryRepository : PetaPocoRepositoryBase, IDictionaryRepository { + private IRepositoryCachePolicy _cachePolicy; + public DictionaryRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider syntax) : base(work, cache, logger, syntax) - { - } + { } - private IRepositoryCachePolicyFactory _cachePolicyFactory; - protected override IRepositoryCachePolicyFactory CachePolicyFactory + protected override IRepositoryCachePolicy CachePolicy { get { - //custom cache policy which will not cache any results for GetAll - return _cachePolicyFactory ?? (_cachePolicyFactory = new OnlySingleItemsRepositoryCachePolicyFactory( - RuntimeCache, - new RepositoryCachePolicyOptions - { - //allow zero to be cached - GetAllCacheAllowZeroCount = true - })); + if (_cachePolicy != null) return _cachePolicy; + + var options = new RepositoryCachePolicyOptions + { + //allow zero to be cached + GetAllCacheAllowZeroCount = true + }; + + _cachePolicy = new SingleItemsOnlyRepositoryCachePolicy(RuntimeCache, options); + + return _cachePolicy; } } @@ -52,7 +55,7 @@ namespace Umbraco.Core.Persistence.Repositories var dto = Database.Fetch(new DictionaryLanguageTextRelator().Map, sql).FirstOrDefault(); if (dto == null) return null; - + var entity = ConvertFromDto(dto); //on initial construction we don't want to have dirty properties tracked @@ -80,7 +83,7 @@ namespace Umbraco.Core.Persistence.Repositories var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate(); sql.OrderBy(x => x.UniqueId, SqlSyntax); - + return Database.Fetch(new DictionaryLanguageTextRelator().Map, sql) .Select(x => ConvertFromDto(x)); } @@ -149,7 +152,7 @@ namespace Umbraco.Core.Persistence.Repositories translation.Key = dictionaryItem.Key; } - dictionaryItem.ResetDirtyProperties(); + dictionaryItem.ResetDirtyProperties(); } protected override void PersistUpdatedItem(IDictionaryItem entity) @@ -223,7 +226,7 @@ namespace Umbraco.Core.Persistence.Repositories var list = new List(); foreach (var textDto in dto.LanguageTextDtos) - { + { if (textDto.LanguageId <= 0) continue; @@ -250,7 +253,7 @@ namespace Umbraco.Core.Persistence.Repositories return keyRepo.Get(key); } } - + private IEnumerable GetRootDictionaryItems() { var query = Query.Builder.Where(x => x.ParentId == null); @@ -291,6 +294,7 @@ namespace Umbraco.Core.Persistence.Repositories private class DictionaryByUniqueIdRepository : SimpleGetRepository { + private IRepositoryCachePolicy _cachePolicy; private readonly DictionaryRepository _dictionaryRepository; public DictionaryByUniqueIdRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) @@ -329,25 +333,28 @@ namespace Umbraco.Core.Persistence.Repositories return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("id") + " in (@ids)"; } - private IRepositoryCachePolicyFactory _cachePolicyFactory; - protected override IRepositoryCachePolicyFactory CachePolicyFactory + protected override IRepositoryCachePolicy CachePolicy { get { - //custom cache policy which will not cache any results for GetAll - return _cachePolicyFactory ?? (_cachePolicyFactory = new OnlySingleItemsRepositoryCachePolicyFactory( - RuntimeCache, - new RepositoryCachePolicyOptions - { - //allow zero to be cached - GetAllCacheAllowZeroCount = true - })); + if (_cachePolicy != null) return _cachePolicy; + + var options = new RepositoryCachePolicyOptions + { + //allow zero to be cached + GetAllCacheAllowZeroCount = true + }; + + _cachePolicy = new SingleItemsOnlyRepositoryCachePolicy(RuntimeCache, options); + + return _cachePolicy; } } } private class DictionaryByKeyRepository : SimpleGetRepository { + private IRepositoryCachePolicy _cachePolicy; private readonly DictionaryRepository _dictionaryRepository; public DictionaryByKeyRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) @@ -386,23 +393,23 @@ namespace Umbraco.Core.Persistence.Repositories return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("key") + " in (@ids)"; } - private IRepositoryCachePolicyFactory _cachePolicyFactory; - protected override IRepositoryCachePolicyFactory CachePolicyFactory + protected override IRepositoryCachePolicy CachePolicy { get { - //custom cache policy which will not cache any results for GetAll - return _cachePolicyFactory ?? (_cachePolicyFactory = new OnlySingleItemsRepositoryCachePolicyFactory( - RuntimeCache, - new RepositoryCachePolicyOptions - { - //allow zero to be cached - GetAllCacheAllowZeroCount = true - })); + if (_cachePolicy != null) return _cachePolicy; + + var options = new RepositoryCachePolicyOptions + { + //allow zero to be cached + GetAllCacheAllowZeroCount = true + }; + + _cachePolicy = new SingleItemsOnlyRepositoryCachePolicy(RuntimeCache, options); + + return _cachePolicy; } } } - - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs index 7b6cc162a8..682c75eeb6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs @@ -18,19 +18,22 @@ namespace Umbraco.Core.Persistence.Repositories internal class DomainRepository : PetaPocoRepositoryBase, IDomainRepository { + private IRepositoryCachePolicy _cachePolicy; + public DomainRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } - private FullDataSetRepositoryCachePolicyFactory _cachePolicyFactory; - protected override IRepositoryCachePolicyFactory CachePolicyFactory + protected override IRepositoryCachePolicy CachePolicy { get { - //Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection - return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory( - RuntimeCache, GetEntityId, () => PerformGetAll(), false)); + if (_cachePolicy != null) return _cachePolicy; + + _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ false); + + return _cachePolicy; } } diff --git a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs index f9a8e59cfa..7911df8edb 100644 --- a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs @@ -19,19 +19,22 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class LanguageRepository : PetaPocoRepositoryBase, ILanguageRepository { + private IRepositoryCachePolicy _cachePolicy; + public LanguageRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } - private FullDataSetRepositoryCachePolicyFactory _cachePolicyFactory; - protected override IRepositoryCachePolicyFactory CachePolicyFactory + protected override IRepositoryCachePolicy CachePolicy { get { - //Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection - return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory( - RuntimeCache, GetEntityId, () => PerformGetAll(), false)); + if (_cachePolicy != null) return _cachePolicy; + + _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ false); + + return _cachePolicy; } } diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs index 50a89bfd65..516c08330a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs @@ -22,22 +22,22 @@ namespace Umbraco.Core.Persistence.Repositories ///
    internal class MediaTypeRepository : ContentTypeBaseRepository, IMediaTypeRepository { + private IRepositoryCachePolicy _cachePolicy; public MediaTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } - private FullDataSetRepositoryCachePolicyFactory _cachePolicyFactory; - protected override IRepositoryCachePolicyFactory CachePolicyFactory + protected override IRepositoryCachePolicy CachePolicy { get { - //Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection - return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory( - RuntimeCache, GetEntityId, () => PerformGetAll(), - //allow this cache to expire - expires: true)); + if (_cachePolicy != null) return _cachePolicy; + + _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ true); + + return _cachePolicy; } } diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs index 7d5defe8c9..fd26afac89 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs @@ -21,25 +21,25 @@ namespace Umbraco.Core.Persistence.Repositories ///
    internal class MemberTypeRepository : ContentTypeBaseRepository, IMemberTypeRepository { + private IRepositoryCachePolicy _cachePolicy; public MemberTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } - private FullDataSetRepositoryCachePolicyFactory _cachePolicyFactory; - protected override IRepositoryCachePolicyFactory CachePolicyFactory + protected override IRepositoryCachePolicy CachePolicy { get { - //Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection - return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory( - RuntimeCache, GetEntityId, () => PerformGetAll(), - //allow this cache to expire - expires: true)); + if (_cachePolicy != null) return _cachePolicy; + + _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ true); + + return _cachePolicy; } } - + protected override IMemberType PerformGet(int id) { //use the underlying GetAll which will force cache all content types diff --git a/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs index fe363fea16..c02316e7f4 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Data.SqlServerCe; using Umbraco.Core.Logging; using Umbraco.Core.Models.EntityBase; diff --git a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs index 37200b2172..c34952f4be 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs @@ -15,18 +15,21 @@ namespace Umbraco.Core.Persistence.Repositories { internal class PublicAccessRepository : PetaPocoRepositoryBase, IPublicAccessRepository { + private IRepositoryCachePolicy _cachePolicy; + public PublicAccessRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } - private FullDataSetRepositoryCachePolicyFactory _cachePolicyFactory; - protected override IRepositoryCachePolicyFactory CachePolicyFactory + protected override IRepositoryCachePolicy CachePolicy { get { - //Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection - return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory( - RuntimeCache, GetEntityId, () => PerformGetAll(), false)); + if (_cachePolicy != null) return _cachePolicy; + + _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ false); + + return _cachePolicy; } } diff --git a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs index 2c54897ff7..148ebba456 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs @@ -19,20 +19,21 @@ namespace Umbraco.Core.Persistence.Repositories ///
    internal class RelationTypeRepository : PetaPocoRepositoryBase, IRelationTypeRepository { + private IRepositoryCachePolicy _cachePolicy; + public RelationTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } - // assuming we don't have tons of relation types, use a FullDataSet policy, ie - // cache the entire GetAll result once in a single collection - which can expire - private FullDataSetRepositoryCachePolicyFactory _cachePolicyFactory; - protected override IRepositoryCachePolicyFactory CachePolicyFactory + protected override IRepositoryCachePolicy CachePolicy { get { - return _cachePolicyFactory - ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory( - RuntimeCache, GetEntityId, () => PerformGetAll(), expires: true)); + if (_cachePolicy != null) return _cachePolicy; + + _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ true); + + return _cachePolicy; } } diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index 41946d48d4..201520cbe3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -82,7 +82,6 @@ namespace Umbraco.Core.Persistence.Repositories { } - #region Static Queries private IQuery _hasIdQuery; @@ -102,30 +101,25 @@ namespace Umbraco.Core.Persistence.Repositories get { return RepositoryCache.IsolatedRuntimeCache.GetOrCreateCache(); } } - private IRepositoryCachePolicyFactory _cachePolicyFactory; - /// - /// Returns the Cache Policy for the repository - /// - /// - /// The Cache Policy determines how each entity or entity collection is cached - /// - protected virtual IRepositoryCachePolicyFactory CachePolicyFactory + private IRepositoryCachePolicy _cachePolicy; + + protected virtual IRepositoryCachePolicy CachePolicy { get { - return _cachePolicyFactory ?? (_cachePolicyFactory = new DefaultRepositoryCachePolicyFactory( - RuntimeCache, - new RepositoryCachePolicyOptions(() => - { - //create it once if it is needed (no need for locking here) - if (_hasIdQuery == null) - { - _hasIdQuery = Query.Builder.Where(x => x.Id != 0); - } + if (_cachePolicy != null) return _cachePolicy; - //Get count of all entities of current type (TEntity) to ensure cached result is correct - return PerformCount(_hasIdQuery); - }))); + var options = new RepositoryCachePolicyOptions(() => + { + //Get count of all entities of current type (TEntity) to ensure cached result is correct + //create query once if it is needed (no need for locking here) + var query = _hasIdQuery ?? (_hasIdQuery = Query.Builder.Where(x => x.Id != 0)); + return PerformCount(query); + }); + + _cachePolicy = new DefaultRepositoryCachePolicy(RuntimeCache, options); + + return _cachePolicy; } } @@ -166,10 +160,7 @@ namespace Umbraco.Core.Persistence.Repositories /// public TEntity Get(TId id) { - using (var p = CachePolicyFactory.CreatePolicy()) - { - return p.Get(id, PerformGet); - } + return CachePolicy.Get(id, PerformGet, PerformGetAll); } protected abstract IEnumerable PerformGetAll(params TId[] ids); @@ -192,13 +183,9 @@ namespace Umbraco.Core.Persistence.Repositories throw new InvalidOperationException("Cannot perform a query with more than 2000 parameters"); } - using (var p = CachePolicyFactory.CreatePolicy()) - { - var result = p.GetAll(ids, PerformGetAll); - return result; - } + return CachePolicy.GetAll(ids, PerformGetAll); } - + protected abstract IEnumerable PerformGetByQuery(IQuery query); /// /// Gets a list of entities by the passed in query @@ -220,10 +207,7 @@ namespace Umbraco.Core.Persistence.Repositories /// public bool Exists(TId id) { - using (var p = CachePolicyFactory.CreatePolicy()) - { - return p.Exists(id, PerformExists); - } + return CachePolicy.Exists(id, PerformExists, PerformGetAll); } protected abstract int PerformCount(IQuery query); @@ -236,19 +220,14 @@ namespace Umbraco.Core.Persistence.Repositories { return PerformCount(query); } - + /// /// Unit of work method that tells the repository to persist the new entity /// /// public virtual void PersistNewItem(IEntity entity) { - var casted = (TEntity)entity; - - using (var p = CachePolicyFactory.CreatePolicy()) - { - p.CreateOrUpdate(casted, PersistNewItem); - } + CachePolicy.Create((TEntity) entity, PersistNewItem); } /// @@ -257,12 +236,7 @@ namespace Umbraco.Core.Persistence.Repositories /// public virtual void PersistUpdatedItem(IEntity entity) { - var casted = (TEntity)entity; - - using (var p = CachePolicyFactory.CreatePolicy()) - { - p.CreateOrUpdate(casted, PersistUpdatedItem); - } + CachePolicy.Update((TEntity) entity, PersistUpdatedItem); } /// @@ -271,20 +245,13 @@ namespace Umbraco.Core.Persistence.Repositories /// public virtual void PersistDeletedItem(IEntity entity) { - var casted = (TEntity)entity; - - using (var p = CachePolicyFactory.CreatePolicy()) - { - p.Remove(casted, PersistDeletedItem); - } + CachePolicy.Delete((TEntity) entity, PersistDeletedItem); } - protected abstract void PersistNewItem(TEntity item); protected abstract void PersistUpdatedItem(TEntity item); protected abstract void PersistDeletedItem(TEntity item); - /// /// Dispose disposable properties /// diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index 41743601fb..a164e7e5ba 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -33,6 +33,7 @@ namespace Umbraco.Core.Persistence.Repositories private readonly ITemplatesSection _templateConfig; private readonly ViewHelper _viewHelper; private readonly MasterPageHelper _masterPageHelper; + private IRepositoryCachePolicy _cachePolicy; internal TemplateRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem, ITemplatesSection templateConfig) : base(work, cache, logger, sqlSyntax) @@ -45,14 +46,15 @@ namespace Umbraco.Core.Persistence.Repositories } - private FullDataSetRepositoryCachePolicyFactory _cachePolicyFactory; - protected override IRepositoryCachePolicyFactory CachePolicyFactory + protected override IRepositoryCachePolicy CachePolicy { get { - //Use a FullDataSet cache policy - this will cache the entire GetAll result in a single collection - return _cachePolicyFactory ?? (_cachePolicyFactory = new FullDataSetRepositoryCachePolicyFactory( - RuntimeCache, GetEntityId, () => PerformGetAll(), false)); + if (_cachePolicy != null) return _cachePolicy; + + _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ false); + + return _cachePolicy; } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index afa85609d0..fc778bc53b 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -136,9 +136,7 @@ - - @@ -146,7 +144,6 @@ - @@ -156,7 +153,6 @@ - diff --git a/src/Umbraco.Tests/Cache/DefaultCachePolicyTests.cs b/src/Umbraco.Tests/Cache/DefaultCachePolicyTests.cs index 9b0aaac78b..83b0db237f 100644 --- a/src/Umbraco.Tests/Cache/DefaultCachePolicyTests.cs +++ b/src/Umbraco.Tests/Cache/DefaultCachePolicyTests.cs @@ -24,10 +24,8 @@ namespace Umbraco.Tests.Cache }); var defaultPolicy = new DefaultRepositoryCachePolicy(cache.Object, new RepositoryCachePolicyOptions()); - using (defaultPolicy) - { - var found = defaultPolicy.Get(1, o => new AuditItem(1, "blah", AuditType.Copy, 123)); - } + + var found = defaultPolicy.Get(1, id => new AuditItem(1, "blah", AuditType.Copy, 123), o => null); Assert.IsTrue(isCached); } @@ -38,11 +36,9 @@ namespace Umbraco.Tests.Cache cache.Setup(x => x.GetCacheItem(It.IsAny())).Returns(new AuditItem(1, "blah", AuditType.Copy, 123)); var defaultPolicy = new DefaultRepositoryCachePolicy(cache.Object, new RepositoryCachePolicyOptions()); - using (defaultPolicy) - { - var found = defaultPolicy.Get(1, o => (AuditItem) null); - Assert.IsNotNull(found); - } + + var found = defaultPolicy.Get(1, id => null, ids => null); + Assert.IsNotNull(found); } [Test] @@ -59,14 +55,12 @@ namespace Umbraco.Tests.Cache cache.Setup(x => x.GetCacheItemsByKeySearch(It.IsAny())).Returns(new AuditItem[] {}); var defaultPolicy = new DefaultRepositoryCachePolicy(cache.Object, new RepositoryCachePolicyOptions()); - using (defaultPolicy) + + var found = defaultPolicy.GetAll(new object[] { }, ids => new[] { - var found = defaultPolicy.GetAll(new object[] {}, o => new[] - { - new AuditItem(1, "blah", AuditType.Copy, 123), - new AuditItem(2, "blah2", AuditType.Copy, 123) - }); - } + new AuditItem(1, "blah", AuditType.Copy, 123), + new AuditItem(2, "blah2", AuditType.Copy, 123) + }); Assert.AreEqual(2, cached.Count); } @@ -82,11 +76,9 @@ namespace Umbraco.Tests.Cache }); var defaultPolicy = new DefaultRepositoryCachePolicy(cache.Object, new RepositoryCachePolicyOptions()); - using (defaultPolicy) - { - var found = defaultPolicy.GetAll(new object[] {}, o => new[] {(AuditItem) null}); - Assert.AreEqual(2, found.Length); - } + + var found = defaultPolicy.GetAll(new object[] { }, ids => new[] { (AuditItem)null }); + Assert.AreEqual(2, found.Length); } [Test] @@ -103,13 +95,7 @@ namespace Umbraco.Tests.Cache var defaultPolicy = new DefaultRepositoryCachePolicy(cache.Object, new RepositoryCachePolicyOptions()); try { - using (defaultPolicy) - { - defaultPolicy.CreateOrUpdate(new AuditItem(1, "blah", AuditType.Copy, 123), item => - { - throw new Exception("blah!"); - }); - } + defaultPolicy.Update(new AuditItem(1, "blah", AuditType.Copy, 123), item => { throw new Exception("blah!"); }); } catch { @@ -135,13 +121,7 @@ namespace Umbraco.Tests.Cache var defaultPolicy = new DefaultRepositoryCachePolicy(cache.Object, new RepositoryCachePolicyOptions()); try { - using (defaultPolicy) - { - defaultPolicy.Remove(new AuditItem(1, "blah", AuditType.Copy, 123), item => - { - throw new Exception("blah!"); - }); - } + defaultPolicy.Delete(new AuditItem(1, "blah", AuditType.Copy, 123), item => { throw new Exception("blah!"); }); } catch { diff --git a/src/Umbraco.Tests/Cache/FullDataSetCachePolicyTests.cs b/src/Umbraco.Tests/Cache/FullDataSetCachePolicyTests.cs index 96e22e3aff..ee519afb76 100644 --- a/src/Umbraco.Tests/Cache/FullDataSetCachePolicyTests.cs +++ b/src/Umbraco.Tests/Cache/FullDataSetCachePolicyTests.cs @@ -32,11 +32,9 @@ namespace Umbraco.Tests.Cache isCached = true; }); - var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, () => getAll, false); - using (defaultPolicy) - { - var found = defaultPolicy.Get(1, o => new AuditItem(1, "blah", AuditType.Copy, 123)); - } + var policy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, false); + + var found = policy.Get(1, id => new AuditItem(1, "blah", AuditType.Copy, 123), ids => getAll); Assert.IsTrue(isCached); } @@ -52,12 +50,10 @@ namespace Umbraco.Tests.Cache var cache = new Mock(); cache.Setup(x => x.GetCacheItem(It.IsAny())).Returns(new AuditItem(1, "blah", AuditType.Copy, 123)); - var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, () => getAll, false); - using (defaultPolicy) - { - var found = defaultPolicy.Get(1, o => (AuditItem)null); - Assert.IsNotNull(found); - } + var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, false); + + var found = defaultPolicy.Get(1, id => null, ids => getAll); + Assert.IsNotNull(found); } [Test] @@ -84,21 +80,17 @@ namespace Umbraco.Tests.Cache return cached.Any() ? new DeepCloneableList(ListCloneBehavior.CloneOnce) : null; }); - var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, () => getAll, false); - using (defaultPolicy) - { - var found = defaultPolicy.GetAll(new object[] {}, o => getAll); - } + var policy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, false); + + var found = policy.GetAll(new object[] { }, ids => getAll); Assert.AreEqual(1, cached.Count); Assert.IsNotNull(list); //Do it again, ensure that its coming from the cache! - defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, () => getAll, false); - using (defaultPolicy) - { - var found = defaultPolicy.GetAll(new object[] { }, o => getAll); - } + policy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, false); + + found = policy.GetAll(new object[] { }, ids => getAll); Assert.AreEqual(1, cached.Count); Assert.IsNotNull(list); @@ -127,11 +119,9 @@ namespace Umbraco.Tests.Cache }); cache.Setup(x => x.GetCacheItem(It.IsAny())).Returns(new AuditItem[] { }); - var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, () => getAll, false); - using (defaultPolicy) - { - var found = defaultPolicy.GetAll(new object[] { }, o => getAll); - } + var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, false); + + var found = defaultPolicy.GetAll(new object[] { }, ids => getAll); Assert.AreEqual(1, cached.Count); Assert.IsNotNull(list); @@ -150,12 +140,10 @@ namespace Umbraco.Tests.Cache new AuditItem(2, "blah2", AuditType.Copy, 123) }); - var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, () => getAll, false); - using (defaultPolicy) - { - var found = defaultPolicy.GetAll(new object[] { }, o => getAll); - Assert.AreEqual(2, found.Length); - } + var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, false); + + var found = defaultPolicy.GetAll(new object[] { }, ids => getAll); + Assert.AreEqual(2, found.Length); } [Test] @@ -175,16 +163,10 @@ namespace Umbraco.Tests.Cache cacheCleared = true; }); - var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, () => getAll, false); + var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, false); try { - using (defaultPolicy) - { - defaultPolicy.CreateOrUpdate(new AuditItem(1, "blah", AuditType.Copy, 123), item => - { - throw new Exception("blah!"); - }); - } + defaultPolicy.Update(new AuditItem(1, "blah", AuditType.Copy, 123), item => { throw new Exception("blah!"); }); } catch { @@ -213,16 +195,10 @@ namespace Umbraco.Tests.Cache cacheCleared = true; }); - var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, () => getAll, false); + var defaultPolicy = new FullDataSetRepositoryCachePolicy(cache.Object, item => item.Id, false); try { - using (defaultPolicy) - { - defaultPolicy.Remove(new AuditItem(1, "blah", AuditType.Copy, 123), item => - { - throw new Exception("blah!"); - }); - } + defaultPolicy.Delete(new AuditItem(1, "blah", AuditType.Copy, 123), item => { throw new Exception("blah!"); }); } catch { diff --git a/src/Umbraco.Tests/Cache/SingleItemsOnlyCachePolicyTests.cs b/src/Umbraco.Tests/Cache/SingleItemsOnlyCachePolicyTests.cs index b8e77e9267..55a7f2a893 100644 --- a/src/Umbraco.Tests/Cache/SingleItemsOnlyCachePolicyTests.cs +++ b/src/Umbraco.Tests/Cache/SingleItemsOnlyCachePolicyTests.cs @@ -25,14 +25,12 @@ namespace Umbraco.Tests.Cache cache.Setup(x => x.GetCacheItemsByKeySearch(It.IsAny())).Returns(new AuditItem[] { }); var defaultPolicy = new SingleItemsOnlyRepositoryCachePolicy(cache.Object, new RepositoryCachePolicyOptions()); - using (defaultPolicy) + + var found = defaultPolicy.GetAll(new object[] { }, ids => new[] { - var found = defaultPolicy.GetAll(new object[] { }, o => new[] - { - new AuditItem(1, "blah", AuditType.Copy, 123), - new AuditItem(2, "blah2", AuditType.Copy, 123) - }); - } + new AuditItem(1, "blah", AuditType.Copy, 123), + new AuditItem(2, "blah2", AuditType.Copy, 123) + }); Assert.AreEqual(0, cached.Count); } @@ -50,10 +48,8 @@ namespace Umbraco.Tests.Cache }); var defaultPolicy = new SingleItemsOnlyRepositoryCachePolicy(cache.Object, new RepositoryCachePolicyOptions()); - using (defaultPolicy) - { - var found = defaultPolicy.Get(1, o => new AuditItem(1, "blah", AuditType.Copy, 123)); - } + + var found = defaultPolicy.Get(1, id => new AuditItem(1, "blah", AuditType.Copy, 123), ids => null); Assert.IsTrue(isCached); } } From db414e8045d0a455dae568a4f67675eab6c3ccef Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 19 Jan 2017 12:51:56 +1100 Subject: [PATCH 281/599] Fixes issue with implicitly non-published items getting into the index, fixes issue with sql syntax columns (i.e for mysql) for queries that query the cmsContentXml table --- .../Repositories/ContentRepository.cs | 12 +++-- .../Interfaces/IRepositoryVersionable.cs | 3 +- .../Repositories/VersionableRepositoryBase.cs | 20 ++++++-- src/Umbraco.Core/Services/ContentService.cs | 6 ++- src/Umbraco.Core/Services/MediaService.cs | 2 +- src/Umbraco.Core/Services/MemberService.cs | 2 +- src/UmbracoExamine/UmbracoContentIndexer.cs | 47 ++++++++++++++++++- 7 files changed, 78 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 14e6c0e9a6..294f869c3f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -624,13 +624,13 @@ namespace Umbraco.Core.Persistence.Repositories var sqlClause = GetBaseQuery(false); var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate() - .Where(x => x.Published) + .Where(x => x.Published, SqlSyntax) .OrderBy(x => x.Level, SqlSyntax) .OrderBy(x => x.SortOrder, SqlSyntax); return ProcessQuery(sql, true); } - + /// /// This builds the Xml document used for the XML cache /// @@ -662,10 +662,14 @@ namespace Umbraco.Core.Persistence.Repositories parent.Attributes.Append(pIdAtt); xmlDoc.AppendChild(parent); - const string sql = @"select umbracoNode.id, umbracoNode.parentID, umbracoNode.sortOrder, cmsContentXml.xml, umbracoNode.level from umbracoNode + //Ensure that only nodes that have published versions are selected + var sql = string.Format(@"select umbracoNode.id, umbracoNode.parentID, umbracoNode.sortOrder, cmsContentXml.{0}, umbracoNode.{1} from umbracoNode inner join cmsContentXml on cmsContentXml.nodeId = umbracoNode.id and umbracoNode.nodeObjectType = @type where umbracoNode.id in (select cmsDocument.nodeId from cmsDocument where cmsDocument.published = 1) -order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder"; +order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder", + SqlSyntax.GetQuotedColumnName("xml"), + SqlSyntax.GetQuotedColumnName("level"), + SqlSyntax.GetQuotedColumnName("level")); XmlElement last = null; diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs index 256ef36c05..b318223ca7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IRepositoryVersionable.cs @@ -74,8 +74,9 @@ namespace Umbraco.Core.Persistence.Repositories /// Path starts with /// Page number /// Page size + /// /// Total records the query would return without paging /// A paged enumerable of XML entries of content items - IEnumerable GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords); + IEnumerable GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, string[] orderBy, out long totalRecords); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index df15ab049c..19f4b8fdfb 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -139,24 +139,34 @@ namespace Umbraco.Core.Persistence.Repositories #endregion /// - /// Gets paged content descendants as XML by path + /// Gets paged document descendants as XML by path /// /// Path starts with /// Page number /// Page size + /// /// Total records the query would return without paging /// A paged enumerable of XML entries of content items - public IEnumerable GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, out long totalRecords) + public virtual IEnumerable GetPagedXmlEntriesByPath(string path, long pageIndex, int pageSize, string[] orderBy, out long totalRecords) { - Sql query; + var query = new Sql().Select(string.Format("umbracoNode.id, cmsContentXml.{0}", SqlSyntax.GetQuotedColumnName("xml"))) + .From("umbracoNode") + .InnerJoin("cmsContentXml").On("cmsContentXml.nodeId = umbracoNode.id"); + if (path == "-1") { - query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE nodeObjectType = @0)", NodeObjectTypeId).OrderBy("nodeId"); + query.Where("umbracoNode.nodeObjectType = @type", new { type = NodeObjectTypeId }); } else { - query = new Sql().Select("nodeId, xml").From("cmsContentXml").Where("nodeId IN (SELECT id FROM umbracoNode WHERE path LIKE (@0))", path.EnsureEndsWith(",%")).OrderBy("nodeId"); + query.Where(string.Format("umbracoNode.{0} LIKE (@0)", SqlSyntax.GetQuotedColumnName("path")), path.EnsureEndsWith(",%")); } + + //each order by param needs to be in a bracket! see: https://github.com/toptensoftware/PetaPoco/issues/177 + query.OrderBy(orderBy == null + ? "(umbracoNode.id)" + : string.Join(",", orderBy.Select(x => string.Format("({0})", SqlSyntax.GetQuotedColumnName(x))))); + var pagedResult = Database.Page(pageIndex + 1, pageSize, query); totalRecords = pagedResult.TotalItems; return pagedResult.Items.Select(dto => XElement.Parse(dto.Xml)); diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index fff0ac61c8..6f2007912f 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -1780,7 +1780,11 @@ namespace Umbraco.Core.Services var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateContentRepository(uow)) { - var contents = repository.GetPagedXmlEntriesByPath(path, pageIndex, pageSize, out totalRecords); + var contents = repository.GetPagedXmlEntriesByPath(path, pageIndex, pageSize, + //This order by is VERY important! This allows us to figure out what is implicitly not published, see ContentRepository.BuildXmlCache and + // UmbracoContentIndexer.PerformIndexAll which uses the logic based on this sort order + new[] {"level", "parentID", "sortOrder"}, + out totalRecords); return contents; } } diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index 50b323ed0a..9a3a30c8bd 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -1228,7 +1228,7 @@ namespace Umbraco.Core.Services var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateMediaRepository(uow)) { - var contents = repository.GetPagedXmlEntriesByPath(path, pageIndex, pageSize, out totalRecords); + var contents = repository.GetPagedXmlEntriesByPath(path, pageIndex, pageSize, null, out totalRecords); return contents; } } diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index cf833dcd28..70d45926fc 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -627,7 +627,7 @@ namespace Umbraco.Core.Services var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateMemberRepository(uow)) { - var contents = repository.GetPagedXmlEntriesByPath("-1", pageIndex, pageSize, out totalRecords); + var contents = repository.GetPagedXmlEntriesByPath("-1", pageIndex, pageSize, null, out totalRecords); return contents; } } diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index c2dbecf83d..32fa2607ac 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -399,13 +399,58 @@ namespace UmbracoExamine if (SupportUnpublishedContent == false && DisableXmlDocumentLookup == false) { + //get all node Ids that have a published version - this is a fail safe check, in theory + // only document nodes that have a published version would exist in the cmsContentXml table + var allNodesWithPublishedVersions = ApplicationContext.Current.DatabaseContext.Database.Fetch( + "select DISTINCT cmsDocument.nodeId from cmsDocument where cmsDocument.published = 1"); + + XElement last = null; + var trackedIds = new HashSet(); + ReindexWithXmlEntries(type, contentParentId, () => _contentTypeService.GetAllContentTypes().ToArray(), (path, pIndex, pSize) => { long totalContent; + + //sorted by: umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder var result = _contentService.GetPagedXmlEntries(path, pIndex, pSize, out totalContent).ToArray(); - return new Tuple(totalContent, result); + + //then like we do in the ContentRepository.BuildXmlCache we need to track what Parents have been processed + // already so that we can then exclude implicitly unpublished content items + var filtered = new List(); + + foreach (var xml in result) + { + var id = xml.AttributeValue("id"); + + //don't include this if it doesn't have a published version + if (allNodesWithPublishedVersions.Contains(id) == false) + continue; + + var parentId = xml.AttributeValue("parentID"); + + if (parentId == null) continue; //this shouldn't happen + + //if the parentid is changing + if (last != null && last.AttributeValue("parentID") != parentId) + { + var found = trackedIds.Contains(parentId); + if (found == false) + { + //Need to short circuit here, if the parent is not there it means that the parent is unpublished + // and therefore the child is not published either so cannot be included in the xml cache + continue; + } + } + + last = xml; + trackedIds.Add(xml.AttributeValue("id")); + + filtered.Add(xml); + } + + return new Tuple(totalContent, filtered.ToArray()); }, i => _contentService.GetById(i)); } From 4f596d20a9bbe2638d1252ace88c2d1136cb77d5 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 19 Jan 2017 13:13:49 +1100 Subject: [PATCH 282/599] refactors the fix for U4-9398 so it's not iterating multiple times over the descendants Enumerable --- src/UmbracoExamine/UmbracoContentIndexer.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index c4045936a7..c36ad37adb 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -460,36 +460,36 @@ namespace UmbracoExamine //currently this is not in use apart form in tests var notPublished = new HashSet(); - IContent[] content; int currentPageSize; do { long total; - IEnumerable descendants; + IContent[] descendants; if (SupportUnpublishedContent) { - descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total); + descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total).ToArray(); } else { //get all paged records but order by level ascending, we need to do this because we need to track which nodes are not published so that we can determine // which descendent nodes are implicitly not published - descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "level", Direction.Ascending, true, (string)null); + descendants = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, "level", Direction.Ascending, true, (string)null).ToArray(); } // need to store decendants count before filtering, in order for loop to work correctly - currentPageSize = descendants.Count(); + currentPageSize = descendants.Length; //if specific types are declared we need to post filter them //TODO: Update the service layer to join the cmsContentType table so we can query by content type too + IEnumerable content; if (IndexerData.IncludeNodeTypes.Any()) { - content = descendants.Where(x => IndexerData.IncludeNodeTypes.Contains(x.ContentType.Alias)).ToArray(); + content = descendants.Where(x => IndexerData.IncludeNodeTypes.Contains(x.ContentType.Alias)); } else { - content = descendants.ToArray(); + content = descendants; } AddNodesToIndex(GetSerializedContent( From c96e017e65b905d37e1ccc27eb632af1ffeaa89b Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 19 Jan 2017 14:00:33 +1100 Subject: [PATCH 283/599] Fixes PocoToSqlExpressionVisitor (oops), fixes examine tests by using the correct usings and NRT indexer/searcher to avoid waiting on thread timers --- .../Querying/ModelToSqlExpressionVisitor.cs | 1 + .../Querying/PocoToSqlExpressionVisitor.cs | 7 +- .../LegacyExamineBackedMediaTests.cs | 20 ++++-- .../PublishedContent/PublishedMediaTests.cs | 64 +++++++++++++------ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../UmbracoExamine/EventsTest.cs | 62 ++++++++---------- .../UmbracoExamine/IndexInitializer.cs | 25 ++++---- src/Umbraco.Tests/UmbracoExamine/IndexTest.cs | 55 ++++++++-------- .../UmbracoExamine/RandomIdRAMDirectory.cs | 14 ++++ .../UmbracoExamine/SearchTests.cs | 21 +++--- src/UmbracoExamine/BaseUmbracoIndexer.cs | 14 ++++ src/UmbracoExamine/UmbracoContentIndexer.cs | 29 +++++++++ src/UmbracoExamine/UmbracoExamineSearcher.cs | 61 ++++++++++-------- 13 files changed, 230 insertions(+), 144 deletions(-) create mode 100644 src/Umbraco.Tests/UmbracoExamine/RandomIdRAMDirectory.cs diff --git a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionVisitor.cs b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionVisitor.cs index b265a5b587..7f5e479af6 100644 --- a/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionVisitor.cs +++ b/src/Umbraco.Core/Persistence/Querying/ModelToSqlExpressionVisitor.cs @@ -20,6 +20,7 @@ namespace Umbraco.Core.Persistence.Querying _mapper = mapper; } + [Obsolete("Use the overload the specifies a SqlSyntaxProvider")] public ModelToSqlExpressionVisitor() : this(SqlSyntaxContext.SqlSyntaxProvider, MappingResolver.Current.ResolveMapperByType(typeof(T))) { } diff --git a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs index e0a4f07e48..4569b95853 100644 --- a/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs +++ b/src/Umbraco.Core/Persistence/Querying/PocoToSqlExpressionVisitor.cs @@ -18,14 +18,13 @@ namespace Umbraco.Core.Persistence.Querying public PocoToSqlExpressionVisitor(ISqlSyntaxProvider syntaxProvider) : base(syntaxProvider) { - + _pd = new Database.PocoData(typeof(T)); } [Obsolete("Use the overload the specifies a SqlSyntaxProvider")] public PocoToSqlExpressionVisitor() - : base(SqlSyntaxContext.SqlSyntaxProvider) - { - _pd = new Database.PocoData(typeof(T)); + : this(SqlSyntaxContext.SqlSyntaxProvider) + { } protected override string VisitMemberAccess(MemberExpression m) diff --git a/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs index 5bf6a4edc5..f48887498e 100644 --- a/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/LegacyExamineBackedMediaTests.cs @@ -1,6 +1,8 @@ using System; using System.Linq; +using Lucene.Net.Analysis.Standard; using Lucene.Net.Documents; +using Lucene.Net.Index; using Lucene.Net.Store; using Moq; using NUnit.Framework; @@ -31,12 +33,15 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Ensure_Children_Are_Sorted() { - using (var luceneDir = new RAMDirectory()) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { - var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); + //var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); indexer.RebuildIndex(); - var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); + //var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); var result = searcher.Search(searcher.CreateSearchCriteria().Id(1111).Compile()); Assert.IsNotNull(result); Assert.AreEqual(1, result.TotalItemCount); @@ -60,12 +65,15 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Ensure_Result_Has_All_Values() { - using (var luceneDir = new RAMDirectory()) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { - var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); + //var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); indexer.RebuildIndex(); - var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); + //var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); var result = searcher.Search(searcher.CreateSearchCriteria().Id(1111).Compile()); Assert.IsNotNull(result); Assert.AreEqual(1, result.TotalItemCount); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 6ec347cc0f..eadc08d834 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -27,6 +27,7 @@ using UmbracoExamine; using UmbracoExamine.DataServices; using umbraco.BusinessLogic; using System.Linq; +using Lucene.Net.Index; namespace Umbraco.Tests.PublishedContent { @@ -106,11 +107,14 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Ensure_Children_Sorted_With_Examine() { - using (var luceneDir = new RAMDirectory()) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { - var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); + //var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); indexer.RebuildIndex(); - var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); + //var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); var ctx = GetUmbracoContext("/test", 1234); var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx); @@ -135,11 +139,14 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Do_Not_Find_In_Recycle_Bin() { - using (var luceneDir = new RAMDirectory()) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { - var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); + //var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); indexer.RebuildIndex(); - var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); + //var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); var ctx = GetUmbracoContext("/test", 1234); var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx); @@ -175,11 +182,14 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Children_With_Examine() { - using (var luceneDir = new RAMDirectory()) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { - var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); + //var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); indexer.RebuildIndex(); - var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); + //var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); var ctx = GetUmbracoContext("/test", 1234); var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx); @@ -197,11 +207,14 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Descendants_With_Examine() { - using (var luceneDir = new RAMDirectory()) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { - var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); + //var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); indexer.RebuildIndex(); - var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); + //var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); var ctx = GetUmbracoContext("/test", 1234); var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx); @@ -219,11 +232,14 @@ namespace Umbraco.Tests.PublishedContent [Test] public void DescendantsOrSelf_With_Examine() { - using (var luceneDir = new RAMDirectory()) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { - var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); + //var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); indexer.RebuildIndex(); - var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); + //var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); var ctx = GetUmbracoContext("/test", 1234); var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx); @@ -241,12 +257,15 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Ancestors_With_Examine() { - using (var luceneDir = new RAMDirectory()) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { - var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); + //var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); indexer.RebuildIndex(); var ctx = GetUmbracoContext("/test", 1234); - var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); + //var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx); //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace @@ -260,12 +279,15 @@ namespace Umbraco.Tests.PublishedContent [Test] public void AncestorsOrSelf_With_Examine() { - using (var luceneDir = new RAMDirectory()) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { - var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); + //var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir); indexer.RebuildIndex(); var ctx = GetUmbracoContext("/test", 1234); - var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); + //var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); var cache = new ContextualPublishedMediaCache(new PublishedMediaCache(ctx.Application, searcher, indexer), ctx); //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 6e20740bcb..f0fb6f5921 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -164,6 +164,7 @@ + diff --git a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs index 3e7377f3b6..34c7a1f6ac 100644 --- a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs @@ -1,6 +1,8 @@ using System; using System.Linq; using Examine; +using Lucene.Net.Analysis.Standard; +using Lucene.Net.Index; using Lucene.Net.Store; using NUnit.Framework; using Umbraco.Tests.TestHelpers; @@ -15,53 +17,39 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Events_Ignoring_Node() { - //change the parent id so that they are all ignored - var existingCriteria = _indexer.IndexerData; - _indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes, - 999); //change to 999 + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) + { + //change the parent id so that they are all ignored + var existingCriteria = indexer.IndexerData; + indexer.IndexerData = new IndexCriteria(existingCriteria.StandardFields, existingCriteria.UserFields, existingCriteria.IncludeNodeTypes, existingCriteria.ExcludeNodeTypes, + 999); //change to 999 - var isIgnored = false; + var isIgnored = false; - EventHandler ignoringNode = (s, e) => - { - isIgnored = true; - }; + EventHandler ignoringNode = (s, e) => + { + isIgnored = true; + }; - _indexer.IgnoringNode += ignoringNode; + indexer.IgnoringNode += ignoringNode; - //get a node from the data repo - var node = _contentService.GetPublishedContentByXPath("//*[string-length(@id)>0 and number(@id)>0]") - .Root - .Elements() - .First(); + //get a node from the data repo + var node = _contentService.GetPublishedContentByXPath("//*[string-length(@id)>0 and number(@id)>0]") + .Root + .Elements() + .First(); - _indexer.ReIndexNode(node, IndexTypes.Content); + indexer.ReIndexNode(node, IndexTypes.Content); - Assert.IsTrue(isIgnored); - + Assert.IsTrue(isIgnored); + } } private readonly TestContentService _contentService = new TestContentService(); - private static UmbracoExamineSearcher _searcher; - private static UmbracoContentIndexer _indexer; - private Lucene.Net.Store.Directory _luceneDir; - - public override void Initialize() - { - base.Initialize(); - - _luceneDir = new RAMDirectory(); - _indexer = IndexInitializer.GetUmbracoIndexer(_luceneDir); - _indexer.RebuildIndex(); - _searcher = IndexInitializer.GetUmbracoSearcher(_luceneDir); - } - - public override void TearDown() - { - base.TearDown(); - _luceneDir.Dispose(); - } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index f57b1af213..c7922b9d58 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -7,6 +7,7 @@ using Examine.LuceneEngine.Config; using Examine.LuceneEngine.Providers; using Lucene.Net.Analysis; using Lucene.Net.Analysis.Standard; +using Lucene.Net.Index; using Lucene.Net.Store; using Moq; using Umbraco.Core; @@ -33,7 +34,7 @@ namespace Umbraco.Tests.UmbracoExamine internal static class IndexInitializer { public static UmbracoContentIndexer GetUmbracoIndexer( - Directory luceneDir, + IndexWriter writer, Analyzer analyzer = null, IDataService dataService = null, IContentService contentService = null, @@ -178,15 +179,14 @@ namespace Umbraco.Tests.UmbracoExamine var indexCriteria = indexSet.ToIndexCriteria(dataService, UmbracoContentIndexer.IndexFieldPolicies); var i = new UmbracoContentIndexer(indexCriteria, - luceneDir, //custom lucene directory - dataService, - contentService, - mediaService, - dataTypeService, - userService, - contentTypeService, - analyzer, - false) + writer, + dataService, + contentService, + mediaService, + dataTypeService, + userService, + contentTypeService, + false) { SupportUnpublishedContent = supportUnpublishedContent }; @@ -197,13 +197,14 @@ namespace Umbraco.Tests.UmbracoExamine return i; } - public static UmbracoExamineSearcher GetUmbracoSearcher(Directory luceneDir, Analyzer analyzer = null) + + public static UmbracoExamineSearcher GetUmbracoSearcher(IndexWriter writer, Analyzer analyzer = null) { if (analyzer == null) { analyzer = new StandardAnalyzer(Version.LUCENE_29); } - return new UmbracoExamineSearcher(luceneDir, analyzer); + return new UmbracoExamineSearcher(writer, analyzer); } public static LuceneSearcher GetLuceneSearcher(Directory luceneDir) diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index 5f3f5525c1..3f324bb2ed 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -1,11 +1,11 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Examine; using Examine.LuceneEngine; using Examine.LuceneEngine.Providers; using Examine.LuceneEngine.SearchCriteria; using Examine.SearchCriteria; +using Lucene.Net.Analysis.Standard; using Lucene.Net.Index; using Lucene.Net.Search; using Lucene.Net.Store; @@ -15,7 +15,6 @@ using UmbracoExamine; namespace Umbraco.Tests.UmbracoExamine { - /// /// Tests the standard indexing capabilities /// @@ -30,9 +29,10 @@ namespace Umbraco.Tests.UmbracoExamine public void Index_Protected_Content_Not_Indexed() { - using (var luceneDir = new RAMDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir)) - using (var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir)) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { indexer.RebuildIndex(); @@ -59,9 +59,10 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID() { - using (var luceneDir = new RAMDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir)) - using (var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir)) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { indexer.RebuildIndex(); @@ -77,7 +78,7 @@ namespace Umbraco.Tests.UmbracoExamine //ensure that node 2112 doesn't exist var results = searcher.Search(searcher.CreateSearchCriteria().Id(2112).Compile()); - Assert.AreEqual(0, results.Count()); + Assert.AreEqual(0, results.TotalItemCount); //get a node from the data repo (this one exists underneath 2222) var node = mediaService.GetLatestMediaByXpath("//*[string-length(@id)>0 and number(@id)>0]") @@ -103,19 +104,19 @@ namespace Umbraco.Tests.UmbracoExamine //now ensure it's deleted var newResults = searcher.Search(searcher.CreateSearchCriteria().Id(2112).Compile()); - Assert.AreEqual(1, newResults.Count()); + Assert.AreEqual(1, newResults.TotalItemCount); } } [Test] - [Ignore] public void Index_Move_Media_To_Non_Indexable_ParentID() { - using (var luceneDir = new RAMDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir)) - using (var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir)) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { indexer.RebuildIndex(); @@ -152,7 +153,7 @@ namespace Umbraco.Tests.UmbracoExamine //now ensure it's deleted var results = searcher.Search(searcher.CreateSearchCriteria().Id(2112).Compile()); - Assert.AreEqual(0, results.Count()); + Assert.AreEqual(0, results.TotalItemCount); } } @@ -164,9 +165,10 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Reindex_Content() { - using (var luceneDir = new RAMDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir, supportUnpublishedContent:true)) - using (var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir)) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer, supportUnpublishedContent: true)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { indexer.RebuildIndex(); @@ -175,10 +177,9 @@ namespace Umbraco.Tests.UmbracoExamine //first delete all 'Content' (not media). This is done by directly manipulating the index with the Lucene API, not examine! var contentTerm = new Term(LuceneIndexer.IndexTypeFieldName, IndexTypes.Content); - var writer = indexer.GetIndexWriter(); writer.DeleteDocuments(contentTerm); writer.Commit(); - + //make sure the content is gone. This is done with lucene APIs, not examine! var collector = new AllHitsCollector(false, true); var query = new TermQuery(contentTerm); @@ -205,13 +206,13 @@ namespace Umbraco.Tests.UmbracoExamine /// This will delete an item from the index and ensure that all children of the node are deleted too! /// [Test] - [Ignore] public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed() { - using (var luceneDir = new RAMDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir)) - using (var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir)) + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) { indexer.RebuildIndex(); @@ -221,10 +222,10 @@ namespace Umbraco.Tests.UmbracoExamine //this node had children: 1141 & 1142, let's ensure they are also removed var results = searcher.Search(searcher.CreateSearchCriteria().Id(1141).Compile()); - Assert.AreEqual(0, results.Count()); + Assert.AreEqual(0, results.TotalItemCount); results = searcher.Search(searcher.CreateSearchCriteria().Id(1142).Compile()); - Assert.AreEqual(0, results.Count()); + Assert.AreEqual(0, results.TotalItemCount); } } diff --git a/src/Umbraco.Tests/UmbracoExamine/RandomIdRAMDirectory.cs b/src/Umbraco.Tests/UmbracoExamine/RandomIdRAMDirectory.cs new file mode 100644 index 0000000000..34b69274c8 --- /dev/null +++ b/src/Umbraco.Tests/UmbracoExamine/RandomIdRAMDirectory.cs @@ -0,0 +1,14 @@ +using System; +using Lucene.Net.Store; + +namespace Umbraco.Tests.UmbracoExamine +{ + public class RandomIdRAMDirectory : RAMDirectory + { + private readonly string _lockId = Guid.NewGuid().ToString(); + public override string GetLockID() + { + return _lockId; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index 7eb92ad49d..f739286f98 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -8,6 +8,8 @@ using Examine.LuceneEngine.Providers; using Lucene.Net.Store; using NUnit.Framework; using Examine.LuceneEngine.SearchCriteria; +using Lucene.Net.Analysis.Standard; +using Lucene.Net.Index; using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.UmbracoExamine @@ -20,16 +22,17 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Test_Sort_Order_Sorting() { - using (var luceneDir = new RAMDirectory()) - { - var indexer = IndexInitializer.GetUmbracoIndexer(luceneDir, null, + using (var luceneDir = new RandomIdRAMDirectory()) + using (var writer = new IndexWriter(luceneDir, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_29), IndexWriter.MaxFieldLength.LIMITED)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(writer, null, new TestDataService() - { - ContentService = new TestContentService(TestFiles.umbraco_sort) - }, - supportUnpublishedContent:true); - indexer.RebuildIndex(); - var searcher = IndexInitializer.GetUmbracoSearcher(luceneDir); + { + ContentService = new TestContentService(TestFiles.umbraco_sort) + }, + supportUnpublishedContent: true)) + using (var searcher = IndexInitializer.GetUmbracoSearcher(writer)) + { + indexer.RebuildIndex(); var s = (LuceneSearcher)searcher; var luceneSearcher = s.GetSearcher(); diff --git a/src/UmbracoExamine/BaseUmbracoIndexer.cs b/src/UmbracoExamine/BaseUmbracoIndexer.cs index 9ff68b8685..ed9271c1c0 100644 --- a/src/UmbracoExamine/BaseUmbracoIndexer.cs +++ b/src/UmbracoExamine/BaseUmbracoIndexer.cs @@ -49,6 +49,7 @@ namespace UmbracoExamine /// /// /// + /// protected BaseUmbracoIndexer(IIndexCriteria indexerData, DirectoryInfo indexPath, IDataService dataService, Analyzer analyzer, bool async) : base(indexerData, indexPath, analyzer, async) { @@ -61,6 +62,19 @@ namespace UmbracoExamine DataService = dataService; } + /// + /// Creates an NRT indexer + /// + /// + /// + /// + /// + protected BaseUmbracoIndexer(IIndexCriteria indexerData, IndexWriter writer, IDataService dataService, bool async) + : base(indexerData, writer, async) + { + DataService = dataService; + } + #endregion /// diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index c36ad37adb..f7e4c45f50 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -17,6 +17,7 @@ using Examine.LuceneEngine; using Examine.LuceneEngine.Config; using UmbracoExamine.Config; using Lucene.Net.Analysis; +using Lucene.Net.Index; using Umbraco.Core.Persistence.Querying; using IContentService = Umbraco.Core.Services.IContentService; using IMediaService = Umbraco.Core.Services.IMediaService; @@ -146,6 +147,34 @@ namespace UmbracoExamine _contentTypeService = contentTypeService; } + /// + /// Creates an NRT indexer + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public UmbracoContentIndexer(IIndexCriteria indexerData, IndexWriter writer, IDataService dataService, + IContentService contentService, + IMediaService mediaService, + IDataTypeService dataTypeService, + IUserService userService, + IContentTypeService contentTypeService, + bool async) + : base(indexerData, writer, dataService, async) + { + _contentService = contentService; + _mediaService = mediaService; + _dataTypeService = dataTypeService; + _userService = userService; + _contentTypeService = contentTypeService; + } + #endregion #region Constants & Fields diff --git a/src/UmbracoExamine/UmbracoExamineSearcher.cs b/src/UmbracoExamine/UmbracoExamineSearcher.cs index 7a2a80c3fe..26b02c904e 100644 --- a/src/UmbracoExamine/UmbracoExamineSearcher.cs +++ b/src/UmbracoExamine/UmbracoExamineSearcher.cs @@ -51,12 +51,41 @@ namespace UmbracoExamine /// public override string Name { - get - { - return _name; - } + get { return _name; } + } + + /// + /// Constructor to allow for creating an indexer at runtime + /// + /// + /// + + public UmbracoExamineSearcher(DirectoryInfo indexPath, Analyzer analyzer) + : base(indexPath, analyzer) + { } + /// + /// Constructor to allow for creating an indexer at runtime + /// + /// + /// + public UmbracoExamineSearcher(Lucene.Net.Store.Directory luceneDirectory, Analyzer analyzer) + : base(luceneDirectory, analyzer) + { + } + + /// + /// Creates an NRT searcher + /// + /// + /// + public UmbracoExamineSearcher(IndexWriter writer, Analyzer analyzer) + : base(writer, analyzer) + { + } + + #endregion public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { @@ -115,30 +144,6 @@ namespace UmbracoExamine } } - /// - /// Constructor to allow for creating an indexer at runtime - /// - /// - /// - - public UmbracoExamineSearcher(DirectoryInfo indexPath, Analyzer analyzer) - : base(indexPath, analyzer) - { - } - - /// - /// Constructor to allow for creating an indexer at runtime - /// - /// - /// - - public UmbracoExamineSearcher(Lucene.Net.Store.Directory luceneDirectory, Analyzer analyzer) - : base(luceneDirectory, analyzer) - { - } - - #endregion - /// /// Used for unit tests /// From 994f91e4967f6bfc7de9eafb62f4ae9f4a9b2d82 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 19 Jan 2017 15:37:17 +1100 Subject: [PATCH 284/599] Creates IScopeUnitOfWork and IScopeUnitOfWorkProvider as internal along with ScopeRepositoryService which all services inherit from, this is maintaining backwards compat too --- src/Umbraco.Core/Persistence/LockingRepository.cs | 12 ++++++------ .../Persistence/UnitOfWork/IDatabaseUnitOfWork.cs | 5 +++++ .../UnitOfWork/IDatabaseUnitOfWorkProvider.cs | 11 +++++++++++ .../Persistence/UnitOfWork/PetaPocoUnitOfWork.cs | 5 +++-- .../UnitOfWork/PetaPocoUnitOfWorkProvider.cs | 6 ++++-- src/Umbraco.Core/Services/AuditService.cs | 2 +- src/Umbraco.Core/Services/ContentService.cs | 2 +- src/Umbraco.Core/Services/ContentTypeServiceBase.cs | 2 +- src/Umbraco.Core/Services/DataTypeService.cs | 2 +- src/Umbraco.Core/Services/DomainService.cs | 2 +- src/Umbraco.Core/Services/EntityService.cs | 2 +- src/Umbraco.Core/Services/ExternalLoginService.cs | 2 +- src/Umbraco.Core/Services/FileService.cs | 2 +- src/Umbraco.Core/Services/LocalizationService.cs | 2 +- src/Umbraco.Core/Services/MacroService.cs | 2 +- src/Umbraco.Core/Services/MediaService.cs | 2 +- src/Umbraco.Core/Services/MemberGroupService.cs | 2 +- src/Umbraco.Core/Services/MemberService.cs | 2 +- src/Umbraco.Core/Services/MigrationEntryService.cs | 2 +- src/Umbraco.Core/Services/PublicAccessService.cs | 2 +- src/Umbraco.Core/Services/RedirectUrlService.cs | 2 +- src/Umbraco.Core/Services/RelationService.cs | 2 +- src/Umbraco.Core/Services/RepositoryService.cs | 13 +++++++++++++ .../Services/ServerRegistrationService.cs | 8 ++++---- src/Umbraco.Core/Services/TagService.cs | 2 +- src/Umbraco.Core/Services/TaskService.cs | 2 +- src/Umbraco.Core/Services/UserService.cs | 2 +- 27 files changed, 66 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Core/Persistence/LockingRepository.cs b/src/Umbraco.Core/Persistence/LockingRepository.cs index 09195f8ce6..992c8cb1ad 100644 --- a/src/Umbraco.Core/Persistence/LockingRepository.cs +++ b/src/Umbraco.Core/Persistence/LockingRepository.cs @@ -10,11 +10,11 @@ namespace Umbraco.Core.Persistence internal class LockingRepository where TRepository : IDisposable, IRepository { - private readonly IDatabaseUnitOfWorkProvider _uowProvider; + private readonly IScopeUnitOfWorkProvider _uowProvider; private readonly Func _repositoryFactory; private readonly int[] _readLockIds, _writeLockIds; - public LockingRepository(IDatabaseUnitOfWorkProvider uowProvider, Func repositoryFactory, + public LockingRepository(IScopeUnitOfWorkProvider uowProvider, Func repositoryFactory, IEnumerable readLockIds, IEnumerable writeLockIds) { Mandate.ParameterNotNull(uowProvider, "uowProvider"); @@ -28,7 +28,7 @@ namespace Umbraco.Core.Persistence public void WithReadLocked(Action> action, bool autoCommit = true) { - using (var uow = ((PetaPocoUnitOfWorkProvider) _uowProvider).GetUnitOfWork(IsolationLevel.RepeatableRead)) + using (var uow = _uowProvider.GetUnitOfWork(IsolationLevel.RepeatableRead)) { // getting the database creates a scope and a transaction // the scope is IsolationLevel.RepeatableRead (because UnitOfWork is) @@ -49,7 +49,7 @@ namespace Umbraco.Core.Persistence public TResult WithReadLocked(Func, TResult> func, bool autoCommit = true) { - using (var uow = ((PetaPocoUnitOfWorkProvider) _uowProvider).GetUnitOfWork(IsolationLevel.RepeatableRead)) + using (var uow = _uowProvider.GetUnitOfWork(IsolationLevel.RepeatableRead)) { // getting the database creates a scope and a transaction // the scope is IsolationLevel.RepeatableRead (because UnitOfWork is) @@ -71,7 +71,7 @@ namespace Umbraco.Core.Persistence public void WithWriteLocked(Action> action, bool autoCommit = true) { - using (var uow = ((PetaPocoUnitOfWorkProvider) _uowProvider).GetUnitOfWork(IsolationLevel.RepeatableRead)) + using (var uow = _uowProvider.GetUnitOfWork(IsolationLevel.RepeatableRead)) { // getting the database creates a scope and a transaction // the scope is IsolationLevel.RepeatableRead (because UnitOfWork is) @@ -92,7 +92,7 @@ namespace Umbraco.Core.Persistence public TResult WithWriteLocked(Func, TResult> func, bool autoCommit = true) { - using (var uow = ((PetaPocoUnitOfWorkProvider) _uowProvider).GetUnitOfWork(IsolationLevel.RepeatableRead)) + using (var uow = _uowProvider.GetUnitOfWork(IsolationLevel.RepeatableRead)) { // getting the database creates a scope and a transaction // the scope is IsolationLevel.RepeatableRead (because UnitOfWork is) diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWork.cs index 616009988f..e2e7fc982a 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWork.cs @@ -9,4 +9,9 @@ namespace Umbraco.Core.Persistence.UnitOfWork { UmbracoDatabase Database { get; } } + + internal interface IScopeUnitOfWork : IDatabaseUnitOfWork + { + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWorkProvider.cs index a63299d0a6..c6996df65f 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWorkProvider.cs @@ -1,3 +1,6 @@ +using System.Data; +using Umbraco.Core.Scoping; + namespace Umbraco.Core.Persistence.UnitOfWork { /// @@ -7,4 +10,12 @@ namespace Umbraco.Core.Persistence.UnitOfWork { IDatabaseUnitOfWork GetUnitOfWork(); } + + /// + /// Defines a Unit of Work Provider for working with + /// + internal interface IScopeUnitOfWorkProvider : IDatabaseUnitOfWorkProvider + { + IDatabaseUnitOfWork GetUnitOfWork(IsolationLevel isolationLevel); + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs index 9caf787ddc..2165ec8899 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs @@ -9,8 +9,8 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// /// Represents the Unit of Work implementation for PetaPoco /// - internal class PetaPocoUnitOfWork : DisposableObject, IDatabaseUnitOfWork - { + internal class PetaPocoUnitOfWork : DisposableObject, IScopeUnitOfWork + { private readonly Queue _operations = new Queue(); private readonly IsolationLevel _isolationLevel; private readonly IScopeProvider _scopeProvider; @@ -27,6 +27,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// Creates a new unit of work instance /// /// + /// /// /// This should normally not be used directly and should be created with the UnitOfWorkProvider /// diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs index 4d1b638016..72e05b3866 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs @@ -6,10 +6,12 @@ using Umbraco.Core.Scoping; namespace Umbraco.Core.Persistence.UnitOfWork { + + /// /// Represents a Unit of Work Provider for creating a /// - public class PetaPocoUnitOfWorkProvider : IDatabaseUnitOfWorkProvider + public class PetaPocoUnitOfWorkProvider : IScopeUnitOfWorkProvider { private readonly IScopeProvider _scopeProvider; @@ -45,7 +47,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork { } /// - /// Constructor accepting an IDatabaseFactory instance + /// Constructor accepting a instance /// /// public PetaPocoUnitOfWorkProvider(IScopeProvider scopeProvider) diff --git a/src/Umbraco.Core/Services/AuditService.cs b/src/Umbraco.Core/Services/AuditService.cs index 3e076084f2..02f7d03426 100644 --- a/src/Umbraco.Core/Services/AuditService.cs +++ b/src/Umbraco.Core/Services/AuditService.cs @@ -7,7 +7,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - public sealed class AuditService : RepositoryService, IAuditService + public sealed class AuditService : ScopeRepositoryService, IAuditService { public AuditService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) : base(provider, repositoryFactory, logger, eventMessagesFactory) diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index e0edaa38c4..5751bba542 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -29,7 +29,7 @@ namespace Umbraco.Core.Services /// /// Represents the Content Service, which is an easy access to operations involving /// - public class ContentService : RepositoryService, IContentService, IContentServiceOperations + public class ContentService : ScopeRepositoryService, IContentService, IContentServiceOperations { private readonly IPublishingStrategy _publishingStrategy; private readonly EntityXmlSerializer _entitySerializer = new EntityXmlSerializer(); diff --git a/src/Umbraco.Core/Services/ContentTypeServiceBase.cs b/src/Umbraco.Core/Services/ContentTypeServiceBase.cs index df29012b90..41802e74ae 100644 --- a/src/Umbraco.Core/Services/ContentTypeServiceBase.cs +++ b/src/Umbraco.Core/Services/ContentTypeServiceBase.cs @@ -9,7 +9,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - public class ContentTypeServiceBase : RepositoryService + public class ContentTypeServiceBase : ScopeRepositoryService { public ContentTypeServiceBase(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) : base(provider, repositoryFactory, logger, eventMessagesFactory) diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index 2cdcfc76d5..801869d078 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Services /// /// Represents the DataType Service, which is an easy access to operations involving /// - public class DataTypeService : RepositoryService, IDataTypeService + public class DataTypeService : ScopeRepositoryService, IDataTypeService { public DataTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) diff --git a/src/Umbraco.Core/Services/DomainService.cs b/src/Umbraco.Core/Services/DomainService.cs index 3ffcb92778..cd96ce08a8 100644 --- a/src/Umbraco.Core/Services/DomainService.cs +++ b/src/Umbraco.Core/Services/DomainService.cs @@ -10,7 +10,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - public class DomainService : RepositoryService, IDomainService + public class DomainService : ScopeRepositoryService, IDomainService { public DomainService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) : base(provider, repositoryFactory, logger, eventMessagesFactory) diff --git a/src/Umbraco.Core/Services/EntityService.cs b/src/Umbraco.Core/Services/EntityService.cs index 08b801229e..1e970d61a9 100644 --- a/src/Umbraco.Core/Services/EntityService.cs +++ b/src/Umbraco.Core/Services/EntityService.cs @@ -14,7 +14,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - public class EntityService : RepositoryService, IEntityService + public class EntityService : ScopeRepositoryService, IEntityService { private readonly IRuntimeCacheProvider _runtimeCache; private readonly Dictionary>> _supportedObjectTypes; diff --git a/src/Umbraco.Core/Services/ExternalLoginService.cs b/src/Umbraco.Core/Services/ExternalLoginService.cs index 91ca77872d..0e0604e89a 100644 --- a/src/Umbraco.Core/Services/ExternalLoginService.cs +++ b/src/Umbraco.Core/Services/ExternalLoginService.cs @@ -10,7 +10,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - public class ExternalLoginService : RepositoryService, IExternalLoginService + public class ExternalLoginService : ScopeRepositoryService, IExternalLoginService { public ExternalLoginService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) : base(provider, repositoryFactory, logger, eventMessagesFactory) diff --git a/src/Umbraco.Core/Services/FileService.cs b/src/Umbraco.Core/Services/FileService.cs index 6360ad8988..a8ad4b4885 100644 --- a/src/Umbraco.Core/Services/FileService.cs +++ b/src/Umbraco.Core/Services/FileService.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Services /// /// Represents the File Service, which is an easy access to operations involving objects like Scripts, Stylesheets and Templates /// - public class FileService : RepositoryService, IFileService + public class FileService : ScopeRepositoryService, IFileService { private readonly IUnitOfWorkProvider _fileUowProvider; diff --git a/src/Umbraco.Core/Services/LocalizationService.cs b/src/Umbraco.Core/Services/LocalizationService.cs index 261db8672b..6f368b69f0 100644 --- a/src/Umbraco.Core/Services/LocalizationService.cs +++ b/src/Umbraco.Core/Services/LocalizationService.cs @@ -16,7 +16,7 @@ namespace Umbraco.Core.Services /// /// Represents the Localization Service, which is an easy access to operations involving and /// - public class LocalizationService : RepositoryService, ILocalizationService + public class LocalizationService : ScopeRepositoryService, ILocalizationService { diff --git a/src/Umbraco.Core/Services/MacroService.cs b/src/Umbraco.Core/Services/MacroService.cs index a6de12ed32..cf0c364a37 100644 --- a/src/Umbraco.Core/Services/MacroService.cs +++ b/src/Umbraco.Core/Services/MacroService.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Services /// /// Represents the Macro Service, which is an easy access to operations involving /// - public class MacroService : RepositoryService, IMacroService + public class MacroService : ScopeRepositoryService, IMacroService { public MacroService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index f582ef1eaa..b6ab4e60e6 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -25,7 +25,7 @@ namespace Umbraco.Core.Services /// /// Represents the Media Service, which is an easy access to operations involving /// - public class MediaService : RepositoryService, IMediaService, IMediaServiceOperations + public class MediaService : ScopeRepositoryService, IMediaService, IMediaServiceOperations { //Support recursive locks because some of the methods that require locking call other methods that require locking. diff --git a/src/Umbraco.Core/Services/MemberGroupService.cs b/src/Umbraco.Core/Services/MemberGroupService.cs index 47b62917fc..87251c5d23 100644 --- a/src/Umbraco.Core/Services/MemberGroupService.cs +++ b/src/Umbraco.Core/Services/MemberGroupService.cs @@ -11,7 +11,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - public class MemberGroupService : RepositoryService, IMemberGroupService + public class MemberGroupService : ScopeRepositoryService, IMemberGroupService { public MemberGroupService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index 094539d66e..237ecd6f27 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Services /// /// Represents the MemberService. /// - public class MemberService : RepositoryService, IMemberService + public class MemberService : ScopeRepositoryService, IMemberService { private readonly IMemberGroupService _memberGroupService; private readonly EntityXmlSerializer _entitySerializer = new EntityXmlSerializer(); diff --git a/src/Umbraco.Core/Services/MigrationEntryService.cs b/src/Umbraco.Core/Services/MigrationEntryService.cs index 2bd463c6d0..9a6b312ea2 100644 --- a/src/Umbraco.Core/Services/MigrationEntryService.cs +++ b/src/Umbraco.Core/Services/MigrationEntryService.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Services /// /// Manages migration entries in the database /// - public sealed class MigrationEntryService : RepositoryService, IMigrationEntryService + public sealed class MigrationEntryService : ScopeRepositoryService, IMigrationEntryService { public MigrationEntryService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) : base(provider, repositoryFactory, logger, eventMessagesFactory) diff --git a/src/Umbraco.Core/Services/PublicAccessService.cs b/src/Umbraco.Core/Services/PublicAccessService.cs index c2eb536bca..d203eef8dc 100644 --- a/src/Umbraco.Core/Services/PublicAccessService.cs +++ b/src/Umbraco.Core/Services/PublicAccessService.cs @@ -11,7 +11,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - public class PublicAccessService : RepositoryService, IPublicAccessService + public class PublicAccessService : ScopeRepositoryService, IPublicAccessService { public PublicAccessService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) : base(provider, repositoryFactory, logger, eventMessagesFactory) diff --git a/src/Umbraco.Core/Services/RedirectUrlService.cs b/src/Umbraco.Core/Services/RedirectUrlService.cs index 0283332401..4018fcc316 100644 --- a/src/Umbraco.Core/Services/RedirectUrlService.cs +++ b/src/Umbraco.Core/Services/RedirectUrlService.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - internal class RedirectUrlService : RepositoryService, IRedirectUrlService + internal class RedirectUrlService : ScopeRepositoryService, IRedirectUrlService { public RedirectUrlService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) : base(provider, repositoryFactory, logger, eventMessagesFactory) diff --git a/src/Umbraco.Core/Services/RelationService.cs b/src/Umbraco.Core/Services/RelationService.cs index 69f79e756e..51f7eb8b2a 100644 --- a/src/Umbraco.Core/Services/RelationService.cs +++ b/src/Umbraco.Core/Services/RelationService.cs @@ -11,7 +11,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - public class RelationService : RepositoryService, IRelationService + public class RelationService : ScopeRepositoryService, IRelationService { private readonly IEntityService _entityService; diff --git a/src/Umbraco.Core/Services/RepositoryService.cs b/src/Umbraco.Core/Services/RepositoryService.cs index 88807f0291..b1e0cf7d21 100644 --- a/src/Umbraco.Core/Services/RepositoryService.cs +++ b/src/Umbraco.Core/Services/RepositoryService.cs @@ -6,6 +6,19 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { + public abstract class ScopeRepositoryService : RepositoryService + { + protected ScopeRepositoryService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) + : base(provider, repositoryFactory, logger, eventMessagesFactory) + { + var scopeUow = provider as IScopeUnitOfWorkProvider; + if (scopeUow == null) throw new NotSupportedException("The provider type passed in: " + provider.GetType() + " is not of type " + typeof(IScopeUnitOfWorkProvider)); + UowProvider = scopeUow; + } + + internal new IScopeUnitOfWorkProvider UowProvider { get; private set; } + } + /// /// Base service class /// diff --git a/src/Umbraco.Core/Services/ServerRegistrationService.cs b/src/Umbraco.Core/Services/ServerRegistrationService.cs index b2d5ca855e..9e28f9a91c 100644 --- a/src/Umbraco.Core/Services/ServerRegistrationService.cs +++ b/src/Umbraco.Core/Services/ServerRegistrationService.cs @@ -15,9 +15,9 @@ namespace Umbraco.Core.Services /// /// Manages server registrations in the database. /// - public sealed class ServerRegistrationService : RepositoryService, IServerRegistrationService + public sealed class ServerRegistrationService : ScopeRepositoryService, IServerRegistrationService { - private readonly static string CurrentServerIdentityValue = NetworkHelper.MachineName // eg DOMAIN\SERVER + private static readonly string CurrentServerIdentityValue = NetworkHelper.MachineName // eg DOMAIN\SERVER + "/" + HttpRuntime.AppDomainAppId; // eg /LM/S3SVC/11/ROOT private static readonly int[] LockingRepositoryIds = { Constants.Locks.Servers }; @@ -33,11 +33,11 @@ namespace Umbraco.Core.Services /// public ServerRegistrationService(IDatabaseUnitOfWorkProvider uowProvider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) : base(uowProvider, repositoryFactory, logger, eventMessagesFactory) - { + { _lrepo = new LockingRepository(UowProvider, x => RepositoryFactory.CreateServerRegistrationRepository(x), LockingRepositoryIds, LockingRepositoryIds); - } + } /// /// Touches a server to mark it as active; deactivate stale servers. diff --git a/src/Umbraco.Core/Services/TagService.cs b/src/Umbraco.Core/Services/TagService.cs index d3b502e14b..52f127b0a0 100644 --- a/src/Umbraco.Core/Services/TagService.cs +++ b/src/Umbraco.Core/Services/TagService.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Services /// /// If there is unpublished content with tags, those tags will not be contained /// - public class TagService : RepositoryService, ITagService + public class TagService : ScopeRepositoryService, ITagService { public TagService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) diff --git a/src/Umbraco.Core/Services/TaskService.cs b/src/Umbraco.Core/Services/TaskService.cs index 9845c5ebb9..450ab13ccf 100644 --- a/src/Umbraco.Core/Services/TaskService.cs +++ b/src/Umbraco.Core/Services/TaskService.cs @@ -10,7 +10,7 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - public class TaskService : RepositoryService, ITaskService + public class TaskService : ScopeRepositoryService, ITaskService { public TaskService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) : base(provider, repositoryFactory, logger, eventMessagesFactory) diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 8e984d1e5d..beea62acb7 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Services /// /// Represents the UserService, which is an easy access to operations involving , and eventually Backoffice Users. /// - public class UserService : RepositoryService, IUserService + public class UserService : ScopeRepositoryService, IUserService { //TODO: We need to change the isUpgrading flag to use an app state enum as described here: http://issues.umbraco.org/issue/U4-6816 From 4f5347fa4d372a3286bb88ce57f77f8305995509 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 19 Jan 2017 16:31:59 +1100 Subject: [PATCH 285/599] Creates IEventManager, IEventDefinition uses explicit implementation and 'new' keyword to force already existing uow providers to be IScopeUnitOfWork, creates extensions for the event manager so there's less work to do to wire it all up --- src/Umbraco.Core/ApplicationContext.cs | 2 +- src/Umbraco.Core/Events/EventDefinition.cs | 70 +++++++++++++++++++ .../Events/EventDefinitionBase.cs | 14 ++++ src/Umbraco.Core/Events/EventExtensions.cs | 40 ++++++++++- .../{MessageType.cs => EventMessageType.cs} | 0 src/Umbraco.Core/Events/IEventDefinition.cs | 11 +++ src/Umbraco.Core/Events/IEventManager.cs | 18 +++++ .../Events/NoScopedEventManager.cs | 34 +++++++++ src/Umbraco.Core/Events/ScopedEventManager.cs | 35 ++++++++++ src/Umbraco.Core/Events/TypedEventHandler.cs | 2 +- .../UnitOfWork/IDatabaseUnitOfWork.cs | 5 -- .../UnitOfWork/IDatabaseUnitOfWorkProvider.cs | 11 --- .../UnitOfWork/IScopeUnitOfWork.cs | 9 +++ .../UnitOfWork/IScopeUnitOfWorkProvider.cs | 14 ++++ .../UnitOfWork/PetaPocoUnitOfWork.cs | 8 ++- .../UnitOfWork/PetaPocoUnitOfWorkProvider.cs | 9 ++- src/Umbraco.Core/Scoping/IScope.cs | 5 ++ src/Umbraco.Core/Scoping/NoScope.cs | 15 +++- src/Umbraco.Core/Scoping/Scope.cs | 12 +++- src/Umbraco.Core/Umbraco.Core.csproj | 10 ++- 20 files changed, 298 insertions(+), 26 deletions(-) create mode 100644 src/Umbraco.Core/Events/EventDefinition.cs create mode 100644 src/Umbraco.Core/Events/EventDefinitionBase.cs rename src/Umbraco.Core/Events/{MessageType.cs => EventMessageType.cs} (100%) create mode 100644 src/Umbraco.Core/Events/IEventDefinition.cs create mode 100644 src/Umbraco.Core/Events/IEventManager.cs create mode 100644 src/Umbraco.Core/Events/NoScopedEventManager.cs create mode 100644 src/Umbraco.Core/Events/ScopedEventManager.cs create mode 100644 src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWork.cs create mode 100644 src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWorkProvider.cs diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index 4be0787a31..07d4508279 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -164,7 +164,7 @@ namespace Umbraco.Core public static ApplicationContext Current { get; internal set; } // fixme - public IScopeProvider ScopeProvider { get { return DatabaseContext.ScopeProvider; } } + internal IScopeProvider ScopeProvider { get { return DatabaseContext.ScopeProvider; } } /// /// Returns the application wide cache accessor diff --git a/src/Umbraco.Core/Events/EventDefinition.cs b/src/Umbraco.Core/Events/EventDefinition.cs new file mode 100644 index 0000000000..e7da58f0b5 --- /dev/null +++ b/src/Umbraco.Core/Events/EventDefinition.cs @@ -0,0 +1,70 @@ +using System; + +namespace Umbraco.Core.Events +{ + internal class EventDefinition : EventDefinitionBase + { + private readonly EventHandler _trackedEvent; + private readonly object _sender; + private readonly EventArgs _args; + + public EventDefinition(EventHandler trackedEvent, object sender, EventArgs args) + { + _trackedEvent = trackedEvent; + _sender = sender; + _args = args; + } + + public override void RaiseEvent() + { + if (_trackedEvent != null) + { + _trackedEvent(_sender, _args); + } + } + } + + internal class EventDefinition : EventDefinitionBase + { + private readonly EventHandler _trackedEvent; + private readonly object _sender; + private readonly TEventArgs _args; + + public EventDefinition(EventHandler trackedEvent, object sender, TEventArgs args) + { + _trackedEvent = trackedEvent; + _sender = sender; + _args = args; + } + + public override void RaiseEvent() + { + if (_trackedEvent != null) + { + _trackedEvent(_sender, _args); + } + } + } + + internal class EventDefinition : EventDefinitionBase + { + private readonly TypedEventHandler _trackedEvent; + private readonly TSender _sender; + private readonly TEventArgs _args; + + public EventDefinition(TypedEventHandler trackedEvent, TSender sender, TEventArgs args) + { + _trackedEvent = trackedEvent; + _sender = sender; + _args = args; + } + + public override void RaiseEvent() + { + if (_trackedEvent != null) + { + _trackedEvent(_sender, _args); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Events/EventDefinitionBase.cs b/src/Umbraco.Core/Events/EventDefinitionBase.cs new file mode 100644 index 0000000000..4e33a6f6e6 --- /dev/null +++ b/src/Umbraco.Core/Events/EventDefinitionBase.cs @@ -0,0 +1,14 @@ +using System; + +namespace Umbraco.Core.Events +{ + public abstract class EventDefinitionBase : IEventDefinition + { + protected EventDefinitionBase() + { + EventId = Guid.NewGuid(); + } + public Guid EventId { get; private set; } + public abstract void RaiseEvent(); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Events/EventExtensions.cs b/src/Umbraco.Core/Events/EventExtensions.cs index bd66b605fd..b1ca0143ac 100644 --- a/src/Umbraco.Core/Events/EventExtensions.cs +++ b/src/Umbraco.Core/Events/EventExtensions.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; namespace Umbraco.Core.Events { @@ -30,7 +31,23 @@ namespace Umbraco.Core.Events return args.Cancel; } - + + internal static bool IsRaisedEventCancelled( + this TypedEventHandler eventHandler, + TArgs args, + TSender sender, + IEventManager eventManager) + where TArgs : CancellableEventArgs + { + if (eventManager.SupportsEventCancellation) + { + eventManager.TrackEvent(eventHandler, sender, args); + return args.Cancel; + } + + return false; + } + /// /// Raises the event /// @@ -49,8 +66,27 @@ namespace Umbraco.Core.Events eventHandler(sender, args); } + /// + /// Raises/tracks an event with the event manager + /// + /// + /// + /// + /// + /// + /// + internal static void RaiseEvent( + this TypedEventHandler eventHandler, + TArgs args, + TSender sender, + IEventManager eventManager) + where TArgs : EventArgs + { + eventManager.TrackEvent(eventHandler, sender, args); + } + // moves the last handler that was added to an instance event, to first position - public static void PromoteLastHandler(object sender, string eventName) + public static void PromoteLastHandler(object sender, string eventName) { var fieldInfo = sender.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic); if (fieldInfo == null) throw new InvalidOperationException("No event named " + eventName + "."); diff --git a/src/Umbraco.Core/Events/MessageType.cs b/src/Umbraco.Core/Events/EventMessageType.cs similarity index 100% rename from src/Umbraco.Core/Events/MessageType.cs rename to src/Umbraco.Core/Events/EventMessageType.cs diff --git a/src/Umbraco.Core/Events/IEventDefinition.cs b/src/Umbraco.Core/Events/IEventDefinition.cs new file mode 100644 index 0000000000..9d3a6426fa --- /dev/null +++ b/src/Umbraco.Core/Events/IEventDefinition.cs @@ -0,0 +1,11 @@ +using System; + +namespace Umbraco.Core.Events +{ + public interface IEventDefinition + { + Guid EventId { get; } + + void RaiseEvent(); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Events/IEventManager.cs b/src/Umbraco.Core/Events/IEventManager.cs new file mode 100644 index 0000000000..21339f07b1 --- /dev/null +++ b/src/Umbraco.Core/Events/IEventManager.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Events +{ + public interface IEventManager + { + IEnumerable GetEvents(); + void TrackEvent(EventHandler e, object sender, EventArgs args); + void TrackEvent(EventHandler e, object sender, TEventArgs args); + void TrackEvent(TypedEventHandler e, TSender sender, TEventArgs args); + + /// + /// True or false depending on if the event manager supports event cancellation + /// + bool SupportsEventCancellation { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Events/NoScopedEventManager.cs b/src/Umbraco.Core/Events/NoScopedEventManager.cs new file mode 100644 index 0000000000..35f964bf0a --- /dev/null +++ b/src/Umbraco.Core/Events/NoScopedEventManager.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Core.Events +{ + internal class NoScopedEventManager : IEventManager + { + public void TrackEvent(EventHandler e, object sender, EventArgs args) + { + if (e != null) e(sender, args); + } + + public void TrackEvent(EventHandler e, object sender, TEventArgs args) + { + if (e != null) e(sender, args); + } + + public void TrackEvent(TypedEventHandler e, TSender sender, TEventArgs args) + { + if (e != null) e(sender, args); + } + + public IEnumerable GetEvents() + { + return Enumerable.Empty(); + } + + public bool SupportsEventCancellation + { + get { return true; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Events/ScopedEventManager.cs b/src/Umbraco.Core/Events/ScopedEventManager.cs new file mode 100644 index 0000000000..01e2d5e23f --- /dev/null +++ b/src/Umbraco.Core/Events/ScopedEventManager.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Events +{ + internal class ScopedEventManager : IEventManager + { + public void TrackEvent(EventHandler e, object sender, EventArgs args) + { + _tracked.Add(new EventDefinition(e, sender, args)); + } + + public void TrackEvent(EventHandler e, object sender, TEventArgs args) + { + _tracked.Add(new EventDefinition(e, sender, args)); + } + + public void TrackEvent(TypedEventHandler e, TSender sender, TEventArgs args) + { + _tracked.Add(new EventDefinition(e, sender, args)); + } + + public IEnumerable GetEvents() + { + return _tracked; + } + + private readonly List _tracked = new List(); + + public bool SupportsEventCancellation + { + get { return false; } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Events/TypedEventHandler.cs b/src/Umbraco.Core/Events/TypedEventHandler.cs index a8170190d4..113bb82f7e 100644 --- a/src/Umbraco.Core/Events/TypedEventHandler.cs +++ b/src/Umbraco.Core/Events/TypedEventHandler.cs @@ -2,6 +2,6 @@ using System; namespace Umbraco.Core.Events { - [Serializable] + [Serializable] public delegate void TypedEventHandler(TSender sender, TEventArgs e); } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWork.cs index e2e7fc982a..616009988f 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWork.cs @@ -9,9 +9,4 @@ namespace Umbraco.Core.Persistence.UnitOfWork { UmbracoDatabase Database { get; } } - - internal interface IScopeUnitOfWork : IDatabaseUnitOfWork - { - - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWorkProvider.cs index c6996df65f..a63299d0a6 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/IDatabaseUnitOfWorkProvider.cs @@ -1,6 +1,3 @@ -using System.Data; -using Umbraco.Core.Scoping; - namespace Umbraco.Core.Persistence.UnitOfWork { /// @@ -10,12 +7,4 @@ namespace Umbraco.Core.Persistence.UnitOfWork { IDatabaseUnitOfWork GetUnitOfWork(); } - - /// - /// Defines a Unit of Work Provider for working with - /// - internal interface IScopeUnitOfWorkProvider : IDatabaseUnitOfWorkProvider - { - IDatabaseUnitOfWork GetUnitOfWork(IsolationLevel isolationLevel); - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWork.cs new file mode 100644 index 0000000000..6bc59da9ae --- /dev/null +++ b/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWork.cs @@ -0,0 +1,9 @@ +using Umbraco.Core.Events; + +namespace Umbraco.Core.Persistence.UnitOfWork +{ + internal interface IScopeUnitOfWork : IDatabaseUnitOfWork + { + IEventManager EventManager { get; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWorkProvider.cs new file mode 100644 index 0000000000..6cedc56c33 --- /dev/null +++ b/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWorkProvider.cs @@ -0,0 +1,14 @@ +using System.Data; +using Umbraco.Core.Scoping; + +namespace Umbraco.Core.Persistence.UnitOfWork +{ + /// + /// Defines a Unit of Work Provider for working with + /// + internal interface IScopeUnitOfWorkProvider : IDatabaseUnitOfWorkProvider + { + new IScopeUnitOfWork GetUnitOfWork(); + IScopeUnitOfWork GetUnitOfWork(IsolationLevel isolationLevel); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs index 2165ec8899..ac677732c8 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data; +using Umbraco.Core.Events; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Scoping; @@ -198,5 +199,10 @@ namespace Umbraco.Core.Persistence.UnitOfWork _scope.Dispose(); _scope = null; } - } + + public IEventManager EventManager + { + get { throw new NotImplementedException(); } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs index 72e05b3866..25f85a7ea2 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs @@ -72,7 +72,14 @@ namespace Umbraco.Core.Persistence.UnitOfWork return new PetaPocoUnitOfWork(_scopeProvider); } - public IDatabaseUnitOfWork GetUnitOfWork(IsolationLevel isolationLevel) + //explicit implementation + IScopeUnitOfWork IScopeUnitOfWorkProvider.GetUnitOfWork() + { + return new PetaPocoUnitOfWork(_scopeProvider); + } + + //explicit implementation + IScopeUnitOfWork IScopeUnitOfWorkProvider.GetUnitOfWork(IsolationLevel isolationLevel) { return new PetaPocoUnitOfWork(_scopeProvider, isolationLevel); } diff --git a/src/Umbraco.Core/Scoping/IScope.cs b/src/Umbraco.Core/Scoping/IScope.cs index 4960350575..077e7398b2 100644 --- a/src/Umbraco.Core/Scoping/IScope.cs +++ b/src/Umbraco.Core/Scoping/IScope.cs @@ -20,6 +20,11 @@ namespace Umbraco.Core.Scoping /// IList Messages { get; } + /// + /// Gets the event manager + /// + IEventManager EventManager { get; } + /// /// Completes the scope. /// diff --git a/src/Umbraco.Core/Scoping/NoScope.cs b/src/Umbraco.Core/Scoping/NoScope.cs index 5c53e89047..6b8407a8f0 100644 --- a/src/Umbraco.Core/Scoping/NoScope.cs +++ b/src/Umbraco.Core/Scoping/NoScope.cs @@ -14,6 +14,7 @@ namespace Umbraco.Core.Scoping private bool _disposed; private UmbracoDatabase _database; + private IEventManager _eventManager; private IList _messages; public NoScope(ScopeProvider scopeProvider) @@ -57,7 +58,7 @@ namespace Umbraco.Core.Scoping return _messages ?? (_messages = new List()); } } - + public IList MessagesOrNull { get @@ -67,6 +68,16 @@ namespace Umbraco.Core.Scoping } } + /// + public IEventManager EventManager + { + get + { + EnsureNotDisposed(); + return _eventManager ?? (_eventManager = new NoScopedEventManager()); + } + } + /// public void Complete() { @@ -97,6 +108,6 @@ namespace Umbraco.Core.Scoping _disposed = true; GC.SuppressFinalize(this); - } + } } } diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index 4979aa6ae1..ba0092b9cb 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -14,6 +14,7 @@ namespace Umbraco.Core.Scoping { private readonly ScopeProvider _scopeProvider; private readonly IsolationLevel _isolationLevel; + private IEventManager _eventManager; private bool _disposed; private bool? _completed; @@ -154,6 +155,15 @@ namespace Umbraco.Core.Scoping } } + public IEventManager EventManager + { + get + { + EnsureNotDisposed(); + return _eventManager ?? (_eventManager = new ScopedEventManager()); + } + } + /// public void Complete() { @@ -222,6 +232,6 @@ namespace Umbraco.Core.Scoping _database.Dispose(); _database = null; } - } + } } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index afa85609d0..cb4a157d18 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -312,14 +312,20 @@ + + + + + + - + @@ -503,6 +509,8 @@ + + From 27eeb383b41d15e3c839596e4d13e211e2bbc8d0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 19 Jan 2017 16:51:39 +1100 Subject: [PATCH 286/599] Updates all repos to use IScopeUnitOfWork --- .../Persistence/LockingRepository.cs | 4 +- .../Repositories/AuditRepository.cs | 2 +- .../Repositories/ContentPreviewRepository.cs | 2 +- .../Repositories/ContentRepository.cs | 2 +- .../Repositories/ContentTypeBaseRepository.cs | 2 +- .../Repositories/ContentTypeRepository.cs | 2 +- .../Repositories/ContentXmlRepository.cs | 2 +- .../DataTypeDefinitionRepository.cs | 4 +- .../Repositories/DictionaryRepository.cs | 6 +- .../Repositories/DomainRepository.cs | 2 +- .../Repositories/EntityContainerRepository.cs | 2 +- .../Repositories/ExternalLoginRepository.cs | 2 +- .../Repositories/LanguageRepository.cs | 2 +- .../Repositories/MacroRepository.cs | 2 +- .../Repositories/MediaRepository.cs | 2 +- .../Repositories/MediaTypeRepository.cs | 2 +- .../Repositories/MemberGroupRepository.cs | 8 +-- .../Repositories/MemberRepository.cs | 2 +- .../Repositories/MemberTypeRepository.cs | 2 +- .../Repositories/MigrationEntryRepository.cs | 2 +- .../Repositories/PetaPocoRepositoryBase.cs | 8 +-- .../Repositories/PublicAccessRepository.cs | 2 +- .../Repositories/RecycleBinRepository.cs | 2 +- .../Repositories/RedirectUrlRepository.cs | 2 +- .../Repositories/RelationRepository.cs | 2 +- .../Repositories/RelationTypeRepository.cs | 2 +- .../ServerRegistrationRepository.cs | 2 +- .../Repositories/SimpleGetRepository.cs | 2 +- .../Persistence/Repositories/TagRepository.cs | 2 +- .../Repositories/TaskRepository.cs | 2 +- .../Repositories/TaskTypeRepository.cs | 2 +- .../Repositories/TemplateRepository.cs | 2 +- .../Repositories/UserRepository.cs | 2 +- .../Repositories/UserTypeRepository.cs | 2 +- .../Repositories/VersionableRepositoryBase.cs | 2 +- .../Persistence/RepositoryFactory.cs | 58 +++++++++---------- .../UnitOfWork/IScopeUnitOfWork.cs | 2 +- .../UnitOfWork/IScopeUnitOfWorkProvider.cs | 2 +- .../UnitOfWork/PetaPocoUnitOfWorkProvider.cs | 26 +++------ src/Umbraco.Core/Services/PackagingService.cs | 4 +- .../Services/ServerRegistrationService.cs | 2 +- src/Umbraco.Core/Services/ServiceContext.cs | 4 +- .../Repositories/ContentRepositoryTest.cs | 6 +- .../Repositories/ContentTypeRepositoryTest.cs | 8 +-- .../DataTypeDefinitionRepositoryTest.cs | 4 +- .../Repositories/DictionaryRepositoryTest.cs | 2 +- .../Repositories/DomainRepositoryTest.cs | 2 +- .../Repositories/LanguageRepositoryTest.cs | 2 +- .../Repositories/LockedRepositoryTests.cs | 2 +- .../Repositories/MediaRepositoryTest.cs | 2 +- .../Repositories/MediaTypeRepositoryTest.cs | 4 +- .../Repositories/MemberRepositoryTest.cs | 2 +- .../Repositories/MemberTypeRepositoryTest.cs | 2 +- .../PublicAccessRepositoryTest.cs | 2 +- .../RedirectUrlRepositoryTests.cs | 2 +- .../Repositories/RelationRepositoryTest.cs | 2 +- .../RelationTypeRepositoryTest.cs | 2 +- .../ServerRegistrationRepositoryTest.cs | 2 +- .../Repositories/TagRepositoryTest.cs | 6 +- .../Repositories/TemplateRepositoryTest.cs | 2 +- .../Repositories/UserRepositoryTest.cs | 2 +- .../Repositories/UserTypeRepositoryTest.cs | 2 +- .../Services/ContentServiceTests.cs | 5 +- .../Templates/TemplateRepositoryTests.cs | 2 +- .../Web/Mvc/UmbracoViewPageTests.cs | 2 +- 65 files changed, 123 insertions(+), 132 deletions(-) diff --git a/src/Umbraco.Core/Persistence/LockingRepository.cs b/src/Umbraco.Core/Persistence/LockingRepository.cs index 992c8cb1ad..6d3d5d54c1 100644 --- a/src/Umbraco.Core/Persistence/LockingRepository.cs +++ b/src/Umbraco.Core/Persistence/LockingRepository.cs @@ -11,10 +11,10 @@ namespace Umbraco.Core.Persistence where TRepository : IDisposable, IRepository { private readonly IScopeUnitOfWorkProvider _uowProvider; - private readonly Func _repositoryFactory; + private readonly Func _repositoryFactory; private readonly int[] _readLockIds, _writeLockIds; - public LockingRepository(IScopeUnitOfWorkProvider uowProvider, Func repositoryFactory, + public LockingRepository(IScopeUnitOfWorkProvider uowProvider, Func repositoryFactory, IEnumerable readLockIds, IEnumerable writeLockIds) { Mandate.ParameterNotNull(uowProvider, "uowProvider"); diff --git a/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs b/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs index 53ffda9364..d425755579 100644 --- a/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/AuditRepository.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Persistence.Repositories { internal class AuditRepository : PetaPocoRepositoryBase, IAuditRepository { - public AuditRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public AuditRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs index b368488833..f148334825 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence.Repositories internal class ContentPreviewRepository : PetaPocoRepositoryBase> where TContent : IContentBase { - public ContentPreviewRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public ContentPreviewRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 7f3e657dc2..ebc35b997d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -31,7 +31,7 @@ namespace Umbraco.Core.Persistence.Repositories private readonly ContentPreviewRepository _contentPreviewRepository; private readonly ContentXmlRepository _contentXmlRepository; - public ContentRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider syntaxProvider, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, IContentSection contentSection) + public ContentRepository(IScopeUnitOfWork work, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider syntaxProvider, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, IContentSection contentSection) : base(work, cacheHelper, logger, syntaxProvider, contentSection) { if (contentTypeRepository == null) throw new ArgumentNullException("contentTypeRepository"); diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index d86f77168e..508ba06bb1 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -30,7 +30,7 @@ namespace Umbraco.Core.Persistence.Repositories internal abstract class ContentTypeBaseRepository : PetaPocoRepositoryBase, IReadRepository where TEntity : class, IContentTypeComposition { - protected ContentTypeBaseRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + protected ContentTypeBaseRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs index b7b4ddd583..a46ee942a2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs @@ -25,7 +25,7 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly ITemplateRepository _templateRepository; - public ContentTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, ITemplateRepository templateRepository) + public ContentTypeRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, ITemplateRepository templateRepository) : base(work, cache, logger, sqlSyntax) { _templateRepository = templateRepository; diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs index b242bb34f7..464ae16961 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentXmlRepository.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence.Repositories internal class ContentXmlRepository : PetaPocoRepositoryBase> where TContent : IContentBase { - public ContentXmlRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public ContentXmlRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index a4d71885f8..b121acc96f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -29,7 +29,7 @@ namespace Umbraco.Core.Persistence.Repositories private readonly IContentTypeRepository _contentTypeRepository; private readonly DataTypePreValueRepository _preValRepository; - public DataTypeDefinitionRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, + public DataTypeDefinitionRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IContentTypeRepository contentTypeRepository) : base(work, cache, logger, sqlSyntax) { @@ -519,7 +519,7 @@ AND umbracoNode.id <> @id", /// private class DataTypePreValueRepository : PetaPocoRepositoryBase { - public DataTypePreValueRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public DataTypePreValueRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs index 971efc4f2d..87a3d560a9 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class DictionaryRepository : PetaPocoRepositoryBase, IDictionaryRepository { - public DictionaryRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider syntax) + public DictionaryRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider syntax) : base(work, cache, logger, syntax) { } @@ -293,7 +293,7 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly DictionaryRepository _dictionaryRepository; - public DictionaryByUniqueIdRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public DictionaryByUniqueIdRepository(DictionaryRepository dictionaryRepository, IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { _dictionaryRepository = dictionaryRepository; @@ -350,7 +350,7 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly DictionaryRepository _dictionaryRepository; - public DictionaryByKeyRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public DictionaryByKeyRepository(DictionaryRepository dictionaryRepository, IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { _dictionaryRepository = dictionaryRepository; diff --git a/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs index 7b6cc162a8..0a3767a365 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Persistence.Repositories internal class DomainRepository : PetaPocoRepositoryBase, IDomainRepository { - public DomainRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public DomainRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs index cc13275798..02d1daea39 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly Guid _containerObjectType; - public EntityContainerRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, Guid containerObjectType) + public EntityContainerRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, Guid containerObjectType) : base(work, cache, logger, sqlSyntax) { var allowedContainers = new[] {Constants.ObjectTypes.DocumentTypeContainerGuid, Constants.ObjectTypes.MediaTypeContainerGuid, Constants.ObjectTypes.DataTypeContainerGuid}; diff --git a/src/Umbraco.Core/Persistence/Repositories/ExternalLoginRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ExternalLoginRepository.cs index 7c80bd10b7..fcaee87975 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ExternalLoginRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ExternalLoginRepository.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Persistence.Repositories { internal class ExternalLoginRepository : PetaPocoRepositoryBase, IExternalLoginRepository { - public ExternalLoginRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public ExternalLoginRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs index f9a8e59cfa..f8b80ed9da 100644 --- a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class LanguageRepository : PetaPocoRepositoryBase, ILanguageRepository { - public LanguageRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public LanguageRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs index 2e1e481219..398a7cbb54 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence.Repositories internal class MacroRepository : PetaPocoRepositoryBase, IMacroRepository { - public MacroRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public MacroRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 08c2f7e0be..7b760410e3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -28,7 +28,7 @@ namespace Umbraco.Core.Persistence.Repositories private readonly ContentXmlRepository _contentXmlRepository; private readonly ContentPreviewRepository _contentPreviewRepository; - public MediaRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection) + public MediaRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, IContentSection contentSection) : base(work, cache, logger, sqlSyntax, contentSection) { if (mediaTypeRepository == null) throw new ArgumentNullException("mediaTypeRepository"); diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs index 50a89bfd65..bf1b1455ad 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs @@ -23,7 +23,7 @@ namespace Umbraco.Core.Persistence.Repositories internal class MediaTypeRepository : ContentTypeBaseRepository, IMediaTypeRepository { - public MediaTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public MediaTypeRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs index 2c0e910428..cf972f849a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core.Persistence.Repositories internal class MemberGroupRepository : PetaPocoRepositoryBase, IMemberGroupRepository { - public MemberGroupRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public MemberGroupRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } @@ -159,15 +159,15 @@ namespace Umbraco.Core.Persistence.Repositories Name = roleName }; PersistNewItem(grp); - - if (SavingMemberGroup.IsRaisedEventCancelled(new SaveEventArgs(grp), this)) + + if (SavingMemberGroup.IsRaisedEventCancelled(new SaveEventArgs(grp), this, UnitOfWork.EventManager)) { return null; } transaction.Complete(); - SavedMemberGroup.RaiseEvent(new SaveEventArgs(grp), this); + SavedMemberGroup.RaiseEvent(new SaveEventArgs(grp), this, UnitOfWork.EventManager); return grp; } diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index dcab898685..2902b3e959 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -29,7 +29,7 @@ namespace Umbraco.Core.Persistence.Repositories private readonly ContentXmlRepository _contentXmlRepository; private readonly ContentPreviewRepository _contentPreviewRepository; - public MemberRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, IContentSection contentSection) + public MemberRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, IContentSection contentSection) : base(work, cache, logger, sqlSyntax, contentSection) { if (memberTypeRepository == null) throw new ArgumentNullException("memberTypeRepository"); diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs index 7d5defe8c9..dbfdc48ad8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs @@ -22,7 +22,7 @@ namespace Umbraco.Core.Persistence.Repositories internal class MemberTypeRepository : ContentTypeBaseRepository, IMemberTypeRepository { - public MemberTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public MemberTypeRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/MigrationEntryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MigrationEntryRepository.cs index b4a21f5d21..bd5db622d6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MigrationEntryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MigrationEntryRepository.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Repositories { internal class MigrationEntryRepository : PetaPocoRepositoryBase, IMigrationEntryRepository { - public MigrationEntryRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public MigrationEntryRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs index fe363fea16..193cdb6b53 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PetaPocoRepositoryBase.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core.Persistence.Repositories { public ISqlSyntaxProvider SqlSyntax { get; private set; } - protected PetaPocoRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + protected PetaPocoRepositoryBase(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger) { if (sqlSyntax == null) throw new ArgumentNullException("sqlSyntax"); @@ -28,11 +28,11 @@ namespace Umbraco.Core.Persistence.Repositories } /// - /// Returns the database Unit of Work added to the repository + /// Returns the Scope Unit of Work added to the repository /// - protected internal new IDatabaseUnitOfWork UnitOfWork + protected internal new IScopeUnitOfWork UnitOfWork { - get { return (IDatabaseUnitOfWork)base.UnitOfWork; } + get { return (IScopeUnitOfWork)base.UnitOfWork; } } protected UmbracoDatabase Database diff --git a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs index 37200b2172..6f9ac12669 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Persistence.Repositories { internal class PublicAccessRepository : PetaPocoRepositoryBase, IPublicAccessRepository { - public PublicAccessRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public PublicAccessRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs index f17a0dd0d2..e5f93d3111 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Persistence.Repositories internal abstract class RecycleBinRepository : VersionableRepositoryBase, IRecycleBinRepository where TEntity : class, IUmbracoEntity { - protected RecycleBinRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IContentSection contentSection) + protected RecycleBinRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IContentSection contentSection) : base(work, cache, logger, sqlSyntax, contentSection) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/RedirectUrlRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RedirectUrlRepository.cs index 5452e868a0..acab12a10a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RedirectUrlRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RedirectUrlRepository.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Repositories { internal class RedirectUrlRepository : PetaPocoRepositoryBase, IRedirectUrlRepository { - public RedirectUrlRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public RedirectUrlRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs index 4511ebe35d..69caaa78af 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RelationRepository.cs @@ -20,7 +20,7 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly IRelationTypeRepository _relationTypeRepository; - public RelationRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IRelationTypeRepository relationTypeRepository) + public RelationRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IRelationTypeRepository relationTypeRepository) : base(work, cache, logger, sqlSyntax) { _relationTypeRepository = relationTypeRepository; diff --git a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs index 2c54897ff7..104c66557a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class RelationTypeRepository : PetaPocoRepositoryBase, IRelationTypeRepository { - public RelationTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public RelationTypeRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs index 02794ef1fb..5929476509 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly ICacheProvider _staticCache; - public ServerRegistrationRepository(IDatabaseUnitOfWork work, ICacheProvider staticCache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public ServerRegistrationRepository(IScopeUnitOfWork work, ICacheProvider staticCache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax) { _staticCache = staticCache; diff --git a/src/Umbraco.Core/Persistence/Repositories/SimpleGetRepository.cs b/src/Umbraco.Core/Persistence/Repositories/SimpleGetRepository.cs index a88672ea6f..67c9149422 100644 --- a/src/Umbraco.Core/Persistence/Repositories/SimpleGetRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/SimpleGetRepository.cs @@ -18,7 +18,7 @@ namespace Umbraco.Core.Persistence.Repositories where TDto: class { - protected SimpleGetRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + protected SimpleGetRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs index 142740e9d8..0456e16bf3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TagRepository.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Persistence.Repositories { internal class TagRepository : PetaPocoRepositoryBase, ITagRepository { - internal TagRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + internal TagRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/TaskRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TaskRepository.cs index c02362f8fd..f90e81428b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TaskRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TaskRepository.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Persistence.Repositories { internal class TaskRepository : PetaPocoRepositoryBase, ITaskRepository { - public TaskRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public TaskRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/TaskTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TaskTypeRepository.cs index a970880669..71c3d2d1a4 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TaskTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TaskTypeRepository.cs @@ -13,7 +13,7 @@ namespace Umbraco.Core.Persistence.Repositories { internal class TaskTypeRepository : PetaPocoRepositoryBase, ITaskTypeRepository { - public TaskTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public TaskTypeRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index 41743601fb..bb1b4a2515 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -34,7 +34,7 @@ namespace Umbraco.Core.Persistence.Repositories private readonly ViewHelper _viewHelper; private readonly MasterPageHelper _masterPageHelper; - internal TemplateRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem, ITemplatesSection templateConfig) + internal TemplateRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem, ITemplatesSection templateConfig) : base(work, cache, logger, sqlSyntax) { _masterpagesFileSystem = masterpageFileSystem; diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 4652ce47be..bad4af0ebc 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -26,7 +26,7 @@ namespace Umbraco.Core.Persistence.Repositories private readonly IUserTypeRepository _userTypeRepository; private readonly CacheHelper _cacheHelper; - public UserRepository(IDatabaseUnitOfWork work, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider sqlSyntax, IUserTypeRepository userTypeRepository) + public UserRepository(IScopeUnitOfWork work, CacheHelper cacheHelper, ILogger logger, ISqlSyntaxProvider sqlSyntax, IUserTypeRepository userTypeRepository) : base(work, cacheHelper, logger, sqlSyntax) { _userTypeRepository = userTypeRepository; diff --git a/src/Umbraco.Core/Persistence/Repositories/UserTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserTypeRepository.cs index 1fd94ca357..81e3a5765e 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserTypeRepository.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence.Repositories ///
    internal class UserTypeRepository : PetaPocoRepositoryBase, IUserTypeRepository { - public UserTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public UserTypeRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index 163e0810ad..712959b072 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly IContentSection _contentSection; - protected VersionableRepositoryBase(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IContentSection contentSection) + protected VersionableRepositoryBase(IScopeUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IContentSection contentSection) : base(work, cache, logger, sqlSyntax) { _contentSection = contentSection; diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index e04c898015..620aa54546 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -88,42 +88,42 @@ namespace Umbraco.Core.Persistence #endregion - public virtual IExternalLoginRepository CreateExternalLoginRepository(IDatabaseUnitOfWork uow) + public virtual IExternalLoginRepository CreateExternalLoginRepository(IScopeUnitOfWork uow) { return new ExternalLoginRepository(uow, _cacheHelper, _logger, _sqlSyntax); } - public virtual IPublicAccessRepository CreatePublicAccessRepository(IDatabaseUnitOfWork uow) + public virtual IPublicAccessRepository CreatePublicAccessRepository(IScopeUnitOfWork uow) { return new PublicAccessRepository(uow, _cacheHelper, _logger, _sqlSyntax); } - public virtual ITaskRepository CreateTaskRepository(IDatabaseUnitOfWork uow) + public virtual ITaskRepository CreateTaskRepository(IScopeUnitOfWork uow) { return new TaskRepository(uow, _noCache, //never cache _logger, _sqlSyntax); } - public virtual IAuditRepository CreateAuditRepository(IDatabaseUnitOfWork uow) + public virtual IAuditRepository CreateAuditRepository(IScopeUnitOfWork uow) { return new AuditRepository(uow, _noCache, //never cache _logger, _sqlSyntax); } - public virtual ITagRepository CreateTagRepository(IDatabaseUnitOfWork uow) + public virtual ITagRepository CreateTagRepository(IScopeUnitOfWork uow) { return new TagRepository( uow, _cacheHelper, _logger, _sqlSyntax); } - public virtual IContentRepository CreateContentRepository(IDatabaseUnitOfWork uow) + public virtual IContentRepository CreateContentRepository(IScopeUnitOfWork uow) { return new ContentRepository( uow, @@ -139,7 +139,7 @@ namespace Umbraco.Core.Persistence }; } - public virtual IContentTypeRepository CreateContentTypeRepository(IDatabaseUnitOfWork uow) + public virtual IContentTypeRepository CreateContentTypeRepository(IScopeUnitOfWork uow) { return new ContentTypeRepository( uow, @@ -148,7 +148,7 @@ namespace Umbraco.Core.Persistence CreateTemplateRepository(uow)); } - public virtual IDataTypeDefinitionRepository CreateDataTypeDefinitionRepository(IDatabaseUnitOfWork uow) + public virtual IDataTypeDefinitionRepository CreateDataTypeDefinitionRepository(IScopeUnitOfWork uow) { return new DataTypeDefinitionRepository( uow, @@ -157,7 +157,7 @@ namespace Umbraco.Core.Persistence CreateContentTypeRepository(uow)); } - public virtual IDictionaryRepository CreateDictionaryRepository(IDatabaseUnitOfWork uow) + public virtual IDictionaryRepository CreateDictionaryRepository(IScopeUnitOfWork uow) { return new DictionaryRepository( uow, @@ -166,7 +166,7 @@ namespace Umbraco.Core.Persistence _sqlSyntax); } - public virtual ILanguageRepository CreateLanguageRepository(IDatabaseUnitOfWork uow) + public virtual ILanguageRepository CreateLanguageRepository(IScopeUnitOfWork uow) { return new LanguageRepository( uow, @@ -174,7 +174,7 @@ namespace Umbraco.Core.Persistence _logger, _sqlSyntax); } - public virtual IMediaRepository CreateMediaRepository(IDatabaseUnitOfWork uow) + public virtual IMediaRepository CreateMediaRepository(IScopeUnitOfWork uow) { return new MediaRepository( uow, @@ -185,7 +185,7 @@ namespace Umbraco.Core.Persistence _settings.Content); } - public virtual IMediaTypeRepository CreateMediaTypeRepository(IDatabaseUnitOfWork uow) + public virtual IMediaTypeRepository CreateMediaTypeRepository(IScopeUnitOfWork uow) { return new MediaTypeRepository( uow, @@ -193,7 +193,7 @@ namespace Umbraco.Core.Persistence _logger, _sqlSyntax); } - public virtual IRelationRepository CreateRelationRepository(IDatabaseUnitOfWork uow) + public virtual IRelationRepository CreateRelationRepository(IScopeUnitOfWork uow) { return new RelationRepository( uow, @@ -202,7 +202,7 @@ namespace Umbraco.Core.Persistence CreateRelationTypeRepository(uow)); } - public virtual IRelationTypeRepository CreateRelationTypeRepository(IDatabaseUnitOfWork uow) + public virtual IRelationTypeRepository CreateRelationTypeRepository(IScopeUnitOfWork uow) { return new RelationTypeRepository( uow, @@ -230,7 +230,7 @@ namespace Umbraco.Core.Persistence return new StylesheetRepository(uow, FileSystemProviderManager.Current.StylesheetsFileSystem); } - public virtual ITemplateRepository CreateTemplateRepository(IDatabaseUnitOfWork uow) + public virtual ITemplateRepository CreateTemplateRepository(IScopeUnitOfWork uow) { return new TemplateRepository(uow, _cacheHelper, @@ -245,7 +245,7 @@ namespace Umbraco.Core.Persistence return new XsltFileRepository(uow, FileSystemProviderManager.Current.XsltFileSystem); } - public virtual IMigrationEntryRepository CreateMigrationEntryRepository(IDatabaseUnitOfWork uow) + public virtual IMigrationEntryRepository CreateMigrationEntryRepository(IScopeUnitOfWork uow) { return new MigrationEntryRepository( uow, @@ -253,7 +253,7 @@ namespace Umbraco.Core.Persistence _logger, _sqlSyntax); } - public virtual IServerRegistrationRepository CreateServerRegistrationRepository(IDatabaseUnitOfWork uow) + public virtual IServerRegistrationRepository CreateServerRegistrationRepository(IScopeUnitOfWork uow) { return new ServerRegistrationRepository( uow, @@ -261,7 +261,7 @@ namespace Umbraco.Core.Persistence _logger, _sqlSyntax); } - public virtual IUserTypeRepository CreateUserTypeRepository(IDatabaseUnitOfWork uow) + public virtual IUserTypeRepository CreateUserTypeRepository(IScopeUnitOfWork uow) { return new UserTypeRepository( uow, @@ -270,7 +270,7 @@ namespace Umbraco.Core.Persistence _logger, _sqlSyntax); } - public virtual IUserRepository CreateUserRepository(IDatabaseUnitOfWork uow) + public virtual IUserRepository CreateUserRepository(IScopeUnitOfWork uow) { return new UserRepository( uow, @@ -280,14 +280,14 @@ namespace Umbraco.Core.Persistence CreateUserTypeRepository(uow)); } - internal virtual IMacroRepository CreateMacroRepository(IDatabaseUnitOfWork uow) + internal virtual IMacroRepository CreateMacroRepository(IScopeUnitOfWork uow) { return new MacroRepository(uow, _cacheHelper, _logger, _sqlSyntax); } - public virtual IMemberRepository CreateMemberRepository(IDatabaseUnitOfWork uow) + public virtual IMemberRepository CreateMemberRepository(IScopeUnitOfWork uow) { return new MemberRepository( uow, @@ -299,38 +299,38 @@ namespace Umbraco.Core.Persistence _settings.Content); } - public virtual IMemberTypeRepository CreateMemberTypeRepository(IDatabaseUnitOfWork uow) + public virtual IMemberTypeRepository CreateMemberTypeRepository(IScopeUnitOfWork uow) { return new MemberTypeRepository(uow, _cacheHelper, _logger, _sqlSyntax); } - public virtual IMemberGroupRepository CreateMemberGroupRepository(IDatabaseUnitOfWork uow) + public virtual IMemberGroupRepository CreateMemberGroupRepository(IScopeUnitOfWork uow) { return new MemberGroupRepository(uow, _cacheHelper, _logger, _sqlSyntax); } - public virtual IEntityRepository CreateEntityRepository(IDatabaseUnitOfWork uow) + public virtual IEntityRepository CreateEntityRepository(IScopeUnitOfWork uow) { return new EntityRepository(uow); } - public virtual IDomainRepository CreateDomainRepository(IDatabaseUnitOfWork uow) + public virtual IDomainRepository CreateDomainRepository(IScopeUnitOfWork uow) { return new DomainRepository(uow, _cacheHelper, _logger, _sqlSyntax); } - public ITaskTypeRepository CreateTaskTypeRepository(IDatabaseUnitOfWork uow) + public ITaskTypeRepository CreateTaskTypeRepository(IScopeUnitOfWork uow) { return new TaskTypeRepository(uow, _noCache, //never cache _logger, _sqlSyntax); } - internal virtual EntityContainerRepository CreateEntityContainerRepository(IDatabaseUnitOfWork uow, Guid containerObjectType) + internal virtual EntityContainerRepository CreateEntityContainerRepository(IScopeUnitOfWork uow, Guid containerObjectType) { return new EntityContainerRepository( uow, @@ -339,7 +339,7 @@ namespace Umbraco.Core.Persistence containerObjectType); } - public IRedirectUrlRepository CreateRedirectUrlRepository(IDatabaseUnitOfWork uow) + public IRedirectUrlRepository CreateRedirectUrlRepository(IScopeUnitOfWork uow) { return new RedirectUrlRepository( uow, @@ -348,7 +348,7 @@ namespace Umbraco.Core.Persistence _sqlSyntax); } - internal IStylesheetRepository CreateStylesheetRepository(IDatabaseUnitOfWork uow) + internal IStylesheetRepository CreateStylesheetRepository(IScopeUnitOfWork uow) { return new StylesheetRepository(uow, FileSystemProviderManager.Current.StylesheetsFileSystem); } diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWork.cs index 6bc59da9ae..e5a76cc94e 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWork.cs @@ -2,7 +2,7 @@ using Umbraco.Core.Events; namespace Umbraco.Core.Persistence.UnitOfWork { - internal interface IScopeUnitOfWork : IDatabaseUnitOfWork + public interface IScopeUnitOfWork : IDatabaseUnitOfWork { IEventManager EventManager { get; } } diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWorkProvider.cs index 6cedc56c33..b6f8335e1a 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/IScopeUnitOfWorkProvider.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// /// Defines a Unit of Work Provider for working with /// - internal interface IScopeUnitOfWorkProvider : IDatabaseUnitOfWorkProvider + public interface IScopeUnitOfWorkProvider : IDatabaseUnitOfWorkProvider { new IScopeUnitOfWork GetUnitOfWork(); IScopeUnitOfWork GetUnitOfWork(IsolationLevel isolationLevel); diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs index 25f85a7ea2..ef311f9d49 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs @@ -57,6 +57,12 @@ namespace Umbraco.Core.Persistence.UnitOfWork } #region Implementation of IUnitOfWorkProvider + + //explicit implementation + IDatabaseUnitOfWork IDatabaseUnitOfWorkProvider.GetUnitOfWork() + { + return new PetaPocoUnitOfWork(_scopeProvider); + } /// /// Creates a Unit of work with a new UmbracoDatabase instance for the work item/transaction. @@ -67,13 +73,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// the ApplicationContext.Current.DatabaseContext.Database. This is because each transaction should use it's own Database /// and we Dispose of this Database object when the UOW is disposed. /// - public IDatabaseUnitOfWork GetUnitOfWork() - { - return new PetaPocoUnitOfWork(_scopeProvider); - } - - //explicit implementation - IScopeUnitOfWork IScopeUnitOfWorkProvider.GetUnitOfWork() + public IScopeUnitOfWork GetUnitOfWork() { return new PetaPocoUnitOfWork(_scopeProvider); } @@ -85,16 +85,6 @@ namespace Umbraco.Core.Persistence.UnitOfWork } #endregion - - /// - /// Static helper method to return a new unit of work - /// - /// - internal static IDatabaseUnitOfWork CreateUnitOfWork(ILogger logger) - { - // fixme wtf? - var provider = new PetaPocoUnitOfWorkProvider(logger); - return provider.GetUnitOfWork(); - } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/PackagingService.cs b/src/Umbraco.Core/Services/PackagingService.cs index 4d8a2773ba..ca083391b4 100644 --- a/src/Umbraco.Core/Services/PackagingService.cs +++ b/src/Umbraco.Core/Services/PackagingService.cs @@ -40,7 +40,7 @@ namespace Umbraco.Core.Services private readonly ILocalizationService _localizationService; private readonly IEntityService _entityService; private readonly RepositoryFactory _repositoryFactory; - private readonly IDatabaseUnitOfWorkProvider _uowProvider; + private readonly IScopeUnitOfWorkProvider _uowProvider; private Dictionary _importedContentTypes; private IPackageInstallation _packageInstallation; private readonly IUserService _userService; @@ -58,7 +58,7 @@ namespace Umbraco.Core.Services IEntityService entityService, IUserService userService, RepositoryFactory repositoryFactory, - IDatabaseUnitOfWorkProvider uowProvider) + IScopeUnitOfWorkProvider uowProvider) { _logger = logger; _contentService = contentService; diff --git a/src/Umbraco.Core/Services/ServerRegistrationService.cs b/src/Umbraco.Core/Services/ServerRegistrationService.cs index 9e28f9a91c..fbd737fdf7 100644 --- a/src/Umbraco.Core/Services/ServerRegistrationService.cs +++ b/src/Umbraco.Core/Services/ServerRegistrationService.cs @@ -31,7 +31,7 @@ namespace Umbraco.Core.Services /// A repository factory. /// A logger. /// - public ServerRegistrationService(IDatabaseUnitOfWorkProvider uowProvider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) + public ServerRegistrationService(IScopeUnitOfWorkProvider uowProvider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) : base(uowProvider, repositoryFactory, logger, eventMessagesFactory) { _lrepo = new LockingRepository(UowProvider, diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index f2969de3be..48421e5345 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -161,7 +161,7 @@ namespace Umbraco.Core.Services /// public ServiceContext( RepositoryFactory repositoryFactory, - IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, + IScopeUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, BasePublishingStrategy publishingStrategy, CacheHelper cache, @@ -187,7 +187,7 @@ namespace Umbraco.Core.Services /// Builds the various services /// private void BuildServiceCache( - IDatabaseUnitOfWorkProvider dbUnitOfWorkProvider, + IScopeUnitOfWorkProvider dbUnitOfWorkProvider, IUnitOfWorkProvider fileUnitOfWorkProvider, BasePublishingStrategy publishingStrategy, CacheHelper cache, diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index 311ea52071..9f8dac1ecb 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -44,7 +44,7 @@ namespace Umbraco.Tests.Persistence.Repositories base.TearDown(); } - private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out DataTypeDefinitionRepository dtdRepository) + private ContentRepository CreateRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out DataTypeDefinitionRepository dtdRepository) { TemplateRepository tr; var ctRepository = CreateRepository(unitOfWork, out contentTypeRepository, out tr); @@ -52,13 +52,13 @@ namespace Umbraco.Tests.Persistence.Repositories return ctRepository; } - private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) + private ContentRepository CreateRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) { TemplateRepository tr; return CreateRepository(unitOfWork, out contentTypeRepository, out tr); } - private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out TemplateRepository templateRepository) + private ContentRepository CreateRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out TemplateRepository templateRepository) { templateRepository = new TemplateRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, Mock.Of(), Mock.Of(), Mock.Of()); var tagRepository = new TagRepository(unitOfWork, CacheHelper, Logger, SqlSyntax); diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index 84cdef73e1..ab75258cee 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -40,7 +40,7 @@ namespace Umbraco.Tests.Persistence.Repositories base.TearDown(); } - private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) + private ContentRepository CreateRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) { var templateRepository = new TemplateRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, Mock.Of(), Mock.Of(), Mock.Of()); var tagRepository = new TagRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); @@ -49,20 +49,20 @@ namespace Umbraco.Tests.Persistence.Repositories return repository; } - private ContentTypeRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) + private ContentTypeRepository CreateRepository(IScopeUnitOfWork unitOfWork) { var templateRepository = new TemplateRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, Mock.Of(), Mock.Of(), Mock.Of()); var contentTypeRepository = new ContentTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, templateRepository); return contentTypeRepository; } - private MediaTypeRepository CreateMediaTypeRepository(IDatabaseUnitOfWork unitOfWork) + private MediaTypeRepository CreateMediaTypeRepository(IScopeUnitOfWork unitOfWork) { var contentTypeRepository = new MediaTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); return contentTypeRepository; } - private EntityContainerRepository CreateContainerRepository(IDatabaseUnitOfWork unitOfWork, Guid containerEntityType) + private EntityContainerRepository CreateContainerRepository(IScopeUnitOfWork unitOfWork, Guid containerEntityType) { return new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, containerEntityType); } diff --git a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs index 4db63f6085..b2f8ff9d73 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs @@ -31,7 +31,7 @@ namespace Umbraco.Tests.Persistence.Repositories base.Initialize(); } - private DataTypeDefinitionRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) + private DataTypeDefinitionRepository CreateRepository(IScopeUnitOfWork unitOfWork) { var dataTypeDefinitionRepository = new DataTypeDefinitionRepository( unitOfWork, CacheHelper.CreateDisabledCacheHelper(), @@ -41,7 +41,7 @@ namespace Umbraco.Tests.Persistence.Repositories return dataTypeDefinitionRepository; } - private EntityContainerRepository CreateContainerRepository(IDatabaseUnitOfWork unitOfWork) + private EntityContainerRepository CreateContainerRepository(IScopeUnitOfWork unitOfWork) { return new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, Constants.ObjectTypes.DataTypeContainerGuid); } diff --git a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs index 6b56181eac..3a85978f40 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DictionaryRepositoryTest.cs @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Persistence.Repositories CreateTestData(); } - private DictionaryRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) + private DictionaryRepository CreateRepository(IScopeUnitOfWork unitOfWork) { var dictionaryRepository = new DictionaryRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), new SqlCeSyntaxProvider()); return dictionaryRepository; diff --git a/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs index 782eeaf4e9..f49caf65af 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs @@ -20,7 +20,7 @@ namespace Umbraco.Tests.Persistence.Repositories [TestFixture] public class DomainRepositoryTest : BaseDatabaseFactoryTest { - private DomainRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out ContentRepository contentRepository, out LanguageRepository languageRepository) + private DomainRepository CreateRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out ContentRepository contentRepository, out LanguageRepository languageRepository) { var templateRepository = new TemplateRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, SqlSyntax, Mock.Of(), Mock.Of(), Mock.Of()); var tagRepository = new TagRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Logger, SqlSyntax); diff --git a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs index e4c403b280..6dac497488 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/LanguageRepositoryTest.cs @@ -26,7 +26,7 @@ namespace Umbraco.Tests.Persistence.Repositories CreateTestData(); } - private LanguageRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) + private LanguageRepository CreateRepository(IScopeUnitOfWork unitOfWork) { return new LanguageRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); } diff --git a/src/Umbraco.Tests/Persistence/Repositories/LockedRepositoryTests.cs b/src/Umbraco.Tests/Persistence/Repositories/LockedRepositoryTests.cs index 7307be9d52..a17a648a08 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/LockedRepositoryTests.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/LockedRepositoryTests.cs @@ -19,7 +19,7 @@ namespace Umbraco.Tests.Persistence.Repositories [TestFixture] public class LockedRepositoryTests : BaseDatabaseFactoryTest { - private static IServerRegistrationRepository CreateRepository(IDatabaseUnitOfWork uow, ILogger logger, CacheHelper cacheHelper, ISqlSyntaxProvider sqlSyntax) + private static IServerRegistrationRepository CreateRepository(IScopeUnitOfWork uow, ILogger logger, CacheHelper cacheHelper, ISqlSyntaxProvider sqlSyntax) { return new ServerRegistrationRepository( uow, diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs index 1566441d5a..6883510a3f 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs @@ -34,7 +34,7 @@ namespace Umbraco.Tests.Persistence.Repositories CreateTestData(); } - private MediaRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out MediaTypeRepository mediaTypeRepository) + private MediaRepository CreateRepository(IScopeUnitOfWork unitOfWork, out MediaTypeRepository mediaTypeRepository) { mediaTypeRepository = new MediaTypeRepository(unitOfWork, CacheHelper, Mock.Of(), SqlSyntax); var tagRepository = new TagRepository(unitOfWork, CacheHelper, Mock.Of(), SqlSyntax); diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs index b9ff045c71..ef039f4121 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs @@ -26,12 +26,12 @@ namespace Umbraco.Tests.Persistence.Repositories base.Initialize(); } - private MediaTypeRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) + private MediaTypeRepository CreateRepository(IScopeUnitOfWork unitOfWork) { return new MediaTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); } - private EntityContainerRepository CreateContainerRepository(IDatabaseUnitOfWork unitOfWork) + private EntityContainerRepository CreateContainerRepository(IScopeUnitOfWork unitOfWork) { return new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, Constants.ObjectTypes.MediaTypeContainerGuid); } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs index e97fff03c8..befa2259ed 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs @@ -35,7 +35,7 @@ namespace Umbraco.Tests.Persistence.Repositories base.TearDown(); } - private MemberRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out MemberTypeRepository memberTypeRepository, out MemberGroupRepository memberGroupRepository) + private MemberRepository CreateRepository(IScopeUnitOfWork unitOfWork, out MemberTypeRepository memberTypeRepository, out MemberGroupRepository memberGroupRepository) { memberTypeRepository = new MemberTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); memberGroupRepository = new MemberGroupRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs index 32341fac10..6b4f354703 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Persistence.Repositories base.TearDown(); } - private MemberTypeRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) + private MemberTypeRepository CreateRepository(IScopeUnitOfWork unitOfWork) { return new MemberTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); } diff --git a/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs index 4fa212f9bc..6d446e9174 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs @@ -225,7 +225,7 @@ namespace Umbraco.Tests.Persistence.Repositories } - private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) + private ContentRepository CreateRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) { var templateRepository = new TemplateRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, Mock.Of(), Mock.Of(), Mock.Of()); var tagRepository = new TagRepository(unitOfWork, CacheHelper, Logger, SqlSyntax); diff --git a/src/Umbraco.Tests/Persistence/Repositories/RedirectUrlRepositoryTests.cs b/src/Umbraco.Tests/Persistence/Repositories/RedirectUrlRepositoryTests.cs index 084d43d24c..d82004bb05 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/RedirectUrlRepositoryTests.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/RedirectUrlRepositoryTests.cs @@ -190,7 +190,7 @@ namespace Umbraco.Tests.Persistence.Repositories } } - private IRedirectUrlRepository CreateRepository(IDatabaseUnitOfWork uow) + private IRedirectUrlRepository CreateRepository(IScopeUnitOfWork uow) { return new RedirectUrlRepository(uow, CacheHelper.CreateDisabledCacheHelper(), Logger, SqlSyntax); } diff --git a/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs index 82eec202ae..3639d03f2a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/RelationRepositoryTest.cs @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Persistence.Repositories CreateTestData(); } - private RelationRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out RelationTypeRepository relationTypeRepository) + private RelationRepository CreateRepository(IScopeUnitOfWork unitOfWork, out RelationTypeRepository relationTypeRepository) { relationTypeRepository = new RelationTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); var repository = new RelationRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, relationTypeRepository); diff --git a/src/Umbraco.Tests/Persistence/Repositories/RelationTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/RelationTypeRepositoryTest.cs index 2a95957ea8..9355b90546 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/RelationTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/RelationTypeRepositoryTest.cs @@ -27,7 +27,7 @@ namespace Umbraco.Tests.Persistence.Repositories CreateTestData(); } - private RelationTypeRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) + private RelationTypeRepository CreateRepository(IScopeUnitOfWork unitOfWork) { return new RelationTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); } diff --git a/src/Umbraco.Tests/Persistence/Repositories/ServerRegistrationRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ServerRegistrationRepositoryTest.cs index bcb49068fa..ef75fe2c99 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ServerRegistrationRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ServerRegistrationRepositoryTest.cs @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Persistence.Repositories CreateTestData(); } - private ServerRegistrationRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) + private ServerRegistrationRepository CreateRepository(IScopeUnitOfWork unitOfWork) { return new ServerRegistrationRepository(unitOfWork, _staticCache, Mock.Of(), SqlSyntax); } diff --git a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs index d79a45c2e9..fda7b26f4a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs @@ -33,7 +33,7 @@ namespace Umbraco.Tests.Persistence.Repositories base.TearDown(); } - private TagRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) + private TagRepository CreateRepository(IScopeUnitOfWork unitOfWork) { var tagRepository = new TagRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); return tagRepository; @@ -1032,7 +1032,7 @@ namespace Umbraco.Tests.Persistence.Repositories } - private ContentRepository CreateContentRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) + private ContentRepository CreateContentRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) { var templateRepository = new TemplateRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, Mock.Of(), Mock.Of(), Mock.Of()); var tagRepository = new TagRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); @@ -1041,7 +1041,7 @@ namespace Umbraco.Tests.Persistence.Repositories return repository; } - private MediaRepository CreateMediaRepository(IDatabaseUnitOfWork unitOfWork, out MediaTypeRepository mediaTypeRepository) + private MediaRepository CreateMediaRepository(IScopeUnitOfWork unitOfWork, out MediaTypeRepository mediaTypeRepository) { var tagRepository = new TagRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); mediaTypeRepository = new MediaTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); diff --git a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs index 1a4a7a2338..c8986d1036 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs @@ -28,7 +28,7 @@ namespace Umbraco.Tests.Persistence.Repositories private IFileSystem _masterPageFileSystem; private IFileSystem _viewsFileSystem; - private ITemplateRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, ITemplatesSection templatesSection = null) + private ITemplateRepository CreateRepository(IScopeUnitOfWork unitOfWork, ITemplatesSection templatesSection = null) { return new TemplateRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, _masterPageFileSystem, _viewsFileSystem, templatesSection ?? Mock.Of(t => t.DefaultRenderingEngine == RenderingEngine.Mvc)); diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index 1d26e59113..0aae63d995 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Persistence.Repositories base.TearDown(); } - private UserRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out UserTypeRepository userTypeRepository) + private UserRepository CreateRepository(IScopeUnitOfWork unitOfWork, out UserTypeRepository userTypeRepository) { userTypeRepository = new UserTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); var repository = new UserRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, userTypeRepository); diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserTypeRepositoryTest.cs index 5efc139931..963f9eb6aa 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserTypeRepositoryTest.cs @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Persistence.Repositories base.TearDown(); } - private UserTypeRepository CreateRepository(IDatabaseUnitOfWork unitOfWork) + private UserTypeRepository CreateRepository(IScopeUnitOfWork unitOfWork) { return new UserTypeRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); } diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 46af227ecd..b238a144f7 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1469,7 +1469,8 @@ namespace Umbraco.Tests.Services [Test] public void Can_Save_Lazy_Content() { - var unitOfWork = PetaPocoUnitOfWorkProvider.CreateUnitOfWork(Mock.Of()); + var uowProvider = new PetaPocoUnitOfWorkProvider(Mock.Of()); + var unitOfWork = uowProvider.GetUnitOfWork(); var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage"); var root = ServiceContext.ContentService.GetById(NodeDto.NodeIdSeed + 1); @@ -1743,7 +1744,7 @@ namespace Umbraco.Tests.Services return list; } - private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) + private ContentRepository CreateRepository(IScopeUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) { var templateRepository = new TemplateRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax, Mock.Of(), Mock.Of(), Mock.Of()); var tagRepository = new TagRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of(), SqlSyntax); diff --git a/src/Umbraco.Tests/Templates/TemplateRepositoryTests.cs b/src/Umbraco.Tests/Templates/TemplateRepositoryTests.cs index c385799411..05c4059662 100644 --- a/src/Umbraco.Tests/Templates/TemplateRepositoryTests.cs +++ b/src/Umbraco.Tests/Templates/TemplateRepositoryTests.cs @@ -19,7 +19,7 @@ namespace Umbraco.Tests.Templates [TestFixture] public class TemplateRepositoryTests { - private readonly Mock _unitOfWorkMock = new Mock(); + private readonly Mock _unitOfWorkMock = new Mock(); private readonly Mock _cacheMock = new Mock(); private TemplateRepository _templateRepository; private readonly Mock _viewFileSystemMock = new Mock(); diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index 6d776b6d35..3ba6e06c7c 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -393,7 +393,7 @@ namespace Umbraco.Tests.Web.Mvc new Mock().Object, new Mock().Object, new RepositoryFactory(CacheHelper.CreateDisabledCacheHelper(), logger, Mock.Of(), umbracoSettings), - new Mock().Object), + new Mock().Object), new Mock().Object, new RelationService( new Mock().Object, From 8de8b53039b0b56fa310648aafb8490453fcb0a0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 19 Jan 2017 16:58:10 +1100 Subject: [PATCH 287/599] missing interface implementation --- .../UnitOfWork/PetaPocoUnitOfWork.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs index ac677732c8..49ba09066f 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs @@ -156,12 +156,17 @@ namespace Umbraco.Core.Persistence.UnitOfWork get { return ThisScope.Database; } } - #region Operation + public IEventManager EventManager + { + get { return ThisScope.EventManager; } + } - /// - /// Provides a snapshot of an entity and the repository reference it belongs to. - /// - private sealed class Operation + #region Operation + + /// + /// Provides a snapshot of an entity and the repository reference it belongs to. + /// + private sealed class Operation { /// /// Gets or sets the entity. @@ -199,10 +204,6 @@ namespace Umbraco.Core.Persistence.UnitOfWork _scope.Dispose(); _scope = null; } - - public IEventManager EventManager - { - get { throw new NotImplementedException(); } - } + } } \ No newline at end of file From 9965aa461a9d4535f63e5b8e0b970acd508ff703 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 19 Jan 2017 17:18:06 +1100 Subject: [PATCH 288/599] Updates ContentService to raise events all with the EventManager --- src/Umbraco.Core/Services/ContentService.cs | 264 +++++++++++--------- 1 file changed, 143 insertions(+), 121 deletions(-) diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 5751bba542..343b4b66ad 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -164,8 +164,9 @@ namespace Umbraco.Core.Services var parent = GetById(content.ParentId); content.Path = string.Concat(parent.IfNotNull(x => x.Path, content.ParentId.ToString()), ",", content.Id); + var uow = UowProvider.GetUnitOfWork(); - if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parentId), this)) + if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parentId), this, uow.EventManager)) { content.WasCancelled = true; return content; @@ -174,9 +175,8 @@ namespace Umbraco.Core.Services content.CreatorId = userId; content.WriterId = userId; - Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parentId), this); - - var uow = UowProvider.GetUnitOfWork(); + Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parentId), this, uow.EventManager); + using (var auditRepo = RepositoryFactory.CreateAuditRepository(uow)) { auditRepo.AddOrUpdate(new AuditItem(content.Id, string.Format("Content '{0}' was created", name), AuditType.New, content.CreatorId)); @@ -208,20 +208,23 @@ namespace Umbraco.Core.Services var content = new Content(name, parent, contentType); content.Path = string.Concat(parent.Path, ",", content.Id); - if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parent), this)) + using (var uow = UowProvider.GetUnitOfWork()) { - content.WasCancelled = true; + if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parent), this, uow.EventManager)) + { + content.WasCancelled = true; + return content; + } + + content.CreatorId = userId; + content.WriterId = userId; + + Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parent), this, uow.EventManager); + + Audit(AuditType.New, string.Format("Content '{0}' was created", name), content.CreatorId, content.Id); + return content; } - - content.CreatorId = userId; - content.WriterId = userId; - - Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parent), this); - - Audit(AuditType.New, string.Format("Content '{0}' was created", name), content.CreatorId, content.Id); - - return content; } /// @@ -242,21 +245,22 @@ namespace Umbraco.Core.Services var contentType = FindContentTypeByAlias(contentTypeAlias); var content = new Content(name, parentId, contentType); + var uow = UowProvider.GetUnitOfWork(); + //NOTE: I really hate the notion of these Creating/Created events - they are so inconsistent, I've only just found // out that in these 'WithIdentity' methods, the Saving/Saved events were not fired, wtf. Anyways, they're added now. - if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parentId), this)) + if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parentId), this, uow.EventManager)) { content.WasCancelled = true; return content; } - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this)) + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this, uow.EventManager)) { content.WasCancelled = true; return content; } - - var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { content.CreatorId = userId; @@ -265,12 +269,12 @@ namespace Umbraco.Core.Services //Generate a new preview repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c)); uow.Commit(); + + Saved.RaiseEvent(new SaveEventArgs(content, false), this, uow.EventManager); + + Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parentId), this, uow.EventManager); } - Saved.RaiseEvent(new SaveEventArgs(content, false), this); - - Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parentId), this); - Audit(AuditType.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id); return content; @@ -296,21 +300,22 @@ namespace Umbraco.Core.Services var contentType = FindContentTypeByAlias(contentTypeAlias); var content = new Content(name, parent, contentType); + var uow = UowProvider.GetUnitOfWork(); + //NOTE: I really hate the notion of these Creating/Created events - they are so inconsistent, I've only just found // out that in these 'WithIdentity' methods, the Saving/Saved events were not fired, wtf. Anyways, they're added now. - if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parent), this)) + if (Creating.IsRaisedEventCancelled(new NewEventArgs(content, contentTypeAlias, parent), this, uow.EventManager)) { content.WasCancelled = true; return content; } - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this)) + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(content), this, uow.EventManager)) { content.WasCancelled = true; return content; } - - var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { content.CreatorId = userId; @@ -319,12 +324,12 @@ namespace Umbraco.Core.Services //Generate a new preview repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c)); uow.Commit(); + + Saved.RaiseEvent(new SaveEventArgs(content, false), this, uow.EventManager); + + Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parent), this, uow.EventManager); } - Saved.RaiseEvent(new SaveEventArgs(content, false), this); - - Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parent), this); - Audit(AuditType.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id); return content; @@ -1003,6 +1008,8 @@ namespace Umbraco.Core.Services using (new WriteLock(Locker)) { + var uow = UowProvider.GetUnitOfWork(); + //Hack: this ensures that the entity's path is valid and if not it fixes/persists it //see: http://issues.umbraco.org/issue/U4-9336 content.EnsureValidPath(Logger, entity => GetById(entity.ParentId), QuickUpdate); @@ -1011,7 +1018,7 @@ namespace Umbraco.Core.Services if (Trashing.IsRaisedEventCancelled( new MoveEventArgs(evtMsgs, new MoveEventInfo(content, originalPath, Constants.System.RecycleBinContent)), - this)) + this, uow.EventManager)) { return OperationStatus.Cancelled(evtMsgs); } @@ -1035,8 +1042,7 @@ namespace Umbraco.Core.Services //TODO: this shouldn't be a 'sub operation', and if it needs to be it cannot raise events and cannot be cancelled! UnPublish(descendant, userId); } - - var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { content.WriterId = userId; @@ -1056,7 +1062,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - Trashed.RaiseEvent(new MoveEventArgs(false, evtMsgs, moveInfo.ToArray()), this); + Trashed.RaiseEvent(new MoveEventArgs(false, evtMsgs, moveInfo.ToArray()), this, uow.EventManager); Audit(AuditType.Move, "Move Content to Recycle Bin performed by user", userId, content.Id); @@ -1177,11 +1183,13 @@ namespace Umbraco.Core.Services var evtMsgs = EventMessagesFactory.Get(); + var uow = UowProvider.GetUnitOfWork(); + if (raiseEvents) { if (Saving.IsRaisedEventCancelled( new SaveEventArgs(asArray, evtMsgs), - this)) + this, uow.EventManager)) { return OperationStatus.Cancelled(evtMsgs); } @@ -1189,8 +1197,7 @@ namespace Umbraco.Core.Services using (new WriteLock(Locker)) { var containsNew = asArray.Any(x => x.HasIdentity == false); - - var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { if (containsNew) @@ -1223,7 +1230,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - Saved.RaiseEvent(new SaveEventArgs(asArray, false, evtMsgs), this); + Saved.RaiseEvent(new SaveEventArgs(asArray, false, evtMsgs), this, uow.EventManager); Audit(AuditType.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, Constants.System.Root); @@ -1246,9 +1253,11 @@ namespace Umbraco.Core.Services using (new WriteLock(Locker)) { + var uow = UowProvider.GetUnitOfWork(); + if (Deleting.IsRaisedEventCancelled( new DeleteEventArgs(content, evtMsgs), - this)) + this, uow.EventManager)) { return OperationStatus.Cancelled(evtMsgs); } @@ -1265,15 +1274,14 @@ namespace Umbraco.Core.Services { Delete(child, userId); } - - var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { repository.Delete(content); uow.Commit(); var args = new DeleteEventArgs(content, false, evtMsgs); - Deleted.RaiseEvent(args, this); + Deleted.RaiseEvent(args, this, uow.EventManager); //remove any flagged media files repository.DeleteMediaFiles(args.MediaFilesToDelete); @@ -1346,7 +1354,7 @@ namespace Umbraco.Core.Services var query = Query.Builder.Where(x => x.ContentTypeId == contentTypeId); var contents = repository.GetByQuery(query).ToArray(); - if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(contents), this)) + if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(contents), this, uow.EventManager)) return; foreach (var content in contents.OrderByDescending(x => x.ParentId)) @@ -1396,17 +1404,18 @@ namespace Umbraco.Core.Services /// Optional Id of the User deleting versions of a Content object public void DeleteVersions(int id, DateTime versionDate, int userId = 0) { - if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, dateToRetain: versionDate), this)) - return; - var uow = UowProvider.GetUnitOfWork(); + + if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, dateToRetain: versionDate), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { repository.DeleteVersions(id, versionDate); uow.Commit(); } - DeletedVersions.RaiseEvent(new DeleteRevisionsEventArgs(id, false, dateToRetain: versionDate), this); + DeletedVersions.RaiseEvent(new DeleteRevisionsEventArgs(id, false, dateToRetain: versionDate), this, uow.EventManager); Audit(AuditType.Delete, "Delete Content by version date performed by user", userId, Constants.System.Root); } @@ -1423,7 +1432,9 @@ namespace Umbraco.Core.Services { using (new WriteLock(Locker)) { - if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, specificVersion: versionId), this)) + var uow = UowProvider.GetUnitOfWork(); + + if (DeletingVersions.IsRaisedEventCancelled(new DeleteRevisionsEventArgs(id, specificVersion: versionId), this, uow.EventManager)) return; if (deletePriorVersions) @@ -1431,15 +1442,14 @@ namespace Umbraco.Core.Services var content = GetByVersion(versionId); DeleteVersions(id, content.UpdateDate, userId); } - - var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { repository.DeleteVersion(versionId); uow.Commit(); } - DeletedVersions.RaiseEvent(new DeleteRevisionsEventArgs(id, false, specificVersion: versionId), this); + DeletedVersions.RaiseEvent(new DeleteRevisionsEventArgs(id, false, specificVersion: versionId), this, uow.EventManager); Audit(AuditType.Delete, "Delete Content by version performed by user", userId, Constants.System.Root); } @@ -1478,21 +1488,24 @@ namespace Umbraco.Core.Services return; } - if (Moving.IsRaisedEventCancelled( - new MoveEventArgs( - new MoveEventInfo(content, content.Path, parentId)), this)) + using (var uow = UowProvider.GetUnitOfWork()) { - return; + if (Moving.IsRaisedEventCancelled( + new MoveEventArgs( + new MoveEventInfo(content, content.Path, parentId)), this, uow.EventManager)) + { + return; + } + + //used to track all the moved entities to be given to the event + var moveInfo = new List>(); + + //call private method that does the recursive moving + PerformMove(content, parentId, userId, moveInfo); + + Moved.RaiseEvent(new MoveEventArgs(false, moveInfo.ToArray()), this, uow.EventManager); } - //used to track all the moved entities to be given to the event - var moveInfo = new List>(); - - //call private method that does the recursive moving - PerformMove(content, parentId, userId, moveInfo); - - Moved.RaiseEvent(new MoveEventArgs(false, moveInfo.ToArray()), this); - Audit(AuditType.Move, "Move Content performed by user", userId, content.Id); } } @@ -1509,7 +1522,8 @@ namespace Umbraco.Core.Services bool success; var nodeObjectType = new Guid(Constants.ObjectTypes.Document); - using (var repository = RepositoryFactory.CreateContentRepository(UowProvider.GetUnitOfWork())) + var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { //Create a dictionary of ids -> dictionary of property aliases + values entities = repository.GetEntitiesInRecycleBin() @@ -1519,12 +1533,12 @@ namespace Umbraco.Core.Services files = ((ContentRepository)repository).GetFilesInRecycleBinForUploadField(); - if (EmptyingRecycleBin.IsRaisedEventCancelled(new RecycleBinEventArgs(nodeObjectType, entities, files), this)) + if (EmptyingRecycleBin.IsRaisedEventCancelled(new RecycleBinEventArgs(nodeObjectType, entities, files), this, uow.EventManager)) return; success = repository.EmptyRecycleBin(); - EmptiedRecycleBin.RaiseEvent(new RecycleBinEventArgs(nodeObjectType, entities, files, success), this); + EmptiedRecycleBin.RaiseEvent(new RecycleBinEventArgs(nodeObjectType, entities, files, success), this, uow.EventManager); if (success) repository.DeleteMediaFiles(files); @@ -1570,10 +1584,11 @@ namespace Umbraco.Core.Services // A copy should never be set to published automatically even if the original was. copy.ChangePublishedState(PublishedState.Unpublished); - if (Copying.IsRaisedEventCancelled(new CopyEventArgs(content, copy, parentId), this)) - return null; - var uow = UowProvider.GetUnitOfWork(); + + if (Copying.IsRaisedEventCancelled(new CopyEventArgs(content, copy, parentId), this, uow.EventManager)) + return null; + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { // Update the create author and last edit author @@ -1612,7 +1627,7 @@ namespace Umbraco.Core.Services } } - Copied.RaiseEvent(new CopyEventArgs(content, copy, false, parentId, relateToOriginal), this); + Copied.RaiseEvent(new CopyEventArgs(content, copy, false, parentId, relateToOriginal), this, uow.EventManager); Audit(AuditType.Copy, "Copy Content performed by user", content.WriterId, content.Id); return copy; @@ -1628,17 +1643,20 @@ namespace Umbraco.Core.Services /// True if sending publication was succesfull otherwise false public bool SendToPublication(IContent content, int userId = 0) { - if (SendingToPublish.IsRaisedEventCancelled(new SendToPublishEventArgs(content), this)) - return false; + using (var uow = UowProvider.GetUnitOfWork()) + { + if (SendingToPublish.IsRaisedEventCancelled(new SendToPublishEventArgs(content), this, uow.EventManager)) + return false; - //Save before raising event - Save(content, userId); + //Save before raising event + Save(content, userId); - SentToPublish.RaiseEvent(new SendToPublishEventArgs(content, false), this); + SentToPublish.RaiseEvent(new SendToPublishEventArgs(content, false), this, uow.EventManager); - Audit(AuditType.SendToPublish, "Send to Publish performed by user", content.WriterId, content.Id); + Audit(AuditType.SendToPublish, "Send to Publish performed by user", content.WriterId, content.Id); - return true; + return true; + } } /// @@ -1657,10 +1675,11 @@ namespace Umbraco.Core.Services { var content = GetByVersion(versionId); - if (RollingBack.IsRaisedEventCancelled(new RollbackEventArgs(content), this)) - return content; - var uow = UowProvider.GetUnitOfWork(); + + if (RollingBack.IsRaisedEventCancelled(new RollbackEventArgs(content), this, uow.EventManager)) + return content; + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { content.WriterId = userId; @@ -1673,7 +1692,7 @@ namespace Umbraco.Core.Services uow.Commit(); } - RolledBack.RaiseEvent(new RollbackEventArgs(content, false), this); + RolledBack.RaiseEvent(new RollbackEventArgs(content, false), this, uow.EventManager); Audit(AuditType.RollBack, "Content rollback performed by user", content.WriterId, content.Id); @@ -1694,19 +1713,20 @@ namespace Umbraco.Core.Services /// True if sorting succeeded, otherwise False public bool Sort(IEnumerable items, int userId = 0, bool raiseEvents = true) { - var asArray = items.ToArray(); - if (raiseEvents) - { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(asArray), this)) - return false; - } - var shouldBePublished = new List(); var shouldBeSaved = new List(); using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + var asArray = items.ToArray(); + if (raiseEvents) + { + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(asArray), this, uow.EventManager)) + return false; + } + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { int i = 0; @@ -1747,15 +1767,15 @@ namespace Umbraco.Core.Services uow.Commit(); } - } - if (raiseEvents) - Saved.RaiseEvent(new SaveEventArgs(asArray, false), this); + if (raiseEvents) + Saved.RaiseEvent(new SaveEventArgs(asArray, false), this, uow.EventManager); - if (shouldBePublished.Any()) - { - //TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable! - _publishingStrategy.PublishingFinalized(shouldBePublished, false); + if (shouldBePublished.Any()) + { + //TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable! + _publishingStrategy.PublishingFinalized(shouldBePublished, false); + } } @@ -2080,19 +2100,21 @@ namespace Umbraco.Core.Services /// True if publishing succeeded, otherwise False private Attempt SaveAndPublishDo(IContent content, int userId = 0, bool raiseEvents = true) { - var evtMsgs = EventMessagesFactory.Get(); - - if (raiseEvents) - { - if (Saving.IsRaisedEventCancelled( - new SaveEventArgs(content, evtMsgs), this)) - { - return Attempt.Fail(new PublishStatus(content, PublishStatusType.FailedCancelledByEvent, evtMsgs)); - } - } + var evtMsgs = EventMessagesFactory.Get(); using (new WriteLock(Locker)) { + var uow = UowProvider.GetUnitOfWork(); + + if (raiseEvents) + { + if (Saving.IsRaisedEventCancelled( + new SaveEventArgs(content, evtMsgs), this, uow.EventManager)) + { + return Attempt.Fail(new PublishStatus(content, PublishStatusType.FailedCancelledByEvent, evtMsgs)); + } + } + //Has this content item previously been published? If so, we don't need to refresh the children var previouslyPublished = content.HasIdentity && HasPublishedVersion(content.Id); //content might not have an id var publishStatus = new PublishStatus(content, PublishStatusType.Success, evtMsgs); //initially set to success @@ -2119,8 +2141,7 @@ namespace Umbraco.Core.Services //we are successfully published if our publishStatus is still Successful bool published = publishStatus.StatusType == PublishStatusType.Success; - - var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { if (published == false) @@ -2149,7 +2170,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - Saved.RaiseEvent(new SaveEventArgs(content, false, evtMsgs), this); + Saved.RaiseEvent(new SaveEventArgs(content, false, evtMsgs), this, uow.EventManager); //Save xml to db and call following method to fire event through PublishingStrategy to update cache if (published) @@ -2181,20 +2202,21 @@ namespace Umbraco.Core.Services private Attempt Save(IContent content, bool changeState, int userId = 0, bool raiseEvents = true) { var evtMsgs = EventMessagesFactory.Get(); - - if (raiseEvents) - { - if (Saving.IsRaisedEventCancelled( - new SaveEventArgs(content, evtMsgs), - this)) - { - return OperationStatus.Cancelled(evtMsgs); - } - } - + using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + if (raiseEvents) + { + if (Saving.IsRaisedEventCancelled( + new SaveEventArgs(content, evtMsgs), + this, uow.EventManager)) + { + return OperationStatus.Cancelled(evtMsgs); + } + } + using (var repository = RepositoryFactory.CreateContentRepository(uow)) { if (content.HasIdentity == false) @@ -2216,7 +2238,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - Saved.RaiseEvent(new SaveEventArgs(content, false, evtMsgs), this); + Saved.RaiseEvent(new SaveEventArgs(content, false, evtMsgs), this, uow.EventManager); Audit(AuditType.Save, "Save Content performed by user", userId, content.Id); From 2a95ed3c7a4473aa707dab4cc3bee6741a237935 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 19 Jan 2017 17:29:36 +1100 Subject: [PATCH 289/599] no need for explicit implementation for this since we changed the old one --- .../Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs index ef311f9d49..e06d522499 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs @@ -79,7 +79,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork } //explicit implementation - IScopeUnitOfWork IScopeUnitOfWorkProvider.GetUnitOfWork(IsolationLevel isolationLevel) + public IScopeUnitOfWork GetUnitOfWork(IsolationLevel isolationLevel) { return new PetaPocoUnitOfWork(_scopeProvider, isolationLevel); } From a275567dbaf00eb454623f42dbfd3f8ee7e3b612 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 19 Jan 2017 17:34:15 +1100 Subject: [PATCH 290/599] Adds equality members to the EventDefinitionBase so they are tracked by their unique id, makes IEventManager disposable and disposes it on scope end. --- .../Events/EventDefinitionBase.cs | 32 ++++++++++++++++++- src/Umbraco.Core/Events/IEventManager.cs | 2 +- .../Events/NoScopedEventManager.cs | 10 +++++- src/Umbraco.Core/Events/ScopedEventManager.cs | 10 +++++- .../UnitOfWork/PetaPocoUnitOfWorkProvider.cs | 10 +++++- src/Umbraco.Core/Scoping/Scope.cs | 5 +++ 6 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Events/EventDefinitionBase.cs b/src/Umbraco.Core/Events/EventDefinitionBase.cs index 4e33a6f6e6..754ed2fce5 100644 --- a/src/Umbraco.Core/Events/EventDefinitionBase.cs +++ b/src/Umbraco.Core/Events/EventDefinitionBase.cs @@ -2,7 +2,7 @@ using System; namespace Umbraco.Core.Events { - public abstract class EventDefinitionBase : IEventDefinition + public abstract class EventDefinitionBase : IEventDefinition, IEquatable { protected EventDefinitionBase() { @@ -10,5 +10,35 @@ namespace Umbraco.Core.Events } public Guid EventId { get; private set; } public abstract void RaiseEvent(); + + public bool Equals(EventDefinitionBase other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return EventId.Equals(other.EventId); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((EventDefinitionBase) obj); + } + + public override int GetHashCode() + { + return EventId.GetHashCode(); + } + + public static bool operator ==(EventDefinitionBase left, EventDefinitionBase right) + { + return Equals(left, right); + } + + public static bool operator !=(EventDefinitionBase left, EventDefinitionBase right) + { + return Equals(left, right) == false; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/IEventManager.cs b/src/Umbraco.Core/Events/IEventManager.cs index 21339f07b1..d22d31f930 100644 --- a/src/Umbraco.Core/Events/IEventManager.cs +++ b/src/Umbraco.Core/Events/IEventManager.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; namespace Umbraco.Core.Events { - public interface IEventManager + public interface IEventManager : IDisposable { IEnumerable GetEvents(); void TrackEvent(EventHandler e, object sender, EventArgs args); diff --git a/src/Umbraco.Core/Events/NoScopedEventManager.cs b/src/Umbraco.Core/Events/NoScopedEventManager.cs index 35f964bf0a..27bb68f8e6 100644 --- a/src/Umbraco.Core/Events/NoScopedEventManager.cs +++ b/src/Umbraco.Core/Events/NoScopedEventManager.cs @@ -4,7 +4,10 @@ using System.Linq; namespace Umbraco.Core.Events { - internal class NoScopedEventManager : IEventManager + /// + /// This event manager supports event cancellation and will raise the events as soon as they are tracked, it does not store tracked events + /// + internal class NoScopedEventManager : DisposableObject, IEventManager { public void TrackEvent(EventHandler e, object sender, EventArgs args) { @@ -30,5 +33,10 @@ namespace Umbraco.Core.Events { get { return true; } } + + protected override void DisposeResources() + { + //noop + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/ScopedEventManager.cs b/src/Umbraco.Core/Events/ScopedEventManager.cs index 01e2d5e23f..354858cdb1 100644 --- a/src/Umbraco.Core/Events/ScopedEventManager.cs +++ b/src/Umbraco.Core/Events/ScopedEventManager.cs @@ -3,7 +3,10 @@ using System.Collections.Generic; namespace Umbraco.Core.Events { - internal class ScopedEventManager : IEventManager + /// + /// This event manager does not support event cancellation and will track all raised events in a list which can be retrieved + /// + internal class ScopedEventManager : DisposableObject, IEventManager { public void TrackEvent(EventHandler e, object sender, EventArgs args) { @@ -31,5 +34,10 @@ namespace Umbraco.Core.Events { get { return false; } } + + protected override void DisposeResources() + { + _tracked.Clear(); + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs index e06d522499..7bcfcba2b9 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs @@ -78,7 +78,15 @@ namespace Umbraco.Core.Persistence.UnitOfWork return new PetaPocoUnitOfWork(_scopeProvider); } - //explicit implementation + /// + /// Creates a Unit of work with a new UmbracoDatabase instance for the work item/transaction. + /// + /// + /// + /// Each PetaPoco UOW uses it's own Database object, not the shared Database object that comes from + /// the ApplicationContext.Current.DatabaseContext.Database. This is because each transaction should use it's own Database + /// and we Dispose of this Database object when the UOW is disposed. + /// public IScopeUnitOfWork GetUnitOfWork(IsolationLevel isolationLevel) { return new PetaPocoUnitOfWork(_scopeProvider, isolationLevel); diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index ba0092b9cb..a5f026412a 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -208,6 +208,11 @@ namespace Umbraco.Core.Scoping else DisposeLastScope(); + if (_eventManager != null) + { + _eventManager.Dispose(); + } + _disposed = true; GC.SuppressFinalize(this); } From b51e571803d652bbc06eb1e618bca482f809bd04 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 19 Jan 2017 18:25:45 +1100 Subject: [PATCH 291/599] Adds unit tests and fixes others --- .../Services/RepositoryService.cs | 13 --- .../Services/ScopeRepositoryService.cs | 21 +++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Scoping/NoScopedEventManagerTests.cs | 67 ++++++++++++++ src/Umbraco.Tests/Scoping/ScopeTests.cs | 1 + .../Scoping/ScopedEventManagerTests.cs | 90 +++++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 2 + .../Web/Mvc/UmbracoViewPageTests.cs | 2 +- 8 files changed, 183 insertions(+), 14 deletions(-) create mode 100644 src/Umbraco.Core/Services/ScopeRepositoryService.cs create mode 100644 src/Umbraco.Tests/Scoping/NoScopedEventManagerTests.cs create mode 100644 src/Umbraco.Tests/Scoping/ScopedEventManagerTests.cs diff --git a/src/Umbraco.Core/Services/RepositoryService.cs b/src/Umbraco.Core/Services/RepositoryService.cs index b1e0cf7d21..88807f0291 100644 --- a/src/Umbraco.Core/Services/RepositoryService.cs +++ b/src/Umbraco.Core/Services/RepositoryService.cs @@ -6,19 +6,6 @@ using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { - public abstract class ScopeRepositoryService : RepositoryService - { - protected ScopeRepositoryService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) - : base(provider, repositoryFactory, logger, eventMessagesFactory) - { - var scopeUow = provider as IScopeUnitOfWorkProvider; - if (scopeUow == null) throw new NotSupportedException("The provider type passed in: " + provider.GetType() + " is not of type " + typeof(IScopeUnitOfWorkProvider)); - UowProvider = scopeUow; - } - - internal new IScopeUnitOfWorkProvider UowProvider { get; private set; } - } - /// /// Base service class /// diff --git a/src/Umbraco.Core/Services/ScopeRepositoryService.cs b/src/Umbraco.Core/Services/ScopeRepositoryService.cs new file mode 100644 index 0000000000..0942aefd6f --- /dev/null +++ b/src/Umbraco.Core/Services/ScopeRepositoryService.cs @@ -0,0 +1,21 @@ +using System; +using Umbraco.Core.Events; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Services +{ + public abstract class ScopeRepositoryService : RepositoryService + { + protected ScopeRepositoryService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory) + : base(provider, repositoryFactory, logger, eventMessagesFactory) + { + var scopeUow = provider as IScopeUnitOfWorkProvider; + if (scopeUow == null) throw new NotSupportedException("The provider type passed in: " + provider.GetType() + " is not of type " + typeof(IScopeUnitOfWorkProvider)); + UowProvider = scopeUow; + } + + internal new IScopeUnitOfWorkProvider UowProvider { get; private set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index cb4a157d18..88555db603 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -574,6 +574,7 @@ + diff --git a/src/Umbraco.Tests/Scoping/NoScopedEventManagerTests.cs b/src/Umbraco.Tests/Scoping/NoScopedEventManagerTests.cs new file mode 100644 index 0000000000..6975b578ab --- /dev/null +++ b/src/Umbraco.Tests/Scoping/NoScopedEventManagerTests.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Events; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Scoping; + +namespace Umbraco.Tests.Scoping +{ + [TestFixture] + public class NoScopedEventManagerTests + { + + [Test] + public void Does_Support_Event_Cancellation() + { + var scopeProvider = new ScopeProvider(Mock.Of()); + Assert.IsTrue(scopeProvider.AmbientOrNoScope.EventManager.SupportsEventCancellation); + } + + [Test] + public void Does_Immediately_Raise_Events() + { + var counter = 0; + + this.DoThing1 += (sender, args) => + { + counter++; + }; + + this.DoThing2 += (sender, args) => + { + counter++; + }; + + this.DoThing3 += (sender, args) => + { + counter++; + }; + + var scopeProvider = new ScopeProvider(Mock.Of()); + scopeProvider.AmbientOrNoScope.EventManager.TrackEvent(DoThing1, this, new EventArgs()); + scopeProvider.AmbientOrNoScope.EventManager.TrackEvent(DoThing2, this, new EventArgs()); + scopeProvider.AmbientOrNoScope.EventManager.TrackEvent(DoThing3, this, new EventArgs()); + + Assert.AreEqual(3, counter); + + } + + [Test] + public void Can_Not_Raise_Events_Later() + { + var scopeProvider = new ScopeProvider(Mock.Of()); + scopeProvider.AmbientOrNoScope.EventManager.TrackEvent(DoThing1, this, new EventArgs()); + scopeProvider.AmbientOrNoScope.EventManager.TrackEvent(DoThing2, this, new EventArgs()); + scopeProvider.AmbientOrNoScope.EventManager.TrackEvent(DoThing3, this, new EventArgs()); + + Assert.AreEqual(0, scopeProvider.AmbientOrNoScope.EventManager.GetEvents().Count()); + } + + public event EventHandler DoThing1; + public event EventHandler DoThing2; + public event TypedEventHandler DoThing3; + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Scoping/ScopeTests.cs b/src/Umbraco.Tests/Scoping/ScopeTests.cs index f6a9ab71df..c8ec425bca 100644 --- a/src/Umbraco.Tests/Scoping/ScopeTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeTests.cs @@ -4,6 +4,7 @@ using Umbraco.Core; using Umbraco.Core.Persistence; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; +using Umbraco.Core.Events; namespace Umbraco.Tests.Scoping { diff --git a/src/Umbraco.Tests/Scoping/ScopedEventManagerTests.cs b/src/Umbraco.Tests/Scoping/ScopedEventManagerTests.cs new file mode 100644 index 0000000000..3ddd0f7e08 --- /dev/null +++ b/src/Umbraco.Tests/Scoping/ScopedEventManagerTests.cs @@ -0,0 +1,90 @@ +using System; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Events; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Scoping; + +namespace Umbraco.Tests.Scoping +{ + [TestFixture] + public class ScopedEventManagerTests + { + + [Test] + public void Does_Not_Support_Event_Cancellation() + { + var provider = new PetaPocoUnitOfWorkProvider(new ScopeProvider(Mock.Of())); + using (var uow = provider.GetUnitOfWork()) + { + Assert.IsFalse(uow.EventManager.SupportsEventCancellation); + } + } + + [Test] + public void Does_Not_Immediately_Raise_Events() + { + this.DoThing1 += OnDoThingFail; + this.DoThing2 += OnDoThingFail; + this.DoThing3 += OnDoThingFail; + + var provider = new PetaPocoUnitOfWorkProvider(new ScopeProvider(Mock.Of())); + using (var uow = provider.GetUnitOfWork()) + { + uow.EventManager.TrackEvent(DoThing1, this, new EventArgs()); + uow.EventManager.TrackEvent(DoThing2, this, new EventArgs()); + uow.EventManager.TrackEvent(DoThing3, this, new EventArgs()); + + Assert.Pass(); + } + } + + [Test] + public void Can_Raise_Events_Later() + { + var counter = 0; + + this.DoThing1 += (sender, args) => + { + counter++; + }; + + this.DoThing2 += (sender, args) => + { + counter++; + }; + + this.DoThing3 += (sender, args) => + { + counter++; + }; + + var provider = new PetaPocoUnitOfWorkProvider(new ScopeProvider(Mock.Of())); + using (var uow = provider.GetUnitOfWork()) + { + uow.EventManager.TrackEvent(DoThing1, this, new EventArgs()); + uow.EventManager.TrackEvent(DoThing2, this, new EventArgs()); + uow.EventManager.TrackEvent(DoThing3, this, new EventArgs()); + + Assert.AreEqual(0, counter); + + foreach (var e in uow.EventManager.GetEvents()) + { + e.RaiseEvent(); + } + + Assert.AreEqual(3, counter); + } + } + + private void OnDoThingFail(object sender, EventArgs eventArgs) + { + Assert.Fail(); + } + + public event EventHandler DoThing1; + public event EventHandler DoThing2; + public event TypedEventHandler DoThing3; + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index bdfcac3420..45cfc6cfd7 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -170,6 +170,8 @@ + + diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index 3ba6e06c7c..f83b8a6369 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -396,7 +396,7 @@ namespace Umbraco.Tests.Web.Mvc new Mock().Object), new Mock().Object, new RelationService( - new Mock().Object, + new Mock().Object, new RepositoryFactory(CacheHelper.CreateDisabledCacheHelper(), logger, Mock.Of(), umbracoSettings), logger, new TransientMessagesFactory(), From 051af5c48e1708f0f098ad00c9025de733aad613 Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Thu, 19 Jan 2017 09:49:27 +0100 Subject: [PATCH 292/599] Wire up the editors and controllers --- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 8 - src/Umbraco.Web.UI/config/trees.config | 7 +- .../umbraco/settings/views/EditView.aspx | 88 ------ .../umbraco/settings/views/EditView.aspx.cs | 280 ------------------ .../settings/views/EditView.aspx.designer.cs | 132 --------- src/Umbraco.Web/Editors/CodeFileController.cs | 6 +- .../Trees/PartialViewMacrosTree.cs | 49 --- .../Trees/PartialViewMacrosTreeController.cs | 49 +++ src/Umbraco.Web/Trees/PartialViewsTree.cs | 88 ------ .../Trees/PartialViewsTreeController.cs | 49 +++ src/Umbraco.Web/Trees/ScriptTreeController.cs | 46 +++ src/Umbraco.Web/Umbraco.Web.csproj | 5 +- 12 files changed, 154 insertions(+), 653 deletions(-) delete mode 100644 src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx delete mode 100644 src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx.cs delete mode 100644 src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx.designer.cs delete mode 100644 src/Umbraco.Web/Trees/PartialViewMacrosTree.cs create mode 100644 src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs delete mode 100644 src/Umbraco.Web/Trees/PartialViewsTree.cs create mode 100644 src/Umbraco.Web/Trees/PartialViewsTreeController.cs create mode 100644 src/Umbraco.Web/Trees/ScriptTreeController.cs diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 27e437d8bf..2c4cd4859d 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -562,13 +562,6 @@ EditStyleSheetProperty.aspx - - EditView.aspx - ASPXCodeBehind - - - EditView.aspx - treeInit.aspx ASPXCodeBehind @@ -751,7 +744,6 @@ - diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index 81c0c03bc8..13da9246b7 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -9,10 +9,10 @@ - + - + @@ -22,7 +22,7 @@ - + @@ -41,5 +41,4 @@ - \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx b/src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx deleted file mode 100644 index 6f5518706c..0000000000 --- a/src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx +++ /dev/null @@ -1,88 +0,0 @@ -<%@ Page Language="C#" MasterPageFile="../../masterpages/umbracoPage.Master" AutoEventWireup="True" - CodeBehind="EditView.aspx.cs" Inherits="Umbraco.Web.UI.Umbraco.Settings.Views.EditView" - ValidateRequest="False" %> - -<%@ OutputCache Location="None" %> - -<%@ Import Namespace="Umbraco.Core" %> -<%@ Import Namespace="Umbraco.Core.IO" %> -<%@ Import Namespace="Umbraco.Web" %> -<%@ Register TagPrefix="cc1" Namespace="umbraco.uicontrols" Assembly="controls" %> -<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx.cs b/src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx.cs deleted file mode 100644 index c08e4ba48c..0000000000 --- a/src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx.cs +++ /dev/null @@ -1,280 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Web; -using System.Web.UI; -using System.Web.UI.WebControls; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Web.Trees; -using Umbraco.Web.UI.Controls; -using umbraco; -using umbraco.BasePages; -using umbraco.cms.businesslogic.template; -using umbraco.cms.helpers; -using umbraco.cms.presentation.Trees; -using Umbraco.Core; -using umbraco.uicontrols; - -namespace Umbraco.Web.UI.Umbraco.Settings.Views -{ - public partial class EditView : global::umbraco.BasePages.UmbracoEnsuredPage - { - private Template _template; - public MenuButton SaveButton; - - public EditView() - { - CurrentApp = global::umbraco.BusinessLogic.DefaultApps.settings.ToString(); - } - - /// - /// The type of MVC/Umbraco view the editor is editing - /// - public enum ViewEditorType - { - Template, - PartialView, - PartialViewMacro - } - - /// - /// Returns the type of view being edited - /// - protected ViewEditorType EditorType - { - get - { - if (_template != null) return ViewEditorType.Template; - if (Request.QueryString["treeType"].IsNullOrWhiteSpace() == false && Request.QueryString["treeType"].InvariantEquals("partialViewMacros")) return ViewEditorType.PartialViewMacro; - return ViewEditorType.PartialView; - } - } - - protected string TemplateTreeSyncPath { get; private set; } - - /// - /// This view is shared between different trees so we'll look for the query string - /// - protected string CurrentTreeType - { - get - { - if (Request.QueryString["treeType"].IsNullOrWhiteSpace()) - { - return TreeDefinitionCollection.Instance.FindTree().Tree.Alias; - } - return Request.CleanForXss("treeType"); - } - } - - /// - /// Returns the original file name that the editor was loaded with - /// - /// - /// this is used for editing a partial view - /// - protected string OriginalFileName { get; private set; } - - protected override void OnLoad(EventArgs e) - { - base.OnLoad(e); - - if (!IsPostBack) - { - - //configure screen for editing a template - if (_template != null) - { - MasterTemplate.Items.Add(new ListItem(ui.Text("none"), "0")); - var selectedTemplate = string.Empty; - - foreach (var t in Template.GetAllAsList()) - { - if (t.Id == _template.Id) continue; - - var li = new ListItem(t.Text, t.Id.ToString(CultureInfo.InvariantCulture)); - li.Attributes.Add("id", t.Alias.Replace(" ", "") + ".cshtml"); - MasterTemplate.Items.Add(li); - } - - try - { - if (_template.MasterTemplate > 0) - MasterTemplate.SelectedValue = _template.MasterTemplate.ToString(CultureInfo.InvariantCulture); - } - catch (Exception ex) - { - LogHelper.Error("An error occurred setting a master template id", ex); - } - - MasterTemplate.SelectedValue = selectedTemplate; - NameTxt.Text = _template.GetRawText(); - AliasTxt.Text = _template.Alias; - editorSource.Text = _template.Design; - PathPrefix.Visible = false; - } - else - { - //configure editor for editing a file.... - - NameTxt.Text = OriginalFileName; - var svce = ApplicationContext.Current.Services.FileService; - var file = EditorType == ViewEditorType.PartialView - ? svce.GetPartialView(OriginalFileName) - : svce.GetPartialViewMacro(OriginalFileName); - editorSource.Text = file.Content; - - const string prefixFormat = "{0}"; - PathPrefix.Text = string.Format(prefixFormat, EditorType == ViewEditorType.PartialView - ? "Partials/" - : "MacroPartials/"); - } - } - - ClientTools - .SetActiveTreeType(CurrentTreeType) - .SyncTree(TemplateTreeSyncPath, false); - } - - - protected override void OnInit(EventArgs e) - { - base.OnInit(e); - - //check if a templateId is assigned, meaning we are editing a template - if (!Request.QueryString["templateID"].IsNullOrWhiteSpace()) - { - _template = new Template(int.Parse(Request.QueryString["templateID"])); - TemplateTreeSyncPath = "-1,init," + _template.Path.Replace("-1,", ""); - } - else if (!Request.QueryString["file"].IsNullOrWhiteSpace()) - { - //we are editing a view (i.e. partial view) - OriginalFileName = HttpUtility.UrlDecode(Request.QueryString["file"]); - - //TemplateTreeSyncPath = "-1,init," + Path.GetFileName(OriginalFileName); - - TemplateTreeSyncPath = DeepLink.GetTreePathFromFilePath(OriginalFileName.TrimStart("MacroPartials/").TrimStart("Partials/")); - } - else - { - throw new InvalidOperationException("Cannot render the editor without a supplied templateId or a file"); - } - - Panel1.hasMenu = true; - var editor = Panel1.NewTabPage(ui.Text("template")); - editor.Controls.Add(Pane8); - - var props = Panel1.NewTabPage(ui.Text("properties")); - props.Controls.Add(Pane7); - - - SaveButton = Panel1.Menu.NewButton(); - SaveButton.Text = ui.Text("save"); - SaveButton.ButtonType = MenuButtonType.Primary; - SaveButton.ID = "save"; - SaveButton.CssClass = "client-side"; - - Panel1.Text = ui.Text("edittemplate"); - pp_name.Text = ui.Text("name", base.getUser()); - pp_alias.Text = ui.Text("alias", base.getUser()); - pp_masterTemplate.Text = ui.Text("mastertemplate", base.getUser()); - - // Editing buttons - MenuIconI umbField = editorSource.Menu.NewIcon(); - umbField.ImageURL = UmbracoPath + "/images/editor/insField.gif"; - umbField.OnClickCommand = - ClientTools.Scripts.OpenModalWindow( - IOHelper.ResolveUrl(SystemDirectories.Umbraco) + "/dialogs/umbracoField.aspx?objectId=" + - editorSource.ClientID + "&tagName=UMBRACOGETDATA&mvcView=true", ui.Text("template", "insertPageField"), 640, 550); - umbField.AltText = ui.Text("template", "insertPageField"); - - - // TODO: Update icon - MenuIconI umbDictionary = editorSource.Menu.NewIcon(); - umbDictionary.ImageURL = GlobalSettings.Path + "/images/editor/dictionaryItem.gif"; - umbDictionary.OnClickCommand = - ClientTools.Scripts.OpenModalWindow( - IOHelper.ResolveUrl(SystemDirectories.Umbraco) + "/dialogs/umbracoField.aspx?objectId=" + - editorSource.ClientID + "&tagName=UMBRACOGETDICTIONARY&mvcView=true", ui.Text("template", "insertDictionaryItem"), - 640, 550); - umbDictionary.AltText = "Insert umbraco dictionary item"; - - var macroSplitButton = new InsertMacroSplitButton - { - ClientCallbackInsertMacroMarkup = "function(alias) {editViewEditor.insertMacroMarkup(alias);}", - ClientCallbackOpenMacroModel = "function(alias) {editViewEditor.openMacroModal(alias);}" - }; - editorSource.Menu.InsertNewControl(macroSplitButton, 40); - - MenuIconI umbTemplateQueryBuilder = editorSource.Menu.NewIcon(); - umbTemplateQueryBuilder.ImageURL = UmbracoPath + "/images/editor/inshtml.gif"; - umbTemplateQueryBuilder.OnClickCommand = "editViewEditor.openQueryModal()"; - umbTemplateQueryBuilder.AltText = "Open query builder"; - - if (_template == null) - { - InitializeEditorForPartialView(); - } - else - { - InitializeEditorForTemplate(); - } - - } - - protected override void OnPreRender(EventArgs e) - { - base.OnPreRender(e); - ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference("../webservices/codeEditorSave.asmx")); - ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference("../webservices/legacyAjaxCalls.asmx")); - } - - /// - /// Configure the editor for partial view editing - /// - private void InitializeEditorForPartialView() - { - pp_masterTemplate.Visible = false; - pp_alias.Visible = false; - pp_name.Text = "Filename"; - } - - /// - /// Configure the editor for editing a template - /// - private void InitializeEditorForTemplate() - { - - //TODO: implement content placeholders, etc... just like we had in v5 - - editorSource.Menu.InsertSplitter(); - - MenuIconI umbRenderBody = editorSource.Menu.NewIcon(); - umbRenderBody.ImageURL = UmbracoPath + "/images/editor/renderbody.gif"; - //umbContainer.AltText = ui.Text("template", "insertContentAreaPlaceHolder"); - umbRenderBody.AltText = "Insert @RenderBody()"; - - umbRenderBody.OnClickCommand = "editViewEditor.insertRenderBody()"; - - MenuIconI umbSection = editorSource.Menu.NewIcon(); - umbSection.ImageURL = UmbracoPath + "/images/editor/masterpagePlaceHolder.gif"; - //umbContainer.AltText = ui.Text("template", "insertContentAreaPlaceHolder"); - umbSection.AltText = "Insert Section"; - - umbSection.OnClickCommand = "editViewEditor.openSnippetModal('section')"; - - MenuIconI umbRenderSection = editorSource.Menu.NewIcon(); - umbRenderSection.ImageURL = UmbracoPath + "/images/editor/masterpageContent.gif"; - //umbContainer.AltText = ui.Text("template", "insertContentAreaPlaceHolder"); - umbRenderSection.AltText = "Insert @RenderSection"; - - umbRenderSection.OnClickCommand = "editViewEditor.openSnippetModal('rendersection')"; - - } - - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx.designer.cs b/src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx.designer.cs deleted file mode 100644 index dd81da023a..0000000000 --- a/src/Umbraco.Web.UI/umbraco/settings/views/EditView.aspx.designer.cs +++ /dev/null @@ -1,132 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Umbraco.Settings.Views { - - - public partial class EditView { - - /// - /// JsInclude1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.JsInclude JsInclude1; - - /// - /// Panel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.TabView Panel1; - - /// - /// Pane8 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane Pane8; - - /// - /// pp_source control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_source; - - /// - /// editorSource control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.CodeArea editorSource; - - /// - /// Pane7 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.Pane Pane7; - - /// - /// pp_name control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_name; - - /// - /// PathPrefix control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal PathPrefix; - - /// - /// NameTxt control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox NameTxt; - - /// - /// pp_alias control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_alias; - - /// - /// AliasTxt control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox AliasTxt; - - /// - /// pp_masterTemplate control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::umbraco.uicontrols.PropertyPanel pp_masterTemplate; - - /// - /// MasterTemplate control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.DropDownList MasterTemplate; - } -} diff --git a/src/Umbraco.Web/Editors/CodeFileController.cs b/src/Umbraco.Web/Editors/CodeFileController.cs index 647d49c8c9..45dd747c4c 100644 --- a/src/Umbraco.Web/Editors/CodeFileController.cs +++ b/src/Umbraco.Web/Editors/CodeFileController.cs @@ -194,12 +194,13 @@ namespace Umbraco.Web.Editors if (viewMacro != null) { viewMacro.Content = display.Content; + viewMacro.Path = display.Name; var result = Services.FileService.SavePartialViewMacro(viewMacro, Security.CurrentUser.Id); if (result.Success == false) { display.AddErrorNotification( - Services.TextService.Localize("speechBubbles/partialViewErrorHeader"), - Services.TextService.Localize("speechBubbles/partialViewErrorText")); + Services.TextService.Localize("speechBubbles/macroPartialViewErrorHeader"), + Services.TextService.Localize("speechBubbles/macroPartialViewErrorText")); } } else @@ -213,6 +214,7 @@ namespace Umbraco.Web.Editors if (script != null) { script.Content = display.Content; + script.Path = display.Name; Services.FileService.SaveScript(script, Security.CurrentUser.Id); } diff --git a/src/Umbraco.Web/Trees/PartialViewMacrosTree.cs b/src/Umbraco.Web/Trees/PartialViewMacrosTree.cs deleted file mode 100644 index dfc0636804..0000000000 --- a/src/Umbraco.Web/Trees/PartialViewMacrosTree.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Text; -using Umbraco.Core.IO; -using umbraco.businesslogic; -using umbraco.cms.presentation.Trees; -using Umbraco.Core; - -namespace Umbraco.Web.Trees -{ - /// - /// Tree for displaying partial view macros in the developer app - /// - [Tree(Constants.Applications.Developer, "partialViewMacros", "Partial View Macro Files", sortOrder: 6)] - public class PartialViewMacrosTree : PartialViewsTree - { - public PartialViewMacrosTree(string application) : base(application) - { - } - - protected override string FilePath - { - get { return SystemDirectories.MvcViews + "/MacroPartials/"; } - } - - public override void RenderJS(ref StringBuilder javascript) - { - javascript.Append( - @" - function openMacroPartialView(id) { - UmbClientMgr.contentFrame('Settings/Views/EditView.aspx?treeType=partialViewMacros&file=' + id); - } - "); - - }/// - /// Ensures that no folders can be added - /// - /// - protected override void OnRenderFolderNode(ref XmlTreeNode xNode) - { - base.OnRenderFolderNode(ref xNode); - - xNode.NodeType = "partialViewMacrosFolder"; - } - - protected override void ChangeNodeAction(XmlTreeNode xNode) - { - xNode.Action = xNode.Action.Replace("openFile", "openMacroPartialView"); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs b/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs new file mode 100644 index 0000000000..60b0711154 --- /dev/null +++ b/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs @@ -0,0 +1,49 @@ +using Umbraco.Core; +using Umbraco.Core.IO; +using umbraco.BusinessLogic.Actions; +using Umbraco.Web.Models.Trees; +using System.Net.Http.Formatting; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Trees +{ + /// + /// Tree for displaying partial view macros in the developer app + /// + [Tree(Constants.Applications.Developer, "partialViewMacros", "Partial View Macro Files", sortOrder: 6)] + public class PartialViewMacrosTreeController : FileSystemTreeController + { + protected override string FilePath + { + get { return SystemDirectories.MacroPartials; } + } + + protected override string FileSearchPattern + { + get { return "*.cshtml"; } + } + + protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) + { + var menu = new MenuItemCollection(); + + if (id == Constants.System.Root.ToInvariantString()) + { + + //refresh action + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); + + return menu; + } + + // TODO: Wire up new delete dialog + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); + return menu; + } + + protected override void OnRenderFileNode(ref TreeNode treeNode) + { + base.OnRenderFileNode(ref treeNode); + } + } +} diff --git a/src/Umbraco.Web/Trees/PartialViewsTree.cs b/src/Umbraco.Web/Trees/PartialViewsTree.cs deleted file mode 100644 index 11d6e3e804..0000000000 --- a/src/Umbraco.Web/Trees/PartialViewsTree.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Web; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using umbraco.BusinessLogic.Actions; -using umbraco.businesslogic; -using umbraco.cms.businesslogic.template; -using umbraco.cms.presentation.Trees; -using umbraco.interfaces; - -namespace Umbraco.Web.Trees -{ - /// - /// Tree for displaying partial views in the settings app - /// - [Tree(Constants.Applications.Settings, "partialViews", "Partial Views", sortOrder: 2)] - public class PartialViewsTree : FileSystemTree - { - public PartialViewsTree(string application) : base(application) { } - - public override void RenderJS(ref StringBuilder javascript) - { - javascript.Append( - @" - function openPartialView(id) { - UmbClientMgr.contentFrame('Settings/Views/EditView.aspx?treeType=partialViews&file=' + id); - } - "); - } - - protected override void CreateRootNode(ref XmlTreeNode rootNode) - { - rootNode.NodeType = TreeAlias; - rootNode.NodeID = "init"; - } - - protected override string FilePath - { - get { return SystemDirectories.MvcViews + "/Partials/"; } - } - - protected override string FileSearchPattern - { - get { return "*.cshtml"; } - } - - /// - /// Ensures that no folders can be added - /// - /// - protected override void OnRenderFolderNode(ref XmlTreeNode xNode) - { - // We should allow folder hierarchy for organization in large sites. - xNode.Action = "javascript:void(0);"; - xNode.NodeType = "partialViewsFolder"; - xNode.Menu = new List(new IAction[] - { - ActionNew.Instance, - ContextMenuSeperator.Instance, - ActionDelete.Instance, - ContextMenuSeperator.Instance, - ActionRefresh.Instance - }); - - } - - protected virtual void ChangeNodeAction(XmlTreeNode xNode) - { - xNode.Action = xNode.Action.Replace("openFile", "openPartialView"); - } - - protected override void OnRenderFileNode(ref XmlTreeNode xNode) - { - ChangeNodeAction(xNode); - xNode.Icon = "icon-article"; - xNode.OpenIcon = "icon-article"; - - xNode.Text = xNode.Text.StripFileExtension(); - } - - - } -} diff --git a/src/Umbraco.Web/Trees/PartialViewsTreeController.cs b/src/Umbraco.Web/Trees/PartialViewsTreeController.cs new file mode 100644 index 0000000000..de45148160 --- /dev/null +++ b/src/Umbraco.Web/Trees/PartialViewsTreeController.cs @@ -0,0 +1,49 @@ +using Umbraco.Core; +using Umbraco.Core.IO; +using umbraco.BusinessLogic.Actions; +using Umbraco.Web.Models.Trees; +using System.Net.Http.Formatting; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Trees +{ + /// + /// Tree for displaying partial views in the settings app + /// + [Tree(Constants.Applications.Settings, "partialViews", "Partial Views", sortOrder: 2)] + public class PartialViewsTreeController : FileSystemTreeController + { + protected override string FilePath + { + get { return SystemDirectories.PartialViews; } + } + + protected override string FileSearchPattern + { + get { return "*.cshtml"; } + } + + protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) + { + var menu = new MenuItemCollection(); + + if (id == Constants.System.Root.ToInvariantString()) + { + + //refresh action + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); + + return menu; + } + + // TODO: Wire up new delete dialog + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); + return menu; + } + + protected override void OnRenderFileNode(ref TreeNode treeNode) + { + base.OnRenderFileNode(ref treeNode); + } + } +} diff --git a/src/Umbraco.Web/Trees/ScriptTreeController.cs b/src/Umbraco.Web/Trees/ScriptTreeController.cs new file mode 100644 index 0000000000..ec8037ae9b --- /dev/null +++ b/src/Umbraco.Web/Trees/ScriptTreeController.cs @@ -0,0 +1,46 @@ +using Umbraco.Core; +using Umbraco.Core.IO; +using umbraco.BusinessLogic.Actions; +using Umbraco.Web.Models.Trees; +using System.Net.Http.Formatting; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Trees +{ + [Tree(Constants.Applications.Settings, "scripts", "Scripts", sortOrder: 4)] + public class ScriptTreeController : FileSystemTreeController + { + protected override string FilePath + { + get { return SystemDirectories.Scripts; } + } + + protected override string FileSearchPattern + { + get { return "*.js"; } + } + + protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) + { + var menu = new MenuItemCollection(); + + if (id == Constants.System.Root.ToInvariantString()) + { + + //refresh action + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); + + return menu; + } + + // TODO: Wire up new delete dialog + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); + return menu; + } + + protected override void OnRenderFileNode(ref TreeNode treeNode) + { + base.OnRenderFileNode(ref treeNode); + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index f9f6446491..2160297c71 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -394,6 +394,7 @@ + @@ -1048,8 +1049,8 @@ - - + + From 86ffd66c8a0ff983331b3716a3f020fa218cc6c9 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 19 Jan 2017 09:50:18 +0100 Subject: [PATCH 293/599] update danish translations --- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 32b1ef780e..b7d178ef41 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -291,7 +291,7 @@ Vælg medlem Vælg medlemsgruppe Der er ingen parametre for denne makro - Der er ikke tilføjet nogle makroer + Der er ikke tilføjet nogen makroer Link dit Fjern link fra dit konto @@ -1007,39 +1007,38 @@ Mange hilsner fra Umbraco robotten - Rediger skabelong + Rediger skabelon Sektioner Indsæt indholdsområde - Indsæt indholdsområde placeholder + Indsæt pladsholder for indholdsområde Indsæt Hvad vil du indsætte ? Oversættelse - Indsætter en oversætbar tekst som skifter efter det sprog som websitet vises i. + Indsætter en oversætbar tekst, som skifter efter det sprog, som websitet vises i. Makro - En makro er et element som kan have forskellige indstillinger når det indsættes. - Brug det som en genbrugelig del af dit design såsom gallerier, formularer - og lister. + En makro er et element, som kan have forskellige indstillinger, når det indsættes. + Brug det som en genbrugelig del af dit design såsom gallerier, formularer og lister. - Side værdi + Sideværdi - Viser værdien af et felt fra den nuværende side. Kan indstilles til at bruge rekursive værdier eller - vise en standard værdi i tilfælde af at feltet er tomt. + Viser værdien af et felt fra den nuværende side. Kan indstilles til at bruge rekursive værdier eller + vise en standardværdi i tilfælde af, at feltet er tomt. Partial view - Et Partial View er et skabelon element som kan indsættes i andre skabeloner og derved - genbruges og deles på tværs af side-skabelonerne. + Et Partial View er et skabelonelement, som kan indsættes i andre skabeloner og derved + genbruges og deles på tværs af sideskabelonerne. Master skabelon - Ingen master skabelon + Ingen masterskabelon Ingen master Indsæt en underliggende skabelon @@ -1069,11 +1068,11 @@ Mange hilsner fra Umbraco robotten ]]> - Sektion navn - Sektionen er oblikatorisk + Sektionsnavn + Sektionen er obligatorisk - Hvis oblikatorisk, skal under-skabelonen indeholde en @section definition. + Hvis obligatorisk, skal underskabelonen indeholde en @section -definition. From afae06a866d042258e2c4225a841d9e641b9ac33 Mon Sep 17 00:00:00 2001 From: Niels Hartvig Date: Thu, 19 Jan 2017 10:41:08 +0100 Subject: [PATCH 294/599] Adds support for icons and encoding of directories for Code files --- src/Umbraco.Web/Editors/CodeFileController.cs | 6 +++++- src/Umbraco.Web/Trees/FileSystemTreeController.cs | 3 ++- src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs | 5 +++++ src/Umbraco.Web/Trees/PartialViewsTreeController.cs | 4 ++++ src/Umbraco.Web/Trees/ScriptTreeController.cs | 4 ++++ 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Editors/CodeFileController.cs b/src/Umbraco.Web/Editors/CodeFileController.cs index 45dd747c4c..44d3429689 100644 --- a/src/Umbraco.Web/Editors/CodeFileController.cs +++ b/src/Umbraco.Web/Editors/CodeFileController.cs @@ -49,6 +49,7 @@ namespace Umbraco.Web.Editors { if (string.IsNullOrWhiteSpace(type) == false && string.IsNullOrWhiteSpace(virtualPath) == false) { + virtualPath = System.Web.HttpUtility.UrlDecode(virtualPath); if (type == Core.Constants.Trees.PartialViews) { var view = Services.FileService.GetPartialView(virtualPath); @@ -170,8 +171,11 @@ namespace Umbraco.Web.Editors var view = Services.FileService.GetPartialView(display.VirtualPath); if (view != null) { + // might need to find the path + var orgPath = view.OriginalPath.Substring(0, view.OriginalPath.IndexOf(view.Name)); + view.Path = orgPath + display.Name; + view.Content = display.Content; - view.Path = display.Name; var result = Services.FileService.SavePartialView(view, Security.CurrentUser.Id); if (result.Success == true) { diff --git a/src/Umbraco.Web/Trees/FileSystemTreeController.cs b/src/Umbraco.Web/Trees/FileSystemTreeController.cs index f9ebaaf2d6..8740d1c939 100644 --- a/src/Umbraco.Web/Trees/FileSystemTreeController.cs +++ b/src/Umbraco.Web/Trees/FileSystemTreeController.cs @@ -13,6 +13,7 @@ namespace Umbraco.Web.Trees { protected abstract string FilePath { get; } protected abstract string FileSearchPattern { get; } + protected abstract string FileIcon { get; } /// /// Inheritors can override this method to modify the file node that is created. @@ -79,7 +80,7 @@ namespace Umbraco.Web.Trees if (filterByMultipleExtensions && Array.IndexOf(allowedExtensions, file.Extension.ToLower().Trim('.')) < 0) continue; - var node = CreateTreeNode(orgPath + file.Name, orgPath, queryStrings, file.Name, "icon-file", false); + var node = CreateTreeNode(System.Web.HttpUtility.UrlEncode(orgPath + file.Name), orgPath, queryStrings, file.Name, FileIcon, false); OnRenderFileNode(ref node); diff --git a/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs b/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs index 60b0711154..5afb036fdd 100644 --- a/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs +++ b/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs @@ -23,6 +23,11 @@ namespace Umbraco.Web.Trees get { return "*.cshtml"; } } + protected override string FileIcon + { + get { return "icon-article"; } + } + protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) { var menu = new MenuItemCollection(); diff --git a/src/Umbraco.Web/Trees/PartialViewsTreeController.cs b/src/Umbraco.Web/Trees/PartialViewsTreeController.cs index de45148160..826b0c2d90 100644 --- a/src/Umbraco.Web/Trees/PartialViewsTreeController.cs +++ b/src/Umbraco.Web/Trees/PartialViewsTreeController.cs @@ -22,6 +22,10 @@ namespace Umbraco.Web.Trees { get { return "*.cshtml"; } } + protected override string FileIcon + { + get { return "icon-article"; } + } protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) { diff --git a/src/Umbraco.Web/Trees/ScriptTreeController.cs b/src/Umbraco.Web/Trees/ScriptTreeController.cs index ec8037ae9b..f9d5c63a68 100644 --- a/src/Umbraco.Web/Trees/ScriptTreeController.cs +++ b/src/Umbraco.Web/Trees/ScriptTreeController.cs @@ -19,6 +19,10 @@ namespace Umbraco.Web.Trees { get { return "*.js"; } } + protected override string FileIcon + { + get { return "icon-script"; } + } protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) { From a09755ef7fe86c308183758aace6d353f1096e6c Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 19 Jan 2017 11:19:34 +0100 Subject: [PATCH 295/599] remove unused code in partial view editor --- .../src/views/partialviews/edit.controller.js | 6 ------ src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html | 7 ------- 2 files changed, 13 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js index cf377259af..2bc0172881 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js @@ -9,7 +9,6 @@ vm.page = {}; vm.page.loading = true; vm.partialView = {}; - vm.partialViewPathPrefix = "Partials /"; //menu vm.page.menu = {}; @@ -95,11 +94,6 @@ var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name); insert(code); break; - - case "partial": - var code = templateHelper.getInsertPartialSnippet(model.insert.node.name); - insert(code); - break; case "umbracoField": insert(model.insert.umbracoField); diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html index fe5f061141..2c974fcfaa 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.html @@ -120,11 +120,4 @@ view="vm.queryBuilderOverlay.view"> - - -
    From a57abce96ef8bd4253683900805a769746e64146 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 19 Jan 2017 11:30:14 +0100 Subject: [PATCH 296/599] Manually applying https://github.com/umbraco/Umbraco-CMS/pull/1688 --- .../views/propertyeditors/googlemaps/googlemaps.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/googlemaps/googlemaps.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/googlemaps/googlemaps.controller.js index f8e89d7200..1d6261c10a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/googlemaps/googlemaps.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/googlemaps/googlemaps.controller.js @@ -2,7 +2,7 @@ angular.module("umbraco") .controller("Umbraco.PropertyEditors.GoogleMapsController", function ($element, $rootScope, $scope, notificationsService, dialogService, assetsService, $log, $timeout) { - assetsService.loadJs('http://www.google.com/jsapi') + assetsService.loadJs('https://www.google.com/jsapi') .then(function () { google.load("maps", "3", { From 4119380af517e886d34d9f3aeb37b1573ec62e0c Mon Sep 17 00:00:00 2001 From: Claus Date: Thu, 19 Jan 2017 11:39:09 +0100 Subject: [PATCH 297/599] U4-9414 EntityContainer repository PerformGetAll query returns nothing --- .../Repositories/EntityContainerRepository.cs | 24 +++++++++---- .../Repositories/ContentTypeRepositoryTest.cs | 32 +++++++++++++++++ .../Services/ContentTypeServiceTests.cs | 36 +++++++++++++++++++ 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs index cc13275798..b76abf4db6 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs @@ -62,17 +62,27 @@ namespace Umbraco.Core.Persistence.Repositories protected override IEnumerable PerformGetAll(params int[] ids) { - //we need to batch these in groups of 2000 so we don't exceed the max 2100 limit - return ids.InGroupsOf(2000).SelectMany(@group => + if (ids.Any()) + { + //we need to batch these in groups of 2000 so we don't exceed the max 2100 limit + return ids.InGroupsOf(2000).SelectMany(@group => + { + var sql = GetBaseQuery(false) + .Where("nodeObjectType=@umbracoObjectTypeId", new {umbracoObjectTypeId = NodeObjectTypeId}) + .Where(string.Format("{0} IN (@ids)", SqlSyntax.GetQuotedColumnName("id")), new {ids = @group}); + + sql.OrderBy(x => x.Level, SqlSyntax); + + return Database.Fetch(sql).Select(CreateEntity); + }); + } + else { var sql = GetBaseQuery(false) - .Where("nodeObjectType=@umbracoObjectTypeId", new { umbracoObjectTypeId = NodeObjectTypeId }) - .Where(string.Format("{0} IN (@ids)", SqlSyntax.GetQuotedColumnName("id")), new { ids = @group }); - + .Where("nodeObjectType=@umbracoObjectTypeId", new {umbracoObjectTypeId = NodeObjectTypeId}); sql.OrderBy(x => x.Level, SqlSyntax); - return Database.Fetch(sql).Select(CreateEntity); - }); + } } protected override IEnumerable PerformGetByQuery(IQuery query) diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index 84cdef73e1..699e563407 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -172,6 +172,38 @@ namespace Umbraco.Tests.Persistence.Repositories } } + [Test] + public void Can_Get_All_Containers() + { + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + EntityContainer container1, container2, container3; + using (var containerRepository = CreateContainerRepository(unitOfWork, Constants.ObjectTypes.DocumentTypeContainerGuid)) + { + container1 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) { Name = "container1" }; + containerRepository.AddOrUpdate(container1); + container2 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) { Name = "container2" }; + containerRepository.AddOrUpdate(container2); + container3 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid) { Name = "container3" }; + containerRepository.AddOrUpdate(container3); + unitOfWork.Commit(); + Assert.That(container1.Id, Is.GreaterThan(0)); + Assert.That(container2.Id, Is.GreaterThan(0)); + Assert.That(container3.Id, Is.GreaterThan(0)); + } + using (var containerRepository = CreateContainerRepository(unitOfWork, Constants.ObjectTypes.DocumentTypeContainerGuid)) + { + var found1 = containerRepository.Get(container1.Id); + Assert.IsNotNull(found1); + var found2 = containerRepository.Get(container2.Id); + Assert.IsNotNull(found2); + var found3 = containerRepository.Get(container3.Id); + Assert.IsNotNull(found3); + var allContainers = containerRepository.GetAll(); + Assert.AreEqual(3, allContainers.Count()); + } + } + [Test] public void Can_Delete_Container() { diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index 3fd0e1f6cd..78f4a5dad9 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -304,6 +304,42 @@ namespace Umbraco.Tests.Services Assert.IsNull(deletedContentType); } + [Test] + public void Can_Create_Container() + { + // Arrange + var cts = ServiceContext.ContentTypeService; + + // Act + var container = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid); + container.Name = "container1"; + cts.SaveContentTypeContainer(container); + + // Assert + var createdContainer = cts.GetContentTypeContainer(container.Id); + Assert.IsNotNull(createdContainer); + } + + [Test] + public void Can_Get_All_Containers() + { + // Arrange + var cts = ServiceContext.ContentTypeService; + + // Act + var container1 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid); + container1.Name = "container1"; + cts.SaveContentTypeContainer(container1); + + var container2 = new EntityContainer(Constants.ObjectTypes.DocumentTypeGuid); + container2.Name = "container2"; + cts.SaveContentTypeContainer(container2); + + // Assert + var containers = cts.GetContentTypeContainers(new int[0]); + Assert.AreEqual(2, containers.Count()); + } + [Test] public void Deleting_ContentType_Sends_Correct_Number_Of_DeletedEntities_In_Events() { From eefd13547c82f63d90479cab4f296f6f3b8f697e Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 19 Jan 2017 12:53:39 +0100 Subject: [PATCH 298/599] More localization of querybuilder --- .../querybuilder/querybuilder.controller.js | 15 ++++++++++++--- .../overlays/querybuilder/querybuilder.html | 4 ++-- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 5 ++++- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 8 +++++--- .../Editors/TemplateQueryController.cs | 6 ++++-- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js index 3939882969..8f28bd3674 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js @@ -1,7 +1,10 @@ (function () { "use strict"; - function QueryBuilderOverlayController($scope, templateQueryResource) { + function QueryBuilderOverlayController($scope, templateQueryResource, localizationService) { + + var everything = localizationService.localize("template_allContent"); + var myWebsite = localizationService.localize("template_myWebsite"); var vm = this; @@ -17,10 +20,10 @@ vm.query = { contentType: { - name: "Everything" + name: everything }, source: { - name: "My website" + name: myWebsite }, filters: [ { @@ -113,6 +116,12 @@ function trashFilter(query) { query.filters.splice(query, 1); + + //if we remove the last one, add a new one to generate ui for it. + if (query.filters.length == 0) { + query.filters.push({}); + } + } function changeSortOrder(query) { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html index 678733f1c8..542b7a3c6f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.html @@ -5,7 +5,7 @@
    - I Want + I want
    @@ -91,7 +91,7 @@ - + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index b7d178ef41..6e4905cc7a 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -1080,8 +1080,11 @@ Mange hilsner fra Umbraco robotten sider returneret, på Returner - alt + alt indhold + indhold af typen "%0%" + fra + mit website filtre og diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 7243a34490..24391288bd 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -1096,15 +1096,17 @@ To manage your website, simply open the Umbraco back office and start adding con Query builder items returned, in - I want - everything + I want + all content + content of type "%0%" from + my website where and - Template + diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs index e3c63dd22e..8693350c74 100644 --- a/src/Umbraco.Web/Editors/TemplateQueryController.cs +++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs @@ -9,6 +9,7 @@ using System; using System.Diagnostics; using Umbraco.Web.Dynamics; using Umbraco.Web.Models.TemplateQuery; +using Umbraco.Core.Services; namespace Umbraco.Web.Editors { @@ -297,9 +298,10 @@ namespace Umbraco.Web.Editors { var contentTypes = ApplicationContext.Services.ContentTypeService.GetAllContentTypes() - .Select(x => new ContentTypeModel() { Alias = x.Alias, Name = x.Name }) + .Select(x => new ContentTypeModel() { Alias = x.Alias, Name = Services.TextService.Localize("template/contentOfType", tokens: new string[] { x.Name } ) }) .OrderBy(x => x.Name).ToList(); - contentTypes.Insert(0, new ContentTypeModel() { Alias = string.Empty, Name = "Everything" }); + + contentTypes.Insert(0, new ContentTypeModel() { Alias = string.Empty, Name = Services.TextService.Localize("template/allContent") }); return contentTypes; } From b8734e5eead43872be0884edeb7e9c2e33cb3e7c Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 19 Jan 2017 15:11:37 +0100 Subject: [PATCH 299/599] fixes: U4-9419 The new content picker adds picked items in a random order --- .../contentpicker/contentpicker.controller.js | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index ba29b92477..947603794a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -259,18 +259,24 @@ function contentPickerController($scope, entityResource, editorState, iconHelper }); function setEntityUrl(entity) { + // get url for content and media items if(entityType !== "Member") { entityResource.getUrl(entity.id, entityType).then(function(data){ - // update url - entity.url = data; - - // push item to render model - addSelectedItem(entity); + // update url + angular.forEach($scope.renderModel, function(item){ + if(item.id === entity.id) { + item.url = data; + } + }); }); - } else { - addSelectedItem(entity); } + + // add the selected item to the renderModel + // if it needs to show a url the item will get + // updated when the url comes back from server + addSelectedItem(entity); + } function addSelectedItem(item) { From 61d71d05526bc94544fd26c2335198c5c7902155 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 19 Jan 2017 14:40:10 +0000 Subject: [PATCH 300/599] Update codeFileResource docs --- .../src/common/resources/codefile.resource.js | 61 ++++++++++++------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js index cb0f821abd..3f5c20b05a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js @@ -17,7 +17,14 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) { * * ##usage *
    -         * codefileResource.getByPath('partialView', 'oooh-la-la')
    +         * codefileResource.getByPath('scripts', 'oooh-la-la.js')
    +         *    .then(function(codefile) {
    +         *        alert('its here!');
    +         *    });
    +         * 
    + * + *
    +         * codefileResource.getByPath('partialView', 'Grid%2fEditors%2fBase.cshtml')
              *    .then(function(codefile) {
              *        alert('its here!');
              *    });
    @@ -41,15 +48,15 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) {
     
             /**
              * @ngdoc method
    -         * @name umbraco.resources.templateResource#getByAlias
    -         * @methodOf umbraco.resources.templateResource
    +         * @name umbraco.resources.codefileResource#getByAlias
    +         * @methodOf umbraco.resources.codefileResource
              *
              * @description
              * Gets a template item with a given alias
              *
              * ##usage
              * 
    -         * templateResource.getByAlias("upload")
    +         * codefileResource.getByAlias("upload")
              *    .then(function(template) {
              *        alert('its here!');
              *    });
    @@ -73,23 +80,23 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) {
     
             /**
              * @ngdoc method
    -         * @name umbraco.resources.templateResource#getScaffold
    -         * @methodOf umbraco.resources.templateResource
    +         * @name umbraco.resources.codefileResource#getScaffold
    +         * @methodOf umbraco.resources.codefileResource
              *
              * @description
    -         * Returns a scaffold of an empty template item
    +         * Returns a scaffold of an empty codefile item
              *
    -         * The scaffold is used to build editors for templates that has not yet been populated with data.
    +         * The scaffold is used to build editors for code file editors that has not yet been populated with data.
              *
              * ##usage
              * 
    -         * templateResource.getScaffold()
    +         * codefileResource.getScaffold()
              *    .then(function(template) {
              *        alert('its here!');
              *    });
              * 
    * - * @returns {Promise} resourcePromise object containing the template scaffold. + * @returns {Promise} resourcePromise object containing the codefile scaffold. * */ getScaffold: function (id) { @@ -105,21 +112,29 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) { /** * @ngdoc method - * @name umbraco.resources.templateResource#deleteById - * @methodOf umbraco.resources.templateResource + * @name umbraco.resources.codefileResource#deleteByPath + * @methodOf umbraco.resources.codefileResource * * @description - * Deletes a template with a given id + * Deletes a codefile with a given type & path * * ##usage *
    -         * templateResource.deleteById(1234)
    +         * codefileResource.deleteByPath('scripts', 'oooh-la-la.js')
    +         *    .then(function() {
    +         *        alert('its gone!');
    +         *    });
    +         * 
    + * + *
    +         * codefileResource.deleteByPath('partialView', 'Grid%2fEditors%2fBase.cshtml')
              *    .then(function() {
              *        alert('its gone!');
              *    });
              * 
    * - * @param {Int} id id of template to delete + * @param {type} the type of script (partialView, partialViewMacro, script) + * @param {virtualpath} the virtual path of the script * @returns {Promise} resourcePromise object. * */ @@ -135,16 +150,16 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) { /** * @ngdoc method - * @name umbraco.resources.templateResource#save - * @methodOf umbraco.resources.templateResource + * @name umbraco.resources.codefileResource#save + * @methodOf umbraco.resources.codefileResource * * @description - * Saves or update a template + * Saves or update a codeFile * * ##usage *
    -         * templateResource.save(template)
    -         *    .then(function(template) {
    +         * codefileResource.save(codeFile)
    +         *    .then(function(codeFile) {
              *        alert('its saved!');
              *    });
              * 
    @@ -153,14 +168,14 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) { * @returns {Promise} resourcePromise object. * */ - save: function (partialView) { + save: function (codeFile) { return umbRequestHelper.resourcePromise( $http.post( umbRequestHelper.getApiUrl( "codeFileApiBaseUrl", "PostSave"), - partialView), - "Failed to save data for partialView " + partialView.virtualPath); + codeFile), + "Failed to save data for code file " + partialView.virtualPath); } }; } From c8c7de065d9376573214a8d5aeac29950d0f4d3d Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 19 Jan 2017 14:56:25 +0000 Subject: [PATCH 301/599] Forgot to update the var name in codeFileResource --- .../src/common/resources/codefile.resource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js index 3f5c20b05a..4655b6fa3b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js @@ -175,7 +175,7 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) { "codeFileApiBaseUrl", "PostSave"), codeFile), - "Failed to save data for code file " + partialView.virtualPath); + "Failed to save data for code file " + codeFile.virtualPath); } }; } From 2424a1c04b11eb36935ec38b78c7b47c2bafc4cb Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 19 Jan 2017 14:57:23 +0000 Subject: [PATCH 302/599] Adds in localised sucess notification when saving partial view macros --- .../src/views/partialviewmacros/edit.controller.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.controller.js index d0a47aaa77..c402f42ee7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/edit.controller.js @@ -42,7 +42,12 @@ rebindCallback: function (orignal, saved) {} }).then(function (saved) { - notificationsService.success("Partial View Macro File saved"); + localizationService.localize("speechBubbles_partialViewSavedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_partialViewSavedText").then(function(msgValue) { + notificationsService.success(headerValue, msgValue); + }); + }); + vm.page.saveButtonState = "success"; vm.partialViewMacro = saved; From ef64fbb839163701d6fd6e466a1e2a793cabf7e6 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 19 Jan 2017 15:01:14 +0000 Subject: [PATCH 303/599] Same change for the partialview editor - to display a localized success notifcation --- .../src/views/partialviews/edit.controller.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js index 2bc0172881..fea9c0540e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js @@ -43,6 +43,12 @@ }).then(function (saved) { notificationsService.success("Partial View saved"); + localizationService.localize("speechBubbles_partialViewSavedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_partialViewSavedText").then(function(msgValue) { + notificationsService.success(headerValue, msgValue); + }); + }); + vm.page.saveButtonState = "success"; vm.partialView = saved; From 04f6a0119697c86a72f57303384596b10f940188 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 19 Jan 2017 15:04:36 +0000 Subject: [PATCH 304/599] Forgot to remove the old notification - as was display two --- .../src/views/partialviews/edit.controller.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js index fea9c0540e..119fe7fa48 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js @@ -42,7 +42,6 @@ rebindCallback: function (orignal, saved) {} }).then(function (saved) { - notificationsService.success("Partial View saved"); localizationService.localize("speechBubbles_partialViewSavedHeader").then(function (headerValue) { localizationService.localize("speechBubbles_partialViewSavedText").then(function(msgValue) { notificationsService.success(headerValue, msgValue); From a7e626f4a728ad81471690a05a636ab038c8ce83 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 19 Jan 2017 15:05:05 +0000 Subject: [PATCH 305/599] Localisation notification message for saving scripts --- .../src/views/scripts/edit.controller.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js index fe8837e50d..2f3cae83cc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js @@ -39,7 +39,12 @@ rebindCallback: function (orignal, saved) {} }).then(function (saved) { - notificationsService.success("Script saved"); + localizationService.localize("speechBubbles_scriptSavedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_scriptSavedText").then(function(msgValue) { + notificationsService.success(headerValue, msgValue); + }); + }); + vm.page.saveButtonState = "success"; vm.script = saved; From 1defc1e0e4c02d823388c3f289f9ca29bc7e5cbd Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 19 Jan 2017 15:14:14 +0000 Subject: [PATCH 306/599] Changed key for saving scripts - as it mentioned 'script view' Whats that?! So changed to more generic file --- .../src/views/scripts/edit.controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js index 2f3cae83cc..cd10ae75d5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/scripts/edit.controller.js @@ -39,8 +39,8 @@ rebindCallback: function (orignal, saved) {} }).then(function (saved) { - localizationService.localize("speechBubbles_scriptSavedHeader").then(function (headerValue) { - localizationService.localize("speechBubbles_scriptSavedText").then(function(msgValue) { + localizationService.localize("speechBubbles_fileSavedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_fileSavedText").then(function(msgValue) { notificationsService.success(headerValue, msgValue); }); }); From 3b54db9b49d93fd817a756282f672c964eb0dc3d Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 19 Jan 2017 15:14:46 +0000 Subject: [PATCH 307/599] Update main templat editor to use a localised notification --- .../src/views/templates/edit.controller.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 4e33b1febf..5e10cb5478 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -33,7 +33,13 @@ rebindCallback: function (orignal, saved) {} }).then(function (saved) { - notificationsService.success("Template saved"); + localizationService.localize("speechBubbles_templateSavedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_templateSavedText").then(function(msgValue) { + notificationsService.success(headerValue, msgValue); + }); + }); + + vm.page.saveButtonState = "success"; vm.template = saved; From abebb404610bbb65eb6b10aca51f0511f03166ed Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 19 Jan 2017 15:18:12 +0000 Subject: [PATCH 308/599] Update some copy/paste typos in code comments --- .../src/views/partialviews/edit.controller.js | 2 +- .../src/views/templates/edit.controller.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js index 119fe7fa48..11dba8a63e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/edit.controller.js @@ -35,7 +35,7 @@ saveMethod: codefileResource.save, scope: $scope, content: vm.partialView, - //We do not redirect on failure for stylesheets - this is because it is not possible to actually save the doc + //We do not redirect on failure for partialviews - this is because it is not possible to actually save the partialviews // type when server side validation fails - as opposed to content where we are capable of saving the content // item if server side validation fails redirectOnFailure: false, diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js index 5e10cb5478..c69210fc65 100644 --- a/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/templates/edit.controller.js @@ -26,7 +26,7 @@ saveMethod: templateResource.save, scope: $scope, content: vm.template, - //We do not redirect on failure for templates - this is because it is not possible to actually save the doc + //We do not redirect on failure for templates - this is because it is not possible to actually save the template // type when server side validation fails - as opposed to content where we are capable of saving the content // item if server side validation fails redirectOnFailure: false, From 2f7b532209a057a518e7e798b0c691ee3fa831c6 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 19 Jan 2017 15:43:29 +0000 Subject: [PATCH 309/599] Adds in code summaries for C# doc generation. Removes unused references & some tidying up (mostly sensible R# suggestions) --- src/Umbraco.Web/Editors/CodeFileController.cs | 150 +++++++++--------- 1 file changed, 73 insertions(+), 77 deletions(-) diff --git a/src/Umbraco.Web/Editors/CodeFileController.cs b/src/Umbraco.Web/Editors/CodeFileController.cs index 44d3429689..60dcf624a0 100644 --- a/src/Umbraco.Web/Editors/CodeFileController.cs +++ b/src/Umbraco.Web/Editors/CodeFileController.cs @@ -1,11 +1,6 @@ using AutoMapper; -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; using System.Net.Http; -using System.Text; -using System.Threading.Tasks; using System.Web.Http; using Umbraco.Core.Models; using Umbraco.Core.Services; @@ -22,6 +17,12 @@ namespace Umbraco.Web.Editors public class CodeFileController : BackOfficeNotificationsController { + /// + /// Used to create a brand new file + /// + /// This is a string but will be 'scripts' 'partialViews', 'partialViewMacros' + /// + /// Will return a simple 200 if file creation succeeds [ValidationFilter] public HttpResponseMessage PostCreate(string type, CodeFileDisplay display) { @@ -31,27 +32,41 @@ namespace Umbraco.Web.Editors var view = new PartialView(display.VirtualPath); var result = Services.FileService.CreatePartialView(view, display.Snippet, Security.CurrentUser.Id); return result.Success == true ? Request.CreateResponse(HttpStatusCode.OK) : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); + case Core.Constants.Trees.PartialViewMacros: var viewMacro = new PartialView(display.VirtualPath); var resultMacro = Services.FileService.CreatePartialViewMacro(viewMacro, display.Snippet, Security.CurrentUser.Id); return resultMacro.Success == true ? Request.CreateResponse(HttpStatusCode.OK) : Request.CreateNotificationValidationErrorResponse(resultMacro.Exception.Message); + case Core.Constants.Trees.Scripts: var script = new Script(display.VirtualPath); Services.FileService.SaveScript(script, Security.CurrentUser.Id); return Request.CreateResponse(HttpStatusCode.OK); + default: return Request.CreateResponse(HttpStatusCode.NotFound); } } - + /// + /// Used to get a specific file from disk via the FileService + /// + /// This is a string but will be 'scripts' 'partialViews', 'partialViewMacros' + /// The filename or urlencoded path of the file to open + /// The file and its contents from the virtualPath public CodeFileDisplay GetByPath(string type, string virtualPath) { - if (string.IsNullOrWhiteSpace(type) == false && string.IsNullOrWhiteSpace(virtualPath) == false) + if (string.IsNullOrWhiteSpace(type) || string.IsNullOrWhiteSpace(virtualPath)) { - virtualPath = System.Web.HttpUtility.UrlDecode(virtualPath); - if (type == Core.Constants.Trees.PartialViews) - { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + virtualPath = System.Web.HttpUtility.UrlDecode(virtualPath); + + + switch (type) + { + case Core.Constants.Trees.PartialViews: var view = Services.FileService.GetPartialView(virtualPath); if (view != null) { @@ -59,13 +74,9 @@ namespace Umbraco.Web.Editors display.FileType = Core.Constants.Trees.PartialViews; return display; } - else - { - return null; - } - } - else if (type == Core.Constants.Trees.PartialViewMacros) - { + return null; + + case Core.Constants.Trees.PartialViewMacros: var viewMacro = Services.FileService.GetPartialViewMacro(virtualPath); if (viewMacro != null) { @@ -73,13 +84,9 @@ namespace Umbraco.Web.Editors display.FileType = Core.Constants.Trees.PartialViewMacros; return display; } - else - { - return null; - } - } - else if (type == Core.Constants.Trees.Scripts) - { + return null; + + case Core.Constants.Trees.Scripts: var script = Services.FileService.GetScriptByName(virtualPath); if (script != null) { @@ -87,23 +94,18 @@ namespace Umbraco.Web.Editors display.FileType = Core.Constants.Trees.Scripts; return display; } - else - { - return null; - } + return null; + } - } - else - { - throw new HttpResponseException(HttpStatusCode.NotFound); - } - } - else - { - throw new HttpResponseException(HttpStatusCode.NotFound); - } + throw new HttpResponseException(HttpStatusCode.NotFound); } + /// + /// Used to delete a specific file from disk via the FileService + /// + /// This is a string but will be 'scripts' 'partialViews', 'partialViewMacros' + /// The filename or urlencoded path of the file to delete + /// Will return a simple 200 if file deletion succeeds [HttpDelete] [HttpPost] public HttpResponseMessage Delete(string type, string virtualPath) @@ -117,42 +119,36 @@ namespace Umbraco.Web.Editors { return Request.CreateResponse(HttpStatusCode.OK); } - else - { - return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View found with the specified path"); - } - break; + return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View found with the specified path"); + case Core.Constants.Trees.PartialViewMacros: if (Services.FileService.DeletePartialViewMacro(virtualPath, Security.CurrentUser.Id)) { return Request.CreateResponse(HttpStatusCode.OK); } - else - { - return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro found with the specified path"); - } - break; + return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro found with the specified path"); + case Core.Constants.Trees.Scripts: if (Services.FileService.GetScriptByName(virtualPath) != null) { Services.FileService.DeleteScript(virtualPath, Security.CurrentUser.Id); return Request.CreateResponse(HttpStatusCode.OK); } - else - { - return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script found with the specified path"); - } - break; + return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script found with the specified path"); + default: return Request.CreateResponse(HttpStatusCode.NotFound); } } - else - { - throw new HttpResponseException(HttpStatusCode.NotFound); - } + + throw new HttpResponseException(HttpStatusCode.NotFound); } + /// + /// Used to save/update an existing file after its initial creation + /// + /// + /// The updated CodeFileDisplay model public CodeFileDisplay PostSave(CodeFileDisplay display) { if (ModelState.IsValid == false) @@ -160,14 +156,15 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } - if (display == null && string.IsNullOrWhiteSpace(display.FileType) == true) + if (display == null || string.IsNullOrWhiteSpace(display.FileType)) { throw new HttpResponseException(HttpStatusCode.NotFound); } - else + + + switch (display.FileType) { - if (display.FileType == Core.Constants.Trees.PartialViews) - { + case Core.Constants.Trees.PartialViews: var view = Services.FileService.GetPartialView(display.VirtualPath); if (view != null) { @@ -180,20 +177,19 @@ namespace Umbraco.Web.Editors if (result.Success == true) { return Mapper.Map(view, display); - } else - { - display.AddErrorNotification( - Services.TextService.Localize("speechBubbles/partialViewErrorHeader"), - Services.TextService.Localize("speechBubbles/partialViewErrorText")); } + + display.AddErrorNotification( + Services.TextService.Localize("speechBubbles/partialViewErrorHeader"), + Services.TextService.Localize("speechBubbles/partialViewErrorText")); } else { throw new HttpResponseException(HttpStatusCode.NotFound); } - } - else if (display.FileType == Core.Constants.Trees.PartialViewMacros) - { + break; + + case Core.Constants.Trees.PartialViewMacros: var viewMacro = Services.FileService.GetPartialViewMacro(display.VirtualPath); if (viewMacro != null) { @@ -211,9 +207,9 @@ namespace Umbraco.Web.Editors { throw new HttpResponseException(HttpStatusCode.NotFound); } - } - else if (display.FileType == Core.Constants.Trees.Scripts) - { + break; + + case Core.Constants.Trees.Scripts: var script = Services.FileService.GetScriptByName(display.VirtualPath); if (script != null) { @@ -226,13 +222,13 @@ namespace Umbraco.Web.Editors { throw new HttpResponseException(HttpStatusCode.NotFound); } - } - else - { + break; + + default: throw new HttpResponseException(HttpStatusCode.NotFound); - } - return display; } + + return display; } } } From 434703f4ec66aa9605299ba488e17ece576a3fd7 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 19 Jan 2017 19:47:09 +0100 Subject: [PATCH 310/599] U4-9322 - begin implementing scoped repository caches --- .../Cache/DefaultRepositoryCachePolicy.cs | 4 +- .../ScopedDefaultRepositoryCachePolicy.cs | 83 ++++++++++++++ .../Repositories/RepositoryBase.cs | 54 +++++++-- .../UnitOfWork/PetaPocoUnitOfWork.cs | 4 +- src/Umbraco.Core/Scoping/IScope.cs | 9 ++ src/Umbraco.Core/Scoping/IScopeProvider.cs | 4 +- src/Umbraco.Core/Scoping/NoScope.cs | 10 ++ .../Scoping/RepositoryCacheMode.cs | 16 +++ src/Umbraco.Core/Scoping/Scope.cs | 93 ++++++++++++--- src/Umbraco.Core/Scoping/ScopeProvider.cs | 12 +- src/Umbraco.Core/Umbraco.Core.csproj | 2 + src/Umbraco.Tests/Scoping/ScopeTests.cs | 20 ++++ .../Scoping/ScopedRepositoryTests.cs | 107 ++++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + 14 files changed, 384 insertions(+), 35 deletions(-) create mode 100644 src/Umbraco.Core/Cache/ScopedDefaultRepositoryCachePolicy.cs create mode 100644 src/Umbraco.Core/Scoping/RepositoryCacheMode.cs create mode 100644 src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs diff --git a/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs index 656d532d8a..5d08e36f6a 100644 --- a/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs @@ -238,7 +238,9 @@ namespace Umbraco.Core.Cache /// public override void ClearAll() { - Cache.ClearCacheByKeySearch(GetEntityTypeCacheKey()); + // fixme the cache should NOT contain anything else so we can clean all, can't we? + Cache.ClearAllCache(); + //Cache.ClearCacheByKeySearch(GetEntityTypeCacheKey()); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Cache/ScopedDefaultRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/ScopedDefaultRepositoryCachePolicy.cs new file mode 100644 index 0000000000..3c237f307c --- /dev/null +++ b/src/Umbraco.Core/Cache/ScopedDefaultRepositoryCachePolicy.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Scoping; + +namespace Umbraco.Core.Cache +{ + internal class ScopedDefaultRepositoryCachePolicy : IRepositoryCachePolicy + where TEntity : class, IAggregateRoot + { + private readonly DefaultRepositoryCachePolicy _cachePolicy; + private readonly IRuntimeCacheProvider _globalIsolatedCache; + private readonly IScope _scope; + + public ScopedDefaultRepositoryCachePolicy(DefaultRepositoryCachePolicy cachePolicy, IRuntimeCacheProvider globalIsolatedCache, IScope scope) + { + _cachePolicy = cachePolicy; + _globalIsolatedCache = globalIsolatedCache; + _scope = scope; + } + + // when the scope completes we need to clear the global isolated cache + // for now, we are not doing it selectively at all - just kill everything + private void RegisterDirty(TEntity entity = null) + { + // "name" would be used to de-duplicate? + // fixme - casting! + ((Scope) _scope).Register("name", completed => _globalIsolatedCache.ClearAllCache()); + } + + public TEntity Get(TId id, Func performGet, Func> performGetAll) + { + // loads into the local cache only, ok for now + return _cachePolicy.Get(id, performGet, performGetAll); + } + + public TEntity GetCached(TId id) + { + // loads into the local cache only, ok for now + return _cachePolicy.GetCached(id); + } + + public bool Exists(TId id, Func performExists, Func> performGetAll) + { + // loads into the local cache only, ok for now + return _cachePolicy.Exists(id, performExists, performGetAll); + } + + public void Create(TEntity entity, Action persistNew) + { + // writes into the local cache + _cachePolicy.Create(entity, persistNew); + RegisterDirty(entity); + } + + public void Update(TEntity entity, Action persistUpdated) + { + // writes into the local cache + _cachePolicy.Update(entity, persistUpdated); + RegisterDirty(entity); + } + + public void Delete(TEntity entity, Action persistDeleted) + { + // deletes the local cache + _cachePolicy.Delete(entity, persistDeleted); + RegisterDirty(entity); + } + + public TEntity[] GetAll(TId[] ids, Func> performGetAll) + { + // loads into the local cache only, ok for now + return _cachePolicy.GetAll(ids, performGetAll); + } + + public void ClearAll() + { + // fixme - what's this doing? + _cachePolicy.ClearAll(); + RegisterDirty(); + } + } +} diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index 201520cbe3..09b4d7609d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Core.Scoping; namespace Umbraco.Core.Persistence.Repositories { @@ -84,7 +85,7 @@ namespace Umbraco.Core.Persistence.Repositories #region Static Queries - private IQuery _hasIdQuery; + private static IQuery _hasIdQuery; #endregion @@ -101,23 +102,56 @@ namespace Umbraco.Core.Persistence.Repositories get { return RepositoryCache.IsolatedRuntimeCache.GetOrCreateCache(); } } - private IRepositoryCachePolicy _cachePolicy; + private static RepositoryCachePolicyOptions _defaultOptions; + protected virtual RepositoryCachePolicyOptions DefaultOptions + { + get + { + return _defaultOptions ?? (_defaultOptions + = new RepositoryCachePolicyOptions(() => + { + // get count of all entities of current type (TEntity) to ensure cached result is correct + // create query once if it is needed (no need for locking here) - query is static! + var query = _hasIdQuery ?? (_hasIdQuery = Query.Builder.Where(x => x.Id != 0)); + return PerformCount(query); + })); + } + } + // this would be better for perfs BUT it breaks the tests - l8tr + // + //private static IRepositoryCachePolicy _defaultCachePolicy; + //protected virtual IRepositoryCachePolicy DefaultCachePolicy + //{ + // get + // { + // return _defaultCachePolicy ?? (_defaultCachePolicy + // = new DefaultRepositoryCachePolicy(RuntimeCache, DefaultOptions)); + // } + //} + + private IRepositoryCachePolicy _cachePolicy; protected virtual IRepositoryCachePolicy CachePolicy { get { if (_cachePolicy != null) return _cachePolicy; - var options = new RepositoryCachePolicyOptions(() => + var scope = ((PetaPocoUnitOfWork) UnitOfWork).Scope; + switch (scope.RepositoryCacheMode) { - //Get count of all entities of current type (TEntity) to ensure cached result is correct - //create query once if it is needed (no need for locking here) - var query = _hasIdQuery ?? (_hasIdQuery = Query.Builder.Where(x => x.Id != 0)); - return PerformCount(query); - }); - - _cachePolicy = new DefaultRepositoryCachePolicy(RuntimeCache, options); + case RepositoryCacheMode.Default: + //_cachePolicy = DefaultCachePolicy; + _cachePolicy = new DefaultRepositoryCachePolicy(RuntimeCache, DefaultOptions); + break; + case RepositoryCacheMode.Scoped: + var scopedCache = scope.IsolatedRuntimeCache.GetOrCreateCache(); + var scopedPolicy = new DefaultRepositoryCachePolicy(scopedCache, DefaultOptions); + _cachePolicy = new ScopedDefaultRepositoryCachePolicy(scopedPolicy, RuntimeCache, scope); + break; + default: + throw new Exception(); + } return _cachePolicy; } diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs index 9caf787ddc..b9564e92cb 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs @@ -144,14 +144,14 @@ namespace Umbraco.Core.Persistence.UnitOfWork get { return _key; } } - private IScope ThisScope + public IScope Scope { get { return _scope ?? (_scope = _scopeProvider.CreateScope(_isolationLevel)); } } public UmbracoDatabase Database { - get { return ThisScope.Database; } + get { return Scope.Database; } } #region Operation diff --git a/src/Umbraco.Core/Scoping/IScope.cs b/src/Umbraco.Core/Scoping/IScope.cs index 4960350575..e9af72b55e 100644 --- a/src/Umbraco.Core/Scoping/IScope.cs +++ b/src/Umbraco.Core/Scoping/IScope.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Umbraco.Core.Cache; using Umbraco.Core.Events; using Umbraco.Core.Persistence; @@ -20,6 +21,14 @@ namespace Umbraco.Core.Scoping ///
    IList Messages { get; } + /// + /// Gets the repository cache mode. + /// + RepositoryCacheMode RepositoryCacheMode { get; } + + // fixme + IsolatedRuntimeCache IsolatedRuntimeCache { get; } + /// /// Completes the scope. /// diff --git a/src/Umbraco.Core/Scoping/IScopeProvider.cs b/src/Umbraco.Core/Scoping/IScopeProvider.cs index 9a3e935a78..9f3f18bf46 100644 --- a/src/Umbraco.Core/Scoping/IScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/IScopeProvider.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Scoping /// If an ambient scope already exists, it becomes the parent of the created scope. /// When the created scope is disposed, the parent scope becomes the ambient scope again. /// - IScope CreateScope(IsolationLevel isolationLevel = IsolationLevel.Unspecified); + IScope CreateScope(IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified); /// /// Creates a detached scope. @@ -27,7 +27,7 @@ namespace Umbraco.Core.Scoping /// A detached scope is not ambient and has no parent. /// It is meant to be attached by . /// - IScope CreateDetachedScope(IsolationLevel isolationLevel = IsolationLevel.Unspecified); + IScope CreateDetachedScope(IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified); /// /// Attaches a scope. diff --git a/src/Umbraco.Core/Scoping/NoScope.cs b/src/Umbraco.Core/Scoping/NoScope.cs index 5c53e89047..3bcdc8ed8b 100644 --- a/src/Umbraco.Core/Scoping/NoScope.cs +++ b/src/Umbraco.Core/Scoping/NoScope.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Umbraco.Core.Cache; using Umbraco.Core.Events; using Umbraco.Core.Persistence; @@ -29,6 +30,15 @@ namespace Umbraco.Core.Scoping public Guid InstanceId { get { return _instanceId; } } #endif + /// + public RepositoryCacheMode RepositoryCacheMode + { + get { return RepositoryCacheMode.Default; } + } + + /// + public IsolatedRuntimeCache IsolatedRuntimeCache { get { throw new NotImplementedException(); } } + /// public UmbracoDatabase Database { diff --git a/src/Umbraco.Core/Scoping/RepositoryCacheMode.cs b/src/Umbraco.Core/Scoping/RepositoryCacheMode.cs new file mode 100644 index 0000000000..2b25f2eb59 --- /dev/null +++ b/src/Umbraco.Core/Scoping/RepositoryCacheMode.cs @@ -0,0 +1,16 @@ +namespace Umbraco.Core.Scoping +{ + public enum RepositoryCacheMode + { + // ? + Unspecified = 0, + + // the default, full L2 cache + Default = 1, + + // a scoped cache + // reads from and writes to a local cache + // clears the global cache on completion + Scoped = 2 + } +} diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index 4979aa6ae1..773d62b7d9 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Data; +using Umbraco.Core.Cache; using Umbraco.Core.Events; using Umbraco.Core.Persistence; @@ -14,9 +15,11 @@ namespace Umbraco.Core.Scoping { private readonly ScopeProvider _scopeProvider; private readonly IsolationLevel _isolationLevel; + private readonly RepositoryCacheMode _repositoryCacheMode; private bool _disposed; private bool? _completed; + private IsolatedRuntimeCache _isolatedRuntimeCache; private UmbracoDatabase _database; private IList _messages; @@ -24,10 +27,11 @@ namespace Umbraco.Core.Scoping private const IsolationLevel DefaultIsolationLevel = IsolationLevel.ReadCommitted; // initializes a new scope - public Scope(ScopeProvider scopeProvider, IsolationLevel isolationLevel = IsolationLevel.Unspecified, bool detachable = false) + public Scope(ScopeProvider scopeProvider, IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, bool detachable = false) { _scopeProvider = scopeProvider; _isolationLevel = isolationLevel; + _repositoryCacheMode = repositoryCacheMode; Detachable = detachable; #if DEBUG_SCOPES _scopeProvider.Register(this); @@ -35,15 +39,19 @@ namespace Umbraco.Core.Scoping } // initializes a new scope in a nested scopes chain, with its parent - public Scope(ScopeProvider scopeProvider, Scope parent, IsolationLevel isolationLevel = IsolationLevel.Unspecified) - : this(scopeProvider, isolationLevel) + public Scope(ScopeProvider scopeProvider, Scope parent, IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified) + : this(scopeProvider, isolationLevel, repositoryCacheMode) { ParentScope = parent; + + // cannot specify a different mode! + if (repositoryCacheMode != RepositoryCacheMode.Unspecified && parent.RepositoryCacheMode != repositoryCacheMode) + throw new ArgumentException("Cannot be different from parent.", "repositoryCacheMode"); } // initializes a new scope, replacing a NoScope instance - public Scope(ScopeProvider scopeProvider, NoScope noScope, IsolationLevel isolationLevel = IsolationLevel.Unspecified) - : this(scopeProvider, isolationLevel) + public Scope(ScopeProvider scopeProvider, NoScope noScope, IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified) + : this(scopeProvider, isolationLevel, repositoryCacheMode) { // steal everything from NoScope _database = noScope.DatabaseOrNull; @@ -59,6 +67,29 @@ namespace Umbraco.Core.Scoping public Guid InstanceId { get { return _instanceId; } } #endif + /// + public RepositoryCacheMode RepositoryCacheMode + { + get + { + if (_repositoryCacheMode != RepositoryCacheMode.Unspecified) return _repositoryCacheMode; + if (ParentScope != null) return ParentScope.RepositoryCacheMode; + return RepositoryCacheMode.Default; + } + } + + /// + public IsolatedRuntimeCache IsolatedRuntimeCache + { + get + { + if (ParentScope != null) return ParentScope.IsolatedRuntimeCache; + + return _isolatedRuntimeCache ?? (_isolatedRuntimeCache + = new IsolatedRuntimeCache(type => new DeepCloneRuntimeCacheProvider(new ObjectCacheRuntimeCacheProvider()))); + } + } + // a value indicating whether the scope is detachable // ie whether it was created by CreateDetachedScope public bool Detachable { get; private set; } @@ -208,20 +239,54 @@ namespace Umbraco.Core.Scoping // at the moment we are totally not filtering the messages based on completion // status, so whether the scope is committed or rolled back makes no difference - if (_database == null) return; + var completed = _completed.HasValue && _completed.Value; - try + if (_database != null) { - if (_completed.HasValue && _completed.Value) - _database.CompleteTransaction(); - else - _database.AbortTransaction(); + try + { + if (completed) + _database.CompleteTransaction(); + else + _database.AbortTransaction(); + } + finally + { + _database.Dispose(); + _database = null; + } } - finally + + // run everything we need to run when completing + foreach (var action in Actions.Values) + action(completed); // fixme try catch and everything + } + + // fixme - wip + private IDictionary> _actions; + + private IDictionary> Actions + { + get { - _database.Dispose(); - _database = null; + if (ParentScope != null) return ParentScope.Actions; + + return _actions ?? (_actions + = new Dictionary>()); } } + + public void Register(string name, Action action) + { + Actions[name] = completed => + { + if (completed) action(); + }; + } + + public void Register(string name, Action action) + { + Actions[name] = action; + } } } diff --git a/src/Umbraco.Core/Scoping/ScopeProvider.cs b/src/Umbraco.Core/Scoping/ScopeProvider.cs index 7843b4f92d..03c7b6647b 100644 --- a/src/Umbraco.Core/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Core/Scoping/ScopeProvider.cs @@ -114,9 +114,9 @@ namespace Umbraco.Core.Scoping } /// - public IScope CreateDetachedScope(IsolationLevel isolationLevel = IsolationLevel.Unspecified) + public IScope CreateDetachedScope(IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified) { - return new Scope(this, isolationLevel, true); + return new Scope(this, isolationLevel, repositoryCacheMode, true); } /// @@ -157,11 +157,11 @@ namespace Umbraco.Core.Scoping } /// - public IScope CreateScope(IsolationLevel isolationLevel = IsolationLevel.Unspecified) + public IScope CreateScope(IsolationLevel isolationLevel = IsolationLevel.Unspecified, RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified) { var ambient = AmbientScope; if (ambient == null) - return AmbientScope = new Scope(this, isolationLevel); + return AmbientScope = new Scope(this, isolationLevel, repositoryCacheMode); // replace noScope with a real one var noScope = ambient as NoScope; @@ -174,13 +174,13 @@ namespace Umbraco.Core.Scoping var database = noScope.DatabaseOrNull; if (database != null && database.InTransaction) throw new Exception("NoScope is in a transaction."); - return AmbientScope = new Scope(this, noScope, isolationLevel); + return AmbientScope = new Scope(this, noScope, isolationLevel, repositoryCacheMode); } var scope = ambient as Scope; if (scope == null) throw new Exception("Ambient scope is not a Scope instance."); - return AmbientScope = new Scope(this, scope, isolationLevel); + return AmbientScope = new Scope(this, scope, isolationLevel, repositoryCacheMode); } /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index fc778bc53b..2c229c6d73 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -156,6 +156,7 @@ + @@ -515,6 +516,7 @@ + diff --git a/src/Umbraco.Tests/Scoping/ScopeTests.cs b/src/Umbraco.Tests/Scoping/ScopeTests.cs index f6a9ab71df..7124d73074 100644 --- a/src/Umbraco.Tests/Scoping/ScopeTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeTests.cs @@ -418,5 +418,25 @@ namespace Umbraco.Tests.Scoping var db = nested.Database; }); } + + [TestCase(true)] + [TestCase(false)] + public void ScopeAction(bool complete) + { + var scopeProvider = DatabaseContext.ScopeProvider; + + bool? completed = null; + + Assert.IsNull(scopeProvider.AmbientScope); + using (var scope = scopeProvider.CreateScope()) + { + ((Scope) scope).Register("name", x => completed = x); + if (complete) + scope.Complete(); + } + Assert.IsNull(scopeProvider.AmbientScope); + Assert.IsNotNull(completed); + Assert.AreEqual(complete, completed.Value); + } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs new file mode 100644 index 0000000000..329af6833f --- /dev/null +++ b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs @@ -0,0 +1,107 @@ +using System; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Scoping; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Scoping +{ + [TestFixture] + [DatabaseTestBehavior(DatabaseBehavior.NewDbFileAndSchemaPerTest)] + public class ScopedRepositoryTests : BaseDatabaseFactoryTest + { + // setup + public override void Initialize() + { + base.Initialize(); + + Assert.IsNull(DatabaseContext.ScopeProvider.AmbientScope); // gone + } + + protected override CacheHelper CreateCacheHelper() + { + //return CacheHelper.CreateDisabledCacheHelper(); + return new CacheHelper( + new ObjectCacheRuntimeCacheProvider(), + new StaticCacheProvider(), + new NullCacheProvider(), + new IsolatedRuntimeCache(type => new ObjectCacheRuntimeCacheProvider())); + } + + [TestCase(true)] + [TestCase(false)] + public void Test(bool complete) + { + var scopeProvider = DatabaseContext.ScopeProvider; + var userService = ApplicationContext.Services.UserService; + var globalCache = ApplicationContext.ApplicationCache.IsolatedRuntimeCache.GetOrCreateCache(typeof(IUser)); + + var userType = userService.GetUserTypeByAlias("admin"); + var user = (IUser) new User("name", "email", "username", "rawPassword", userType); + userService.Save(user); + + // global cache contains the user entity + var globalCached = (IUser) globalCache.GetCacheItem(GetCacheIdKey(user.Id), () => null); + Assert.IsNotNull(globalCached); + Assert.AreEqual(user.Id, globalCached.Id); + Assert.AreEqual("name", globalCached.Name); + + Assert.IsNull(scopeProvider.AmbientScope); + using (var scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) + { + Assert.IsInstanceOf(scope); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + + // scope has its own isolated cache + var scopedCache = scope.IsolatedRuntimeCache.GetOrCreateCache(typeof (IUser)); + Assert.AreNotSame(globalCache, scopedCache); + + user.Name = "changed"; + ApplicationContext.Services.UserService.Save(user); + + // scoped cache contains the "new" user entity + var scopeCached = (IUser) scopedCache.GetCacheItem(GetCacheIdKey(user.Id), () => null); + Assert.IsNotNull(scopeCached); + Assert.AreEqual(user.Id, scopeCached.Id); + Assert.AreEqual("changed", scopeCached.Name); + + // global cache is unchanged + globalCached = (IUser) globalCache.GetCacheItem(GetCacheIdKey(user.Id), () => null); + Assert.IsNotNull(globalCached); + Assert.AreEqual(user.Id, globalCached.Id); + Assert.AreEqual("name", globalCached.Name); + + if (complete) + scope.Complete(); + } + Assert.IsNull(scopeProvider.AmbientScope); + + // global cache has been cleared + globalCached = (IUser) globalCache.GetCacheItem(GetCacheIdKey(user.Id), () => null); + Assert.IsNull(globalCached); + + // get again, updated if completed + user = userService.GetUserById(user.Id); + Assert.AreEqual(complete ? "changed" : "name", user.Name); + + // global cache contains the entity again + globalCached = (IUser) globalCache.GetCacheItem(GetCacheIdKey(user.Id), () => null); + Assert.IsNotNull(globalCached); + Assert.AreEqual(user.Id, globalCached.Id); + Assert.AreEqual(complete ? "changed" : "name", globalCached.Name); + } + + public static string GetCacheIdKey(object id) + { + return string.Format("{0}{1}", GetCacheTypeKey(), id); + } + + public static string GetCacheTypeKey() + { + return string.Format("uRepo_{0}_", typeof(T).Name); + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index bdfcac3420..696964d0a9 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -170,6 +170,7 @@ + From a6996922b7d276265e2e50da8626b8f9e5568fc3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 20 Jan 2017 17:45:46 +1100 Subject: [PATCH 311/599] Adds more support for auto-finding event names, adds tests to support with examples of how to interrogate queued events --- src/Umbraco.Core/Events/EventDefinition.cs | 3 + .../Events/EventDefinitionBase.cs | 35 +++- src/Umbraco.Core/Events/EventNameExtractor.cs | 150 ++++++++++++++++++ .../Events/EventNameExtractorError.cs | 9 ++ .../Events/EventNameExtractorResult.cs | 18 +++ src/Umbraco.Core/Events/IEventDefinition.cs | 4 +- src/Umbraco.Core/Events/ScopedEventManager.cs | 2 + src/Umbraco.Core/Umbraco.Core.csproj | 3 + .../Scoping/EventNameExtractorTests.cs | 51 ++++++ .../Scoping/ScopedEventManagerTests.cs | 78 +++++++-- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + 11 files changed, 331 insertions(+), 23 deletions(-) create mode 100644 src/Umbraco.Core/Events/EventNameExtractor.cs create mode 100644 src/Umbraco.Core/Events/EventNameExtractorError.cs create mode 100644 src/Umbraco.Core/Events/EventNameExtractorResult.cs create mode 100644 src/Umbraco.Tests/Scoping/EventNameExtractorTests.cs diff --git a/src/Umbraco.Core/Events/EventDefinition.cs b/src/Umbraco.Core/Events/EventDefinition.cs index e7da58f0b5..7a1d82fe8a 100644 --- a/src/Umbraco.Core/Events/EventDefinition.cs +++ b/src/Umbraco.Core/Events/EventDefinition.cs @@ -9,6 +9,7 @@ namespace Umbraco.Core.Events private readonly EventArgs _args; public EventDefinition(EventHandler trackedEvent, object sender, EventArgs args) + : base(sender, args) { _trackedEvent = trackedEvent; _sender = sender; @@ -31,6 +32,7 @@ namespace Umbraco.Core.Events private readonly TEventArgs _args; public EventDefinition(EventHandler trackedEvent, object sender, TEventArgs args) + : base(sender, args) { _trackedEvent = trackedEvent; _sender = sender; @@ -53,6 +55,7 @@ namespace Umbraco.Core.Events private readonly TEventArgs _args; public EventDefinition(TypedEventHandler trackedEvent, TSender sender, TEventArgs args) + : base(sender, args) { _trackedEvent = trackedEvent; _sender = sender; diff --git a/src/Umbraco.Core/Events/EventDefinitionBase.cs b/src/Umbraco.Core/Events/EventDefinitionBase.cs index 754ed2fce5..f2d0c817de 100644 --- a/src/Umbraco.Core/Events/EventDefinitionBase.cs +++ b/src/Umbraco.Core/Events/EventDefinitionBase.cs @@ -4,18 +4,38 @@ namespace Umbraco.Core.Events { public abstract class EventDefinitionBase : IEventDefinition, IEquatable { - protected EventDefinitionBase() + protected EventDefinitionBase(object sender, object args, string eventName) { - EventId = Guid.NewGuid(); + if (sender == null) throw new ArgumentNullException("sender"); + if (eventName == null) throw new ArgumentNullException("eventName"); + Sender = sender; + Args = args; + EventName = eventName; } - public Guid EventId { get; private set; } + + protected EventDefinitionBase(object sender, object args) + { + if (sender == null) throw new ArgumentNullException("sender"); + if (args == null) throw new ArgumentNullException("args"); + Sender = sender; + Args = args; + var findResult = EventNameExtractor.FindEvent(sender, args, EventNameExtractor.MatchIngNames); + if (findResult.Success == false) + throw new InvalidOperationException("Could not automatically find the event name, the event name will need to be explicitly registered for this event definition. Error: " + findResult.Result.Error); + EventName = findResult.Result.Name; + } + + public object Sender { get; private set; } + public object Args { get; private set; } + public string EventName { get; private set; } + public abstract void RaiseEvent(); public bool Equals(EventDefinitionBase other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return EventId.Equals(other.EventId); + return Sender.Equals(other.Sender) && string.Equals(EventName, other.EventName); } public override bool Equals(object obj) @@ -28,7 +48,10 @@ namespace Umbraco.Core.Events public override int GetHashCode() { - return EventId.GetHashCode(); + unchecked + { + return (Sender.GetHashCode() * 397) ^ EventName.GetHashCode(); + } } public static bool operator ==(EventDefinitionBase left, EventDefinitionBase right) @@ -38,7 +61,7 @@ namespace Umbraco.Core.Events public static bool operator !=(EventDefinitionBase left, EventDefinitionBase right) { - return Equals(left, right) == false; + return !Equals(left, right); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Events/EventNameExtractor.cs b/src/Umbraco.Core/Events/EventNameExtractor.cs new file mode 100644 index 0000000000..606a3534cb --- /dev/null +++ b/src/Umbraco.Core/Events/EventNameExtractor.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace Umbraco.Core.Events +{ + /// + /// There is actually no way to discover an event name in c# at the time of raising the event. It is possible + /// to get the event name from the handler that is being executed based on the event being raised, however that is not + /// what we want in this case. We need to find the event name before it is being raised - you would think that it's possible + /// with reflection or anything but that is not the case, the delegate that defines an event has no info attached to it, it + /// is literally just an event. + /// + /// So what this does is take the sender and event args objects, looks up all public/static events on the sender that have + /// a generic event handler with generic arguments (but only) one, then we match the type of event arguments with the ones + /// being passed in. As it turns out, in our services this will work for the majority of our events! In some cases it may not + /// work and we'll have to supply a string but hopefully this saves a bit of magic strings. + /// + /// We can also write tests to validate these are all working correctly for all services. + /// + internal class EventNameExtractor + { + /// + /// Finds the event name on the sender that matches the args type + /// + /// + /// + /// + /// A filter to exclude matched event names, this filter should return true to exclude the event name from being matched + /// + /// + /// null if not found or an ambiguous match + /// + public static Attempt FindEvent(object sender, object args, Func exclude) + { + var argsType = args.GetType(); + + //there is no way we can find a matching event if the argument type is not generic + if (argsType.IsGenericType == false) + return Attempt.Fail(new EventNameExtractorResult(EventNameExtractorError.UnsupportedArgType)); + + var senderType = sender.GetType(); + + var found = MatchedEventNames.GetOrAdd(new Tuple(senderType, argsType), tuple => + { + var events = CandidateEvents.GetOrAdd(senderType, t => + { + return t.GetEvents(BindingFlags.Static | BindingFlags.Public) + //we can only look for events handlers with generic types because that is the only + // way that we can try to find a matching event based on the arg type passed in + .Where(x => x.EventHandlerType.IsGenericType) + .Select(x => new EventInfoArgs(x, x.EventHandlerType.GetGenericArguments())) + //we are only looking for event handlers that have more than one generic argument + .Where(x => + { + if (x.GenericArgs.Length == 1) return true; + + //special case for our own TypedEventHandler + if (x.EventInfo.EventHandlerType.GetGenericTypeDefinition() == typeof(TypedEventHandler<,>) && x.GenericArgs.Length == 2) + { + return true; + } + + return false; + }) + .ToArray(); + }); + + return events.Where(x => + { + if (x.GenericArgs.Length == 1 && x.GenericArgs[0] == tuple.Item2) + return true; + + //special case for our own TypedEventHandler + if (x.EventInfo.EventHandlerType.GetGenericTypeDefinition() == typeof(TypedEventHandler<,>) + && x.GenericArgs.Length == 2 + && x.GenericArgs[1] == tuple.Item2) + { + return true; + } + + return false; + }).Select(x => x.EventInfo.Name).ToArray(); + }); + + var filtered = found.Where(x => exclude(x) == false).ToArray(); + + if (filtered.Length == 0) + return Attempt.Fail(new EventNameExtractorResult(EventNameExtractorError.NoneFound)); + + if (filtered.Length == 1) + return Attempt.Succeed(new EventNameExtractorResult(filtered[0])); + + //there's more than one left so it's ambiguous! + return Attempt.Fail(new EventNameExtractorResult(EventNameExtractorError.Ambiguous)); + } + + /// + /// Return true if the event is named with an ING name such as "Saving" or "RollingBack" + /// + /// + /// + internal static bool MatchIngNames(string eventName) + { + var splitter = new Regex(@"(? + /// Return true if the event is not named with an ING name such as "Saving" or "RollingBack" + /// + /// + /// + internal static bool MatchNonIngNames(string eventName) + { + var splitter = new Regex(@"(? + /// Used to cache all candidate events for a given type so we don't re-look them up + /// + private static readonly ConcurrentDictionary CandidateEvents = new ConcurrentDictionary(); + + /// + /// Used to cache all matched event names by (sender type + arg type) so we don't re-look them up + /// + private static readonly ConcurrentDictionary, string[]> MatchedEventNames = new ConcurrentDictionary, string[]>(); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Events/EventNameExtractorError.cs b/src/Umbraco.Core/Events/EventNameExtractorError.cs new file mode 100644 index 0000000000..13cae1da71 --- /dev/null +++ b/src/Umbraco.Core/Events/EventNameExtractorError.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Core.Events +{ + internal enum EventNameExtractorError + { + UnsupportedArgType, + NoneFound, + Ambiguous + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Events/EventNameExtractorResult.cs b/src/Umbraco.Core/Events/EventNameExtractorResult.cs new file mode 100644 index 0000000000..6213882d1f --- /dev/null +++ b/src/Umbraco.Core/Events/EventNameExtractorResult.cs @@ -0,0 +1,18 @@ +namespace Umbraco.Core.Events +{ + internal class EventNameExtractorResult + { + public EventNameExtractorError? Error { get; private set; } + public string Name { get; private set; } + + public EventNameExtractorResult(string name) + { + Name = name; + } + + public EventNameExtractorResult(EventNameExtractorError error) + { + Error = error; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Events/IEventDefinition.cs b/src/Umbraco.Core/Events/IEventDefinition.cs index 9d3a6426fa..eb4fe65eb8 100644 --- a/src/Umbraco.Core/Events/IEventDefinition.cs +++ b/src/Umbraco.Core/Events/IEventDefinition.cs @@ -4,7 +4,9 @@ namespace Umbraco.Core.Events { public interface IEventDefinition { - Guid EventId { get; } + object Sender { get; } + object Args { get; } + string EventName { get; } void RaiseEvent(); } diff --git a/src/Umbraco.Core/Events/ScopedEventManager.cs b/src/Umbraco.Core/Events/ScopedEventManager.cs index 354858cdb1..3f8409e08a 100644 --- a/src/Umbraco.Core/Events/ScopedEventManager.cs +++ b/src/Umbraco.Core/Events/ScopedEventManager.cs @@ -39,5 +39,7 @@ namespace Umbraco.Core.Events { _tracked.Clear(); } + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 88555db603..8996537b6c 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -315,6 +315,9 @@ + + + diff --git a/src/Umbraco.Tests/Scoping/EventNameExtractorTests.cs b/src/Umbraco.Tests/Scoping/EventNameExtractorTests.cs new file mode 100644 index 0000000000..3faeb51d17 --- /dev/null +++ b/src/Umbraco.Tests/Scoping/EventNameExtractorTests.cs @@ -0,0 +1,51 @@ +using System; +using NUnit.Framework; +using Umbraco.Core.Events; + +namespace Umbraco.Tests.Scoping +{ + [TestFixture] + public class EventNameExtractorTests + { + [Test] + public void Find_Event_Ing() + { + var found = EventNameExtractor.FindEvent(this, new SaveEventArgs("test"), EventNameExtractor.MatchIngNames); + Assert.IsTrue(found.Success); + Assert.AreEqual("FoundMe", found.Result.Name); + } + + [Test] + public void Find_Event_Non_Ing() + { + var found = EventNameExtractor.FindEvent(this, new SaveEventArgs("test"), EventNameExtractor.MatchNonIngNames); + Assert.IsTrue(found.Success); + Assert.AreEqual("FindingMe", found.Result.Name); + } + + [Test] + public void Ambiguous_Match() + { + var found = EventNameExtractor.FindEvent(this, new SaveEventArgs(0), EventNameExtractor.MatchIngNames); + Assert.IsFalse(found.Success); + Assert.AreEqual(EventNameExtractorError.Ambiguous, found.Result.Error); + } + + [Test] + public void No_Match() + { + var found = EventNameExtractor.FindEvent(this, new SaveEventArgs(0), EventNameExtractor.MatchIngNames); + Assert.IsFalse(found.Success); + Assert.AreEqual(EventNameExtractorError.NoneFound, found.Result.Error); + } + + public static event EventHandler> FindingMe; + public static event EventHandler> FoundMe; + + //will lead to ambiguous matches + public static event EventHandler> SavingThis; + public static event EventHandler> SavedThis; + public static event EventHandler> SavingThat; + public static event EventHandler> SavedThat; + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Scoping/ScopedEventManagerTests.cs b/src/Umbraco.Tests/Scoping/ScopedEventManagerTests.cs index 3ddd0f7e08..c98b05600e 100644 --- a/src/Umbraco.Tests/Scoping/ScopedEventManagerTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedEventManagerTests.cs @@ -1,4 +1,10 @@ using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; using Moq; using NUnit.Framework; using Umbraco.Core.Events; @@ -8,9 +14,18 @@ using Umbraco.Core.Scoping; namespace Umbraco.Tests.Scoping { + [TestFixture] public class ScopedEventManagerTests { + [SetUp] + public void Setup() + { + //remove all handlers first + DoThing1 = null; + DoThing2 = null; + DoThing3 = null; + } [Test] public void Does_Not_Support_Event_Cancellation() @@ -22,19 +37,46 @@ namespace Umbraco.Tests.Scoping } } + [Test] - public void Does_Not_Immediately_Raise_Events() + public void Can_Get_Event_Info() { - this.DoThing1 += OnDoThingFail; - this.DoThing2 += OnDoThingFail; - this.DoThing3 += OnDoThingFail; + DoThing1 += OnDoThingFail; + DoThing2 += OnDoThingFail; + DoThing3 += OnDoThingFail; var provider = new PetaPocoUnitOfWorkProvider(new ScopeProvider(Mock.Of())); using (var uow = provider.GetUnitOfWork()) { - uow.EventManager.TrackEvent(DoThing1, this, new EventArgs()); - uow.EventManager.TrackEvent(DoThing2, this, new EventArgs()); - uow.EventManager.TrackEvent(DoThing3, this, new EventArgs()); + uow.EventManager.TrackEvent(DoThing1, this, new SaveEventArgs("test")); + uow.EventManager.TrackEvent(DoThing2, this, new SaveEventArgs(0)); + uow.EventManager.TrackEvent(DoThing3, this, new SaveEventArgs(0)); + + var e = uow.EventManager.GetEvents().ToArray(); + var knownNames = new [] {"DoThing1", "DoThing2", "DoThing3"}; + var knownArgTypes = new [] { typeof(SaveEventArgs), typeof(SaveEventArgs), typeof(SaveEventArgs) }; + + for (int i = 0; i < e.Length; i++) + { + Assert.AreEqual(knownNames[i], e[i].EventName); + Assert.AreEqual(knownArgTypes[i], e[i].Args.GetType()); + } + } + } + + [Test] + public void Does_Not_Immediately_Raise_Events() + { + DoThing1 += OnDoThingFail; + DoThing2 += OnDoThingFail; + DoThing3 += OnDoThingFail; + + var provider = new PetaPocoUnitOfWorkProvider(new ScopeProvider(Mock.Of())); + using (var uow = provider.GetUnitOfWork()) + { + uow.EventManager.TrackEvent(DoThing1, this, new SaveEventArgs("test")); + uow.EventManager.TrackEvent(DoThing2, this, new SaveEventArgs(0)); + uow.EventManager.TrackEvent(DoThing3, this, new SaveEventArgs(0)); Assert.Pass(); } @@ -45,17 +87,17 @@ namespace Umbraco.Tests.Scoping { var counter = 0; - this.DoThing1 += (sender, args) => + DoThing1 += (sender, args) => { counter++; }; - this.DoThing2 += (sender, args) => + DoThing2 += (sender, args) => { counter++; }; - this.DoThing3 += (sender, args) => + DoThing3 += (sender, args) => { counter++; }; @@ -63,9 +105,9 @@ namespace Umbraco.Tests.Scoping var provider = new PetaPocoUnitOfWorkProvider(new ScopeProvider(Mock.Of())); using (var uow = provider.GetUnitOfWork()) { - uow.EventManager.TrackEvent(DoThing1, this, new EventArgs()); - uow.EventManager.TrackEvent(DoThing2, this, new EventArgs()); - uow.EventManager.TrackEvent(DoThing3, this, new EventArgs()); + uow.EventManager.TrackEvent(DoThing1, this, new SaveEventArgs("test")); + uow.EventManager.TrackEvent(DoThing2, this, new SaveEventArgs(0)); + uow.EventManager.TrackEvent(DoThing3, this, new SaveEventArgs(0)); Assert.AreEqual(0, counter); @@ -83,8 +125,12 @@ namespace Umbraco.Tests.Scoping Assert.Fail(); } - public event EventHandler DoThing1; - public event EventHandler DoThing2; - public event TypedEventHandler DoThing3; + + public static event EventHandler> DoThing1; + + public static event EventHandler> DoThing2; + + public static event TypedEventHandler> DoThing3; } + } \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 45cfc6cfd7..49ad8047cb 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -169,6 +169,7 @@ + From 85f054e3c1a70bc76279d8cbec9d6bc1dfc91ef0 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 20 Jan 2017 09:54:59 +0100 Subject: [PATCH 312/599] No need to warn if attribute is not present Adds some logging about indexing being done.. the number of committed items seems to differ a little from the number of items in the index, not sure wh --- .../WebServices/ExamineManagementApiController.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs b/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs index 9c50468465..79e7fe8f53 100644 --- a/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs +++ b/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs @@ -166,6 +166,8 @@ namespace Umbraco.Web.WebServices var msg = ValidateLuceneIndexer(indexerName, out indexer); if (msg.IsSuccessStatusCode) { + LogHelper.Info(string.Format("Rebuilding index '{0}'", indexerName)); + //remove it in case there's a handler there alraedy indexer.IndexOperationComplete -= Indexer_IndexOperationComplete; //now add a single handler @@ -201,6 +203,8 @@ namespace Umbraco.Web.WebServices //ensure it's not listening anymore indexer.IndexOperationComplete -= Indexer_IndexOperationComplete; + LogHelper.Info(string.Format("Rebuilding index '{0}' done, {1} items committed (can differ from the number of items in the index)", indexer.Name, indexer.CommitCount)); + var cacheKey = "temp_indexing_op_" + indexer.Name; ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(cacheKey); } @@ -266,7 +270,10 @@ namespace Umbraco.Web.WebServices var val = p.GetValue(indexer, null); if (val == null) { - LogHelper.Warn("Property value was null when setting up property on indexer: " + indexer.Name + " property: " + p.Name); + // Do not warn for new new attribute that is optional + if(string.Equals(p.Name, "DirectoryFactory", StringComparison.InvariantCultureIgnoreCase) == false) + LogHelper.Warn("Property value was null when setting up property on indexer: " + indexer.Name + " property: " + p.Name); + val = string.Empty; } indexerModel.ProviderProperties.Add(p.Name, val.ToString()); From 12922f841361c6884d266401d0cb548a89d84504 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 20 Jan 2017 09:54:59 +0100 Subject: [PATCH 313/599] No need to warn if attribute is not present Adds some logging about indexing being done.. the number of committed items seems to differ a little from the number of items in the index, not sure wh --- .../WebServices/ExamineManagementApiController.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs b/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs index 9c50468465..79e7fe8f53 100644 --- a/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs +++ b/src/Umbraco.Web/WebServices/ExamineManagementApiController.cs @@ -166,6 +166,8 @@ namespace Umbraco.Web.WebServices var msg = ValidateLuceneIndexer(indexerName, out indexer); if (msg.IsSuccessStatusCode) { + LogHelper.Info(string.Format("Rebuilding index '{0}'", indexerName)); + //remove it in case there's a handler there alraedy indexer.IndexOperationComplete -= Indexer_IndexOperationComplete; //now add a single handler @@ -201,6 +203,8 @@ namespace Umbraco.Web.WebServices //ensure it's not listening anymore indexer.IndexOperationComplete -= Indexer_IndexOperationComplete; + LogHelper.Info(string.Format("Rebuilding index '{0}' done, {1} items committed (can differ from the number of items in the index)", indexer.Name, indexer.CommitCount)); + var cacheKey = "temp_indexing_op_" + indexer.Name; ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheItem(cacheKey); } @@ -266,7 +270,10 @@ namespace Umbraco.Web.WebServices var val = p.GetValue(indexer, null); if (val == null) { - LogHelper.Warn("Property value was null when setting up property on indexer: " + indexer.Name + " property: " + p.Name); + // Do not warn for new new attribute that is optional + if(string.Equals(p.Name, "DirectoryFactory", StringComparison.InvariantCultureIgnoreCase) == false) + LogHelper.Warn("Property value was null when setting up property on indexer: " + indexer.Name + " property: " + p.Name); + val = string.Empty; } indexerModel.ProviderProperties.Add(p.Name, val.ToString()); From f3e78e990f7e8e53674eb9e65fd2ec091c159e91 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Fri, 20 Jan 2017 09:28:14 +0000 Subject: [PATCH 314/599] Fix up failing unit test for the number of Trees found - this has gone down due this test is for implementing interface ITree which is the legacy type of Trees, where the new PartialViews & PartialView Macros now use TreeController --- src/Umbraco.Tests/Plugins/PluginManagerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests/Plugins/PluginManagerTests.cs b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs index ceb2326e6d..95e8fb16e2 100644 --- a/src/Umbraco.Tests/Plugins/PluginManagerTests.cs +++ b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs @@ -297,7 +297,7 @@ namespace Umbraco.Tests.Plugins public void Resolves_Trees() { var trees = _manager.ResolveTrees(); - Assert.AreEqual(39, trees.Count()); + Assert.AreEqual(37, trees.Count()); } [Test] From 848eaf5e2770cbe20c056cae8e2c0fc1911eb609 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 20 Jan 2017 12:08:55 +0100 Subject: [PATCH 315/599] U4-9322 - cont. implementing scoped repository caches --- .../Cache/DefaultRepositoryCachePolicy.cs | 6 + .../Cache/FullDataSetRepositoryCachePolicy.cs | 6 + .../Cache/IRepositoryCachePolicy.cs | 6 +- .../Cache/RepositoryCachePolicyBase.cs | 3 + ...licy.cs => ScopedRepositoryCachePolicy.cs} | 17 ++- .../Repositories/ContentRepository.cs | 2 +- .../Repositories/ContentTypeRepository.cs | 8 +- .../DataTypeDefinitionRepository.cs | 8 +- .../Repositories/DictionaryRepository.cs | 79 +++++------- .../Repositories/DomainRepository.cs | 13 +- .../Repositories/EntityContainerRepository.cs | 9 +- .../Repositories/LanguageRepository.cs | 26 ++-- .../Repositories/MediaRepository.cs | 2 +- .../Repositories/MediaTypeRepository.cs | 13 +- .../Repositories/MemberGroupRepository.cs | 2 +- .../Repositories/MemberRepository.cs | 2 +- .../Repositories/MemberTypeRepository.cs | 13 +- .../Repositories/PublicAccessRepository.cs | 13 +- .../Repositories/RelationTypeRepository.cs | 13 +- .../Repositories/RepositoryBase.cs | 69 ++++++---- .../Repositories/TemplateRepository.cs | 17 +-- src/Umbraco.Core/Umbraco.Core.csproj | 2 +- .../Scoping/ScopedRepositoryTests.cs | 118 ++++++++++++++++-- 23 files changed, 250 insertions(+), 197 deletions(-) rename src/Umbraco.Core/Cache/{ScopedDefaultRepositoryCachePolicy.cs => ScopedRepositoryCachePolicy.cs} (76%) diff --git a/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs index 5d08e36f6a..fc98473c4c 100644 --- a/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/DefaultRepositoryCachePolicy.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Scoping; namespace Umbraco.Core.Cache { @@ -29,6 +30,11 @@ namespace Umbraco.Core.Cache _options = options; } + public override IRepositoryCachePolicy Scoped(IRuntimeCacheProvider runtimeCache, IScope scope) + { + return new ScopedRepositoryCachePolicy(this, runtimeCache, scope); + } + protected string GetEntityCacheKey(object id) { if (id == null) throw new ArgumentNullException("id"); diff --git a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs index 95ad7e2bf0..41c0249877 100644 --- a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core.Collections; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Scoping; namespace Umbraco.Core.Cache { @@ -30,6 +31,11 @@ namespace Umbraco.Core.Cache _expires = expires; } + public override IRepositoryCachePolicy Scoped(IRuntimeCacheProvider runtimeCache, IScope scope) + { + return new ScopedRepositoryCachePolicy(this, runtimeCache, scope); + } + protected static readonly TId[] EmptyIds = new TId[0]; // const protected string GetEntityTypeCacheKey() diff --git a/src/Umbraco.Core/Cache/IRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/IRepositoryCachePolicy.cs index 11a3cf6bb9..cd37ac1e60 100644 --- a/src/Umbraco.Core/Cache/IRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/IRepositoryCachePolicy.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Scoping; namespace Umbraco.Core.Cache { @@ -17,7 +18,10 @@ namespace Umbraco.Core.Cache // it is not *that* complicated but then RepositoryBase needs to have a TRepository generic // type parameter and it all becomes convoluted - keeping it simple for the time being. - /// + // fixme explain + IRepositoryCachePolicy Scoped(IRuntimeCacheProvider runtimeCache, IScope scope); + + /// /// Gets an entity from the cache, else from the repository. /// /// The identifier. diff --git a/src/Umbraco.Core/Cache/RepositoryCachePolicyBase.cs b/src/Umbraco.Core/Cache/RepositoryCachePolicyBase.cs index ce366e3d0b..0b5d2b15c5 100644 --- a/src/Umbraco.Core/Cache/RepositoryCachePolicyBase.cs +++ b/src/Umbraco.Core/Cache/RepositoryCachePolicyBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Scoping; namespace Umbraco.Core.Cache { @@ -18,6 +19,8 @@ namespace Umbraco.Core.Cache Cache = cache; } + public abstract IRepositoryCachePolicy Scoped(IRuntimeCacheProvider runtimeCache, IScope scope); + protected IRuntimeCacheProvider Cache { get; private set; } /// diff --git a/src/Umbraco.Core/Cache/ScopedDefaultRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/ScopedRepositoryCachePolicy.cs similarity index 76% rename from src/Umbraco.Core/Cache/ScopedDefaultRepositoryCachePolicy.cs rename to src/Umbraco.Core/Cache/ScopedRepositoryCachePolicy.cs index 3c237f307c..ec3516cdef 100644 --- a/src/Umbraco.Core/Cache/ScopedDefaultRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/ScopedRepositoryCachePolicy.cs @@ -5,27 +5,34 @@ using Umbraco.Core.Scoping; namespace Umbraco.Core.Cache { - internal class ScopedDefaultRepositoryCachePolicy : IRepositoryCachePolicy + internal class ScopedRepositoryCachePolicy : IRepositoryCachePolicy where TEntity : class, IAggregateRoot { - private readonly DefaultRepositoryCachePolicy _cachePolicy; + private readonly IRepositoryCachePolicy _cachePolicy; private readonly IRuntimeCacheProvider _globalIsolatedCache; private readonly IScope _scope; - public ScopedDefaultRepositoryCachePolicy(DefaultRepositoryCachePolicy cachePolicy, IRuntimeCacheProvider globalIsolatedCache, IScope scope) + public ScopedRepositoryCachePolicy(IRepositoryCachePolicy cachePolicy, IRuntimeCacheProvider globalIsolatedCache, IScope scope) { _cachePolicy = cachePolicy; _globalIsolatedCache = globalIsolatedCache; _scope = scope; } + public IRepositoryCachePolicy Scoped(IRuntimeCacheProvider runtimeCache, IScope scope) + { + throw new InvalidOperationException(); // obviously + } + // when the scope completes we need to clear the global isolated cache // for now, we are not doing it selectively at all - just kill everything + // later on we might want to be more clever private void RegisterDirty(TEntity entity = null) { - // "name" would be used to de-duplicate? + // use unique names to de-duplicate // fixme - casting! - ((Scope) _scope).Register("name", completed => _globalIsolatedCache.ClearAllCache()); + ((Scope) _scope).Register("dirty_" + typeof (TEntity).Name, + () => _globalIsolatedCache.ClearAllCache()); } public TEntity Get(TId id, Func performGet, Func> performGetAll) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 7f3e657dc2..59f64b370c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -853,7 +853,7 @@ order by umbracoNode.level, umbracoNode.parentID, umbracoNode.sortOrder"; // if the cache contains the published version, use it if (withCache) { - var cached = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); + var cached = IsolatedCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); //only use this cached version if the dto returned is also the publish version, they must match if (cached != null && cached.Published && dto.Published) { diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs index ca53b2e04e..57f96ea2f0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs @@ -18,7 +18,6 @@ namespace Umbraco.Core.Persistence.Repositories internal class ContentTypeRepository : ContentTypeBaseRepository, IContentTypeRepository { private readonly ITemplateRepository _templateRepository; - private IRepositoryCachePolicy _cachePolicy; public ContentTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, ITemplateRepository templateRepository) : base(work, cache, logger, sqlSyntax) @@ -26,12 +25,9 @@ namespace Umbraco.Core.Persistence.Repositories _templateRepository = templateRepository; } - protected override IRepositoryCachePolicy CachePolicy + protected override IRepositoryCachePolicy CreateCachePolicy(IRuntimeCacheProvider runtimeCache) { - get - { - return _cachePolicy ?? (_cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ true)); - } + return new FullDataSetRepositoryCachePolicy(runtimeCache, GetEntityId, /*expires:*/ true); } protected override IContentType PerformGet(int id) diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index a4d71885f8..2d76b64521 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -237,7 +237,7 @@ AND umbracoNode.id <> @id", //NOTE: This is a special case, we need to clear the custom cache for pre-values here so they are not stale if devs // are querying for them in the Saved event (before the distributed call cache is clearing it) - RuntimeCache.ClearCacheItem(GetPrefixedCacheKey(entity.Id)); + IsolatedCache.ClearCacheItem(GetPrefixedCacheKey(entity.Id)); entity.ResetDirtyProperties(); } @@ -276,7 +276,7 @@ AND umbracoNode.id <> @id", public PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId) { - var cached = RuntimeCache.GetCacheItemsByKeySearch(GetPrefixedCacheKey(dataTypeId)); + var cached = IsolatedCache.GetCacheItemsByKeySearch(GetPrefixedCacheKey(dataTypeId)); if (cached != null && cached.Any()) { //return from the cache, ensure it's a cloned result @@ -295,7 +295,7 @@ AND umbracoNode.id <> @id", { //We need to see if we can find the cached PreValueCollection based on the cache key above - var cached = RuntimeCache.GetCacheItemsByKeyExpression(GetCacheKeyRegex(preValueId)); + var cached = IsolatedCache.GetCacheItemsByKeyExpression(GetCacheKeyRegex(preValueId)); if (cached != null && cached.Any()) { //return from the cache @@ -463,7 +463,7 @@ AND umbracoNode.id <> @id", + string.Join(",", collection.FormatAsDictionary().Select(x => x.Value.Id).ToArray()); //store into cache - RuntimeCache.InsertCacheItem(key, () => collection, + IsolatedCache.InsertCacheItem(key, () => collection, //30 mins new TimeSpan(0, 0, 30), //sliding is true diff --git a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs index da6d4d94a8..d2cc92ff0b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DictionaryRepository.cs @@ -20,28 +20,19 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class DictionaryRepository : PetaPocoRepositoryBase, IDictionaryRepository { - private IRepositoryCachePolicy _cachePolicy; - public DictionaryRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider syntax) : base(work, cache, logger, syntax) { } - protected override IRepositoryCachePolicy CachePolicy + protected override IRepositoryCachePolicy CreateCachePolicy(IRuntimeCacheProvider runtimeCache) { - get + var options = new RepositoryCachePolicyOptions { - if (_cachePolicy != null) return _cachePolicy; + //allow zero to be cached + GetAllCacheAllowZeroCount = true + }; - var options = new RepositoryCachePolicyOptions - { - //allow zero to be cached - GetAllCacheAllowZeroCount = true - }; - - _cachePolicy = new SingleItemsOnlyRepositoryCachePolicy(RuntimeCache, options); - - return _cachePolicy; - } + return new SingleItemsOnlyRepositoryCachePolicy(runtimeCache, options); } #region Overrides of RepositoryBase @@ -185,8 +176,8 @@ namespace Umbraco.Core.Persistence.Repositories entity.ResetDirtyProperties(); //Clear the cache entries that exist by uniqueid/item key - RuntimeCache.ClearCacheItem(GetCacheIdKey(entity.ItemKey)); - RuntimeCache.ClearCacheItem(GetCacheIdKey(entity.Key)); + IsolatedCache.ClearCacheItem(GetCacheIdKey(entity.ItemKey)); + IsolatedCache.ClearCacheItem(GetCacheIdKey(entity.Key)); } protected override void PersistDeletedItem(IDictionaryItem entity) @@ -197,8 +188,8 @@ namespace Umbraco.Core.Persistence.Repositories Database.Delete("WHERE id = @Id", new { Id = entity.Key }); //Clear the cache entries that exist by uniqueid/item key - RuntimeCache.ClearCacheItem(GetCacheIdKey(entity.ItemKey)); - RuntimeCache.ClearCacheItem(GetCacheIdKey(entity.Key)); + IsolatedCache.ClearCacheItem(GetCacheIdKey(entity.ItemKey)); + IsolatedCache.ClearCacheItem(GetCacheIdKey(entity.Key)); } private void RecursiveDelete(Guid parentId) @@ -212,8 +203,8 @@ namespace Umbraco.Core.Persistence.Repositories Database.Delete("WHERE id = @Id", new { Id = dto.UniqueId }); //Clear the cache entries that exist by uniqueid/item key - RuntimeCache.ClearCacheItem(GetCacheIdKey(dto.Key)); - RuntimeCache.ClearCacheItem(GetCacheIdKey(dto.UniqueId)); + IsolatedCache.ClearCacheItem(GetCacheIdKey(dto.Key)); + IsolatedCache.ClearCacheItem(GetCacheIdKey(dto.UniqueId)); } } @@ -240,7 +231,8 @@ namespace Umbraco.Core.Persistence.Repositories public IDictionaryItem Get(Guid uniqueId) { - using (var uniqueIdRepo = new DictionaryByUniqueIdRepository(this, UnitOfWork, RepositoryCache, Logger, SqlSyntax)) + // note: normal to use GlobalCache here since we're passing it to a new repository + using (var uniqueIdRepo = new DictionaryByUniqueIdRepository(this, UnitOfWork, GlobalCache, Logger, SqlSyntax)) { return uniqueIdRepo.Get(uniqueId); } @@ -248,7 +240,8 @@ namespace Umbraco.Core.Persistence.Repositories public IDictionaryItem Get(string key) { - using (var keyRepo = new DictionaryByKeyRepository(this, UnitOfWork, RepositoryCache, Logger, SqlSyntax)) + // note: normal to use GlobalCache here since we're passing it to a new repository + using (var keyRepo = new DictionaryByKeyRepository(this, UnitOfWork, GlobalCache, Logger, SqlSyntax)) { return keyRepo.Get(key); } @@ -294,7 +287,6 @@ namespace Umbraco.Core.Persistence.Repositories private class DictionaryByUniqueIdRepository : SimpleGetRepository { - private IRepositoryCachePolicy _cachePolicy; private readonly DictionaryRepository _dictionaryRepository; public DictionaryByUniqueIdRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) @@ -333,28 +325,20 @@ namespace Umbraco.Core.Persistence.Repositories return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("id") + " in (@ids)"; } - protected override IRepositoryCachePolicy CachePolicy + protected override IRepositoryCachePolicy CreateCachePolicy(IRuntimeCacheProvider runtimeCache) { - get + var options = new RepositoryCachePolicyOptions { - if (_cachePolicy != null) return _cachePolicy; + //allow zero to be cached + GetAllCacheAllowZeroCount = true + }; - var options = new RepositoryCachePolicyOptions - { - //allow zero to be cached - GetAllCacheAllowZeroCount = true - }; - - _cachePolicy = new SingleItemsOnlyRepositoryCachePolicy(RuntimeCache, options); - - return _cachePolicy; - } + return new SingleItemsOnlyRepositoryCachePolicy(runtimeCache, options); } } private class DictionaryByKeyRepository : SimpleGetRepository { - private IRepositoryCachePolicy _cachePolicy; private readonly DictionaryRepository _dictionaryRepository; public DictionaryByKeyRepository(DictionaryRepository dictionaryRepository, IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) @@ -393,22 +377,15 @@ namespace Umbraco.Core.Persistence.Repositories return "cmsDictionary." + SqlSyntax.GetQuotedColumnName("key") + " in (@ids)"; } - protected override IRepositoryCachePolicy CachePolicy + protected override IRepositoryCachePolicy CreateCachePolicy(IRuntimeCacheProvider runtimeCache) { - get + var options = new RepositoryCachePolicyOptions { - if (_cachePolicy != null) return _cachePolicy; + //allow zero to be cached + GetAllCacheAllowZeroCount = true + }; - var options = new RepositoryCachePolicyOptions - { - //allow zero to be cached - GetAllCacheAllowZeroCount = true - }; - - _cachePolicy = new SingleItemsOnlyRepositoryCachePolicy(RuntimeCache, options); - - return _cachePolicy; - } + return new SingleItemsOnlyRepositoryCachePolicy(runtimeCache, options); } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs index 682c75eeb6..75c6ad0d87 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DomainRepository.cs @@ -18,23 +18,14 @@ namespace Umbraco.Core.Persistence.Repositories internal class DomainRepository : PetaPocoRepositoryBase, IDomainRepository { - private IRepositoryCachePolicy _cachePolicy; - public DomainRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } - protected override IRepositoryCachePolicy CachePolicy + protected override IRepositoryCachePolicy CreateCachePolicy(IRuntimeCacheProvider runtimeCache) { - get - { - if (_cachePolicy != null) return _cachePolicy; - - _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ false); - - return _cachePolicy; - } + return new FullDataSetRepositoryCachePolicy(runtimeCache, GetEntityId, /*expires:*/ false); } protected override IDomain PerformGet(int id) diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs index cc13275798..41910e507a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityContainerRepository.cs @@ -29,12 +29,11 @@ namespace Umbraco.Core.Persistence.Repositories throw new InvalidOperationException("No container type exists with ID: " + _containerObjectType); } - /// - /// Do not cache anything - /// - protected override IRuntimeCacheProvider RuntimeCache + // never cache + private static readonly IRuntimeCacheProvider NullCache = new NullCacheProvider(); + protected override IRuntimeCacheProvider GetIsolatedCache(IsolatedRuntimeCache provider) { - get { return new NullCacheProvider(); } + return NullCache; } protected override EntityContainer PerformGet(int id) diff --git a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs index 7911df8edb..a32a3c4647 100644 --- a/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/LanguageRepository.cs @@ -19,23 +19,13 @@ namespace Umbraco.Core.Persistence.Repositories ///
    internal class LanguageRepository : PetaPocoRepositoryBase, ILanguageRepository { - private IRepositoryCachePolicy _cachePolicy; - public LanguageRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) - { - } + { } - protected override IRepositoryCachePolicy CachePolicy + protected override IRepositoryCachePolicy CreateCachePolicy(IRuntimeCacheProvider runtimeCache) { - get - { - if (_cachePolicy != null) return _cachePolicy; - - _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ false); - - return _cachePolicy; - } + return new FullDataSetRepositoryCachePolicy(runtimeCache, GetEntityId, /*expires:*/ false); } #region Overrides of RepositoryBase @@ -134,8 +124,8 @@ namespace Umbraco.Core.Persistence.Repositories entity.ResetDirtyProperties(); //Clear the cache entries that exist by key/iso - RuntimeCache.ClearCacheItem(GetCacheIdKey(entity.IsoCode)); - RuntimeCache.ClearCacheItem(GetCacheIdKey(entity.CultureName)); + IsolatedCache.ClearCacheItem(GetCacheIdKey(entity.IsoCode)); + IsolatedCache.ClearCacheItem(GetCacheIdKey(entity.CultureName)); } protected override void PersistDeletedItem(ILanguage entity) @@ -143,8 +133,8 @@ namespace Umbraco.Core.Persistence.Repositories base.PersistDeletedItem(entity); //Clear the cache entries that exist by key/iso - RuntimeCache.ClearCacheItem(GetCacheIdKey(entity.IsoCode)); - RuntimeCache.ClearCacheItem(GetCacheIdKey(entity.CultureName)); + IsolatedCache.ClearCacheItem(GetCacheIdKey(entity.IsoCode)); + IsolatedCache.ClearCacheItem(GetCacheIdKey(entity.CultureName)); } #endregion @@ -167,7 +157,5 @@ namespace Umbraco.Core.Persistence.Repositories //use the underlying GetAll which will force cache all languages return GetAll().FirstOrDefault(x => x.IsoCode.InvariantEquals(isoCode)); } - - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 08c2f7e0be..1d1a62f354 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -155,7 +155,7 @@ namespace Umbraco.Core.Persistence.Repositories // if the cache contains the item, use it if (withCache) { - var cached = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); + var cached = IsolatedCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); if (cached != null) { content[i] = cached; diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs index 516c08330a..edf7051d4f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaTypeRepository.cs @@ -22,23 +22,14 @@ namespace Umbraco.Core.Persistence.Repositories ///
    internal class MediaTypeRepository : ContentTypeBaseRepository, IMediaTypeRepository { - private IRepositoryCachePolicy _cachePolicy; - public MediaTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } - protected override IRepositoryCachePolicy CachePolicy + protected override IRepositoryCachePolicy CreateCachePolicy(IRuntimeCacheProvider runtimeCache) { - get - { - if (_cachePolicy != null) return _cachePolicy; - - _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ true); - - return _cachePolicy; - } + return new FullDataSetRepositoryCachePolicy(runtimeCache, GetEntityId, /*expires:*/ true); } protected override IMediaType PerformGet(int id) diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs index 2c0e910428..b98b20ae3f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs @@ -131,7 +131,7 @@ namespace Umbraco.Core.Persistence.Repositories public IMemberGroup GetByName(string name) { - return RuntimeCache.GetCacheItem( + return IsolatedCache.GetCacheItem( string.Format("{0}.{1}", typeof (IMemberGroup).FullName, name), () => { diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index dcab898685..72c367326b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -674,7 +674,7 @@ namespace Umbraco.Core.Persistence.Repositories // if the cache contains the item, use it if (withCache) { - var cached = RuntimeCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); + var cached = IsolatedCache.GetCacheItem(GetCacheIdKey(dto.NodeId)); if (cached != null) { content[i] = cached; diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs index fd26afac89..49c9d47319 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs @@ -21,23 +21,14 @@ namespace Umbraco.Core.Persistence.Repositories ///
    internal class MemberTypeRepository : ContentTypeBaseRepository, IMemberTypeRepository { - private IRepositoryCachePolicy _cachePolicy; - public MemberTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } - protected override IRepositoryCachePolicy CachePolicy + protected override IRepositoryCachePolicy CreateCachePolicy(IRuntimeCacheProvider runtimeCache) { - get - { - if (_cachePolicy != null) return _cachePolicy; - - _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ true); - - return _cachePolicy; - } + return new FullDataSetRepositoryCachePolicy(runtimeCache, GetEntityId, /*expires:*/ true); } protected override IMemberType PerformGet(int id) diff --git a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs index c34952f4be..a4f8705f81 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PublicAccessRepository.cs @@ -15,22 +15,13 @@ namespace Umbraco.Core.Persistence.Repositories { internal class PublicAccessRepository : PetaPocoRepositoryBase, IPublicAccessRepository { - private IRepositoryCachePolicy _cachePolicy; - public PublicAccessRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } - protected override IRepositoryCachePolicy CachePolicy + protected override IRepositoryCachePolicy CreateCachePolicy(IRuntimeCacheProvider runtimeCache) { - get - { - if (_cachePolicy != null) return _cachePolicy; - - _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ false); - - return _cachePolicy; - } + return new FullDataSetRepositoryCachePolicy(runtimeCache, GetEntityId, /*expires:*/ false); } protected override PublicAccessEntry PerformGet(Guid id) diff --git a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs index 148ebba456..198afa818b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RelationTypeRepository.cs @@ -19,22 +19,13 @@ namespace Umbraco.Core.Persistence.Repositories /// internal class RelationTypeRepository : PetaPocoRepositoryBase, IRelationTypeRepository { - private IRepositoryCachePolicy _cachePolicy; - public RelationTypeRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) { } - protected override IRepositoryCachePolicy CachePolicy + protected override IRepositoryCachePolicy CreateCachePolicy(IRuntimeCacheProvider runtimeCache) { - get - { - if (_cachePolicy != null) return _cachePolicy; - - _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ true); - - return _cachePolicy; - } + return new FullDataSetRepositoryCachePolicy(runtimeCache, GetEntityId, /*expires:*/ true); } #region Overrides of RepositoryBase diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index 09b4d7609d..eb07ed226b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -1,9 +1,7 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using Umbraco.Core.Cache; -using Umbraco.Core.Collections; using Umbraco.Core.Logging; using Umbraco.Core.Models.EntityBase; @@ -16,7 +14,7 @@ namespace Umbraco.Core.Persistence.Repositories internal abstract class RepositoryBase : DisposableObject { private readonly IUnitOfWork _work; - private readonly CacheHelper _cache; + private readonly CacheHelper _globalCache; protected RepositoryBase(IUnitOfWork work, CacheHelper cache, ILogger logger) { @@ -25,7 +23,7 @@ namespace Umbraco.Core.Persistence.Repositories if (logger == null) throw new ArgumentNullException("logger"); Logger = logger; _work = work; - _cache = cache; + _globalCache = cache; } /// @@ -44,18 +42,18 @@ namespace Umbraco.Core.Persistence.Repositories get { return (Guid)_work.Key; } } - protected CacheHelper RepositoryCache + /// + /// Gets the global application cache. + /// + protected CacheHelper GlobalCache { - get { return _cache; } + get { return _globalCache; } } /// - /// The runtime cache used for this repo - by standard this is the runtime cache exposed by the CacheHelper but can be overridden + /// Gets the repository isolated cache. /// - protected virtual IRuntimeCacheProvider RuntimeCache - { - get { return _cache.RuntimeCache; } - } + protected abstract IRuntimeCacheProvider IsolatedCache { get; } public static string GetCacheIdKey(object id) { @@ -97,9 +95,34 @@ namespace Umbraco.Core.Persistence.Repositories /// /// The runtime cache used for this repo by default is the isolated cache for this type /// - protected override IRuntimeCacheProvider RuntimeCache + private IRuntimeCacheProvider _isolatedCache; + protected override IRuntimeCacheProvider IsolatedCache { - get { return RepositoryCache.IsolatedRuntimeCache.GetOrCreateCache(); } + get + { + if (_isolatedCache != null) return _isolatedCache; + + var scope = ((PetaPocoUnitOfWork) UnitOfWork).Scope; // fixme cast! + IsolatedRuntimeCache provider; + switch (scope.RepositoryCacheMode) + { + case RepositoryCacheMode.Default: + provider = GlobalCache.IsolatedRuntimeCache; + break; + case RepositoryCacheMode.Scoped: + provider = scope.IsolatedRuntimeCache; + break; + default: + throw new Exception("oops: cache mode."); + } + + return _isolatedCache = GetIsolatedCache(provider); + } + } + + protected virtual IRuntimeCacheProvider GetIsolatedCache(IsolatedRuntimeCache provider) + { + return provider.GetOrCreateCache(); } private static RepositoryCachePolicyOptions _defaultOptions; @@ -126,37 +149,39 @@ namespace Umbraco.Core.Persistence.Repositories // get // { // return _defaultCachePolicy ?? (_defaultCachePolicy - // = new DefaultRepositoryCachePolicy(RuntimeCache, DefaultOptions)); + // = new DefaultRepositoryCachePolicy(IsolatedCache, DefaultOptions)); // } //} private IRepositoryCachePolicy _cachePolicy; - protected virtual IRepositoryCachePolicy CachePolicy + protected IRepositoryCachePolicy CachePolicy { get { if (_cachePolicy != null) return _cachePolicy; - var scope = ((PetaPocoUnitOfWork) UnitOfWork).Scope; + _cachePolicy = CreateCachePolicy(IsolatedCache); + var scope = ((PetaPocoUnitOfWork) UnitOfWork).Scope; // fixme cast! switch (scope.RepositoryCacheMode) { case RepositoryCacheMode.Default: - //_cachePolicy = DefaultCachePolicy; - _cachePolicy = new DefaultRepositoryCachePolicy(RuntimeCache, DefaultOptions); break; case RepositoryCacheMode.Scoped: - var scopedCache = scope.IsolatedRuntimeCache.GetOrCreateCache(); - var scopedPolicy = new DefaultRepositoryCachePolicy(scopedCache, DefaultOptions); - _cachePolicy = new ScopedDefaultRepositoryCachePolicy(scopedPolicy, RuntimeCache, scope); + _cachePolicy = _cachePolicy.Scoped(GetIsolatedCache(GlobalCache.IsolatedRuntimeCache), scope); break; default: - throw new Exception(); + throw new Exception("oops: cache mode."); } return _cachePolicy; } } + protected virtual IRepositoryCachePolicy CreateCachePolicy(IRuntimeCacheProvider runtimeCache) + { + return new DefaultRepositoryCachePolicy(runtimeCache, DefaultOptions); + } + /// /// Adds or Updates an entity of type TEntity /// diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index a164e7e5ba..45a402d36f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Text; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -17,9 +15,7 @@ using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Core.Services; using Umbraco.Core.Strings; -using Umbraco.Core.Sync; namespace Umbraco.Core.Persistence.Repositories { @@ -33,7 +29,6 @@ namespace Umbraco.Core.Persistence.Repositories private readonly ITemplatesSection _templateConfig; private readonly ViewHelper _viewHelper; private readonly MasterPageHelper _masterPageHelper; - private IRepositoryCachePolicy _cachePolicy; internal TemplateRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax, IFileSystem masterpageFileSystem, IFileSystem viewFileSystem, ITemplatesSection templateConfig) : base(work, cache, logger, sqlSyntax) @@ -45,17 +40,9 @@ namespace Umbraco.Core.Persistence.Repositories _masterPageHelper = new MasterPageHelper(_masterpagesFileSystem); } - - protected override IRepositoryCachePolicy CachePolicy + protected override IRepositoryCachePolicy CreateCachePolicy(IRuntimeCacheProvider runtimeCache) { - get - { - if (_cachePolicy != null) return _cachePolicy; - - _cachePolicy = new FullDataSetRepositoryCachePolicy(RuntimeCache, GetEntityId, /*expires:*/ false); - - return _cachePolicy; - } + return new FullDataSetRepositoryCachePolicy(runtimeCache, GetEntityId, /*expires:*/ false); } #region Overrides of RepositoryBase diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 2c229c6d73..59479b5472 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -156,7 +156,7 @@ - + diff --git a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs index 329af6833f..3a7d7ac465 100644 --- a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; +using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; @@ -32,15 +35,15 @@ namespace Umbraco.Tests.Scoping [TestCase(true)] [TestCase(false)] - public void Test(bool complete) + public void DefaultRepositoryCachePolicy(bool complete) { var scopeProvider = DatabaseContext.ScopeProvider; - var userService = ApplicationContext.Services.UserService; + var service = ApplicationContext.Services.UserService; var globalCache = ApplicationContext.ApplicationCache.IsolatedRuntimeCache.GetOrCreateCache(typeof(IUser)); - var userType = userService.GetUserTypeByAlias("admin"); + var userType = service.GetUserTypeByAlias("admin"); var user = (IUser) new User("name", "email", "username", "rawPassword", userType); - userService.Save(user); + service.Save(user); // global cache contains the user entity var globalCached = (IUser) globalCache.GetCacheItem(GetCacheIdKey(user.Id), () => null); @@ -60,7 +63,7 @@ namespace Umbraco.Tests.Scoping Assert.AreNotSame(globalCache, scopedCache); user.Name = "changed"; - ApplicationContext.Services.UserService.Save(user); + service.Save(user); // scoped cache contains the "new" user entity var scopeCached = (IUser) scopedCache.GetCacheItem(GetCacheIdKey(user.Id), () => null); @@ -79,12 +82,20 @@ namespace Umbraco.Tests.Scoping } Assert.IsNull(scopeProvider.AmbientScope); - // global cache has been cleared - globalCached = (IUser) globalCache.GetCacheItem(GetCacheIdKey(user.Id), () => null); - Assert.IsNull(globalCached); + globalCached = (IUser)globalCache.GetCacheItem(GetCacheIdKey(user.Id), () => null); + if (complete) + { + // global cache has been cleared + Assert.IsNull(globalCached); + } + else + { + // global cache has *not* been cleared + Assert.IsNotNull(globalCached); + } // get again, updated if completed - user = userService.GetUserById(user.Id); + user = service.GetUserById(user.Id); Assert.AreEqual(complete ? "changed" : "name", user.Name); // global cache contains the entity again @@ -94,6 +105,95 @@ namespace Umbraco.Tests.Scoping Assert.AreEqual(complete ? "changed" : "name", globalCached.Name); } + [TestCase(true)] + [TestCase(false)] + public void FullDataSetRepositoryCachePolicy(bool complete) + { + var scopeProvider = DatabaseContext.ScopeProvider; + var service = ApplicationContext.Services.LocalizationService; + var globalCache = ApplicationContext.ApplicationCache.IsolatedRuntimeCache.GetOrCreateCache(typeof (ILanguage)); + + var lang = (ILanguage) new Language("fr-FR"); + service.Save(lang); + + // global cache has been flushed, reload + var globalFullCached = (IEnumerable) globalCache.GetCacheItem(GetCacheTypeKey(), () => null); + Assert.IsNull(globalFullCached); + var reload = service.GetLanguageById(lang.Id); + + // global cache contains the user entity + globalFullCached = (IEnumerable) globalCache.GetCacheItem(GetCacheTypeKey(), () => null); + Assert.IsNotNull(globalFullCached); + var globalCached = globalFullCached.First(x => x.Id == lang.Id); + Assert.IsNotNull(globalCached); + Assert.AreEqual(lang.Id, globalCached.Id); + Assert.AreEqual("fr-FR", globalCached.IsoCode); + + Assert.IsNull(scopeProvider.AmbientScope); + using (var scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) + { + Assert.IsInstanceOf(scope); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + + // scope has its own isolated cache + var scopedCache = scope.IsolatedRuntimeCache.GetOrCreateCache(typeof (ILanguage)); + Assert.AreNotSame(globalCache, scopedCache); + + lang.IsoCode = "de-DE"; + service.Save(lang); + + // scoped cache has been flushed, reload + var scopeFullCached = (IEnumerable) scopedCache.GetCacheItem(GetCacheTypeKey(), () => null); + Assert.IsNull(scopeFullCached); + reload = service.GetLanguageById(lang.Id); + + // scoped cache contains the "new" user entity + scopeFullCached = (IEnumerable) scopedCache.GetCacheItem(GetCacheTypeKey(), () => null); + Assert.IsNotNull(scopeFullCached); + var scopeCached = scopeFullCached.First(x => x.Id == lang.Id); + Assert.IsNotNull(scopeCached); + Assert.AreEqual(lang.Id, scopeCached.Id); + Assert.AreEqual("de-DE", scopeCached.IsoCode); + + // global cache is unchanged + globalFullCached = (IEnumerable) globalCache.GetCacheItem(GetCacheTypeKey(), () => null); + Assert.IsNotNull(globalFullCached); + globalCached = globalFullCached.First(x => x.Id == lang.Id); + Assert.IsNotNull(globalCached); + Assert.AreEqual(lang.Id, globalCached.Id); + Assert.AreEqual("fr-FR", globalCached.IsoCode); + + if (complete) + scope.Complete(); + } + Assert.IsNull(scopeProvider.AmbientScope); + + globalFullCached = (IEnumerable) globalCache.GetCacheItem(GetCacheTypeKey(), () => null); + if (complete) + { + // global cache has been cleared + Assert.IsNull(globalFullCached); + } + else + { + // global cache has *not* been cleared + Assert.IsNotNull(globalFullCached); + } + + // get again, updated if completed + lang = service.GetLanguageById(lang.Id); + Assert.AreEqual(complete ? "de-DE" : "fr-FR", lang.IsoCode); + + // global cache contains the entity again + globalFullCached = (IEnumerable) globalCache.GetCacheItem(GetCacheTypeKey(), () => null); + Assert.IsNotNull(globalFullCached); + globalCached = globalFullCached.First(x => x.Id == lang.Id); + Assert.IsNotNull(globalCached); + Assert.AreEqual(lang.Id, globalCached.Id); + Assert.AreEqual(complete ? "de-DE" : "fr-FR", lang.IsoCode); + } + public static string GetCacheIdKey(object id) { return string.Format("{0}{1}", GetCacheTypeKey(), id); From 4ac20de1dd770770755e5206d0e03af87101bdbe Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 20 Jan 2017 15:06:48 +0100 Subject: [PATCH 316/599] U4-9322 - complete scope repository caches --- .../Cache/NoRepositoryCachePolicy.cs | 62 +++++++++++++ .../Cache/ScopedRepositoryCachePolicy.cs | 13 ++- src/Umbraco.Core/CacheHelper.cs | 9 +- .../Repositories/ContentPreviewRepository.cs | 3 +- .../Repositories/ContentRepository.cs | 4 +- .../DataTypeDefinitionRepository.cs | 5 +- .../Repositories/MediaRepository.cs | 4 +- .../Repositories/MemberRepository.cs | 4 +- .../Repositories/RepositoryBase.cs | 3 + .../ServerRegistrationRepository.cs | 18 ++-- .../Persistence/RepositoryFactory.cs | 18 ++-- src/Umbraco.Core/Scoping/IScope.cs | 21 ++++- src/Umbraco.Core/Scoping/NoScope.cs | 12 +++ src/Umbraco.Core/Scoping/Scope.cs | 41 +++++---- src/Umbraco.Core/Umbraco.Core.csproj | 1 + src/Umbraco.Tests/Scoping/ScopeTests.cs | 2 +- .../Scoping/ScopedRepositoryTests.cs | 89 +++++++++++++++++-- 17 files changed, 250 insertions(+), 59 deletions(-) create mode 100644 src/Umbraco.Core/Cache/NoRepositoryCachePolicy.cs diff --git a/src/Umbraco.Core/Cache/NoRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/NoRepositoryCachePolicy.cs new file mode 100644 index 0000000000..54891af48b --- /dev/null +++ b/src/Umbraco.Core/Cache/NoRepositoryCachePolicy.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Scoping; + +namespace Umbraco.Core.Cache +{ + internal class NoRepositoryCachePolicy : IRepositoryCachePolicy + where TEntity : class, IAggregateRoot + { + private static readonly NoRepositoryCachePolicy StaticInstance = new NoRepositoryCachePolicy(); + + private NoRepositoryCachePolicy() + { } + + public static NoRepositoryCachePolicy Instance { get { return StaticInstance; } } + + public IRepositoryCachePolicy Scoped(IRuntimeCacheProvider runtimeCache, IScope scope) + { + throw new NotImplementedException(); + } + + public TEntity Get(TId id, Func performGet, Func> performGetAll) + { + return performGet(id); + } + + public TEntity GetCached(TId id) + { + return null; + } + + public bool Exists(TId id, Func performExists, Func> performGetAll) + { + return performExists(id); + } + + public void Create(TEntity entity, Action persistNew) + { + persistNew(entity); + } + + public void Update(TEntity entity, Action persistUpdated) + { + persistUpdated(entity); + } + + public void Delete(TEntity entity, Action persistDeleted) + { + persistDeleted(entity); + } + + public TEntity[] GetAll(TId[] ids, Func> performGetAll) + { + return performGetAll(ids).ToArray(); + } + + public void ClearAll() + { } + } +} diff --git a/src/Umbraco.Core/Cache/ScopedRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/ScopedRepositoryCachePolicy.cs index ec3516cdef..917e1b2807 100644 --- a/src/Umbraco.Core/Cache/ScopedRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/ScopedRepositoryCachePolicy.cs @@ -27,11 +27,10 @@ namespace Umbraco.Core.Cache // when the scope completes we need to clear the global isolated cache // for now, we are not doing it selectively at all - just kill everything // later on we might want to be more clever - private void RegisterDirty(TEntity entity = null) + private void RegisterDirty() { // use unique names to de-duplicate - // fixme - casting! - ((Scope) _scope).Register("dirty_" + typeof (TEntity).Name, + _scope.OnExit("dirty_" + typeof (TEntity).Name, () => _globalIsolatedCache.ClearAllCache()); } @@ -57,21 +56,21 @@ namespace Umbraco.Core.Cache { // writes into the local cache _cachePolicy.Create(entity, persistNew); - RegisterDirty(entity); + RegisterDirty(); } public void Update(TEntity entity, Action persistUpdated) { // writes into the local cache _cachePolicy.Update(entity, persistUpdated); - RegisterDirty(entity); + RegisterDirty(); } public void Delete(TEntity entity, Action persistDeleted) { // deletes the local cache _cachePolicy.Delete(entity, persistDeleted); - RegisterDirty(entity); + RegisterDirty(); } public TEntity[] GetAll(TId[] ids, Func> performGetAll) @@ -82,7 +81,7 @@ namespace Umbraco.Core.Cache public void ClearAll() { - // fixme - what's this doing? + // clears the local cache _cachePolicy.ClearAll(); RegisterDirty(); } diff --git a/src/Umbraco.Core/CacheHelper.cs b/src/Umbraco.Core/CacheHelper.cs index 0dc5f5b00f..dbf3f89714 100644 --- a/src/Umbraco.Core/CacheHelper.cs +++ b/src/Umbraco.Core/CacheHelper.cs @@ -21,6 +21,10 @@ namespace Umbraco.Core private static readonly ICacheProvider NullRequestCache = new NullCacheProvider(); private static readonly ICacheProvider NullStaticCache = new NullCacheProvider(); private static readonly IRuntimeCacheProvider NullRuntimeCache = new NullCacheProvider(); + private static readonly IsolatedRuntimeCache NullIsolatedCache = new IsolatedRuntimeCache(_ => NullRuntimeCache); + private static readonly CacheHelper NullCache = new CacheHelper(NullRuntimeCache, NullStaticCache, NullRequestCache, NullIsolatedCache); + + public static CacheHelper NoCache { get { return NullCache; } } /// /// Creates a cache helper with disabled caches @@ -31,7 +35,10 @@ namespace Umbraco.Core /// public static CacheHelper CreateDisabledCacheHelper() { - return new CacheHelper(NullRuntimeCache, NullStaticCache, NullRequestCache, new IsolatedRuntimeCache(t => NullRuntimeCache)); + // do *not* return NoCache + // NoCache is a special instance that is detected by RepositoryBase and disables all cache policies + // CreateDisabledCacheHelper is used in tests to use no cache, *but* keep all cache policies + return new CacheHelper(NullRuntimeCache, NullStaticCache, NullRequestCache, NullIsolatedCache); } /// diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs index b368488833..a053cbe977 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentPreviewRepository.cs @@ -19,8 +19,7 @@ namespace Umbraco.Core.Persistence.Repositories { public ContentPreviewRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) - { - } + { } #region Not implemented (don't need to for the purposes of this repo) protected override ContentPreviewEntity PerformGet(int id) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 59f64b370c..ac5cd83a46 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -41,8 +41,8 @@ namespace Umbraco.Core.Persistence.Repositories _templateRepository = templateRepository; _tagRepository = tagRepository; _cacheHelper = cacheHelper; - _contentPreviewRepository = new ContentPreviewRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, syntaxProvider); - _contentXmlRepository = new ContentXmlRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, syntaxProvider); + _contentPreviewRepository = new ContentPreviewRepository(work, CacheHelper.NoCache, logger, syntaxProvider); + _contentXmlRepository = new ContentXmlRepository(work, CacheHelper.NoCache, logger, syntaxProvider); EnsureUniqueNaming = true; } diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index 2d76b64521..4bf27f04cd 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -34,7 +34,7 @@ namespace Umbraco.Core.Persistence.Repositories : base(work, cache, logger, sqlSyntax) { _contentTypeRepository = contentTypeRepository; - _preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax); + _preValRepository = new DataTypePreValueRepository(work, CacheHelper.NoCache, logger, sqlSyntax); } #region Overrides of RepositoryBase @@ -521,8 +521,7 @@ AND umbracoNode.id <> @id", { public DataTypePreValueRepository(IDatabaseUnitOfWork work, CacheHelper cache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, cache, logger, sqlSyntax) - { - } + { } #region Not implemented (don't need to for the purposes of this repo) protected override PreValueEntity PerformGet(int id) diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 1d1a62f354..1125b7f61a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -35,8 +35,8 @@ namespace Umbraco.Core.Persistence.Repositories if (tagRepository == null) throw new ArgumentNullException("tagRepository"); _mediaTypeRepository = mediaTypeRepository; _tagRepository = tagRepository; - _contentXmlRepository = new ContentXmlRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax); - _contentPreviewRepository = new ContentPreviewRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax); + _contentXmlRepository = new ContentXmlRepository(work, CacheHelper.NoCache, logger, sqlSyntax); + _contentPreviewRepository = new ContentPreviewRepository(work, CacheHelper.NoCache, logger, sqlSyntax); EnsureUniqueNaming = contentSection.EnsureUniqueNaming; } diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs index 72c367326b..aceef3518b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs @@ -37,8 +37,8 @@ namespace Umbraco.Core.Persistence.Repositories _memberTypeRepository = memberTypeRepository; _tagRepository = tagRepository; _memberGroupRepository = memberGroupRepository; - _contentXmlRepository = new ContentXmlRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax); - _contentPreviewRepository = new ContentPreviewRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax); + _contentXmlRepository = new ContentXmlRepository(work, CacheHelper.NoCache, logger, sqlSyntax); + _contentPreviewRepository = new ContentPreviewRepository(work, CacheHelper.NoCache, logger, sqlSyntax); } #region Overrides of RepositoryBase diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index eb07ed226b..f0b512a6df 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -160,6 +160,9 @@ namespace Umbraco.Core.Persistence.Repositories { if (_cachePolicy != null) return _cachePolicy; + if (GlobalCache == CacheHelper.NoCache) + return _cachePolicy = NoRepositoryCachePolicy.Instance; + _cachePolicy = CreateCachePolicy(IsolatedCache); var scope = ((PetaPocoUnitOfWork) UnitOfWork).Scope; // fixme cast! switch (scope.RepositoryCacheMode) diff --git a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs index 02794ef1fb..9833a36bf8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs @@ -15,12 +15,14 @@ namespace Umbraco.Core.Persistence.Repositories { internal class ServerRegistrationRepository : PetaPocoRepositoryBase, IServerRegistrationRepository { - private readonly ICacheProvider _staticCache; + private readonly ICacheProvider _globalCache; - public ServerRegistrationRepository(IDatabaseUnitOfWork work, ICacheProvider staticCache, ILogger logger, ISqlSyntaxProvider sqlSyntax) - : base(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax) + public ServerRegistrationRepository(IDatabaseUnitOfWork work, ICacheProvider globalCache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + : base(work, CacheHelper.NoCache, logger, sqlSyntax) { - _staticCache = staticCache; + // managing the cache our own way (no policy etc) + // fixme - ServerRegistrationRepository does *not* implement scoped cache at the moment! + _globalCache = globalCache; } protected override int PerformCount(IQuery query) @@ -49,7 +51,7 @@ namespace Umbraco.Core.Persistence.Repositories // the cache is populated by ReloadCache which should only be called from methods // that ensure proper locking (at least, read-lock in ReadCommited) of the repo. - var all = _staticCache.GetCacheItem>(CacheKey, Enumerable.Empty); + var all = _globalCache.GetCacheItem>(CacheKey, Enumerable.Empty); return ids.Length == 0 ? all : all.Where(x => ids.Contains(x.Id)); } @@ -75,7 +77,7 @@ namespace Umbraco.Core.Persistence.Repositories { var list = new List { - "DELETE FROM umbracoServer WHERE id = @Id" + "DELETE FROM umbracoServer WHERE id = @Id" }; return list; } @@ -127,8 +129,8 @@ namespace Umbraco.Core.Persistence.Repositories .Select(x => factory.BuildEntity(x)) .Cast() .ToArray(); - _staticCache.ClearCacheItem(CacheKey); - _staticCache.GetCacheItem(CacheKey, () => all); + _globalCache.ClearCacheItem(CacheKey); + _globalCache.GetCacheItem(CacheKey, () => all); } public void DeactiveStaleServers(TimeSpan staleTimeout) diff --git a/src/Umbraco.Core/Persistence/RepositoryFactory.cs b/src/Umbraco.Core/Persistence/RepositoryFactory.cs index e04c898015..e12a427202 100644 --- a/src/Umbraco.Core/Persistence/RepositoryFactory.cs +++ b/src/Umbraco.Core/Persistence/RepositoryFactory.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Persistence private readonly ILogger _logger; private readonly ISqlSyntaxProvider _sqlSyntax; private readonly CacheHelper _cacheHelper; - private readonly CacheHelper _noCache; + private readonly CacheHelper _nullCache; private readonly IUmbracoSettingsSection _settings; #region Ctors @@ -52,7 +52,7 @@ namespace Umbraco.Core.Persistence }; } - _noCache = CacheHelper.CreateDisabledCacheHelper(); + _nullCache = CacheHelper.NoCache; _logger = logger; _sqlSyntax = sqlSyntax; _settings = settings; @@ -76,12 +76,12 @@ namespace Umbraco.Core.Persistence { if (cacheHelper == null) throw new ArgumentNullException("cacheHelper"); _cacheHelper = cacheHelper; - _noCache = CacheHelper.CreateDisabledCacheHelper(); + _nullCache = CacheHelper.NoCache; } [Obsolete("Use the ctor specifying all dependencies instead")] public RepositoryFactory(bool disableAllCache) - : this(disableAllCache ? CacheHelper.CreateDisabledCacheHelper() : ApplicationContext.Current.ApplicationCache, LoggerResolver.Current.Logger, SqlSyntaxContext.SqlSyntaxProvider, UmbracoConfig.For.UmbracoSettings()) + : this(disableAllCache ? CacheHelper.NoCache : ApplicationContext.Current.ApplicationCache, LoggerResolver.Current.Logger, SqlSyntaxContext.SqlSyntaxProvider, UmbracoConfig.For.UmbracoSettings()) { } @@ -105,14 +105,14 @@ namespace Umbraco.Core.Persistence public virtual ITaskRepository CreateTaskRepository(IDatabaseUnitOfWork uow) { return new TaskRepository(uow, - _noCache, //never cache + _nullCache, //never cache _logger, _sqlSyntax); } public virtual IAuditRepository CreateAuditRepository(IDatabaseUnitOfWork uow) { return new AuditRepository(uow, - _noCache, //never cache + _nullCache, //never cache _logger, _sqlSyntax); } @@ -197,7 +197,7 @@ namespace Umbraco.Core.Persistence { return new RelationRepository( uow, - _noCache, + _nullCache, _logger, _sqlSyntax, CreateRelationTypeRepository(uow)); } @@ -249,7 +249,7 @@ namespace Umbraco.Core.Persistence { return new MigrationEntryRepository( uow, - _noCache, //never cache + _nullCache, //never cache _logger, _sqlSyntax); } @@ -326,7 +326,7 @@ namespace Umbraco.Core.Persistence public ITaskTypeRepository CreateTaskTypeRepository(IDatabaseUnitOfWork uow) { return new TaskTypeRepository(uow, - _noCache, //never cache + _nullCache, //never cache _logger, _sqlSyntax); } diff --git a/src/Umbraco.Core/Scoping/IScope.cs b/src/Umbraco.Core/Scoping/IScope.cs index e9af72b55e..058b45998f 100644 --- a/src/Umbraco.Core/Scoping/IScope.cs +++ b/src/Umbraco.Core/Scoping/IScope.cs @@ -26,7 +26,9 @@ namespace Umbraco.Core.Scoping /// RepositoryCacheMode RepositoryCacheMode { get; } - // fixme + /// + /// Gets the isolated cache. + /// IsolatedRuntimeCache IsolatedRuntimeCache { get; } /// @@ -34,6 +36,23 @@ namespace Umbraco.Core.Scoping /// void Complete(); + /// + /// Registers an action to execute on exit. + /// + /// The unique key of the action. + /// The action. + /// + /// The key is unique (as in, dictionary key). + /// The action will execute only if the scope completes. + /// + void OnExit(string key, Action action); + + /// + /// The key is unique (as in, dictionary key). + /// The action always executes, with an argument indicating whether the scope completed. + /// + void OnExit(string key, Action action); + #if DEBUG_SCOPES Guid InstanceId { get; } #endif diff --git a/src/Umbraco.Core/Scoping/NoScope.cs b/src/Umbraco.Core/Scoping/NoScope.cs index 3bcdc8ed8b..fd0547b2d2 100644 --- a/src/Umbraco.Core/Scoping/NoScope.cs +++ b/src/Umbraco.Core/Scoping/NoScope.cs @@ -83,6 +83,18 @@ namespace Umbraco.Core.Scoping throw new NotImplementedException(); } + /// + public void OnExit(string key, Action action) + { + throw new NotImplementedException(); + } + + /// + public void OnExit(string key, Action action) + { + throw new NotImplementedException(); + } + private void EnsureNotDisposed() { if (_disposed) diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index 773d62b7d9..b375f72bbf 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -22,6 +22,7 @@ namespace Umbraco.Core.Scoping private IsolatedRuntimeCache _isolatedRuntimeCache; private UmbracoDatabase _database; private IList _messages; + private IDictionary> _exitActions; // this is v7, in v8 this has to change to RepeatableRead private const IsolationLevel DefaultIsolationLevel = IsolationLevel.ReadCommitted; @@ -258,35 +259,45 @@ namespace Umbraco.Core.Scoping } // run everything we need to run when completing - foreach (var action in Actions.Values) - action(completed); // fixme try catch and everything + List exceptions = null; + foreach (var action in ExitActions.Values) + { + try + { + action(completed); // fixme try catch and everything + } + catch (Exception e) + { + if (exceptions == null) + exceptions = new List(); + exceptions.Add(e); + } + } + if (exceptions != null) + throw new AggregateException("Exceptions were throws by complete actions.", exceptions); } - // fixme - wip - private IDictionary> _actions; - - private IDictionary> Actions + private IDictionary> ExitActions { get { - if (ParentScope != null) return ParentScope.Actions; + if (ParentScope != null) return ParentScope.ExitActions; - return _actions ?? (_actions + return _exitActions ?? (_exitActions = new Dictionary>()); } } - public void Register(string name, Action action) + /// + public void OnExit(string key, Action action) { - Actions[name] = completed => - { - if (completed) action(); - }; + ExitActions[key] = completed => { if (completed) action(); }; } - public void Register(string name, Action action) + /// + public void OnExit(string key, Action action) { - Actions[name] = action; + ExitActions[key] = action; } } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 59479b5472..d3b90a27c3 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -145,6 +145,7 @@ + diff --git a/src/Umbraco.Tests/Scoping/ScopeTests.cs b/src/Umbraco.Tests/Scoping/ScopeTests.cs index 7124d73074..d76c14e189 100644 --- a/src/Umbraco.Tests/Scoping/ScopeTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeTests.cs @@ -430,7 +430,7 @@ namespace Umbraco.Tests.Scoping Assert.IsNull(scopeProvider.AmbientScope); using (var scope = scopeProvider.CreateScope()) { - ((Scope) scope).Register("name", x => completed = x); + ((Scope) scope).OnExit("name", x => completed = x); if (complete) scope.Complete(); } diff --git a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs index 3a7d7ac465..e6989d1403 100644 --- a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using NUnit.Framework; using Umbraco.Core; @@ -45,7 +44,7 @@ namespace Umbraco.Tests.Scoping var user = (IUser) new User("name", "email", "username", "rawPassword", userType); service.Save(user); - // global cache contains the user entity + // global cache contains the entity var globalCached = (IUser) globalCache.GetCacheItem(GetCacheIdKey(user.Id), () => null); Assert.IsNotNull(globalCached); Assert.AreEqual(user.Id, globalCached.Id); @@ -65,7 +64,7 @@ namespace Umbraco.Tests.Scoping user.Name = "changed"; service.Save(user); - // scoped cache contains the "new" user entity + // scoped cache contains the "new" entity var scopeCached = (IUser) scopedCache.GetCacheItem(GetCacheIdKey(user.Id), () => null); Assert.IsNotNull(scopeCached); Assert.AreEqual(user.Id, scopeCached.Id); @@ -121,7 +120,7 @@ namespace Umbraco.Tests.Scoping Assert.IsNull(globalFullCached); var reload = service.GetLanguageById(lang.Id); - // global cache contains the user entity + // global cache contains the entity globalFullCached = (IEnumerable) globalCache.GetCacheItem(GetCacheTypeKey(), () => null); Assert.IsNotNull(globalFullCached); var globalCached = globalFullCached.First(x => x.Id == lang.Id); @@ -148,7 +147,7 @@ namespace Umbraco.Tests.Scoping Assert.IsNull(scopeFullCached); reload = service.GetLanguageById(lang.Id); - // scoped cache contains the "new" user entity + // scoped cache contains the "new" entity scopeFullCached = (IEnumerable) scopedCache.GetCacheItem(GetCacheTypeKey(), () => null); Assert.IsNotNull(scopeFullCached); var scopeCached = scopeFullCached.First(x => x.Id == lang.Id); @@ -194,6 +193,84 @@ namespace Umbraco.Tests.Scoping Assert.AreEqual(complete ? "de-DE" : "fr-FR", lang.IsoCode); } + [TestCase(true)] + [TestCase(false)] + public void SingleItemsOnlyRepositoryCachePolicy(bool complete) + { + var scopeProvider = DatabaseContext.ScopeProvider; + var service = ApplicationContext.Services.LocalizationService; + var globalCache = ApplicationContext.ApplicationCache.IsolatedRuntimeCache.GetOrCreateCache(typeof (IDictionaryItem)); + + var lang = (ILanguage)new Language("fr-FR"); + service.Save(lang); + + var item = (IDictionaryItem) new DictionaryItem("item-key"); + item.Translations = new IDictionaryTranslation[] + { + new DictionaryTranslation(lang.Id, "item-value"), + }; + service.Save(item); + + // global cache contains the entity + var globalCached = (IDictionaryItem) globalCache.GetCacheItem(GetCacheIdKey(item.Id), () => null); + Assert.IsNotNull(globalCached); + Assert.AreEqual(item.Id, globalCached.Id); + Assert.AreEqual("item-key", globalCached.ItemKey); + + Assert.IsNull(scopeProvider.AmbientScope); + using (var scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) + { + Assert.IsInstanceOf(scope); + Assert.IsNotNull(scopeProvider.AmbientScope); + Assert.AreSame(scope, scopeProvider.AmbientScope); + + // scope has its own isolated cache + var scopedCache = scope.IsolatedRuntimeCache.GetOrCreateCache(typeof (IDictionaryItem)); + Assert.AreNotSame(globalCache, scopedCache); + + item.ItemKey = "item-changed"; + service.Save(item); + + // scoped cache contains the "new" entity + var scopeCached = (IDictionaryItem) scopedCache.GetCacheItem(GetCacheIdKey(item.Id), () => null); + Assert.IsNotNull(scopeCached); + Assert.AreEqual(item.Id, scopeCached.Id); + Assert.AreEqual("item-changed", scopeCached.ItemKey); + + // global cache is unchanged + globalCached = (IDictionaryItem) globalCache.GetCacheItem(GetCacheIdKey(item.Id), () => null); + Assert.IsNotNull(globalCached); + Assert.AreEqual(item.Id, globalCached.Id); + Assert.AreEqual("item-key", globalCached.ItemKey); + + if (complete) + scope.Complete(); + } + Assert.IsNull(scopeProvider.AmbientScope); + + globalCached = (IDictionaryItem) globalCache.GetCacheItem(GetCacheIdKey(item.Id), () => null); + if (complete) + { + // global cache has been cleared + Assert.IsNull(globalCached); + } + else + { + // global cache has *not* been cleared + Assert.IsNotNull(globalCached); + } + + // get again, updated if completed + item = service.GetDictionaryItemById(item.Id); + Assert.AreEqual(complete ? "item-changed" : "item-key", item.ItemKey); + + // global cache contains the entity again + globalCached = (IDictionaryItem) globalCache.GetCacheItem(GetCacheIdKey(item.Id), () => null); + Assert.IsNotNull(globalCached); + Assert.AreEqual(item.Id, globalCached.Id); + Assert.AreEqual(complete ? "item-changed" : "item-key", globalCached.ItemKey); + } + public static string GetCacheIdKey(object id) { return string.Format("{0}{1}", GetCacheTypeKey(), id); From 1701eb2450d882d05a5bfd2a56e2669e6c0a7b63 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 20 Jan 2017 17:05:22 +0100 Subject: [PATCH 317/599] u4-9322 - events cleanup --- src/Umbraco.Core/Events/ScopedEventManager.cs | 6 ++---- .../Repositories/ServerRegistrationRepository.cs | 2 +- .../Persistence/UnitOfWork/PetaPocoUnitOfWork.cs | 2 +- src/Umbraco.Core/Scoping/NoScope.cs | 2 +- .../Scoping/NoScopedEventManagerTests.cs | 16 ++++++++-------- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Core/Events/ScopedEventManager.cs b/src/Umbraco.Core/Events/ScopedEventManager.cs index 3f8409e08a..8b34454f27 100644 --- a/src/Umbraco.Core/Events/ScopedEventManager.cs +++ b/src/Umbraco.Core/Events/ScopedEventManager.cs @@ -19,14 +19,14 @@ namespace Umbraco.Core.Events } public void TrackEvent(TypedEventHandler e, TSender sender, TEventArgs args) - { + { _tracked.Add(new EventDefinition(e, sender, args)); } public IEnumerable GetEvents() { return _tracked; - } + } private readonly List _tracked = new List(); @@ -39,7 +39,5 @@ namespace Umbraco.Core.Events { _tracked.Clear(); } - - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs index 7d33697e72..e3fad1aaf7 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly ICacheProvider _globalCache; - public ServerRegistrationRepository(IDatabaseUnitOfWork work, ICacheProvider staticCache, ILogger logger, ISqlSyntaxProvider sqlSyntax) + public ServerRegistrationRepository(IScopeUnitOfWork work, ICacheProvider globalCache, ILogger logger, ISqlSyntaxProvider sqlSyntax) : base(work, CacheHelper.NoCache, logger, sqlSyntax) { // managing the cache our own way (no policy etc) diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs index 6276898afc..79d6357625 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs @@ -158,7 +158,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork public IEventManager EventManager { - get { return ThisScope.EventManager; } + get { return Scope.Events; } } #region Operation diff --git a/src/Umbraco.Core/Scoping/NoScope.cs b/src/Umbraco.Core/Scoping/NoScope.cs index 5e5c8aab21..d1eaa140f9 100644 --- a/src/Umbraco.Core/Scoping/NoScope.cs +++ b/src/Umbraco.Core/Scoping/NoScope.cs @@ -79,7 +79,7 @@ namespace Umbraco.Core.Scoping } /// - public IEventManager EventManager + public IEventManager Events { get { diff --git a/src/Umbraco.Tests/Scoping/NoScopedEventManagerTests.cs b/src/Umbraco.Tests/Scoping/NoScopedEventManagerTests.cs index 6975b578ab..ce9266b9f2 100644 --- a/src/Umbraco.Tests/Scoping/NoScopedEventManagerTests.cs +++ b/src/Umbraco.Tests/Scoping/NoScopedEventManagerTests.cs @@ -17,7 +17,7 @@ namespace Umbraco.Tests.Scoping public void Does_Support_Event_Cancellation() { var scopeProvider = new ScopeProvider(Mock.Of()); - Assert.IsTrue(scopeProvider.AmbientOrNoScope.EventManager.SupportsEventCancellation); + Assert.IsTrue(scopeProvider.AmbientOrNoScope.Events.SupportsEventCancellation); } [Test] @@ -41,9 +41,9 @@ namespace Umbraco.Tests.Scoping }; var scopeProvider = new ScopeProvider(Mock.Of()); - scopeProvider.AmbientOrNoScope.EventManager.TrackEvent(DoThing1, this, new EventArgs()); - scopeProvider.AmbientOrNoScope.EventManager.TrackEvent(DoThing2, this, new EventArgs()); - scopeProvider.AmbientOrNoScope.EventManager.TrackEvent(DoThing3, this, new EventArgs()); + scopeProvider.AmbientOrNoScope.Events.TrackEvent(DoThing1, this, new EventArgs()); + scopeProvider.AmbientOrNoScope.Events.TrackEvent(DoThing2, this, new EventArgs()); + scopeProvider.AmbientOrNoScope.Events.TrackEvent(DoThing3, this, new EventArgs()); Assert.AreEqual(3, counter); @@ -53,11 +53,11 @@ namespace Umbraco.Tests.Scoping public void Can_Not_Raise_Events_Later() { var scopeProvider = new ScopeProvider(Mock.Of()); - scopeProvider.AmbientOrNoScope.EventManager.TrackEvent(DoThing1, this, new EventArgs()); - scopeProvider.AmbientOrNoScope.EventManager.TrackEvent(DoThing2, this, new EventArgs()); - scopeProvider.AmbientOrNoScope.EventManager.TrackEvent(DoThing3, this, new EventArgs()); + scopeProvider.AmbientOrNoScope.Events.TrackEvent(DoThing1, this, new EventArgs()); + scopeProvider.AmbientOrNoScope.Events.TrackEvent(DoThing2, this, new EventArgs()); + scopeProvider.AmbientOrNoScope.Events.TrackEvent(DoThing3, this, new EventArgs()); - Assert.AreEqual(0, scopeProvider.AmbientOrNoScope.EventManager.GetEvents().Count()); + Assert.AreEqual(0, scopeProvider.AmbientOrNoScope.Events.GetEvents().Count()); } public event EventHandler DoThing1; From a3150b7d4e191a1e05660ffb6322d288b0eafefd Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 20 Jan 2017 18:33:21 +0100 Subject: [PATCH 318/599] U4-9322 - missing files --- src/Umbraco.Core/Scoping/IScope.cs | 10 +++--- src/Umbraco.Core/Scoping/NoScope.cs | 4 +-- src/Umbraco.Core/Scoping/Scope.cs | 54 ++++++++++++++--------------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Core/Scoping/IScope.cs b/src/Umbraco.Core/Scoping/IScope.cs index db68d4ed78..26e8b7cb49 100644 --- a/src/Umbraco.Core/Scoping/IScope.cs +++ b/src/Umbraco.Core/Scoping/IScope.cs @@ -21,6 +21,11 @@ namespace Umbraco.Core.Scoping /// IList Messages { get; } + /// + /// Gets the event manager + /// + IEventManager Events { get; } + /// /// Gets the repository cache mode. /// @@ -31,11 +36,6 @@ namespace Umbraco.Core.Scoping /// IsolatedRuntimeCache IsolatedRuntimeCache { get; } - /// - /// Gets the event manager - /// - IEventManager EventManager { get; } - /// /// Completes the scope. /// diff --git a/src/Umbraco.Core/Scoping/NoScope.cs b/src/Umbraco.Core/Scoping/NoScope.cs index d1eaa140f9..4f8c705f28 100644 --- a/src/Umbraco.Core/Scoping/NoScope.cs +++ b/src/Umbraco.Core/Scoping/NoScope.cs @@ -68,7 +68,7 @@ namespace Umbraco.Core.Scoping return _messages ?? (_messages = new List()); } } - + public IList MessagesOrNull { get @@ -130,6 +130,6 @@ namespace Umbraco.Core.Scoping _disposed = true; GC.SuppressFinalize(this); - } + } } } diff --git a/src/Umbraco.Core/Scoping/Scope.cs b/src/Umbraco.Core/Scoping/Scope.cs index 90aac6617b..f0e58f1f9c 100644 --- a/src/Umbraco.Core/Scoping/Scope.cs +++ b/src/Umbraco.Core/Scoping/Scope.cs @@ -156,14 +156,14 @@ namespace Umbraco.Core.Scoping } } - public UmbracoDatabase DatabaseOrNull - { - get - { - EnsureNotDisposed(); - return ParentScope == null ? _database : ParentScope.DatabaseOrNull; - } - } + //public UmbracoDatabase DatabaseOrNull + //{ + // get + // { + // EnsureNotDisposed(); + // return ParentScope == null ? _database : ParentScope.DatabaseOrNull; + // } + //} /// public IList Messages @@ -172,26 +172,26 @@ namespace Umbraco.Core.Scoping { EnsureNotDisposed(); if (ParentScope != null) return ParentScope.Messages; - if (_messages == null) - _messages = new List(); - return _messages; + return _messages ?? (_messages = new List()); } } - public IList MessagesOrNull - { - get - { - EnsureNotDisposed(); - return ParentScope == null ? _messages : ParentScope.MessagesOrNull; - } - } - - public IEventManager EventManager + //public IList MessagesOrNull + //{ + // get + // { + // EnsureNotDisposed(); + // return ParentScope == null ? _messages : ParentScope.MessagesOrNull; + // } + //} + + /// + public IEventManager Events { get { EnsureNotDisposed(); + if (ParentScope != null) return ParentScope.Events; return _eventManager ?? (_eventManager = new ScopedEventManager()); } } @@ -240,11 +240,6 @@ namespace Umbraco.Core.Scoping else DisposeLastScope(); - if (_eventManager != null) - { - _eventManager.Dispose(); - } - _disposed = true; GC.SuppressFinalize(this); } @@ -279,7 +274,7 @@ namespace Umbraco.Core.Scoping { try { - action(completed); // fixme try catch and everything + action(completed); } catch (Exception e) { @@ -290,6 +285,11 @@ namespace Umbraco.Core.Scoping } if (exceptions != null) throw new AggregateException("Exceptions were throws by complete actions.", exceptions); + + // now trigger every events + // fixme - should some of them trigger within the transaction? + if (_eventManager != null) + _eventManager.Dispose(); } private IDictionary> ExitActions From 2a4e73c65021cf9c22e0b68001030d559ac159cd Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 23 Jan 2017 00:40:24 +1100 Subject: [PATCH 319/599] Reduced allocations for the Media/Content/Property Factories since we don't need to create new objects every time we want to map values. Changes default sort order for paging from Path to umbracoNode.id since we have no index on Path and it doesn't make a lot of sense. Fixes obsolete warnings for various Sql usages. Reduces the amount of DeepClone calls required when looking up a content type for a content item, even though these are cached they are still deep cloned out of the cache. Fixes the main issue of having nearly 100,000 rows of unsorted property data and then having to query those rows for every document being built, the re-iteration of these rows causes a lot of overhead and is unecessary, instead we ensure the property data set and the document data set is sorted by node id, then use a stored index to continue looking up the property data for the next content item found. --- .../Persistence/Factories/ContentFactory.cs | 17 +- .../Persistence/Factories/MediaFactory.cs | 13 +- .../Persistence/Factories/PropertyFactory.cs | 16 +- .../Repositories/ContentRepository.cs | 52 +++--- .../Repositories/MediaRepository.cs | 60 +++---- .../Repositories/VersionableRepositoryBase.cs | 150 +++++++++++------- src/Umbraco.Core/Services/ContentService.cs | 4 +- src/Umbraco.Core/Services/IContentService.cs | 4 +- src/Umbraco.Core/Services/IMediaService.cs | 4 +- src/Umbraco.Core/Services/MediaService.cs | 4 +- 10 files changed, 199 insertions(+), 125 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs index 5dcec8fed0..88de5a1d6c 100644 --- a/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ContentFactory.cs @@ -27,15 +27,15 @@ namespace Umbraco.Core.Persistence.Factories #region Implementation of IEntityFactory - public IContent BuildEntity(DocumentDto dto) + public static IContent BuildEntity(DocumentDto dto, IContentType contentType) { - var content = new Content(dto.Text, dto.ContentVersionDto.ContentDto.NodeDto.ParentId, _contentType); + var content = new Content(dto.Text, dto.ContentVersionDto.ContentDto.NodeDto.ParentId, contentType); try { content.DisableChangeTracking(); - content.Id = _id; + content.Id = dto.NodeId; content.Key = dto.ContentVersionDto.ContentDto.NodeDto.UniqueId; content.Name = dto.Text; content.NodeName = dto.ContentVersionDto.ContentDto.NodeDto.Text; @@ -49,8 +49,8 @@ namespace Umbraco.Core.Persistence.Factories content.Published = dto.Published; content.CreateDate = dto.ContentVersionDto.ContentDto.NodeDto.CreateDate; content.UpdateDate = dto.ContentVersionDto.VersionDate; - content.ExpireDate = dto.ExpiresDate.HasValue ? dto.ExpiresDate.Value : (DateTime?) null; - content.ReleaseDate = dto.ReleaseDate.HasValue ? dto.ReleaseDate.Value : (DateTime?) null; + content.ExpireDate = dto.ExpiresDate.HasValue ? dto.ExpiresDate.Value : (DateTime?)null; + content.ReleaseDate = dto.ReleaseDate.HasValue ? dto.ReleaseDate.Value : (DateTime?)null; content.Version = dto.ContentVersionDto.VersionId; content.PublishedState = dto.Published ? PublishedState.Published : PublishedState.Unpublished; content.PublishedVersionGuid = dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId; @@ -64,6 +64,13 @@ namespace Umbraco.Core.Persistence.Factories { content.EnableChangeTracking(); } + + } + + [Obsolete("Use the static BuildEntity instead so we don't have to allocate one of these objects everytime we want to map values")] + public IContent BuildEntity(DocumentDto dto) + { + return BuildEntity(dto, _contentType); } public DocumentDto BuildDto(IContent entity) diff --git a/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs b/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs index 0fcb654cb7..5729bb125e 100644 --- a/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/MediaFactory.cs @@ -27,15 +27,15 @@ namespace Umbraco.Core.Persistence.Factories #region Implementation of IEntityFactory - public IMedia BuildEntity(ContentVersionDto dto) + public static IMedia BuildEntity(ContentVersionDto dto, IMediaType contentType) { - var media = new Models.Media(dto.ContentDto.NodeDto.Text, dto.ContentDto.NodeDto.ParentId, _contentType); + var media = new Models.Media(dto.ContentDto.NodeDto.Text, dto.ContentDto.NodeDto.ParentId, contentType); try { media.DisableChangeTracking(); - media.Id = _id; + media.Id = dto.NodeId; media.Key = dto.ContentDto.NodeDto.UniqueId; media.Path = dto.ContentDto.NodeDto.Path; media.CreatorId = dto.ContentDto.NodeDto.UserId.Value; @@ -55,6 +55,13 @@ namespace Umbraco.Core.Persistence.Factories { media.EnableChangeTracking(); } + + } + + [Obsolete("Use the static BuildEntity instead so we don't have to allocate one of these objects everytime we want to map values")] + public IMedia BuildEntity(ContentVersionDto dto) + { + return BuildEntity(dto, _contentType); } public ContentVersionDto BuildDto(IMedia entity) diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs index 446bd426ad..f202d8c321 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs @@ -30,11 +30,11 @@ namespace Umbraco.Core.Persistence.Factories _updateDate = updateDate; } - public IEnumerable BuildEntity(PropertyDataDto[] dtos) + public static IEnumerable BuildEntity(IReadOnlyCollection dtos, PropertyType[] compositionTypeProperties, DateTime createDate, DateTime updateDate) { var properties = new List(); - foreach (var propertyType in _compositionTypeProperties) + foreach (var propertyType in compositionTypeProperties) { var propertyDataDto = dtos.LastOrDefault(x => x.PropertyTypeId == propertyType.Id); var property = propertyDataDto == null @@ -47,8 +47,8 @@ namespace Umbraco.Core.Persistence.Factories //on initial construction we don't want to have dirty properties tracked property.DisableChangeTracking(); - property.CreateDate = _createDate; - property.UpdateDate = _updateDate; + property.CreateDate = createDate; + property.UpdateDate = updateDate; // http://issues.umbraco.org/issue/U4-1946 property.ResetDirtyProperties(false); properties.Add(property); @@ -57,12 +57,18 @@ namespace Umbraco.Core.Persistence.Factories { property.EnableChangeTracking(); } - + } return properties; } + [Obsolete("Use the static method instead, there's no reason to allocate one of these classes everytime we want to map values")] + public IEnumerable BuildEntity(PropertyDataDto[] dtos) + { + return BuildEntity(dtos, _compositionTypeProperties, _createDate, _updateDate); + } + public IEnumerable BuildDto(IEnumerable properties) { var propertyDataDtos = new List(); diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 294f869c3f..b25db8afb2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -55,7 +55,7 @@ namespace Umbraco.Core.Persistence.Repositories { var sql = GetBaseQuery(false) .Where(GetBaseWhereClause(), new { Id = id }) - .Where(x => x.Newest) + .Where(x => x.Newest, SqlSyntax) .OrderByDescending(x => x.VersionDate, SqlSyntax); var dto = Database.Fetch(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault(); @@ -77,7 +77,7 @@ namespace Umbraco.Core.Persistence.Repositories } //we only want the newest ones with this method - sql.Where(x => x.Newest); + sql.Where(x => x.Newest, SqlSyntax); return ProcessQuery(sql); } @@ -87,9 +87,9 @@ namespace Umbraco.Core.Persistence.Repositories var sqlClause = GetBaseQuery(false); var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate() - .Where(x => x.Newest) - .OrderByDescending(x => x.VersionDate) - .OrderBy(x => x.SortOrder); + .Where(x => x.Newest, SqlSyntax) + .OrderByDescending(x => x.VersionDate, SqlSyntax) + .OrderBy(x => x.SortOrder, SqlSyntax); return ProcessQuery(sql); } @@ -183,8 +183,8 @@ namespace Umbraco.Core.Persistence.Repositories query = query .WhereIn(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax); query = query - .Where(x => x.NodeId > baseId && x.Trashed == false) - .Where(x => x.Published) + .Where(x => x.NodeId > baseId && x.Trashed == false, SqlSyntax) + .Where(x => x.Published, SqlSyntax) .OrderBy(x => x.NodeId, SqlSyntax); var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize)) .Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() }) @@ -222,7 +222,7 @@ namespace Umbraco.Core.Persistence.Repositories { var sql = GetBaseQuery(false); sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId }); - sql.OrderByDescending(x => x.VersionDate); + sql.OrderByDescending(x => x.VersionDate, SqlSyntax); var dto = Database.Fetch(sql).FirstOrDefault(); @@ -238,10 +238,10 @@ namespace Umbraco.Core.Persistence.Repositories { var sql = new Sql() .Select("*") - .From() - .InnerJoin().On(left => left.VersionId, right => right.VersionId) - .Where(x => x.VersionId == versionId) - .Where(x => x.Newest != true); + .From(SqlSyntax) + .InnerJoin(SqlSyntax).On(SqlSyntax, left => left.VersionId, right => right.VersionId) + .Where(x => x.VersionId == versionId, SqlSyntax) + .Where(x => x.Newest != true, SqlSyntax); var dto = Database.Fetch(sql).FirstOrDefault(); if (dto == null) return; @@ -848,7 +848,12 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder", var content = new IContent[dtos.Count]; var defs = new List(); var templateIds = new List(); - + + //track the looked up content types, even though the content types are cached + // they still need to be deep cloned out of the cache and we don't want to add + // the overhead of deep cloning them on every item in this loop + var contentTypes = new Dictionary(); + for (var i = 0; i < dtos.Count; i++) { var dto = dtos[i]; @@ -867,9 +872,19 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder", // else, need to fetch from the database // content type repository is full-cache so OK to get each one independently - var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId); - var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId); - content[i] = factory.BuildEntity(dto); + + IContentType contentType; + if (contentTypes.ContainsKey(dto.ContentVersionDto.ContentDto.ContentTypeId)) + { + contentType = contentTypes[dto.ContentVersionDto.ContentDto.ContentTypeId]; + } + else + { + contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId); + contentTypes[dto.ContentVersionDto.ContentDto.ContentTypeId] = contentType; + } + + content[i] = ContentFactory.BuildEntity(dto, contentType); // need template if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0) @@ -910,7 +925,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder", //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 - ((Entity) cc).ResetDirtyProperties(false); + cc.ResetDirtyProperties(false); } return content; @@ -927,8 +942,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder", { var contentType = _contentTypeRepository.Get(dto.ContentVersionDto.ContentDto.ContentTypeId); - var factory = new ContentFactory(contentType, NodeObjectTypeId, dto.NodeId); - var content = factory.BuildEntity(dto); + var content = ContentFactory.BuildEntity(dto, contentType); //Check if template id is set on DocumentDto, and get ITemplate if it is. if (dto.TemplateId.HasValue && dto.TemplateId.Value > 0) diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index 2687848fa5..bf034bd8ff 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -76,7 +76,7 @@ namespace Umbraco.Core.Persistence.Repositories var sqlClause = GetBaseQuery(false); var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate() - .OrderBy(x => x.SortOrder); + .OrderBy(x => x.SortOrder, SqlSyntax); return ProcessQuery(sql); } @@ -89,12 +89,12 @@ namespace Umbraco.Core.Persistence.Repositories { var sql = new Sql(); sql.Select(isCount ? "COUNT(*)" : "*") - .From() - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .InnerJoin() - .On(left => left.NodeId, right => right.NodeId) - .Where(x => x.NodeObjectType == NodeObjectTypeId); + .From(SqlSyntax) + .InnerJoin(SqlSyntax) + .On(SqlSyntax, left => left.NodeId, right => right.NodeId) + .InnerJoin(SqlSyntax) + .On(SqlSyntax, left => left.NodeId, right => right.NodeId, SqlSyntax) + .Where(x => x.NodeObjectType == NodeObjectTypeId, SqlSyntax); return sql; } @@ -148,6 +148,11 @@ namespace Umbraco.Core.Persistence.Repositories var content = new IMedia[dtos.Count]; var defs = new List(); + //track the looked up content types, even though the content types are cached + // they still need to be deep cloned out of the cache and we don't want to add + // the overhead of deep cloning them on every item in this loop + var contentTypes = new Dictionary(); + for (var i = 0; i < dtos.Count; i++) { var dto = dtos[i]; @@ -165,9 +170,19 @@ namespace Umbraco.Core.Persistence.Repositories // else, need to fetch from the database // content type repository is full-cache so OK to get each one independently - var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId); - var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId); - content[i] = factory.BuildEntity(dto); + + IMediaType contentType; + if (contentTypes.ContainsKey(dto.ContentDto.ContentTypeId)) + { + contentType = contentTypes[dto.ContentDto.ContentTypeId]; + } + else + { + contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId); + contentTypes[dto.ContentDto.ContentTypeId] = contentType; + } + + content[i] = MediaFactory.BuildEntity(dto, contentType); // need properties defs.Add(new DocumentDefinition( @@ -195,7 +210,7 @@ namespace Umbraco.Core.Persistence.Repositories //on initial construction we don't want to have dirty properties tracked // http://issues.umbraco.org/issue/U4-1946 - ((Entity) cc).ResetDirtyProperties(false); + cc.ResetDirtyProperties(false); } return content; @@ -205,26 +220,16 @@ namespace Umbraco.Core.Persistence.Repositories { var sql = GetBaseQuery(false); sql.Where("cmsContentVersion.VersionId = @VersionId", new { VersionId = versionId }); - sql.OrderByDescending(x => x.VersionDate); + sql.OrderByDescending(x => x.VersionDate, SqlSyntax); var dto = Database.Fetch(sql).FirstOrDefault(); if (dto == null) return null; - var mediaType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId); + var content = CreateMediaFromDto(dto, versionId, sql); - var factory = new MediaFactory(mediaType, NodeObjectTypeId, dto.NodeId); - var media = factory.BuildEntity(dto); - - var properties = GetPropertyCollection(sql, new[] { new DocumentDefinition(dto.NodeId, dto.VersionId, media.UpdateDate, media.CreateDate, mediaType) }); - - media.Properties = properties[dto.NodeId]; - - //on initial construction we don't want to have dirty properties tracked - // http://issues.umbraco.org/issue/U4-1946 - ((Entity)media).ResetDirtyProperties(false); - return media; + return content; } public void RebuildXmlStructures(Func serializer, int groupSize = 200, IEnumerable contentTypeIds = null) @@ -245,7 +250,7 @@ namespace Umbraco.Core.Persistence.Repositories query = query .WhereIn(x => x.ContentTypeId, contentTypeIdsA, SqlSyntax); query = query - .Where(x => x.NodeId > baseId) + .Where(x => x.NodeId > baseId, SqlSyntax) .OrderBy(x => x.NodeId, SqlSyntax); var xmlItems = ProcessQuery(SqlSyntax.SelectTop(query, groupSize)) .Select(x => new ContentXmlDto { NodeId = x.Id, Xml = serializer(x).ToString() }) @@ -512,9 +517,8 @@ namespace Umbraco.Core.Persistence.Repositories private IMedia CreateMediaFromDto(ContentVersionDto dto, Guid versionId, Sql docSql) { var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId); - - var factory = new MediaFactory(contentType, NodeObjectTypeId, dto.NodeId); - var media = factory.BuildEntity(dto); + + var media = MediaFactory.BuildEntity(dto, contentType); var docDef = new DocumentDefinition(dto.NodeId, versionId, media.UpdateDate, media.CreateDate, contentType); diff --git a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs index 19f4b8fdfb..16bad74612 100644 --- a/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/VersionableRepositoryBase.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Text; @@ -405,11 +406,15 @@ namespace Umbraco.Core.Persistence.Repositories } } - //no matter what we always MUST order the result also by umbracoNode.id to ensure that all records being ordered by are unique. - // if we do not do this then we end up with issues where we are ordering by a field that has duplicate values (i.e. the 'text' column - // is empty for many nodes) - // see: http://issues.umbraco.org/issue/U4-8831 - sortedSql.OrderBy("umbracoNode.id"); + if (orderBySystemField && orderBy != "umbracoNode.id") + { + //no matter what we always MUST order the result also by umbracoNode.id to ensure that all records being ordered by are unique. + // if we do not do this then we end up with issues where we are ordering by a field that has duplicate values (i.e. the 'text' column + // is empty for many nodes) + // see: http://issues.umbraco.org/issue/U4-8831 + sortedSql.OrderBy("umbracoNode.id"); + } + return sortedSql; @@ -511,9 +516,9 @@ namespace Umbraco.Core.Persistence.Repositories protected IDictionary GetPropertyCollection( Sql docSql, - IEnumerable documentDefs) + IReadOnlyCollection documentDefs) { - if (documentDefs.Any() == false) return new Dictionary(); + if (documentDefs.Count == 0) return new Dictionary(); //we need to parse the original SQL statement and reduce the columns to just cmsContent.nodeId, cmsContentVersion.VersionId so that we can use // the statement to go get the property data for all of the items by using an inner join @@ -524,6 +529,15 @@ namespace Umbraco.Core.Persistence.Repositories parsedOriginalSql = parsedOriginalSql.Substring(0, parsedOriginalSql.LastIndexOf("ORDER BY ", StringComparison.Ordinal)); } + //It's Important with the sort order here! We require this to be sorted by node id, + // this is required because this data set can be huge depending on the page size. Due + // to it's size we need to be smart about iterating over the property values to build + // the document. Before we used to use Linq to get the property data for a given content node + // and perform a Distinct() call. This kills performance because that would mean if we had 7000 nodes + // and on each iteration we will perform a lookup on potentially 100,000 property rows against the node + // id which turns out to be a crazy amount of iterations. Instead we know it's sorted by this value we'll + // keep an index stored of the rows being read so we never have to re-iterate the entire data set + // on each document iteration. var propSql = new Sql(@"SELECT cmsPropertyData.* FROM cmsPropertyData INNER JOIN cmsPropertyType @@ -531,8 +545,8 @@ ON cmsPropertyData.propertytypeid = cmsPropertyType.id INNER JOIN (" + string.Format(parsedOriginalSql, "cmsContent.nodeId, cmsContentVersion.VersionId") + @") as docData ON cmsPropertyData.versionId = docData.VersionId AND cmsPropertyData.contentNodeId = docData.nodeId -LEFT OUTER JOIN cmsDataTypePreValues -ON cmsPropertyType.dataTypeId = cmsDataTypePreValues.datatypeNodeId", docSql.Arguments); +ORDER BY contentNodeId, propertytypeid +", docSql.Arguments); var allPropertyData = Database.Fetch(propSql); @@ -556,59 +570,81 @@ WHERE EXISTS( }); var result = new Dictionary(); - var propertiesWithTagSupport = new Dictionary(); + //used to track the resolved composition property types per content type so we don't have to re-resolve (ToArray) the list every time + var resolvedCompositionProperties = new Dictionary(); + var propertyDataSetIndex = 0; - //iterate each definition grouped by it's content type - this will mean less property type iterations while building - // up the property collections - foreach (var compositionGroup in documentDefs.GroupBy(x => x.Composition)) + //This must be sorted by node id because this is how we are sorting the query to lookup property types above, + // which allows us to more efficiently iterate over the large data set of property values + foreach (var def in documentDefs.OrderBy(x => x.Id)) { - var compositionProperties = compositionGroup.Key.CompositionPropertyTypes.ToArray(); - - foreach (var def in compositionGroup) + //get the resolved proeprties from our local cache, or resolve them and put them in cache + PropertyType[] compositionProperties; + if (resolvedCompositionProperties.ContainsKey(def.Composition.Id)) { - var propertyDataDtos = allPropertyData.Where(x => x.NodeId == def.Id).Distinct(); - - var propertyFactory = new PropertyFactory(compositionProperties, def.Version, def.Id, def.CreateDate, def.VersionDate); - var properties = propertyFactory.BuildEntity(propertyDataDtos.ToArray()).ToArray(); - - foreach (var property in properties) - { - //NOTE: The benchmarks run with and without the following code show very little change so this is not a perf bottleneck - var editor = PropertyEditorResolver.Current.GetByAlias(property.PropertyType.PropertyEditorAlias); - - var tagSupport = propertiesWithTagSupport.ContainsKey(property.PropertyType.PropertyEditorAlias) - ? propertiesWithTagSupport[property.PropertyType.PropertyEditorAlias] - : TagExtractor.GetAttribute(editor); - - if (tagSupport != null) - { - //add to local cache so we don't need to reflect next time for this property editor alias - propertiesWithTagSupport[property.PropertyType.PropertyEditorAlias] = tagSupport; - - //this property has tags, so we need to extract them and for that we need the prevals which we've already looked up - var preValData = allPreValues.Value.Where(x => x.DataTypeNodeId == property.PropertyType.DataTypeDefinitionId) - .Distinct() - .ToArray(); - - var asDictionary = preValData.ToDictionary(x => x.Alias, x => new PreValue(x.Id, x.Value, x.SortOrder)); - - var preVals = new PreValueCollection(asDictionary); - - var contentPropData = new ContentPropertyData(property.Value, - preVals, - new Dictionary()); - - TagExtractor.SetPropertyTags(property, contentPropData, property.Value, tagSupport); - } - } - - if (result.ContainsKey(def.Id)) - { - Logger.Warn>("The query returned multiple property sets for document definition " + def.Id + ", " + def.Composition.Name); - } - result[def.Id] = new PropertyCollection(properties); + compositionProperties = resolvedCompositionProperties[def.Composition.Id]; } + else + { + compositionProperties = def.Composition.CompositionPropertyTypes.ToArray(); + resolvedCompositionProperties[def.Composition.Id] = compositionProperties; + } + + var propertyDataDtos = new List(); + + for (var i = propertyDataSetIndex; i < allPropertyData.Count; i++) + { + if (allPropertyData[i].NodeId == def.Id) + { + propertyDataDtos.Add(allPropertyData[i]); + } + else + { + //the node id has changed so we need to exit the loop and store the index + propertyDataSetIndex = i; + break; + } + } + + var properties = PropertyFactory.BuildEntity(propertyDataDtos, compositionProperties, def.CreateDate, def.VersionDate).ToArray(); + + foreach (var property in properties) + { + //NOTE: The benchmarks run with and without the following code show very little change so this is not a perf bottleneck + var editor = PropertyEditorResolver.Current.GetByAlias(property.PropertyType.PropertyEditorAlias); + + var tagSupport = propertiesWithTagSupport.ContainsKey(property.PropertyType.PropertyEditorAlias) + ? propertiesWithTagSupport[property.PropertyType.PropertyEditorAlias] + : TagExtractor.GetAttribute(editor); + + if (tagSupport != null) + { + //add to local cache so we don't need to reflect next time for this property editor alias + propertiesWithTagSupport[property.PropertyType.PropertyEditorAlias] = tagSupport; + + //this property has tags, so we need to extract them and for that we need the prevals which we've already looked up + var preValData = allPreValues.Value.Where(x => x.DataTypeNodeId == property.PropertyType.DataTypeDefinitionId) + .Distinct() + .ToArray(); + + var asDictionary = preValData.ToDictionary(x => x.Alias, x => new PreValue(x.Id, x.Value, x.SortOrder)); + + var preVals = new PreValueCollection(asDictionary); + + var contentPropData = new ContentPropertyData(property.Value, + preVals, + new Dictionary()); + + TagExtractor.SetPropertyTags(property, contentPropData, property.Value, tagSupport); + } + } + + if (result.ContainsKey(def.Id)) + { + Logger.Warn>("The query returned multiple property sets for document definition " + def.Id + ", " + def.Composition.Name); + } + result[def.Id] = new PropertyCollection(properties); } return result; diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 6f2007912f..d5658b9f9a 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -585,7 +585,7 @@ namespace Umbraco.Core.Services [Obsolete("Use the overload with 'long' parameter types instead")] [EditorBrowsable(EditorBrowsableState.Never)] - public IEnumerable GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") + public IEnumerable GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "umbracoNode.id", Direction orderDirection = Direction.Ascending, string filter = "") { long total; var result = GetPagedDescendants(id, Convert.ToInt64(pageIndex), pageSize, out total, orderBy, orderDirection, true, filter); @@ -604,7 +604,7 @@ namespace Umbraco.Core.Services /// Direction to order by /// Search text filter /// An Enumerable list of objects - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") + public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "umbracoNode.id", Direction orderDirection = Direction.Ascending, string filter = "") { return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filter); } diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 7722bf9c65..6d73546571 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -259,7 +259,7 @@ namespace Umbraco.Core.Services [Obsolete("Use the overload with 'long' parameter types instead")] [EditorBrowsable(EditorBrowsableState.Never)] IEnumerable GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalRecords, - string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = ""); + string orderBy = "umbracoNode.id", Direction orderDirection = Direction.Ascending, string filter = ""); /// /// Gets a collection of objects by Parent Id @@ -273,7 +273,7 @@ namespace Umbraco.Core.Services /// Search text filter /// An Enumerable list of objects IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = ""); + string orderBy = "umbracoNode.id", Direction orderDirection = Direction.Ascending, string filter = ""); /// /// Gets a collection of objects by Parent Id diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index d25ddf7f58..a02ac43b93 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -169,7 +169,7 @@ namespace Umbraco.Core.Services [Obsolete("Use the overload with 'long' parameter types instead")] [EditorBrowsable(EditorBrowsableState.Never)] IEnumerable GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalRecords, - string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = ""); + string orderBy = "umbracoNode.id", Direction orderDirection = Direction.Ascending, string filter = ""); /// /// Gets a collection of objects by Parent Id @@ -183,7 +183,7 @@ namespace Umbraco.Core.Services /// Search text filter /// An Enumerable list of objects IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, - string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = ""); + string orderBy = "umbracoNode.id", Direction orderDirection = Direction.Ascending, string filter = ""); /// /// Gets a collection of objects by Parent Id diff --git a/src/Umbraco.Core/Services/MediaService.cs b/src/Umbraco.Core/Services/MediaService.cs index 9a3a30c8bd..f4e963c658 100644 --- a/src/Umbraco.Core/Services/MediaService.cs +++ b/src/Umbraco.Core/Services/MediaService.cs @@ -451,7 +451,7 @@ namespace Umbraco.Core.Services [Obsolete("Use the overload with 'long' parameter types instead")] [EditorBrowsable(EditorBrowsableState.Never)] - public IEnumerable GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") + public IEnumerable GetPagedDescendants(int id, int pageIndex, int pageSize, out int totalChildren, string orderBy = "umbracoNode.id", Direction orderDirection = Direction.Ascending, string filter = "") { long total; var result = GetPagedDescendants(id, Convert.ToInt64(pageIndex), pageSize, out total, orderBy, orderDirection, true, filter); @@ -470,7 +470,7 @@ namespace Umbraco.Core.Services /// Direction to order by /// Search text filter /// An Enumerable list of objects - public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "Path", Direction orderDirection = Direction.Ascending, string filter = "") + public IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalChildren, string orderBy = "umbracoNode.id", Direction orderDirection = Direction.Ascending, string filter = "") { return GetPagedDescendants(id, pageIndex, pageSize, out totalChildren, orderBy, orderDirection, true, filter); } From ac4de99f30c545f3128fe06ad04047aefdac3217 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 23 Jan 2017 20:48:51 +1100 Subject: [PATCH 320/599] Initial commit of changing all IsRaisedEventCancelled to use the event manager and inside of a uow --- src/Umbraco.Core/CoreBootManager.cs | 2 +- .../Repositories/MemberGroupRepository.cs | 2 +- .../Repositories/RepositoryBase.cs | 4 +- .../Persistence/UnitOfWork/FileUnitOfWork.cs | 97 +--- .../UnitOfWork/FileUnitOfWorkProvider.cs | 23 +- .../UnitOfWork/PetaPocoUnitOfWorkProvider.cs | 78 +--- ...taPocoUnitOfWork.cs => ScopeUnitOfWork.cs} | 421 +++++++++--------- .../UnitOfWork/ScopeUnitOfWorkProvider.cs | 54 +++ src/Umbraco.Core/Services/ContentService.cs | 8 +- .../Services/ContentTypeService.cs | 70 +-- src/Umbraco.Core/Services/DataTypeService.cs | 111 ++--- src/Umbraco.Core/Services/FileService.cs | 68 +-- .../Services/LocalizationService.cs | 34 +- src/Umbraco.Core/Services/MacroService.cs | 38 +- src/Umbraco.Core/Services/MediaService.cs | 72 +-- .../Services/MemberGroupService.cs | 24 +- src/Umbraco.Core/Services/MemberService.cs | 56 +-- .../Services/MemberTypeService.cs | 38 +- src/Umbraco.Core/Services/RelationService.cs | 42 +- src/Umbraco.Core/Services/ServiceContext.cs | 6 +- src/Umbraco.Core/Services/UserService.cs | 55 ++- src/Umbraco.Core/Umbraco.Core.csproj | 3 +- .../Persistence/BaseTableByTableTest.cs | 9 +- .../TestHelpers/BaseDatabaseFactoryTest.cs | 2 +- .../TestHelpers/BaseUmbracoApplicationTest.cs | 6 +- src/Umbraco.Web/WebBootManager.cs | 2 +- 26 files changed, 642 insertions(+), 683 deletions(-) rename src/Umbraco.Core/Persistence/UnitOfWork/{PetaPocoUnitOfWork.cs => ScopeUnitOfWork.cs} (92%) create mode 100644 src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWorkProvider.cs diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs index bc8af45504..5b7da056b7 100644 --- a/src/Umbraco.Core/CoreBootManager.cs +++ b/src/Umbraco.Core/CoreBootManager.cs @@ -173,7 +173,7 @@ namespace Umbraco.Core return new ServiceContext( new RepositoryFactory(ApplicationCache, ProfilingLogger.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()), new PetaPocoUnitOfWorkProvider(scopeProvider), - new FileUnitOfWorkProvider(), + new FileUnitOfWorkProvider(scopeProvider), new PublishingStrategy(msgFactory, ProfilingLogger.Logger), ApplicationCache, ProfilingLogger.Logger, diff --git a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs index 9a917b3ea4..e5eef9e659 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs @@ -284,7 +284,7 @@ namespace Umbraco.Core.Persistence.Repositories var missingRoles = roleNames.Except(existingRoles); var missingGroups = missingRoles.Select(x => new MemberGroup {Name = x}).ToArray(); - if (SavingMemberGroup.IsRaisedEventCancelled(new SaveEventArgs(missingGroups), this)) + if (SavingMemberGroup.IsRaisedEventCancelled(new SaveEventArgs(missingGroups), this, UnitOfWork.EventManager)) { return; } diff --git a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs index f0b512a6df..2623677e43 100644 --- a/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/RepositoryBase.cs @@ -102,7 +102,7 @@ namespace Umbraco.Core.Persistence.Repositories { if (_isolatedCache != null) return _isolatedCache; - var scope = ((PetaPocoUnitOfWork) UnitOfWork).Scope; // fixme cast! + var scope = ((ScopeUnitOfWork) UnitOfWork).Scope; // fixme cast! IsolatedRuntimeCache provider; switch (scope.RepositoryCacheMode) { @@ -164,7 +164,7 @@ namespace Umbraco.Core.Persistence.Repositories return _cachePolicy = NoRepositoryCachePolicy.Instance; _cachePolicy = CreateCachePolicy(IsolatedCache); - var scope = ((PetaPocoUnitOfWork) UnitOfWork).Scope; // fixme cast! + var scope = ((ScopeUnitOfWork) UnitOfWork).Scope; // fixme cast! switch (scope.RepositoryCacheMode) { case RepositoryCacheMode.Default: diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWork.cs index c4c31849eb..1d9262384c 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWork.cs @@ -3,73 +3,26 @@ using System.Collections.Generic; using System.Linq; using System.Transactions; using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Scoping; +using IsolationLevel = System.Data.IsolationLevel; namespace Umbraco.Core.Persistence.UnitOfWork { /// /// Represents the Unit of Work implementation for working with files /// - internal class FileUnitOfWork : IUnitOfWork + internal class FileUnitOfWork : ScopeUnitOfWork { private Guid _key; - private readonly Queue _operations = new Queue(); - public FileUnitOfWork() + public FileUnitOfWork(IScopeProvider scopeProvider, IsolationLevel isolationLevel = IsolationLevel.Unspecified) : base(scopeProvider, isolationLevel) { _key = Guid.NewGuid(); } - #region Implementation of IUnitOfWork + #region Implementation of IUnitOfWork - /// - /// Registers an instance to be added through this - /// - /// The - /// The participating in the transaction - public void RegisterAdded(IEntity entity, IUnitOfWorkRepository repository) - { - _operations.Enqueue( - new Operation - { - Entity = entity, - Repository = repository, - Type = TransactionType.Insert - }); - } - - /// - /// Registers an instance to be changed through this - /// - /// The - /// The participating in the transaction - public void RegisterChanged(IEntity entity, IUnitOfWorkRepository repository) - { - _operations.Enqueue( - new Operation - { - Entity = entity, - Repository = repository, - Type = TransactionType.Update - }); - } - - /// - /// Registers an instance to be removed through this - /// - /// The - /// The participating in the transaction - public void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository) - { - _operations.Enqueue( - new Operation - { - Entity = entity, - Repository = repository, - Type = TransactionType.Delete - }); - } - - public void Commit() + public override void Commit() { //NOTE: I'm leaving this in here for reference, but this is useless, transaction scope + Files doesn't do anything, // the closest you can get is transactional NTFS, but that requires distributed transaction coordinator and some other libs/wrappers, @@ -81,9 +34,9 @@ namespace Umbraco.Core.Persistence.UnitOfWork // scope.Complete(); //} - while (_operations.Count > 0) + while (Operations.Count > 0) { - var operation = _operations.Dequeue(); + var operation = Operations.Dequeue(); switch (operation.Type) { case TransactionType.Insert: @@ -99,44 +52,16 @@ namespace Umbraco.Core.Persistence.UnitOfWork } // Clear everything - _operations.Clear(); + Operations.Clear(); _key = Guid.NewGuid(); } - public object Key + public override object Key { get { return _key; } } #endregion - - #region Operation - - /// - /// Provides a snapshot of an entity and the repository reference it belongs to. - /// - private sealed class Operation - { - /// - /// Gets or sets the entity. - /// - /// The entity. - public IEntity Entity { get; set; } - - /// - /// Gets or sets the repository. - /// - /// The repository. - public IUnitOfWorkRepository Repository { get; set; } - - /// - /// Gets or sets the type of operation. - /// - /// The type of operation. - public TransactionType Type { get; set; } - } - - #endregion - + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWorkProvider.cs index c27e86a515..f2c295adb5 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/FileUnitOfWorkProvider.cs @@ -1,17 +1,26 @@ -namespace Umbraco.Core.Persistence.UnitOfWork +using Umbraco.Core.Logging; +using Umbraco.Core.Scoping; + +namespace Umbraco.Core.Persistence.UnitOfWork { /// /// Represents a Unit of Work Provider for creating a /// - public class FileUnitOfWorkProvider : IUnitOfWorkProvider + public class FileUnitOfWorkProvider : ScopeUnitOfWorkProvider { - #region Implementation of IUnitOfWorkProvider - - public IUnitOfWork GetUnitOfWork() + public FileUnitOfWorkProvider() + : this(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, LoggerResolver.Current.Logger))) { - return new FileUnitOfWork(); } - #endregion + public FileUnitOfWorkProvider(IScopeProvider scopeProvider) : base(scopeProvider) + { + } + + public override IScopeUnitOfWork GetUnitOfWork() + { + return new FileUnitOfWork(Provider); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs index 7bcfcba2b9..85e854e394 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWorkProvider.cs @@ -1,98 +1,44 @@ using System; -using System.Data; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Scoping; namespace Umbraco.Core.Persistence.UnitOfWork { - - /// - /// Represents a Unit of Work Provider for creating a + /// Represents a Unit of Work Provider for creating a /// - public class PetaPocoUnitOfWorkProvider : IScopeUnitOfWorkProvider - { - private readonly IScopeProvider _scopeProvider; - + public class PetaPocoUnitOfWorkProvider : ScopeUnitOfWorkProvider + { [Obsolete("Use the constructor specifying an ILogger instead")] public PetaPocoUnitOfWorkProvider() - : this(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, LoggerResolver.Current.Logger))) - { - - } + : base(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, LoggerResolver.Current.Logger))) + { } [Obsolete("Use the constructor specifying an ILogger instead")] public PetaPocoUnitOfWorkProvider(string connectionString, string providerName) - : this(new ScopeProvider(new DefaultDatabaseFactory(connectionString, providerName, LoggerResolver.Current.Logger))) + : base(new ScopeProvider(new DefaultDatabaseFactory(connectionString, providerName, LoggerResolver.Current.Logger))) { } /// - /// Parameterless constructor uses defaults + /// Constructor /// public PetaPocoUnitOfWorkProvider(ILogger logger) - : this(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, logger))) - { - - } + : base(new ScopeProvider(new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, logger))) + { } /// - /// Constructor accepting custom connectino string and provider name + /// Constructor accepting custom connection string and provider name /// /// /// Connection String to use with Database /// Database Provider for the Connection String public PetaPocoUnitOfWorkProvider(ILogger logger, string connectionString, string providerName) - : this(new ScopeProvider(new DefaultDatabaseFactory(connectionString, providerName, logger))) + : base(new ScopeProvider(new DefaultDatabaseFactory(connectionString, providerName, logger))) { } - /// - /// Constructor accepting a instance - /// - /// - public PetaPocoUnitOfWorkProvider(IScopeProvider scopeProvider) + public PetaPocoUnitOfWorkProvider(IScopeProvider scopeProvider) : base(scopeProvider) { - Mandate.ParameterNotNull(scopeProvider, "scopeProvider"); - _scopeProvider = scopeProvider; } - - #region Implementation of IUnitOfWorkProvider - - //explicit implementation - IDatabaseUnitOfWork IDatabaseUnitOfWorkProvider.GetUnitOfWork() - { - return new PetaPocoUnitOfWork(_scopeProvider); - } - - /// - /// Creates a Unit of work with a new UmbracoDatabase instance for the work item/transaction. - /// - /// - /// - /// Each PetaPoco UOW uses it's own Database object, not the shared Database object that comes from - /// the ApplicationContext.Current.DatabaseContext.Database. This is because each transaction should use it's own Database - /// and we Dispose of this Database object when the UOW is disposed. - /// - public IScopeUnitOfWork GetUnitOfWork() - { - return new PetaPocoUnitOfWork(_scopeProvider); - } - - /// - /// Creates a Unit of work with a new UmbracoDatabase instance for the work item/transaction. - /// - /// - /// - /// Each PetaPoco UOW uses it's own Database object, not the shared Database object that comes from - /// the ApplicationContext.Current.DatabaseContext.Database. This is because each transaction should use it's own Database - /// and we Dispose of this Database object when the UOW is disposed. - /// - public IScopeUnitOfWork GetUnitOfWork(IsolationLevel isolationLevel) - { - return new PetaPocoUnitOfWork(_scopeProvider, isolationLevel); - } - - #endregion - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs similarity index 92% rename from src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs rename to src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs index 79d6357625..8bae95e5dd 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/PetaPocoUnitOfWork.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWork.cs @@ -1,209 +1,214 @@ -using System; -using System.Collections.Generic; -using System.Data; -using Umbraco.Core.Events; -using Umbraco.Core.Models.EntityBase; -using Umbraco.Core.Scoping; - -namespace Umbraco.Core.Persistence.UnitOfWork -{ - /// - /// Represents the Unit of Work implementation for PetaPoco - /// - internal class PetaPocoUnitOfWork : DisposableObject, IScopeUnitOfWork - { - private readonly Queue _operations = new Queue(); - private readonly IsolationLevel _isolationLevel; - private readonly IScopeProvider _scopeProvider; - private bool _completeScope = true; // scope is completed by default - private IScope _scope; - private Guid _key; - - /// - /// Used for testing - /// - internal Guid InstanceId { get; private set; } - - /// - /// Creates a new unit of work instance - /// - /// - /// - /// - /// This should normally not be used directly and should be created with the UnitOfWorkProvider - /// - internal PetaPocoUnitOfWork(IScopeProvider scopeProvider, IsolationLevel isolationLevel = IsolationLevel.Unspecified) - { - _scopeProvider = scopeProvider; - _isolationLevel = isolationLevel; - _key = Guid.NewGuid(); - InstanceId = Guid.NewGuid(); - } - - /// - /// Registers an instance to be added through this - /// - /// The - /// The participating in the transaction - public void RegisterAdded(IEntity entity, IUnitOfWorkRepository repository) - { - _operations.Enqueue(new Operation - { - Entity = entity, - Repository = repository, - Type = TransactionType.Insert - }); - } - - /// - /// Registers an instance to be changed through this - /// - /// The - /// The participating in the transaction - public void RegisterChanged(IEntity entity, IUnitOfWorkRepository repository) - { - _operations.Enqueue( - new Operation - { - Entity = entity, - Repository = repository, - Type = TransactionType.Update - }); - } - - /// - /// Registers an instance to be removed through this - /// - /// The - /// The participating in the transaction - public void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository) - { - _operations.Enqueue( - new Operation - { - Entity = entity, - Repository = repository, - Type = TransactionType.Delete - }); - } - - /// - /// Commits all batched changes within the scope of a PetaPoco transaction - /// - /// - /// Unlike a typical unit of work, this UOW will let you commit more than once since a new transaction is creaed per - /// Commit() call instead of having one Transaction per UOW. - /// - public void Commit() - { - Commit(null); - } - - /// - /// Commits all batched changes within the scope of a PetaPoco transaction - /// - /// - /// Allows you to set a callback which is executed before the transaction is committed, allow you to add additional SQL - /// operations to the overall commit process after the queue has been processed. - /// - internal void Commit(Action transactionCompleting) - { - // this happens in a scope-managed transaction - - // in case anything goes wrong - _completeScope = false; - - while (_operations.Count > 0) - { - var operation = _operations.Dequeue(); - switch (operation.Type) - { - case TransactionType.Insert: - operation.Repository.PersistNewItem(operation.Entity); - break; - case TransactionType.Delete: - operation.Repository.PersistDeletedItem(operation.Entity); - break; - case TransactionType.Update: - operation.Repository.PersistUpdatedItem(operation.Entity); - break; - } - } - - if (transactionCompleting != null) - transactionCompleting(Database); - - // all is ok - _completeScope = true; - - // Clear everything - _operations.Clear(); - _key = Guid.NewGuid(); - } - - public object Key - { - get { return _key; } - } - - public IScope Scope - { - get { return _scope ?? (_scope = _scopeProvider.CreateScope(_isolationLevel)); } - } - - public UmbracoDatabase Database - { - get { return Scope.Database; } - } - - public IEventManager EventManager - { - get { return Scope.Events; } - } - - #region Operation - - /// - /// Provides a snapshot of an entity and the repository reference it belongs to. - /// - private sealed class Operation - { - /// - /// Gets or sets the entity. - /// - /// The entity. - public IEntity Entity { get; set; } - - /// - /// Gets or sets the repository. - /// - /// The repository. - public IUnitOfWorkRepository Repository { get; set; } - - /// - /// Gets or sets the type of operation. - /// - /// The type of operation. - public TransactionType Type { get; set; } - } - - #endregion - - /// - /// Ensures disposable objects are disposed - /// - /// - /// Ensures that the Transaction instance is disposed of - /// - protected override void DisposeResources() - { - _operations.Clear(); - if (_scope == null) return; - - if (_completeScope) _scope.Complete(); - _scope.Dispose(); - _scope = null; - } - - } +using System; +using System.Collections.Generic; +using System.Data; +using Umbraco.Core.Events; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Scoping; + +namespace Umbraco.Core.Persistence.UnitOfWork +{ + /// + /// Represents the Unit of Work implementation for a Scope + /// + internal class ScopeUnitOfWork : DisposableObject, IScopeUnitOfWork + { + private readonly Queue _operations = new Queue(); + private readonly IsolationLevel _isolationLevel; + private readonly IScopeProvider _scopeProvider; + private bool _completeScope = true; // scope is completed by default + private IScope _scope; + private Guid _key; + + /// + /// Used for testing + /// + internal Guid InstanceId { get; private set; } + + /// + /// Creates a new unit of work instance + /// + /// + /// + /// + /// This should normally not be used directly and should be created with the UnitOfWorkProvider + /// + internal ScopeUnitOfWork(IScopeProvider scopeProvider, IsolationLevel isolationLevel = IsolationLevel.Unspecified) + { + _scopeProvider = scopeProvider; + _isolationLevel = isolationLevel; + _key = Guid.NewGuid(); + InstanceId = Guid.NewGuid(); + } + + /// + /// Registers an instance to be added through this + /// + /// The + /// The participating in the transaction + public void RegisterAdded(IEntity entity, IUnitOfWorkRepository repository) + { + _operations.Enqueue(new Operation + { + Entity = entity, + Repository = repository, + Type = TransactionType.Insert + }); + } + + /// + /// Registers an instance to be changed through this + /// + /// The + /// The participating in the transaction + public void RegisterChanged(IEntity entity, IUnitOfWorkRepository repository) + { + _operations.Enqueue( + new Operation + { + Entity = entity, + Repository = repository, + Type = TransactionType.Update + }); + } + + /// + /// Registers an instance to be removed through this + /// + /// The + /// The participating in the transaction + public void RegisterRemoved(IEntity entity, IUnitOfWorkRepository repository) + { + _operations.Enqueue( + new Operation + { + Entity = entity, + Repository = repository, + Type = TransactionType.Delete + }); + } + + /// + /// Commits all batched changes within the scope of a PetaPoco transaction + /// + /// + /// Unlike a typical unit of work, this UOW will let you commit more than once since a new transaction is creaed per + /// Commit() call instead of having one Transaction per UOW. + /// + public virtual void Commit() + { + Commit(null); + } + + /// + /// Commits all batched changes within the scope of a PetaPoco transaction + /// + /// + /// Allows you to set a callback which is executed before the transaction is committed, allow you to add additional SQL + /// operations to the overall commit process after the queue has been processed. + /// + internal void Commit(Action transactionCompleting) + { + // this happens in a scope-managed transaction + + // in case anything goes wrong + _completeScope = false; + + while (_operations.Count > 0) + { + var operation = _operations.Dequeue(); + switch (operation.Type) + { + case TransactionType.Insert: + operation.Repository.PersistNewItem(operation.Entity); + break; + case TransactionType.Delete: + operation.Repository.PersistDeletedItem(operation.Entity); + break; + case TransactionType.Update: + operation.Repository.PersistUpdatedItem(operation.Entity); + break; + } + } + + if (transactionCompleting != null) + transactionCompleting(Database); + + // all is ok + _completeScope = true; + + // Clear everything + _operations.Clear(); + _key = Guid.NewGuid(); + } + + public virtual object Key + { + get { return _key; } + } + + public IScope Scope + { + get { return _scope ?? (_scope = _scopeProvider.CreateScope(_isolationLevel)); } + } + + public UmbracoDatabase Database + { + get { return Scope.Database; } + } + + public IEventManager EventManager + { + get { return Scope.Events; } + } + + protected Queue Operations + { + get { return _operations; } + } + + #region Operation + + /// + /// Provides a snapshot of an entity and the repository reference it belongs to. + /// + protected sealed class Operation + { + /// + /// Gets or sets the entity. + /// + /// The entity. + public IEntity Entity { get; set; } + + /// + /// Gets or sets the repository. + /// + /// The repository. + public IUnitOfWorkRepository Repository { get; set; } + + /// + /// Gets or sets the type of operation. + /// + /// The type of operation. + public TransactionType Type { get; set; } + } + + #endregion + + /// + /// Ensures disposable objects are disposed + /// + /// + /// Ensures that the Transaction instance is disposed of + /// + protected override void DisposeResources() + { + _operations.Clear(); + if (_scope == null) return; + + if (_completeScope) _scope.Complete(); + _scope.Dispose(); + _scope = null; + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWorkProvider.cs new file mode 100644 index 0000000000..0b137e985a --- /dev/null +++ b/src/Umbraco.Core/Persistence/UnitOfWork/ScopeUnitOfWorkProvider.cs @@ -0,0 +1,54 @@ +using System.Data; +using Umbraco.Core.Scoping; + +namespace Umbraco.Core.Persistence.UnitOfWork +{ + public abstract class ScopeUnitOfWorkProvider : IScopeUnitOfWorkProvider + { + public IScopeProvider Provider { get; private set; } + + /// + /// Constructor accepting a instance + /// + /// + protected ScopeUnitOfWorkProvider(IScopeProvider scopeProvider) + { + Mandate.ParameterNotNull(scopeProvider, "scopeProvider"); + Provider = scopeProvider; + } + + //explicit implementation + IDatabaseUnitOfWork IDatabaseUnitOfWorkProvider.GetUnitOfWork() + { + return new ScopeUnitOfWork(Provider); + } + + /// + /// Creates a Unit of work with a new UmbracoDatabase instance for the work item/transaction. + /// + /// + /// + /// Each PetaPoco UOW uses it's own Database object, not the shared Database object that comes from + /// the ApplicationContext.Current.DatabaseContext.Database. This is because each transaction should use it's own Database + /// and we Dispose of this Database object when the UOW is disposed. + /// + public virtual IScopeUnitOfWork GetUnitOfWork() + { + return new ScopeUnitOfWork(Provider); + } + + /// + /// Creates a Unit of work with a new UmbracoDatabase instance for the work item/transaction. + /// + /// + /// + /// Each PetaPoco UOW uses it's own Database object, not the shared Database object that comes from + /// the ApplicationContext.Current.DatabaseContext.Database. This is because each transaction should use it's own Database + /// and we Dispose of this Database object when the UOW is disposed. + /// + public IScopeUnitOfWork GetUnitOfWork(IsolationLevel isolationLevel) + { + return new ScopeUnitOfWork(Provider, isolationLevel); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 343b4b66ad..25ea4da823 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -177,12 +177,8 @@ namespace Umbraco.Core.Services Created.RaiseEvent(new NewEventArgs(content, false, contentTypeAlias, parentId), this, uow.EventManager); - using (var auditRepo = RepositoryFactory.CreateAuditRepository(uow)) - { - auditRepo.AddOrUpdate(new AuditItem(content.Id, string.Format("Content '{0}' was created", name), AuditType.New, content.CreatorId)); - uow.Commit(); - } - + Audit(AuditType.New, string.Format("Content '{0}' was created", name), content.CreatorId, content.Id); + return content; } diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 31e86cd128..a8b1e7bc2c 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -719,12 +719,13 @@ namespace Umbraco.Core.Services /// Optional id of the user saving the ContentType public void Save(IContentType contentType, int userId = 0) { - if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs(contentType), this)) - return; - using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs(contentType), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) { ValidateLocked(contentType); // throws if invalid @@ -749,12 +750,13 @@ namespace Umbraco.Core.Services { var asArray = contentTypes.ToArray(); - if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs(asArray), this)) - return; - using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + if (SavingContentType.IsRaisedEventCancelled(new SaveEventArgs(asArray), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) { // all-or-nothing, validate them all first @@ -785,13 +787,14 @@ namespace Umbraco.Core.Services /// Optional id of the user issueing the delete /// Deleting a will delete all the objects based on this public void Delete(IContentType contentType, int userId = 0) - { - if (DeletingContentType.IsRaisedEventCancelled(new DeleteEventArgs(contentType), this)) - return; - + { using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + if (DeletingContentType.IsRaisedEventCancelled(new DeleteEventArgs(contentType), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) { //TODO: This needs to change, if we are deleting a content type, we should just delete the data, @@ -828,14 +831,15 @@ namespace Umbraco.Core.Services /// public void Delete(IEnumerable contentTypes, int userId = 0) { - var asArray = contentTypes.ToArray(); - - if (DeletingContentType.IsRaisedEventCancelled(new DeleteEventArgs(asArray), this)) - return; + var asArray = contentTypes.ToArray(); using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + if (DeletingContentType.IsRaisedEventCancelled(new DeleteEventArgs(asArray), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) { var deletedContentTypes = new List(); @@ -1173,12 +1177,13 @@ namespace Umbraco.Core.Services /// Optional Id of the user saving the MediaType public void Save(IMediaType mediaType, int userId = 0) { - if (SavingMediaType.IsRaisedEventCancelled(new SaveEventArgs(mediaType), this)) - return; - using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + if (SavingMediaType.IsRaisedEventCancelled(new SaveEventArgs(mediaType), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) { ValidateLocked(mediaType); // throws if invalid @@ -1203,13 +1208,14 @@ namespace Umbraco.Core.Services public void Save(IEnumerable mediaTypes, int userId = 0) { var asArray = mediaTypes.ToArray(); - - if (SavingMediaType.IsRaisedEventCancelled(new SaveEventArgs(asArray), this)) - return; - + using (new WriteLock(Locker)) { var uow = UowProvider.GetUnitOfWork(); + + if (SavingMediaType.IsRaisedEventCancelled(new SaveEventArgs(asArray), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) { // all-or-nothing, validate them all first @@ -1241,14 +1247,16 @@ namespace Umbraco.Core.Services /// Optional Id of the user deleting the MediaType /// Deleting a will delete all the objects based on this public void Delete(IMediaType mediaType, int userId = 0) - { - if (DeletingMediaType.IsRaisedEventCancelled(new DeleteEventArgs(mediaType), this)) - return; + { using (new WriteLock(Locker)) { + var uow = UowProvider.GetUnitOfWork(); + + if (DeletingMediaType.IsRaisedEventCancelled(new DeleteEventArgs(mediaType), this, uow.EventManager)) + return; + _mediaService.DeleteMediaOfType(mediaType.Id, userId); - var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) { var deletedMediaTypes = new List() {mediaType}; @@ -1273,17 +1281,19 @@ namespace Umbraco.Core.Services public void Delete(IEnumerable mediaTypes, int userId = 0) { var asArray = mediaTypes.ToArray(); - - if (DeletingMediaType.IsRaisedEventCancelled(new DeleteEventArgs(asArray), this)) - return; + using (new WriteLock(Locker)) { + var uow = UowProvider.GetUnitOfWork(); + + if (DeletingMediaType.IsRaisedEventCancelled(new DeleteEventArgs(asArray), this, uow.EventManager)) + return; + foreach (var mediaType in asArray) { _mediaService.DeleteMediaOfType(mediaType.Id); } - - var uow = UowProvider.GetUnitOfWork(); + using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) { var deletedMediaTypes = new List(); diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index 801869d078..3f42a441d8 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -360,11 +360,12 @@ namespace Umbraco.Core.Services /// to save /// Id of the user issueing the save public void Save(IDataTypeDefinition dataTypeDefinition, int userId = 0) - { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this)) - return; - + { var uow = UowProvider.GetUnitOfWork(); + + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this, uow.EventManager)) + return; + using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) { dataTypeDefinition.CreatorId = userId; @@ -395,27 +396,29 @@ namespace Umbraco.Core.Services /// Boolean indicating whether or not to raise events public void Save(IEnumerable dataTypeDefinitions, int userId, bool raiseEvents) { - if (raiseEvents) + using (var uow = UowProvider.GetUnitOfWork()) { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinitions), this)) - return; - } - - var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) - { - foreach (var dataTypeDefinition in dataTypeDefinitions) - { - dataTypeDefinition.CreatorId = userId; - repository.AddOrUpdate(dataTypeDefinition); - } - uow.Commit(); - if (raiseEvents) - Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinitions, false), this); - } + { + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinitions), this, uow.EventManager)) + return; + } - Audit(AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, -1); + using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) + { + foreach (var dataTypeDefinition in dataTypeDefinitions) + { + dataTypeDefinition.CreatorId = userId; + repository.AddOrUpdate(dataTypeDefinition); + } + uow.Commit(); + + if (raiseEvents) + Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinitions, false), this); + } + + Audit(AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, -1); + } } /// @@ -501,30 +504,32 @@ namespace Umbraco.Core.Services /// public void SaveDataTypeAndPreValues(IDataTypeDefinition dataTypeDefinition, IDictionary values, int userId = 0) { - if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this)) - return; - - // if preValues contain the data type, override the data type definition accordingly - if (values != null && values.ContainsKey(Constants.PropertyEditors.PreValueKeys.DataValueType)) - dataTypeDefinition.DatabaseType = PropertyValueEditor.GetDatabaseType(values[Constants.PropertyEditors.PreValueKeys.DataValueType].Value); - - var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) + using (var uow = UowProvider.GetUnitOfWork()) { - dataTypeDefinition.CreatorId = userId; + if (Saving.IsRaisedEventCancelled(new SaveEventArgs(dataTypeDefinition), this, uow.EventManager)) + return; - //add/update the dtd - repository.AddOrUpdate(dataTypeDefinition); + // if preValues contain the data type, override the data type definition accordingly + if (values != null && values.ContainsKey(Constants.PropertyEditors.PreValueKeys.DataValueType)) + dataTypeDefinition.DatabaseType = PropertyValueEditor.GetDatabaseType(values[Constants.PropertyEditors.PreValueKeys.DataValueType].Value); - //add/update the prevalues - repository.AddOrUpdatePreValues(dataTypeDefinition, values); + using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) + { + dataTypeDefinition.CreatorId = userId; - uow.Commit(); + //add/update the dtd + repository.AddOrUpdate(dataTypeDefinition); - Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinition, false), this); + //add/update the prevalues + repository.AddOrUpdatePreValues(dataTypeDefinition, values); + + uow.Commit(); + + Saved.RaiseEvent(new SaveEventArgs(dataTypeDefinition, false), this); + } + + Audit(AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id); } - - Audit(AuditType.Save, string.Format("Save DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id); } /// @@ -537,21 +542,23 @@ namespace Umbraco.Core.Services /// to delete /// Optional Id of the user issueing the deletion public void Delete(IDataTypeDefinition dataTypeDefinition, int userId = 0) - { - if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(dataTypeDefinition), this)) - return; - - var uow = UowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) - { - repository.Delete(dataTypeDefinition); + { + using (var uow = UowProvider.GetUnitOfWork()) + { + if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs(dataTypeDefinition), this, uow.EventManager)) + return; - uow.Commit(); + using (var repository = RepositoryFactory.CreateDataTypeDefinitionRepository(uow)) + { + repository.Delete(dataTypeDefinition); - Deleted.RaiseEvent(new DeleteEventArgs(dataTypeDefinition, false), this); - } + uow.Commit(); - Audit(AuditType.Delete, string.Format("Delete DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id); + Deleted.RaiseEvent(new DeleteEventArgs(dataTypeDefinition, false), this); + } + + Audit(AuditType.Delete, string.Format("Delete DataTypeDefinition performed by user"), userId, dataTypeDefinition.Id); + } } /// diff --git a/src/Umbraco.Core/Services/FileService.cs b/src/Umbraco.Core/Services/FileService.cs index a8ad4b4885..8361541ff8 100644 --- a/src/Umbraco.Core/Services/FileService.cs +++ b/src/Umbraco.Core/Services/FileService.cs @@ -20,13 +20,13 @@ namespace Umbraco.Core.Services /// public class FileService : ScopeRepositoryService, IFileService { - private readonly IUnitOfWorkProvider _fileUowProvider; + private readonly IScopeUnitOfWorkProvider _fileUowProvider; private const string PartialViewHeader = "@inherits Umbraco.Web.Mvc.UmbracoTemplatePage"; private const string PartialViewMacroHeader = "@inherits Umbraco.Web.Macros.PartialViewMacroPage"; public FileService( - IUnitOfWorkProvider fileProvider, + IScopeUnitOfWorkProvider fileProvider, IDatabaseUnitOfWorkProvider dataProvider, RepositoryFactory repositoryFactory, ILogger logger, @@ -71,19 +71,21 @@ namespace Umbraco.Core.Services /// public void SaveStylesheet(Stylesheet stylesheet, int userId = 0) { - if (SavingStylesheet.IsRaisedEventCancelled(new SaveEventArgs(stylesheet), this)) - return; - - var uow = _fileUowProvider.GetUnitOfWork(); - using (var repository = RepositoryFactory.CreateStylesheetRepository(uow, UowProvider.GetUnitOfWork())) + using (var uow = _fileUowProvider.GetUnitOfWork()) { - repository.AddOrUpdate(stylesheet); - uow.Commit(); + if (SavingStylesheet.IsRaisedEventCancelled(new SaveEventArgs(stylesheet), this, uow.EventManager)) + return; - SavedStylesheet.RaiseEvent(new SaveEventArgs(stylesheet, false), this); + using (var repository = RepositoryFactory.CreateStylesheetRepository(uow, UowProvider.GetUnitOfWork())) + { + repository.AddOrUpdate(stylesheet); + uow.Commit(); + + SavedStylesheet.RaiseEvent(new SaveEventArgs(stylesheet, false), this); + } + + Audit(AuditType.Save, string.Format("Save Stylesheet performed by user"), userId, -1); } - - Audit(AuditType.Save, string.Format("Save Stylesheet performed by user"), userId, -1); } /// @@ -99,7 +101,7 @@ namespace Umbraco.Core.Services var stylesheet = repository.Get(path); if (stylesheet == null) return; - if (DeletingStylesheet.IsRaisedEventCancelled(new DeleteEventArgs(stylesheet), this)) + if (DeletingStylesheet.IsRaisedEventCancelled(new DeleteEventArgs(stylesheet), this, uow.EventManager)) return; repository.Delete(stylesheet); @@ -161,12 +163,12 @@ namespace Umbraco.Core.Services /// public void SaveScript(Script script, int userId = 0) { - if (SavingScript.IsRaisedEventCancelled(new SaveEventArgs