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 6db9dfa..8a539f6 100644 Binary files a/src/Opten.Umbraco.Localization.Web.UI/App_Data/Umbraco.sdf and b/src/Opten.Umbraco.Localization.Web.UI/App_Data/Umbraco.sdf differ 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 8e51d97..053485d 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/App_Plugins/OPTEN.UrlAlias/opten-generate-url.html b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten-generate-url.html new file mode 100644 index 0000000..70ff218 --- /dev/null +++ b/src/Opten.Umbraco.Localization.Web.UI/App_Plugins/OPTEN.UrlAlias/opten-generate-url.html @@ -0,0 +1,10 @@ +
+ {{ 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/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..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,6 +8,10 @@ } }], "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" ] } \ 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..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,31 +1,23 @@ 
+
-
-
- +
+ {{ language.nativeName.split("(")[0].trim() }} + -
- - - -
-
+
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..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,8 @@ + + Designer