From 7e655795e123d70f65439a9aed12485b76b14dcf Mon Sep 17 00:00:00 2001 From: Severin d'Heureuse Date: Wed, 26 Apr 2017 15:19:19 +0200 Subject: [PATCH 1/3] Mockup --- .../directives/headerLocalizedName.html | 70 +++++ .../opten.localization.controller.js | 258 ++++++++++++++++++ .../opten.localization.directive.js | 12 + .../OPTEN.Localization/package.manifest | 3 +- .../Config/opten.localization.config.json | 18 ++ .../Opten.Umbraco.Localization.Web.UI.csproj | 2 + 6 files changed, 362 insertions(+), 1 deletion(-) create mode 100644 src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/directives/headerLocalizedName.html create mode 100644 src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/opten.localization.controller.js diff --git a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/directives/headerLocalizedName.html b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/directives/headerLocalizedName.html new file mode 100644 index 0000000..3fcbdaa --- /dev/null +++ b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/directives/headerLocalizedName.html @@ -0,0 +1,70 @@ +
+
+
+
+
+ + + + + + +
{{ name }}
+ + + + + +
+
+
+ + +
+
diff --git a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/opten.localization.controller.js b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/opten.localization.controller.js new file mode 100644 index 0000000..6871fe7 --- /dev/null +++ b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/opten.localization.controller.js @@ -0,0 +1,258 @@ +; (function () { + 'use strict'; + + angular.module("umbraco").controller("OPTEN.Backoffice.HeaderLocalizedName.Controller", headerLocalizedNameController); + + headerLocalizedNameController.$inject = ["$scope", "$rootScope", "editorState", "languageResource"]; + + function headerLocalizedNameController($scope, $rootScope, editorState, languageResource) { + var vm = this, + errors = []; + + $scope.model = []; //remove this or change + $scope.model.value = null;//remove this or change + + vm.loadings = []; + + vm.checkAvailability = checkAvailability; + vm.getIndex = getIndex; + + // We set a scope with the value otherwise we have a "empty" JSON saved. + $scope.urls = $scope.model.value || []; + + // Legacy fix if json doesn't contain "name" + legacy(); + + $scope.states = { + isEnabled: false, + hasHostnames: false, + isPublished: false, + hasLanguages: false + }; + + $scope.$watchCollection("states", function (oldValue, newValue) { + // Try enable the localizer (set JSON to $scope.model.value) + enable(newValue); + }); + + $rootScope.$watchCollection("languages", function (oldValue, newValue) { + if (newValue && newValue.length && vm.loadings.length < 2) { + vm.loadings.push("languages"); + + $scope.states.hasLanguages = newValue.length > 0; + } + }); + + activate(); + + $scope.$on("formSubmitting", function () { + //TODO: If we have a valid state the json get's created but could be "empty" + // don't know if this is a problem or we have to check each input if it has a value + // and then create the json? => this code here don't work if it's a directive... + + if ($scope.states.isPublished == false || ($scope.states.isEnabled && $scope.states.hasHostnames)) { + // Delete all urls if has hostnames! + $scope.model.value = null; + } + else if ($scope.urls && $scope.urls.length) { + // Check if we have any localized url + // Only set the JSON to the $scope.model.value if we really have something. + if (hasAnyLocalized()) { + $scope.model.value = $scope.urls; + } + else { + $scope.model.value = null; + } + } + }); + + + // Private functions + + function activate() { + + //TODO: Is there a way to get the hostnames on the node by angular? + // so we don't have to pass in the current node id + + var contentId = !editorState.current ? 0 : editorState.current.id; + + //TODO: Only call this when published? + languageResource.getState(contentId).then(function (state) { + vm.loadings.push("isEnabled"); + vm.loadings.push("hasHostnames"); + + $scope.states.isEnabled = state.isEnabled; + $scope.states.hasHostnames = state.hasHostnames; + }); + + $scope.states.isPublished = editorState.current.published; + }; + + function enable(state) { + // We wait until everything is loaded + if (state.isPublished && vm.loadings.length >= 3) { + + // Only set the languages if the states are valid + if (state.isEnabled && + state.hasHostnames == false && + state.isPublished && + state.hasLanguages) { + setLanguages($rootScope.languages); + } + else { + $scope.model.value = null; + } + } + }; + + function setLanguages(languages) { + // We have to set the json with the languages if never set + // or add the new one's but don't delete the old ones + var found = false; + for (var i = 0; i < languages.length; i++) { + found = false; + + for (var y = 0; y < $scope.urls.length; y++) { + if (languages[i].isoCode == $scope.urls[y].isoCode) { + found = true; + break; + } + } + + if (found == false) { + $scope.urls.push({ + isoCode: languages[i].isoCode, + url: '', + name: '', + hasError: false + }); + } + } + + // If the languages are equal we check the availability + angular.forEach(languages, function (language, index) { + checkAvailability(index); + }); + }; + + function getNodeLevel() { + if (!editorState.current.path || editorState.current.path == null) return -1; + return (editorState.current.path.split(',').length - 1); + }; + + function getUrlSegment(name, success) { + if (!name || name.length == 0) { + success(""); + } + else { + languageResource.getUrlSegment(name).then(function (segment) { + success(JSON.parse(segment)); //TODO: Why JSON.parse() somewhow angular returns string in extra quotes + }); + } + }; + + function getIndex(isoCode) { + for (var i = 0; i < $scope.urls.length; i++) { + if ($scope.urls[i].isoCode.toLowerCase() == isoCode.toLowerCase()) { + return i; + } + } + + return -1; + }; + + function checkAvailability(index) { + var name = $scope.urls[index].name; + + errors = []; + + if (name == "" || name.length < 1) { + + // Set the semgent to empty + $scope.urls[index].url = ""; + + // Hide errors because url is empty... + setError(index, false); + } + else if (name != "" && name.length) { + + // Transform name into segment by Umbraco + getUrlSegment(name, function (segment) { + + // Set the semgent + $scope.urls[index].url = segment; + + // Then check if the current url has the same segment + if (editorState.current.urls && editorState.current.urls.length) { + var currentSegment = editorState.current.urls[0].split('/'); + currentSegment = currentSegment[currentSegment.length - 2]; + + setError(index, (segment.toLowerCase() == currentSegment.toLowerCase())); + } + + // Then if other in the same node has the same url + angular.forEach($scope.urls, function (language, i) { + if (index != i && hasError(index) == false) { + + if (setError(index, (language.url.toLowerCase() == $scope.urls[index].url.toLowerCase())) == false) { + + //TODO: This could be slow so maybe load all first and then check it against this? + // or if a node in the same level has the same url + languageResource.getUrlAvailability(editorState.current.id, segment, getNodeLevel()).then(function (isAvailable) { + + setError(index, (isAvailable.toString().toLowerCase() == "false")); + }); + } + } + }); + }); + } + + function setError(index, hasError) { + if (hasError) { + errors.push(index); + } else { + for (var i = 0; i < errors.length; i++) { + if (errors[i] == index) errors.splice(i, 1); + } + } + + $scope.urls[index].hasError = hasError; + $scope.urlAliasForm.$setValidity("isValid", !hasError); + + return hasError; + }; + + function hasError(index) { + for (var i = 0; i < errors.length; i++) { + if (errors[i] == index) return true; + } + return false; + }; + }; + + function hasAnyLocalized() { + var anyLocalized = false; + for (var i = 0; i < $scope.urls.length; i++) { + if ($scope.urls[i].hasError == false && + $scope.urls[i].url != "") { + anyLocalized = true; + break; + } + } + return anyLocalized; + }; + + function legacy() { + if ($scope.urls.length) { + angular.forEach($scope.urls, function (language, i) { + if (!$scope.urls[i].name) { + $scope.urls[i].name = $scope.urls[i].url; + } + }); + } + }; + + }; + +}()); \ No newline at end of file diff --git a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/opten.localization.directive.js b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/opten.localization.directive.js index 3671556..4542c1e 100644 --- a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/opten.localization.directive.js +++ b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/opten.localization.directive.js @@ -86,4 +86,16 @@ }; }; + angular.module("umbraco.directives").directive("optenHeaderLocalizedName", optenHeaderLocalizedName); + + function optenHeaderLocalizedName() { + return { + restrict: "E", + replace: true, + templateUrl: "/App_Plugins/OPTEN.Localization/directives/headerLocalizedName.html?v=ASSEMBLY_VERSION" + }; + }; + + + }()); \ No newline at end of file diff --git a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/package.manifest b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/package.manifest index c735e79..344a2b2 100644 --- a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/package.manifest +++ b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.Localization/package.manifest @@ -3,6 +3,7 @@ "~/App_Plugins/OPTEN.Localization/opten.localization.js?v=ASSEMBLY_VERSION", "~/App_Plugins/OPTEN.Localization/opten.localization.interceptor.js?v=ASSEMBLY_VERSION", "~/App_Plugins/OPTEN.Localization/opten.localization.resource.js?v=ASSEMBLY_VERSION", - "~/App_Plugins/OPTEN.Localization/opten.localization.directive.js?v=ASSEMBLY_VERSION" + "~/App_Plugins/OPTEN.Localization/opten.localization.directive.js?v=ASSEMBLY_VERSION", + "~/App_Plugins/OPTEN.Localization/opten.localization.controller.js?v=ASSEMBLY_VERSION" ] } \ No newline at end of file diff --git a/src/Opten.Umbraco.Localization.Web.UI/Config/opten.localization.config.json b/src/Opten.Umbraco.Localization.Web.UI/Config/opten.localization.config.json index 131d873..80d9d01 100644 --- a/src/Opten.Umbraco.Localization.Web.UI/Config/opten.localization.config.json +++ b/src/Opten.Umbraco.Localization.Web.UI/Config/opten.localization.config.json @@ -48,5 +48,23 @@ "insertAfter": "{{labelstring}}" } ] + }, + { + "insertString": "", + "modifications": [ + { + "url": "views/components/editor/umb-editor-header.html", + "insertAfter": "
{{ description }}
" + } + ] + }, + { + "insertString": " optenHeaderLocalizedName-panel-header", + "modifications": [ + { + "url": "views/components/editor/umb-editor-header.html", + "insertAfter": "umb-editor-header umb-panel-header" + } + ] } ] \ No newline at end of file diff --git a/src/Opten.Umbraco.Localization.Web.UI/Opten.Umbraco.Localization.Web.UI.csproj b/src/Opten.Umbraco.Localization.Web.UI/Opten.Umbraco.Localization.Web.UI.csproj index 0924efc..533f453 100644 --- a/src/Opten.Umbraco.Localization.Web.UI/Opten.Umbraco.Localization.Web.UI.csproj +++ b/src/Opten.Umbraco.Localization.Web.UI/Opten.Umbraco.Localization.Web.UI.csproj @@ -340,10 +340,12 @@ + + From aabfeab7b3be2986e5fa6125edc5e4579cc76aef Mon Sep 17 00:00:00 2001 From: Tobias Lopez Date: Wed, 31 May 2017 16:35:39 +0200 Subject: [PATCH 2/3] styled url alias property --- .../App_Data/Umbraco.sdf | Bin 2031616 -> 2031616 bytes .../OPTEN.UrlAlias/opten.urlalias.css | 28 +++++++++++++++ .../OPTEN.UrlAlias/package.manifest | 3 ++ .../App_Plugins/OPTEN.UrlAlias/urlAlias.html | 34 ++++++++---------- .../Opten.Umbraco.Localization.Web.UI.csproj | 1 + 5 files changed, 46 insertions(+), 20 deletions(-) create mode 100644 src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten.urlalias.css diff --git a/src/Opten.Umbraco.Localization.Web.UI/App_Data/Umbraco.sdf b/src/Opten.Umbraco.Localization.Web.UI/App_Data/Umbraco.sdf index 6db9dfa44f872907af72dabcfc8d5a4225a8f570..22175dcc9e1b1638415de0d1650be40d6141ca70 100644 GIT binary patch delta 1610 zcmb_cYfKzf6h3zzyEDtOGXvYDyOiw|C_W+)N=aiZ&7xw6R@iNAN)x0*+cgsHvVqnn zmbPQNPECxqK$>aPM4`B9#Q-H{k|vFy^232Q)s$^wLVUD%=dnS4`9tsK z%zpEolY72%@0laZJ+j=xet&Kz26$cQWy?_OSmg|=W_$>~c^JU(0R*r$FTM(&wbJlP z-^*sCh3`&afPKv40B`hH6Gg6ACJY}3w3tC$${*w-tns^X<|Gw?Q{${Lhp6+zJQ`of z1bu^&%(g6Lrc7VT_Dy5osfCQTkU>FT_{NRh#|RMN=x!?;Pyht}K|P2FHmKnVI)p0J z2PWvZs5St%`rQOw9ykM19!vpjMoBN!A}9mh16egNNpD9{ZT}=ahmfFc`;@-yWyX-b z@eskYd#<&$Fj&d_I2&E!(jpOIODo=s6=3S9#%9qHrz8m_Nv4z3S>fFL*QE$}7%a6n zSg*z8!gsm;D!DPu(~6gq*2|z097QE@JUR7om=F#Zg7} zDsp~W_Q@v}M|!<+d?@9nG`yN{;jHAfFpZyvrOP}H^JvUvv-oQUlQqjQm(7wGnd~}? z%dwcS*jz{3vF6j?ihbc#!7e9w{VLY&u;MC~ao7#3Sk_@n%}Iv1@=*y9uVYJLOJHlk z_7JxFum!LIHihRDp7)$IhA(fXCD=%+Z4FdF6^)XGd+lDb?qhKFczujEZ^`O3yB`L8 z{-(aD*(ItwQ}UNqIbSl9pB4oG!W%6J@}eC?QQ4 r^)GUYE+~3|q8BQO6n(9t2NgY}=*5b@PSH!!dbsLuiJGP> delta 980 zcmX9+ZD<@t7=C9z?shhJyR*5=T`sXJJz zsvDxzTSDB zhxZvuPg8mtFCTs3SrB7(8t2i-7d_X|u3Qs>)E2D#W%#2AeVLpiMg06v^K-40>wQy5;S}=BV>#X^QIqhhHqdGWw(%vh94KBxadIj-ox6v^2AI_&!ytIKn zO-Kuf|NE^PBiX;}mybNC7NK)kco$<{0gwwVJUffZD7RajOq8y}jv%@tbViD1fRFsX z^XL%&J$KAmb$Ni;2sgutSO1q7=bF$Rj zkfj?`mOim%X++EqxUvL}{Lbd4CV=_orj8uk-lw1{(4JAk(i_RU^VRBN-XI=<>>eFQ;yEez)29i zJal5z5E|W8OvjLF3TC5PsoatI_yPT0ozVoN$$%OZ-wI#+FfCZeyz<+EmBzbJjBls$ z0mRQ&;=M*|p%pSDjZ)K=ZIWb7FU9MMEBe8R$q2#h{qJ{l38f(suO_4(5mFCTE29%e zU|SU5zi1AP3;O#a(=A(mrC-~Am|R_lfu0+$-<Mex;~!M;hs zXbZ-KV2pZWMn8GNV4=mrW)`Mcm}X%M3o|UtvapqfITp6Du$_hXuy9)tb`<^xLFgk( diff --git a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten.urlalias.css b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten.urlalias.css new file mode 100644 index 0000000..885c983 --- /dev/null +++ b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten.urlalias.css @@ -0,0 +1,28 @@ +.url-alias-wrapper { + position: relative; + display: flex; + margin-bottom: 10px; +} + +.url-alias-iso-code { + padding-right: 10px; + display: block; + width: 60px; + line-height: 30px; + text-align: right; + color: #a2a1a6; + flex: 1 1 60px; +} + +.url-alias-alias { + text-align: right; + width: auto; + font-size: 13px; + color: #a2a1a6; + line-height: 30px; + display: block; +} + +.url-alias-alias-wrapper { + top: 0; +} \ No newline at end of file diff --git a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/package.manifest b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/package.manifest index c6be474..3ab7533 100644 --- a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/package.manifest +++ b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/package.manifest @@ -9,5 +9,8 @@ }], "javascript": [ "~/App_Plugins/OPTEN.UrlAlias/opten.urlAlias.controller.js?v=ASSEMBLY_VERSION" + ], + "css": [ + "~/App_Plugins/OPTEN.UrlAlias/opten.urlalias.css?v=ASSEMBLY_VERSION" ] } \ No newline at end of file diff --git a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/urlAlias.html b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/urlAlias.html index f6b881c..8b810fa 100644 --- a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/urlAlias.html +++ b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/urlAlias.html @@ -3,28 +3,22 @@
-
-
- +
+ {{ language.isoCode }} -
- + - -
+
+ + + + {{urls[urlAlias.getIndex(language.isoCode)].url}} +
diff --git a/src/Opten.Umbraco.Localization.Web.UI/Opten.Umbraco.Localization.Web.UI.csproj b/src/Opten.Umbraco.Localization.Web.UI/Opten.Umbraco.Localization.Web.UI.csproj index dde9fd5..63fe474 100644 --- a/src/Opten.Umbraco.Localization.Web.UI/Opten.Umbraco.Localization.Web.UI.csproj +++ b/src/Opten.Umbraco.Localization.Web.UI/Opten.Umbraco.Localization.Web.UI.csproj @@ -351,6 +351,7 @@ + Designer From b9106a61536666edadf268f9fabbc6dabec2f269 Mon Sep 17 00:00:00 2001 From: Severin d'Heureuse Date: Fri, 7 Jul 2017 15:39:14 +0200 Subject: [PATCH 3/3] extended UrlAlias with autogeneration of alias --- .../App_Data/Umbraco.sdf | Bin 2031616 -> 2031616 bytes .../OPTEN.UrlAlias/opten-generate-url.html | 10 ++ .../opten.urlAlias.controller.js | 144 +++++++----------- .../opten.urlAlias.directive.js | 91 +++++++++++ .../OPTEN.UrlAlias/package.manifest | 3 +- .../App_Plugins/OPTEN.UrlAlias/urlAlias.html | 18 +-- .../Opten.Umbraco.Localization.Web.UI.csproj | 1 + 7 files changed, 165 insertions(+), 102 deletions(-) create mode 100644 src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten-generate-url.html create mode 100644 src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten.urlAlias.directive.js diff --git a/src/Opten.Umbraco.Localization.Web.UI/App_Data/Umbraco.sdf b/src/Opten.Umbraco.Localization.Web.UI/App_Data/Umbraco.sdf index 22175dcc9e1b1638415de0d1650be40d6141ca70..8a539f6ac2ae1f618d6a035f94676dbdc2e9eeef 100644 GIT binary patch delta 18917 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 + {{ alias }} +
+ + +
+
\ No newline at end of file diff --git a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten.urlAlias.controller.js b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten.urlAlias.controller.js index f7b59d7..c8d8582 100644 --- a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten.urlAlias.controller.js +++ b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten.urlAlias.controller.js @@ -6,12 +6,10 @@ urlAliasController.$inject = ["$scope", "$rootScope", "editorState", "languageResource"]; function urlAliasController($scope, $rootScope, editorState, languageResource) { - var vm = this, - errors = []; + var vm = this; vm.loadings = []; - vm.checkAvailability = checkAvailability; vm.getIndex = getIndex; // We set a scope with the value otherwise we have a "empty" JSON saved. @@ -27,12 +25,11 @@ hasLanguages: false }; - $scope.$watchCollection("states", function (oldValue, newValue) { - // Try enable the localizer (set JSON to $scope.model.value) + $scope.$watchCollection("states", function (newValue, oldValue) { enable(newValue); }); - $rootScope.$watchCollection("languages", function (oldValue, newValue) { + $rootScope.$watchCollection("languages", function (newValue, oldValue) { if (newValue && newValue.length && vm.loadings.length < 2) { vm.loadings.push("languages"); @@ -40,6 +37,48 @@ } }); + $scope.$watch('urls', function (newValue, oldValue) { + + var errors = []; + + for (var index = 0; index < newValue.length; index++) { + var segment = newValue[index].url; + newValue[index].hasError = false; + validateValidity(); + + // check if the current url has the same segment + if (editorState.current.urls && editorState.current.urls.length) { + var currentSegment = editorState.current.urls[0].split('/'); + currentSegment = currentSegment[currentSegment.length - 2]; + + setError(newValue[index], segment.toLowerCase() === currentSegment.toLowerCase()); + } + + // Then if other in the same node has the same url + angular.forEach(newValue, function (language, i) { + if (index != i) { + setError(newValue[index], language.url.toLowerCase() === segment.toLowerCase()); + } + }); + + (function (index2) { + languageResource.getUrlAvailability(editorState.current.id, segment, getNodeLevel()) + .then(function (isAvailable) { + setError(newValue[index2], (isAvailable.toString().toLowerCase() == "false")); + }); + }(index)); + + + function setError(language, hasError) { + if (hasError) { + language.hasError = true; + } + validateValidity(); + }; + } + + }, true); + activate(); $scope.$on("formSubmitting", function () { @@ -102,6 +141,14 @@ } }; + function validateValidity() { + var isValid = $scope.urls.filter(function (url) { + return url.hasError; + }).length === 0; + + $scope.urlAliasForm.$setValidity("isValid", isValid); + } + function setLanguages(languages) { // We have to set the json with the languages if never set // or add the new one's but don't delete the old ones @@ -126,10 +173,6 @@ } } - // If the languages are equal we check the availability - angular.forEach(languages, function (language, index) { - checkAvailability(index); - }); }; function getNodeLevel() { @@ -137,17 +180,6 @@ return (editorState.current.path.split(',').length - 1); }; - function getUrlSegment(name, success) { - if (!name || name.length == 0) { - success(""); - } - else { - languageResource.getUrlSegment(name).then(function (segment) { - success(JSON.parse(segment)); //TODO: Why JSON.parse() somewhow angular returns string in extra quotes - }); - } - }; - function getIndex(isoCode) { for (var i = 0; i < $scope.urls.length; i++) { if ($scope.urls[i].isoCode.toLowerCase() == isoCode.toLowerCase()) { @@ -158,76 +190,6 @@ return -1; }; - function checkAvailability(index) { - var name = $scope.urls[index].name; - - errors = []; - - if (name == "" || name.length < 1) { - - // Set the semgent to empty - $scope.urls[index].url = ""; - - // Hide errors because url is empty... - setError(index, false); - } - else if (name != "" && name.length) { - - // Transform name into segment by Umbraco - getUrlSegment(name, function (segment) { - - // Set the semgent - $scope.urls[index].url = segment; - - // Then check if the current url has the same segment - if (editorState.current.urls && editorState.current.urls.length) { - var currentSegment = editorState.current.urls[0].split('/'); - currentSegment = currentSegment[currentSegment.length - 2]; - - setError(index, (segment.toLowerCase() == currentSegment.toLowerCase())); - } - - // Then if other in the same node has the same url - angular.forEach($scope.urls, function (language, i) { - if (index != i && hasError(index) == false) { - - if (setError(index, (language.url.toLowerCase() == $scope.urls[index].url.toLowerCase())) == false) { - - //TODO: This could be slow so maybe load all first and then check it against this? - // or if a node in the same level has the same url - languageResource.getUrlAvailability(editorState.current.id, segment, getNodeLevel()).then(function (isAvailable) { - - setError(index, (isAvailable.toString().toLowerCase() == "false")); - }); - } - } - }); - }); - } - - function setError(index, hasError) { - if (hasError) { - errors.push(index); - } else { - for (var i = 0; i < errors.length; i++) { - if (errors[i] == index) errors.splice(i, 1); - } - } - - $scope.urls[index].hasError = hasError; - $scope.urlAliasForm.$setValidity("isValid", !hasError); - - return hasError; - }; - - function hasError(index) { - for (var i = 0; i < errors.length; i++) { - if (errors[i] == index) return true; - } - return false; - }; - }; - function hasAnyLocalized() { var anyLocalized = false; for (var i = 0; i < $scope.urls.length; i++) { diff --git a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten.urlAlias.directive.js b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten.urlAlias.directive.js new file mode 100644 index 0000000..71d9756 --- /dev/null +++ b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten.urlAlias.directive.js @@ -0,0 +1,91 @@ +; (function () { + 'use strict'; + + + angular.module("umbraco.directives").directive('optenGenerateUrl', optenGenerateUrl); + + + function optenGenerateUrl($timeout, languageResource) { + return { + restrict: 'E', + templateUrl: '/App_Plugins/OPTEN.UrlAlias/opten-generate-url.html', + replace: true, + scope: { + alias: '=', + aliasFrom: '=', + enableLock: '=?', + serverValidationField: '@' + }, + link: function (scope, element, attrs, ctrl) { + + var eventBindings = []; + var bindWatcher = true; + var generateAliasTimeout = ""; + var updateAlias = false; + + scope.locked = true; + scope.placeholderText = "Enter alias..."; + + function generateAlias(value) { + + if (generateAliasTimeout) { + $timeout.cancel(generateAliasTimeout); + } + + if (value !== undefined && value !== "" && value !== null) { + + scope.alias = ""; + scope.placeholderText = "Generating Alias..."; + + generateAliasTimeout = $timeout(function () { + updateAlias = true; + languageResource.getUrlSegment(value).then(function (segment) { + if (updateAlias) { + scope.alias = JSON.parse(segment); //TODO: Why JSON.parse() somewhow angular returns string in extra quotes + } + + }); + }, 500); + + } else { + updateAlias = true; + scope.alias = ""; + scope.placeholderText = "Enter alias..."; + } + + } + + // if alias gets unlocked - stop watching alias + eventBindings.push(scope.$watch('locked', function (newValue, oldValue) { + if (newValue === false) { + bindWatcher = false; + } else { + generateAlias(scope.alias); + } + })); + + // validate custom entered alias + eventBindings.push(scope.$watch('alias', function (newValue, oldValue) { + + if (scope.alias === "" && bindWatcher === true || scope.alias === null && bindWatcher === true) { + // add watcher + eventBindings.push(scope.$watch('aliasFrom', function (newValue, oldValue) { + if (bindWatcher) { + generateAlias(newValue); + } + })); + } + })); + + // clean up + scope.$on('$destroy', function () { + // unbind watchers + for (var e in eventBindings) { + eventBindings[e](); + } + }); + + } + }; + } +}()); \ No newline at end of file diff --git a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/package.manifest b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/package.manifest index 3ab7533..03f5172 100644 --- a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/package.manifest +++ b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/package.manifest @@ -8,7 +8,8 @@ } }], "javascript": [ - "~/App_Plugins/OPTEN.UrlAlias/opten.urlAlias.controller.js?v=ASSEMBLY_VERSION" + "~/App_Plugins/OPTEN.UrlAlias/opten.urlAlias.controller.js?v=ASSEMBLY_VERSION", + "~/App_Plugins/OPTEN.UrlAlias/opten.urlAlias.directive.js?v=ASSEMBLY_VERSION" ], "css": [ "~/App_Plugins/OPTEN.UrlAlias/opten.urlalias.css?v=ASSEMBLY_VERSION" diff --git a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/urlAlias.html b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/urlAlias.html index 8b810fa..d55f414 100644 --- a/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/urlAlias.html +++ b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/urlAlias.html @@ -1,25 +1,23 @@ 
+
- {{ language.isoCode }} - + {{ language.nativeName.split("(")[0].trim() }} -
- - - - {{urls[urlAlias.getIndex(language.isoCode)].url}} - -
+
diff --git a/src/Opten.Umbraco.Localization.Web.UI/Opten.Umbraco.Localization.Web.UI.csproj b/src/Opten.Umbraco.Localization.Web.UI/Opten.Umbraco.Localization.Web.UI.csproj index 63fe474..4116602 100644 --- a/src/Opten.Umbraco.Localization.Web.UI/Opten.Umbraco.Localization.Web.UI.csproj +++ b/src/Opten.Umbraco.Localization.Web.UI/Opten.Umbraco.Localization.Web.UI.csproj @@ -351,6 +351,7 @@ +