From f5ceab82f1d4a19352aef73ad838ff9dbc15155e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pjengaard?= Date: Fri, 12 Feb 2016 14:00:45 +0100 Subject: [PATCH 001/389] Update da.xml Added translation for contentPublishedFailedAwaitingRelease --- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 446796dd88..841faab68f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -837,6 +837,7 @@ Mange hilsner fra Umbraco robotten Annulleret Handlingen blev annulleret af et 3. part tilføjelsesprogram Udgivelsen blev standset af et 3. parts modul + Udgivelsen kunne ikke udgives da publiceringsdato er sat Property type eksisterer allerede Egenskabstype oprettet DataType: %1%]]> From c10534a064d8fd2806f3c913ef0f2a1635665dad Mon Sep 17 00:00:00 2001 From: Alexander Bryukhov Date: Thu, 14 Apr 2016 23:31:24 +0600 Subject: [PATCH 002/389] Update UI language ru.xml Missing keys & some typos found --- src/Umbraco.Web.UI/umbraco/config/lang/ru.xml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml index fbf0e0adf2..2fd54be962 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml @@ -215,8 +215,10 @@ ДА, удалить - была перемещена внутрь + перемещены внутрь + скопированы внутрь Выбрать папку для перемещения + Выбрать папку для копирования в структуре дерева Все типы документов @@ -328,7 +330,7 @@ Провайдеры аутентификации Подробное сообщение об ошибке Трассировка стека - Inner Exception + Внутренняя ошибка Связать Разорвать связь @@ -725,6 +727,7 @@ Нажмите, чтобы загрузить + Невозможна загрузка этого файла, этот тип файлов не разрешен для загрузки Перетащите файлы сюда... Ссылка на файл или нажмите сюда, чтобы выбрать файлы @@ -1219,6 +1222,6 @@ Валидация числового значения Валидация по формату Url ...или указать свои правила валидации - Обязательно к заполению + Обязательно к заполнению From 89cd2f50d7beefb395c523d5fec40aca7a35b6c3 Mon Sep 17 00:00:00 2001 From: Alexander Bryukhov Date: Sun, 24 Apr 2016 23:09:46 +0600 Subject: [PATCH 003/389] Update UI language ru.xml New area and keys for unsaved changes dialog localization (PR #1234) New keys for list view localization PR #1235 --- src/Umbraco.Web.UI/umbraco/config/lang/ru.xml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml index 2fd54be962..1db34c7b7b 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml @@ -66,6 +66,7 @@ Полужирный + Снять выбор Уменьшить отступ Вставить поле формы Сгенерировать модели @@ -91,9 +92,10 @@ Сохранить список Выбрать Выбрать текущую папку + выбранные Предварительный просмотр Предварительный просмотр запрещен, так как документу не сопоставлен шаблон - Сделать что-нибудь еще + Другие действия Выбрать стиль Показать стили Вставить таблицу @@ -185,7 +187,7 @@ Композиции Вы не добавили ни одной вкладки Добавить вкладку - Добавитьеще вкладку + Добавить новую вкладку Унаследован от Добавить свойство Обязательная метка @@ -245,7 +247,7 @@ "Типы документов".]]> "Типы медиа-материалов".]]> Выберите тип и заголовок - Типу документа не сопоставлен шаблон + Тип документа без сопоставленного шаблона Обзор сайта @@ -853,6 +855,12 @@ Метка... Укажите описание... + + Остаться + Отменить изменения + Есть несохраненные изменения + Вы уверены, что хотите уйти с этой страницы? - на ней имеются несохраненные изменения + Расширенный: Защита на основе ролей (групп) с использованием групп участников Umbraco.]]> From 02ffaa9ee84ff125a031056874b052b9ad361dde Mon Sep 17 00:00:00 2001 From: Dennis Aaen Date: Tue, 10 May 2016 20:33:03 +0200 Subject: [PATCH 004/389] Fixed issue: U4-8435 so the link is correct in settingsdashboardintro dashboard --- .../src/views/dashboard/settings/settingsdashboardintro.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html index fa9849022c..3a45776872 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardintro.html @@ -9,6 +9,6 @@
  • Download the Editors Manual for details on working with the Umbraco UI
  • Ask a question in the Community Forum
  • Watch our tutorial videos (some are free, some require a subscription)
  • -
  • Find out about our productivity boosting tools and commercial support
  • +
  • Find out about our productivity boosting tools and commercial support
  • Find out about real-life training and certification opportunities
  • From 5fab1ca207db4d82ac064b22741ad16f74a4809a Mon Sep 17 00:00:00 2001 From: bjarnef Date: Sun, 22 May 2016 13:46:48 +0200 Subject: [PATCH 005/389] Localize move and copy overlay titles opened from listview actions. Prepare localization of bulk messages. --- .../src/views/common/overlays/copy/copy.html | 2 +- .../listview/listview.controller.js | 95 ++++++++++++++++--- src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml | 28 ++++++ src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 59 +++++++++++- src/Umbraco.Web.UI/umbraco/config/lang/de.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 28 ++++++ .../umbraco/config/lang/en_us.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/es.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/fr.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/he.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/it.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/ja.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/ko.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/nl.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/pl.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/pt.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/ru.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/sv.xml | 28 ++++++ src/Umbraco.Web.UI/umbraco/config/lang/zh.xml | 28 ++++++ 20 files changed, 615 insertions(+), 17 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.html index adb57b4af0..c0e22c4d27 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/copy/copy.html @@ -31,8 +31,8 @@
    - diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 518c27d110..2ea325b451 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -208,7 +208,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie $timeout(function () { $scope.bulkStatus = ""; $scope.actionInProgress = false; - }, 500); + }, 0); if (reload === true) { $scope.reloadView($scope.contentId); @@ -220,7 +220,9 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie } } else if (successMsg) { - notificationsService.success("Done", successMsg); + localizationService.localize("bulk_done").then(function (v) { + notificationsService.success(v, successMsg); + }); } } @@ -368,28 +370,67 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie $scope.delete = function () { applySelected( function (selected, index) { return deleteItemCallback(getIdCallback(selected[index])); }, - function (count, total) { return "Deleted " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); }, - function (total) { return "Deleted " + total + " item" + (total > 1 ? "s" : ""); }, + function (count, total) { + return "Deleted " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); + //var key = (total === 1 ? "bulk_deletedItemOfItem" : "bulk_deletedItemOfItems"); + //localizationService.localize(key, [count, total]).then(function (value) { + // return value; + //}); + }, + function (total) { + return "Deleted " + total + " item" + (total > 1 ? "s" : ""); + //var key = (total === 1 ? "bulk_deletedItem" : "bulk_deletedItems"); + //localizationService.localize(key, [total]).then(function (value) { + // return value; + //}); + }, + //localizationService.localize("defaultdialogs_confirmdelete") + "?" "Sure you want to delete?"); }; - $scope.publish = function () { + $scope.publish = function () { applySelected( function (selected, index) { return contentResource.publishById(getIdCallback(selected[index])); }, - function (count, total) { return "Published " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); }, - function (total) { return "Published " + total + " item" + (total > 1 ? "s" : ""); }); + function (count, total) { + return "Published " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); + //var key = (total === 1 ? "bulk_publishedItemOfItem" : "bulk_publishedItemOfItems"); + //localizationService.localize(key, [count, total]).then(function (value) { + // console.log(key, value); + // return value; + //}); + }, + function (total) { + return "Published " + total + " item" + (total > 1 ? "s" : ""); + //var key = (total === 1 ? "bulk_publishedItem" : "bulk_publishedItems"); + //localizationService.localize(key, [total]).then(function (value) { + // console.log(key, value); + // return value; + //}); + }); }; $scope.unpublish = function () { applySelected( function (selected, index) { return contentResource.unPublish(getIdCallback(selected[index])); }, - function (count, total) { return "Unpublished " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); }, - function (total) { return "Unpublished " + total + " item" + (total > 1 ? "s" : ""); }); + function (count, total) { + return "Unpublished " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); + //var key = (total === 1 ? "bulk_unpublishedItemOfItem" : "bulk_unpublishedItemOfItems"); + //localizationService.localize(key, [count, total]).then(function (value) { + // return value; + //}); + }, + function (total) { + return "Unpublished " + total + " item" + (total > 1 ? "s" : ""); + //var key = (total === 1 ? "bulk_unpublishedItem" : "bulk_unpublishedItems"); + //localizationService.localize(key, [total]).then(function (value) { + // return value; + //}); + }); }; $scope.move = function () { $scope.moveDialog = {}; - $scope.moveDialog.title = "Move"; + $scope.moveDialog.title = localizationService.localize("general_move"); $scope.moveDialog.section = $scope.entityType; $scope.moveDialog.currentNode = $scope.contentId; $scope.moveDialog.view = "move"; @@ -416,13 +457,25 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie applySelected( function (selected, index) { return contentResource.move({ parentId: target.id, id: getIdCallback(selected[index]) }); }, - function (count, total) { return "Moved " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); }, - function (total) { return "Moved " + total + " item" + (total > 1 ? "s" : ""); }); + function (count, total) { + return "Moved " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); + //var key = (total === 1 ? "bulk_movedItemOfItem" : "bulk_movedItemOfItems"); + //localizationService.localize(key, [count, total]).then(function (value) { + // return value; + //}); + }, + function (total) { + return "Moved " + total + " item" + (total > 1 ? "s" : ""); + //var key = (total === 1 ? "bulk_movedItem" : "bulk_movedItems"); + //localizationService.localize(key, [total]).then(function (value) { + // return value; + //}); + }); } $scope.copy = function () { $scope.copyDialog = {}; - $scope.copyDialog.title = "Copy"; + $scope.copyDialog.title = localizationService.localize("general_copy"); $scope.copyDialog.section = $scope.entityType; $scope.copyDialog.currentNode = $scope.contentId; $scope.copyDialog.view = "copy"; @@ -447,8 +500,20 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie function performCopy(target, relateToOriginal) { applySelected( function (selected, index) { return contentResource.copy({ parentId: target.id, id: getIdCallback(selected[index]), relateToOriginal: relateToOriginal }); }, - function (count, total) { return "Copied " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); }, - function (total) { return "Copied " + total + " item" + (total > 1 ? "s" : ""); }); + function (count, total) { + return "Copied " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); + //var key = (total === 1 ? "bulk_copiedItemOfItem" : "bulk_copiedItemOfItems"); + //localizationService.localize(key, [count, total]).then(function (value) { + // return value; + //}); + }, + function (total) { + return "Copied " + total + " item" + (total > 1 ? "s" : ""); + //var key = (total === 1 ? "bulk_copiedItem" : "bulk_copiedItems"); + //localizationService.localize(key, [total]).then(function (value) { + // return value; + //}); + }); } function getCustomPropertyValue(alias, properties) { diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml index 1ae28daafe..50d5c0d911 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml @@ -185,6 +185,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + Název Spravovat názvy hostitelů diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml index 275267e98c..f3701c706a 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml @@ -191,6 +191,34 @@ Discard changes You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items Navn på lokal link diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 0280a46e05..d68e21daf7 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -198,6 +198,34 @@ Du har ikke-gemte ændringer Er du sikker på du vil navigere væk fra denne side? - du har ikke-gemte ændringer + + Færdig + + Slettede %0% element + Slettede %0% elementer + Slettede %0% ud af %1% element + Slettede %0% ud af %1% elementer + + Udgav %0% element + Udgav %0% elementer + Udgav %0% ud af %1% element + Udgav %0% ud af %1% elementer + + Fjernede %0% element fra udgivelse + Fjernede %0% elementer fra udgivelse + Fjernede %0% ud af %1% element fra udgivelse + Fjernede %0% ud af %1% elementer fra udgivelse + + Flyttede %0% element + Flyttede %0% elementer + Flyttede %0% ud af %1% element + Flyttede %0% ud af %1% elementer + + Kopierede %0% element + Kopierede %0% elementer + Kopierede %0% ud af %1% element + Kopierede %0% ud af %1% elementer + Navn på lokalt link Rediger domæner @@ -242,7 +270,36 @@ Vælg et placeholder id fra listen herunder. Du kan kun vælge id'er fra den nuværende masterskabelon.]]> Klik på billedet for at se den fulde størrelse Vælg - Se Cache Item + Se cache element + Opret mappe... + + Relatér til original + + Link til side + + Åbner det linket dokument i et nyt vindue eller fane + Åbner det linket dokument i fuld visning af vinduet + Åbner det linket dokument i "parent frame" + + Link til medie + + Vælg medie + Vælg ikon + Vælg item + Vælg link + Vælg makro + Vælg indhold + Vælg medlem + Vælg medlemsgruppe + + Der er ingen parametre for denne makro + + Link dit + Fjern link fra dit + + konto + + Vælg editor Rediger de forskellige sprogversioner for ordbogselementet '%0%' herunder. Du tilføjer flere sprog under 'sprog' i menuen til venstre diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/de.xml b/src/Umbraco.Web.UI/umbraco/config/lang/de.xml index 3b4c1ee5ab..d7f2c320b9 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/de.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/de.xml @@ -192,6 +192,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + Name Hostnamen verwalten diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 84dedbb364..2353929a4c 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -205,6 +205,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + Name Manage hostnames 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 ec41299b25..528830d86a 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -207,6 +207,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + Name Manage hostnames diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/es.xml b/src/Umbraco.Web.UI/umbraco/config/lang/es.xml index 80e39fe219..09af60593f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/es.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/es.xml @@ -191,6 +191,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + Nombre Administrar dominios diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml index 59021956a5..4affcf02f0 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/fr.xml @@ -187,6 +187,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + Name Gérer les noms d'hôtes diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/he.xml b/src/Umbraco.Web.UI/umbraco/config/lang/he.xml index aa9c2112e2..02d8311c14 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/he.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/he.xml @@ -137,6 +137,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + שם ניהול שם מתחם diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/it.xml b/src/Umbraco.Web.UI/umbraco/config/lang/it.xml index 9c0682eba0..8be7261a8d 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/it.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/it.xml @@ -133,6 +133,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + Nome Gestione alias Hostnames diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml index 6acfd997e1..beced6eb79 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ja.xml @@ -200,6 +200,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + 名前 ドメインの割り当て diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml index c7178e312b..b72503e987 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ko.xml @@ -131,6 +131,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + 이름 호스트네임 관리 diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml index 7d1a4cc2d3..2955ce716a 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/nl.xml @@ -194,6 +194,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + Naam Beheer domeinnamen diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml index c2c8b4d073..97dcf9ef38 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/pl.xml @@ -131,6 +131,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + Nazwa Zarządzaj nazwami hostów diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml b/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml index 016f7c2a19..e67a4c4b15 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/pt.xml @@ -131,6 +131,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + Nome Gerenciar hostnames diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml index e162dbaead..84b87b872f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/ru.xml @@ -260,6 +260,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + Название Управление доменами diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml index ea1f0c9ef0..10e882f260 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/sv.xml @@ -187,6 +187,34 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items + Namn Hantera domännamn diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml index 372bc480cc..b8c933c101 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/zh.xml @@ -171,6 +171,34 @@ Discard changes You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes + + + Done + + Deleted %0% item + Deleted %0% items + Deleted %0% out of %1% item + Deleted %0% out of %1% items + + Published %0% item + Published %0% items + Published %0% out of %1% item + Published %0% out of %1% items + + Unpublished %0% item + Unpublished %0% items + Unpublished %0% out of %1% item + Unpublished %0% out of %1% items + + Moved %0% item + Moved %0% items + Moved %0% out of %1% item + Moved %0% out of %1% items + + Copied %0% item + Copied %0% items + Copied %0% out of %1% item + Copied %0% out of %1% items 锚点名称 From e2aee08959874ed1647caf3585e321d86a2b7b64 Mon Sep 17 00:00:00 2001 From: bjarnef Date: Sun, 22 May 2016 13:52:02 +0200 Subject: [PATCH 006/389] Change back to original timeout value. --- .../src/views/propertyeditors/listview/listview.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 2ea325b451..5f3810723a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -208,7 +208,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie $timeout(function () { $scope.bulkStatus = ""; $scope.actionInProgress = false; - }, 0); + }, 500); if (reload === true) { $scope.reloadView($scope.contentId); From e39f818389d306852755336c3f42d5ed2f19449a Mon Sep 17 00:00:00 2001 From: bjarnef Date: Sun, 22 May 2016 13:53:43 +0200 Subject: [PATCH 007/389] Fix indent --- src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml index f3701c706a..602f66ddf4 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml @@ -192,7 +192,7 @@ You have unsaved changes Are you sure you want to navigate away from this page? - you have unsaved changes - + Done Deleted %0% item From 45ff11b884573c2925cb246c2d4ad37fdd543a2a Mon Sep 17 00:00:00 2001 From: bjarnef Date: Sun, 22 May 2016 14:26:37 +0200 Subject: [PATCH 008/389] Change "siten" to "sitet" --- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index d68e21daf7..4cb2794976 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -28,7 +28,7 @@ Udgiv Fortryd udgivelse Genindlæs elementer - Genudgiv hele siten + Genudgiv hele sitet Gendan Rettigheder Fortryd ændringer @@ -262,7 +262,7 @@ Fjern makro Obligatorisk Sitet er genindekseret - Siten er nu genudgivet + Sitet er nu genudgivet Websitets cache vil blive genopfrisket. Alt udgivet indhold vil blive opdateret, mens upubliceret indhold vil forblive upubliceret. Antal kolonner Antal rækker From 06077820287171923ed3ab46befa6b66692d44d5 Mon Sep 17 00:00:00 2001 From: bjarnef Date: Sun, 22 May 2016 21:08:21 +0200 Subject: [PATCH 009/389] Added localization for the new password reset stuff. --- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 21 ++++++++++++------- 1 file changed, 14 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 4cb2794976..33e9e4a4d2 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -429,7 +429,7 @@ Layout Henter Låst - Login + Log ind Log af Log ud Makro @@ -606,7 +606,6 @@ Forny for at gemme dine ændringer - Så er det søndag! Smil, det er mandag! Hurra, det er tirsdag! @@ -614,13 +613,21 @@ Glædelig torsdag! Endelig fredag! Glædelig lørdag - - + Log ind nedenfor indtast brugernavn og kodeord Din session er udløbet - - © 2001 - %0%
    umbraco.com

    ]]>
    + Glemt adgangskode? + En e-mail vil blive sendt til den angivne adresse med et link til at nulstille din adgangskode + En e-mail med instruktioner for nulstilling af adgangskoden vil blive sendt til den angivne adresse, hvis det matcher vores optegnelser + Tilbage til login formular + Angiv en ny adgangskode + Din adgangskode er blevet opdateret + Det link, du har klikket på, er ugyldigt eller udløbet + Umbraco: Nulstil adgangskode + + Dit brugernavn til at logge på Umbraco backoffice er: %0%

    Klik her for at nulstille din adgangskode eller kopier/indsæt denne URL i din browser:

    %1%

    ]]> +
    Skrivebord @@ -637,7 +644,7 @@ Intet element valgt, vælg et element i listen ovenfor før der klikkes 'fortsæt' Det nuværende element kan ikke lægges under denne pga. sin type Det nuværende element kan ikke ligge under en af dens undersider - Dette element må ikke findes på rodniveau + Dette element må ikke findes på rodniveau Denne handling er ikke tilladt fordi du ikke har de fornødne rettigheder på et eller flere af under-dokumenterne Relater det kopierede element til originalen From 695c69af7fbbbe49f51611ab90b136cc272567a2 Mon Sep 17 00:00:00 2001 From: bjarnef Date: Sun, 22 May 2016 21:11:51 +0200 Subject: [PATCH 010/389] Updated indent --- src/Umbraco.Web.UI/umbraco/config/lang/da.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml index 33e9e4a4d2..d4b70939ae 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/da.xml @@ -644,9 +644,9 @@ Intet element valgt, vælg et element i listen ovenfor før der klikkes 'fortsæt' Det nuværende element kan ikke lægges under denne pga. sin type Det nuværende element kan ikke ligge under en af dens undersider - Dette element må ikke findes på rodniveau + Dette element må ikke findes på rodniveau Denne handling er ikke tilladt fordi du ikke har de fornødne rettigheder på et eller flere af under-dokumenterne - Relater det kopierede element til originalen + Relater det kopierede element til originalen Rediger dine notificeringer for %0% From b69580967e8b247893782541107305e28d071c63 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 24 May 2016 16:12:54 +0200 Subject: [PATCH 011/389] Updates interceptor files with correct module usage - creates new request interceptor to append a custom Time-Offset header --- .../src/common/security/_module.js | 8 +- .../src/common/security/requestinterceptor.js | 26 +++ .../src/common/security/retryqueue.js | 1 + ...{interceptor.js => securityinterceptor.js} | 188 +++++++++--------- 4 files changed, 125 insertions(+), 98 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/security/requestinterceptor.js rename src/Umbraco.Web.UI.Client/src/common/security/{interceptor.js => securityinterceptor.js} (96%) diff --git a/src/Umbraco.Web.UI.Client/src/common/security/_module.js b/src/Umbraco.Web.UI.Client/src/common/security/_module.js index 15a7663d9b..c8289c754e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/security/_module.js +++ b/src/Umbraco.Web.UI.Client/src/common/security/_module.js @@ -1,4 +1,4 @@ -// Based loosely around work by Witold Szczerba - https://github.com/witoldsz/angular-http-auth -angular.module('umbraco.security', [ - 'umbraco.security.retryQueue', - 'umbraco.security.interceptor']); \ No newline at end of file +//TODO: This is silly and unecessary to have a separate module for this +angular.module('umbraco.security.retryQueue', []); +angular.module('umbraco.security.interceptor', ['umbraco.security.retryQueue']); +angular.module('umbraco.security', ['umbraco.security.retryQueue', 'umbraco.security.interceptor']); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/security/requestinterceptor.js b/src/Umbraco.Web.UI.Client/src/common/security/requestinterceptor.js new file mode 100644 index 0000000000..8557ce1435 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/security/requestinterceptor.js @@ -0,0 +1,26 @@ +angular.module('umbraco.security.interceptor').factory("requestInterceptor", + ['$q', 'requestInterceptorFilter', function ($q, requestInterceptorFilter) { + var requestInterceptor = { + request: function (config) { + + var filtered = _.find(requestInterceptorFilter(), function (val) { + return config.url.indexOf(val) > 0; + }); + if (filtered) { + return config; + } + + config.headers["Time-Offset"] = (new Date().getTimezoneOffset()); + return config; + } + }; + + return requestInterceptor; + } + ]) + // We have to add the interceptor to the queue as a string because the interceptor depends upon service instances that are not available in the config block. + .config([ + '$httpProvider', function($httpProvider) { + $httpProvider.interceptors.push('requestInterceptor'); + } + ]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/security/retryqueue.js b/src/Umbraco.Web.UI.Client/src/common/security/retryqueue.js index e971719398..28d91dd610 100644 --- a/src/Umbraco.Web.UI.Client/src/common/security/retryqueue.js +++ b/src/Umbraco.Web.UI.Client/src/common/security/retryqueue.js @@ -1,3 +1,4 @@ +//TODO: This is silly and unecessary to have a separate module for this angular.module('umbraco.security.retryQueue', []) // This is a generic retry queue for security failures. Each item is expected to expose two functions: retry and cancel. diff --git a/src/Umbraco.Web.UI.Client/src/common/security/interceptor.js b/src/Umbraco.Web.UI.Client/src/common/security/securityinterceptor.js similarity index 96% rename from src/Umbraco.Web.UI.Client/src/common/security/interceptor.js rename to src/Umbraco.Web.UI.Client/src/common/security/securityinterceptor.js index 2b757707ba..b80754ef67 100644 --- a/src/Umbraco.Web.UI.Client/src/common/security/interceptor.js +++ b/src/Umbraco.Web.UI.Client/src/common/security/securityinterceptor.js @@ -1,95 +1,95 @@ -angular.module('umbraco.security.interceptor', ['umbraco.security.retryQueue']) - // This http interceptor listens for authentication successes and failures - .factory('securityInterceptor', ['$injector', 'securityRetryQueue', 'notificationsService', 'requestInterceptorFilter', function ($injector, queue, notifications, requestInterceptorFilter) { - return function(promise) { - - return promise.then( - function(originalResponse) { - // Intercept successful requests - - //Here we'll check if our custom header is in the response which indicates how many seconds the user's session has before it - //expires. Then we'll update the user in the user service accordingly. - var headers = originalResponse.headers(); - if (headers["x-umb-user-seconds"]) { - // We must use $injector to get the $http service to prevent circular dependency - var userService = $injector.get('userService'); - userService.setUserTimeout(headers["x-umb-user-seconds"]); - } - - return promise; - }, function(originalResponse) { - // Intercept failed requests - - //Here we'll check if we should ignore the error, this will be based on an original header set - var headers = originalResponse.config ? originalResponse.config.headers : {}; - if (headers["x-umb-ignore-error"] === "ignore") { - //exit/ignore - return promise; - } - var filtered = _.find(requestInterceptorFilter(), function(val) { - return originalResponse.config.url.indexOf(val) > 0; - }); - if (filtered) { - return promise; - } - - //A 401 means that the user is not logged in - if (originalResponse.status === 401) { - - // The request bounced because it was not authorized - add a new request to the retry queue - promise = queue.pushRetryFn('unauthorized-server', function retryRequest() { - // We must use $injector to get the $http service to prevent circular dependency - return $injector.get('$http')(originalResponse.config); - }); - } - else if (originalResponse.status === 404) { - - //a 404 indicates that the request was not found - this could be due to a non existing url, or it could - //be due to accessing a url with a parameter that doesn't exist, either way we should notifiy the user about it - - var errMsg = "The URL returned a 404 (not found):
    " + originalResponse.config.url.split('?')[0] + ""; - if (originalResponse.data && originalResponse.data.ExceptionMessage) { - errMsg += "
    with error:
    " + originalResponse.data.ExceptionMessage + ""; - } - if (originalResponse.config.data) { - errMsg += "
    with data:
    " + angular.toJson(originalResponse.config.data) + "
    Contact your administrator for information."; - } - - notifications.error( - "Request error", - errMsg); - - } - else if (originalResponse.status === 403) { - //if the status was a 403 it means the user didn't have permission to do what the request was trying to do. - //How do we deal with this now, need to tell the user somehow that they don't have permission to do the thing that was - //requested. We can either deal with this globally here, or we can deal with it globally for individual requests on the umbRequestHelper, - // or completely custom for services calling resources. - - //http://issues.umbraco.org/issue/U4-2749 - - //It was decided to just put these messages into the normal status messages. - - var msg = "Unauthorized access to URL:
    " + originalResponse.config.url.split('?')[0] + ""; - if (originalResponse.config.data) { - msg += "
    with data:
    " + angular.toJson(originalResponse.config.data) + "
    Contact your administrator for information."; - } - - notifications.error( - "Authorization error", - msg); - } - - return promise; - }); - }; - }]) - - .value('requestInterceptorFilter', function() { - return ["www.gravatar.com"]; - }) - - // We have to add the interceptor to the queue as a string because the interceptor depends upon service instances that are not available in the config block. - .config(['$httpProvider', function ($httpProvider) { - $httpProvider.responseInterceptors.push('securityInterceptor'); +angular.module('umbraco.security.interceptor') + // This http interceptor listens for authentication successes and failures + .factory('securityInterceptor', ['$injector', 'securityRetryQueue', 'notificationsService', 'requestInterceptorFilter', function ($injector, queue, notifications, requestInterceptorFilter) { + return function(promise) { + + return promise.then( + function(originalResponse) { + // Intercept successful requests + + //Here we'll check if our custom header is in the response which indicates how many seconds the user's session has before it + //expires. Then we'll update the user in the user service accordingly. + var headers = originalResponse.headers(); + if (headers["x-umb-user-seconds"]) { + // We must use $injector to get the $http service to prevent circular dependency + var userService = $injector.get('userService'); + userService.setUserTimeout(headers["x-umb-user-seconds"]); + } + + return promise; + }, function(originalResponse) { + // Intercept failed requests + + //Here we'll check if we should ignore the error, this will be based on an original header set + var headers = originalResponse.config ? originalResponse.config.headers : {}; + if (headers["x-umb-ignore-error"] === "ignore") { + //exit/ignore + return promise; + } + var filtered = _.find(requestInterceptorFilter(), function(val) { + return originalResponse.config.url.indexOf(val) > 0; + }); + if (filtered) { + return promise; + } + + //A 401 means that the user is not logged in + if (originalResponse.status === 401) { + + // The request bounced because it was not authorized - add a new request to the retry queue + promise = queue.pushRetryFn('unauthorized-server', function retryRequest() { + // We must use $injector to get the $http service to prevent circular dependency + return $injector.get('$http')(originalResponse.config); + }); + } + else if (originalResponse.status === 404) { + + //a 404 indicates that the request was not found - this could be due to a non existing url, or it could + //be due to accessing a url with a parameter that doesn't exist, either way we should notifiy the user about it + + var errMsg = "The URL returned a 404 (not found):
    " + originalResponse.config.url.split('?')[0] + ""; + if (originalResponse.data && originalResponse.data.ExceptionMessage) { + errMsg += "
    with error:
    " + originalResponse.data.ExceptionMessage + ""; + } + if (originalResponse.config.data) { + errMsg += "
    with data:
    " + angular.toJson(originalResponse.config.data) + "
    Contact your administrator for information."; + } + + notifications.error( + "Request error", + errMsg); + + } + else if (originalResponse.status === 403) { + //if the status was a 403 it means the user didn't have permission to do what the request was trying to do. + //How do we deal with this now, need to tell the user somehow that they don't have permission to do the thing that was + //requested. We can either deal with this globally here, or we can deal with it globally for individual requests on the umbRequestHelper, + // or completely custom for services calling resources. + + //http://issues.umbraco.org/issue/U4-2749 + + //It was decided to just put these messages into the normal status messages. + + var msg = "Unauthorized access to URL:
    " + originalResponse.config.url.split('?')[0] + ""; + if (originalResponse.config.data) { + msg += "
    with data:
    " + angular.toJson(originalResponse.config.data) + "
    Contact your administrator for information."; + } + + notifications.error( + "Authorization error", + msg); + } + + return promise; + }); + }; + }]) + + .value('requestInterceptorFilter', function() { + return ["www.gravatar.com"]; + }) + + // We have to add the interceptor to the queue as a string because the interceptor depends upon service instances that are not available in the config block. + .config(['$httpProvider', function ($httpProvider) { + $httpProvider.responseInterceptors.push('securityInterceptor'); }]); \ No newline at end of file From 1fc17e6f46db1c6f4114195e2558ceeb591e8dfa Mon Sep 17 00:00:00 2001 From: Jeroen Breuer Date: Tue, 24 May 2016 17:04:39 +0200 Subject: [PATCH 012/389] Fix for U4-8510 --- .../ValueConverters/MultipleTextStringValueConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs index 39bcf85b12..a3b12a6688 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs @@ -28,7 +28,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters // // - var sourceString = source.ToString(); + var sourceString = source != null ? source.ToString() : null; if (string.IsNullOrWhiteSpace(sourceString)) return Enumerable.Empty(); //SD: I have no idea why this logic is here, I'm pretty sure we've never saved the multiple txt string From 2504586c267c114815f478a9fcf287f2eda65047 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 25 May 2016 09:43:31 +0200 Subject: [PATCH 013/389] removes initial idea of performing conversion on the server since this would be a breaking change, instead we can easily do this on the client side - and this works much better. Have added pre-values to the date/time picker to be able to enable offset times. This is enabled for the publish-at pickers since those must be offset. When the datetime is offset, it shows the server time in small text underneath the picker. Have added js unit tests for the date conversions. Have updated the datepicker controller to set the model date in a single place/method so it's consistent. --- .../src/common/security/requestinterceptor.js | 26 ------- .../src/common/services/util.service.js | 38 ++++++++++ .../datepicker/datepicker.controller.js | 76 ++++++++++++------- .../datepicker/datepicker.html | 3 + .../test/config/karma.conf.js | 1 + .../unit/common/services/date-helper.spec.js | 53 +++++++++++++ .../Editors/BackOfficeController.cs | 4 + .../Editors/ContentControllerBase.cs | 5 +- .../Models/Mapping/ContentModelMapper.cs | 12 ++- .../PropertyEditors/DateTimePreValueEditor.cs | 10 +++ .../PropertyEditors/DateTimePropertyEditor.cs | 13 +++- src/Umbraco.Web/Umbraco.Web.csproj | 1 + .../WebApi/Binders/ContentItemBaseBinder.cs | 12 ++- 13 files changed, 190 insertions(+), 64 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/common/security/requestinterceptor.js create mode 100644 src/Umbraco.Web.UI.Client/test/unit/common/services/date-helper.spec.js create mode 100644 src/Umbraco.Web/PropertyEditors/DateTimePreValueEditor.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/security/requestinterceptor.js b/src/Umbraco.Web.UI.Client/src/common/security/requestinterceptor.js deleted file mode 100644 index 8557ce1435..0000000000 --- a/src/Umbraco.Web.UI.Client/src/common/security/requestinterceptor.js +++ /dev/null @@ -1,26 +0,0 @@ -angular.module('umbraco.security.interceptor').factory("requestInterceptor", - ['$q', 'requestInterceptorFilter', function ($q, requestInterceptorFilter) { - var requestInterceptor = { - request: function (config) { - - var filtered = _.find(requestInterceptorFilter(), function (val) { - return config.url.indexOf(val) > 0; - }); - if (filtered) { - return config; - } - - config.headers["Time-Offset"] = (new Date().getTimezoneOffset()); - return config; - } - }; - - return requestInterceptor; - } - ]) - // We have to add the interceptor to the queue as a string because the interceptor depends upon service instances that are not available in the config block. - .config([ - '$httpProvider', function($httpProvider) { - $httpProvider.interceptors.push('requestInterceptor'); - } - ]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js index 9fbf2947af..2f06d4c393 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js @@ -1,5 +1,43 @@ /*Contains multiple services for various helper tasks */ +function dateHelper() { + + return { + + convertToServerStringTime: function(momentLocal, serverOffsetMinutes, format) { + + //get the formatted offset time in HH:mm (server time offset is in minutes) + var formattedOffset = (serverOffsetMinutes > 0 ? "+" : "-") + + moment() + .startOf('day') + .minutes(Math.abs(serverOffsetMinutes)) + .format('HH:mm'); + + var server = moment.utc(momentLocal).zone(formattedOffset); + return server.format(format ? format : "YYYY-MM-DD HH:mm:ss"); + }, + + convertToLocalMomentTime: function (strVal, serverOffsetMinutes) { + + //get the formatted offset time in HH:mm (server time offset is in minutes) + var formattedOffset = (serverOffsetMinutes > 0 ? "+" : "-") + + moment() + .startOf('day') + .minutes(Math.abs(serverOffsetMinutes)) + .format('HH:mm'); + + //convert to the iso string format + var isoFormat = moment(strVal).format("YYYY-MM-DDTHH:mm:ss") + formattedOffset; + + //create a moment with the iso format which will include the offset with the correct time + // then convert it to local time + return moment.parseZone(isoFormat).local(); + } + + }; +} +angular.module('umbraco.services').factory('dateHelper', dateHelper); + function packageHelper(assetsService, treeService, eventsService, $templateCache) { return { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js index 693bd49ec6..95d0a1bdf6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js @@ -1,4 +1,4 @@ -function dateTimePickerController($scope, notificationsService, assetsService, angularHelper, userService, $element) { +function dateTimePickerController($scope, notificationsService, assetsService, angularHelper, userService, $element, dateHelper) { //setup the default config var config = { @@ -22,6 +22,8 @@ function dateTimePickerController($scope, notificationsService, assetsService, a $scope.model.config.format = $scope.model.config.pickTime ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD"; } + + $scope.hasDatetimePickerValue = $scope.model.value ? true : false; $scope.datetimePickerValue = null; @@ -43,20 +45,46 @@ function dateTimePickerController($scope, notificationsService, assetsService, a if (e.date && e.date.isValid()) { $scope.datePickerForm.datepicker.$setValidity("pickerError", true); $scope.hasDatetimePickerValue = true; - $scope.datetimePickerValue = e.date.format($scope.model.config.format); - $scope.model.value = $scope.datetimePickerValue; + $scope.datetimePickerValue = e.date.format($scope.model.config.format); } else { $scope.hasDatetimePickerValue = false; $scope.datetimePickerValue = null; } - + + setModelValue(); + if (!$scope.model.config.pickTime) { $element.find("div:first").datetimepicker("hide", 0); } }); } + //sets the scope model value accordingly - this is the value to be sent up to the server and depends on + // if the picker is configured to offset time. We always format the date/time in a specific format for sending + // to the server, this is different from the format used to display the date/time. + function setModelValue() { + if ($scope.hasDatetimePickerValue) { + var elementData = $element.find("div:first").data().DateTimePicker; + if ($scope.model.config.pickTime) { + //check if we are supposed to offset the time + if ($scope.model.value && $scope.model.config.offsetTime === "1" && Umbraco.Sys.ServerVariables.application.serverTimeOffset) { + $scope.model.value = dateHelper.convertToServerStringTime(elementData.getDate(), Umbraco.Sys.ServerVariables.application.serverTimeOffset); + $scope.serverTime = dateHelper.convertToServerStringTime(elementData.getDate(), Umbraco.Sys.ServerVariables.application.serverTimeOffset, "YYYY-MM-DD HH:mm:ss Z"); + } + else { + $scope.model.value = elementData.getDate().format("YYYY-MM-DD HH:mm:ss"); + } + } + else { + $scope.model.value = elementData.getDate().format("YYYY-MM-DD"); + } + } + else { + $scope.model.value = null; + } + } + var picker = null; $scope.clearDate = function() { @@ -66,6 +94,8 @@ function dateTimePickerController($scope, notificationsService, assetsService, a $scope.datePickerForm.datepicker.$setValidity("pickerError", true); } + $scope.serverTime = null; + //get the current user to see if we can localize this picker userService.getCurrentUser().then(function (user) { @@ -97,8 +127,17 @@ function dateTimePickerController($scope, notificationsService, assetsService, a }); if ($scope.hasDatetimePickerValue) { - //assign value to plugin/picker - var dateVal = $scope.model.value ? moment($scope.model.value, "YYYY-MM-DD HH:mm:ss") : moment(); + var dateVal; + //check if we are supposed to offset the time + if ($scope.model.value && $scope.model.config.offsetTime === "1" && Umbraco.Sys.ServerVariables.application.serverTimeOffset) { + //get the local time offset from the server + dateVal = dateHelper.convertToLocalMomentTime($scope.model.value, Umbraco.Sys.ServerVariables.application.serverTimeOffset); + $scope.serverTime = dateHelper.convertToServerStringTime(dateVal, Umbraco.Sys.ServerVariables.application.serverTimeOffset, "YYYY-MM-DD HH:mm:ss Z"); + } + else { + //create a normal moment , no offset required + var dateVal = $scope.model.value ? moment($scope.model.value, "YYYY-MM-DD HH:mm:ss") : moment(); + } element.datetimepicker("setValue", dateVal); $scope.datetimePickerValue = dateVal.format($scope.model.config.format); @@ -117,18 +156,7 @@ function dateTimePickerController($scope, notificationsService, assetsService, a var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { - if ($scope.hasDatetimePickerValue) { - var elementData = $element.find("div:first").data().DateTimePicker; - if ($scope.model.config.pickTime) { - $scope.model.value = elementData.getDate().format("YYYY-MM-DD HH:mm:ss"); - } - else { - $scope.model.value = elementData.getDate().format("YYYY-MM-DD"); - } - } - else { - $scope.model.value = null; - } + setModelValue(); }); //unbind doc click event! $scope.$on('$destroy', function () { @@ -142,17 +170,7 @@ function dateTimePickerController($scope, notificationsService, assetsService, a }); var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { - if ($scope.hasDatetimePickerValue) { - if ($scope.model.config.pickTime) { - $scope.model.value = $element.find("div:first").data().DateTimePicker.getDate().format("YYYY-MM-DD HH:mm:ss"); - } - else { - $scope.model.value = $element.find("div:first").data().DateTimePicker.getDate().format("YYYY-MM-DD"); - } - } - else { - $scope.model.value = null; - } + setModelValue(); }); //unbind doc click event! diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html index e6d49e5c6c..c9b83cc8ed 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html @@ -18,6 +18,9 @@ {{datePickerForm.datepicker.errorMsg}} Invalid date +

    + Server time: {{serverTime}} +

    Clear date

    diff --git a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js index 0f323f03aa..ede7c20538 100644 --- a/src/Umbraco.Web.UI.Client/test/config/karma.conf.js +++ b/src/Umbraco.Web.UI.Client/test/config/karma.conf.js @@ -24,6 +24,7 @@ module.exports = function(karma) { 'lib/../build/belle/lib/underscore/underscore-min.js', + 'lib/../build/belle/lib/moment/moment-with-locales.js', 'lib/umbraco/Extensions.js', 'lib/../build/belle/lib/rgrove-lazyload/lazyload.js', 'lib/../build/belle/lib/angular-local-storage/angular-local-storage.min.js', diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/date-helper.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/date-helper.spec.js new file mode 100644 index 0000000000..74d2b38cfa --- /dev/null +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/date-helper.spec.js @@ -0,0 +1,53 @@ +describe('date helper tests', function () { + var dateHelper; + + beforeEach(module('umbraco.services')); + + beforeEach(inject(function ($injector) { + dateHelper = $injector.get('dateHelper'); + })); + + describe('converting to local moments', function () { + + it('converts from a positive offset', function () { + var offsetMin = 600; //+10 + var strDate = "2016-01-01 10:00:00"; + + var result = dateHelper.convertToLocalMomentTime(strDate, offsetMin); + + expect(result.format("YYYY-MM-DD HH:mm:ss Z")).toBe("2016-01-01 01:00:00 +01:00"); + }); + + it('converts from a negataive offset', function () { + var offsetMin = -420; //-7 + var strDate = "2016-01-01 10:00:00"; + + var result = dateHelper.convertToLocalMomentTime(strDate, offsetMin); + + expect(result.format("YYYY-MM-DD HH:mm:ss Z")).toBe("2016-01-01 18:00:00 +01:00"); + }); + + }); + + describe('converting to server strings', function () { + + it('converts to a positive offset', function () { + var offsetMin = 600; //+10 + var localDate = moment("2016-01-01 10:00:00"); + + var result = dateHelper.convertToServerStringTime(localDate, offsetMin, "YYYY-MM-DD HH:mm:ss Z"); + + expect(result).toBe("2016-01-01 19:00:00 +10:00"); + }); + + it('converts from a negataive offset', function () { + var offsetMin = -420; //-7 + var localDate = moment("2016-01-01 10:00:00"); + + var result = dateHelper.convertToServerStringTime(localDate, offsetMin, "YYYY-MM-DD HH:mm:ss Z"); + + expect(result).toBe("2016-01-01 02:00:00 -07:00"); + }); + + }); +}); \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index b5cea7a945..6ee6f260f3 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -677,6 +677,10 @@ namespace Umbraco.Web.Editors app.Add("cdf", ClientDependencySettings.Instance.Version); //useful for dealing with virtual paths on the client side when hosted in virtual directories especially app.Add("applicationPath", HttpContext.Request.ApplicationPath.EnsureEndsWith('/')); + + //add the server's GMT time offset in minutes + app.Add("serverTimeOffset", Convert.ToInt32(DateTimeOffset.Now.Offset.TotalMinutes)); + return app; } diff --git a/src/Umbraco.Web/Editors/ContentControllerBase.cs b/src/Umbraco.Web/Editors/ContentControllerBase.cs index bf9f2056b3..495217cec2 100644 --- a/src/Umbraco.Web/Editors/ContentControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentControllerBase.cs @@ -97,10 +97,11 @@ namespace Umbraco.Web.Editors var d = new Dictionary(); //add the files if any var files = contentItem.UploadedFiles.Where(x => x.PropertyAlias == p.Alias).ToArray(); - if (files.Any()) + if (files.Length > 0) { d.Add("files", files); } + var data = new ContentPropertyData(p.Value, p.PreValues, d); //get the deserialized value from the property editor @@ -113,7 +114,7 @@ namespace Umbraco.Web.Editors var valueEditor = p.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); if (supportTagsAttribute != null) diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index ede1dfc78d..e44fc056da 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -179,7 +179,11 @@ namespace Umbraco.Web.Models.Mapping Label = localizedText.Localize("content/releaseDate"), Value = display.ReleaseDate.HasValue ? display.ReleaseDate.Value.ToIsoString() : null, //Not editible for people without publish permission (U4-287) - View = display.AllowedActions.Contains('P') ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View + View = display.AllowedActions.Contains('P') ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View, + Config = new Dictionary + { + {"offsetTime", "1"} + } //TODO: Fix up hard coded datepicker } , new ContentPropertyDisplay @@ -188,7 +192,11 @@ namespace Umbraco.Web.Models.Mapping Label = localizedText.Localize("content/unpublishDate"), Value = display.ExpireDate.HasValue ? display.ExpireDate.Value.ToIsoString() : null, //Not editible for people without publish permission (U4-287) - View = display.AllowedActions.Contains('P') ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View + View = display.AllowedActions.Contains('P') ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View, + Config = new Dictionary + { + {"offsetTime", "1"} + } //TODO: Fix up hard coded datepicker }, new ContentPropertyDisplay diff --git a/src/Umbraco.Web/PropertyEditors/DateTimePreValueEditor.cs b/src/Umbraco.Web/PropertyEditors/DateTimePreValueEditor.cs new file mode 100644 index 0000000000..242893cbca --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/DateTimePreValueEditor.cs @@ -0,0 +1,10 @@ +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + internal class DateTimePreValueEditor : DatePreValueEditor + { + [PreValueField("offsetTime", "Offset time", "boolean", Description = "When enabled the time displayed will be offset with the server's timezone, this is useful for scenarios like scheduled publishing when an editor is in a different timezone than the hosted server")] + public bool OffsetTime { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs index 38674de14e..2a76c0d50a 100644 --- a/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; namespace Umbraco.Web.PropertyEditors { @@ -14,7 +17,11 @@ namespace Umbraco.Web.PropertyEditors { //NOTE: This is very important that we do not use .Net format's there, this format // is the correct format for the JS picker we are using so you cannot capitalize the HH, they need to be 'hh' - {"format", "YYYY-MM-DD HH:mm:ss"} + {"format", "YYYY-MM-DD HH:mm:ss"}, + //a pre-value indicating if the client/server time should be offset, when set to true the date/time seen + // by the client will be offset with the server time. + // For example, this is forced to true for scheduled publishing date/time pickers + {"offsetTime", "0"} }; } @@ -40,7 +47,9 @@ namespace Umbraco.Web.PropertyEditors protected override PreValueEditor CreatePreValueEditor() { - return new DatePreValueEditor(); + return new DateTimePreValueEditor(); } } + + } \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 48005b1209..a1968d5e3f 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -354,6 +354,7 @@ + diff --git a/src/Umbraco.Web/WebApi/Binders/ContentItemBaseBinder.cs b/src/Umbraco.Web/WebApi/Binders/ContentItemBaseBinder.cs index dda19f9ff9..195c488b1e 100644 --- a/src/Umbraco.Web/WebApi/Binders/ContentItemBaseBinder.cs +++ b/src/Umbraco.Web/WebApi/Binders/ContentItemBaseBinder.cs @@ -166,7 +166,7 @@ namespace Umbraco.Web.WebApi.Binders //create the dto from the persisted model if (model.PersistedContent != null) { - model.ContentDto = MapFromPersisted(model); + model.ContentDto = MapFromPersisted(model); } if (model.ContentDto != null) { @@ -186,9 +186,15 @@ namespace Umbraco.Web.WebApi.Binders /// private static void MapPropertyValuesFromSaved(TModelSave saveModel, ContentItemDto dto) { - foreach (var p in saveModel.Properties.Where(p => dto.Properties.Any(x => x.Alias == p.Alias))) + //NOTE: Don't convert this to linq, this is much quicker + foreach (var p in saveModel.Properties) { - dto.Properties.Single(x => x.Alias == p.Alias).Value = p.Value; + foreach (var propertyDto in dto.Properties) + { + if (propertyDto.Alias != p.Alias) continue; + propertyDto.Value = p.Value; + break; + } } } From bab693f2baac2d4898c8fdb1ea555b253d32ba52 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 25 May 2016 10:56:41 +0200 Subject: [PATCH 014/389] fix build --- src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index 97d81539d2..34cd64c265 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -179,7 +179,7 @@ namespace Umbraco.Web.Models.Mapping Label = localizedText.Localize("content/releaseDate"), Value = display.ReleaseDate.HasValue ? display.ReleaseDate.Value.ToIsoString() : null, //Not editible for people without publish permission (U4-287) - View = display.AllowedActions.Contains(ActionPublish.Instance.Letter) ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View + View = display.AllowedActions.Contains(ActionPublish.Instance.Letter) ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View, Config = new Dictionary { {"offsetTime", "1"} @@ -192,7 +192,7 @@ namespace Umbraco.Web.Models.Mapping Label = localizedText.Localize("content/unpublishDate"), Value = display.ExpireDate.HasValue ? display.ExpireDate.Value.ToIsoString() : null, //Not editible for people without publish permission (U4-287) - View = display.AllowedActions.Contains(ActionPublish.Instance.Letter) ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View + View = display.AllowedActions.Contains(ActionPublish.Instance.Letter) ? "datepicker" : PropertyEditorResolver.Current.GetByAlias(Constants.PropertyEditors.NoEditAlias).ValueEditor.View, Config = new Dictionary { {"offsetTime", "1"} From b168923d5f8880c64e0b08663ccd64fcc28b6a98 Mon Sep 17 00:00:00 2001 From: Darren Ferguson Date: Sun, 29 May 2016 15:46:42 +0100 Subject: [PATCH 015/389] http://issues.umbraco.org/issue/U4-7032 : Include a password checker for Active Directory - as ability to authenticate via AD was removed in 7.3 --- ...eDirectoryBackOfficeUserPasswordChecker.cs | 25 +++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 2 ++ 2 files changed, 27 insertions(+) create mode 100644 src/Umbraco.Core/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs diff --git a/src/Umbraco.Core/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs b/src/Umbraco.Core/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs new file mode 100644 index 0000000000..aef4f3dc76 --- /dev/null +++ b/src/Umbraco.Core/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs @@ -0,0 +1,25 @@ +using System.Configuration; +using System.DirectoryServices.AccountManagement; +using System.Threading.Tasks; +using Umbraco.Core.Models.Identity; + +namespace Umbraco.Core.Security +{ + public class ActiveDirectoryBackOfficeUserPasswordChecker : IBackOfficeUserPasswordChecker + { + public Task CheckPasswordAsync(BackOfficeIdentityUser user, string password) + { + bool isValid; + using (var pc = new PrincipalContext(ContextType.Domain, ConfigurationManager.AppSettings["ActiveDirectoryDomain"])) + { + isValid = pc.ValidateCredentials(user.UserName, password); + } + + var result = isValid + ? BackOfficeUserPasswordCheckerResult.ValidCredentials + : BackOfficeUserPasswordCheckerResult.InvalidCredentials; + + return Task.FromResult(result); + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 141622687e..72bf9ddd42 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -110,6 +110,7 @@ False ..\packages\SqlServerCE.4.0.0.1\lib\System.Data.SqlServerCe.Entity.dll + @@ -485,6 +486,7 @@ + From def690b8c368f784094e5f4c98b75a28f639eb47 Mon Sep 17 00:00:00 2001 From: Darren Ferguson Date: Mon, 30 May 2016 11:15:03 +0100 Subject: [PATCH 016/389] Fix U4-8532 - No built in Active Directory authentication in Umbraco 7.3+ --- .../ActiveDirectoryBackOfficeUserPasswordChecker.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs b/src/Umbraco.Core/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs index aef4f3dc76..819fa87a56 100644 --- a/src/Umbraco.Core/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs +++ b/src/Umbraco.Core/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs @@ -7,10 +7,16 @@ namespace Umbraco.Core.Security { public class ActiveDirectoryBackOfficeUserPasswordChecker : IBackOfficeUserPasswordChecker { + public virtual string ActiveDirectoryDomain { + get { + return ConfigurationManager.AppSettings["ActiveDirectoryDomain"]; + } + } + public Task CheckPasswordAsync(BackOfficeIdentityUser user, string password) { bool isValid; - using (var pc = new PrincipalContext(ContextType.Domain, ConfigurationManager.AppSettings["ActiveDirectoryDomain"])) + using (var pc = new PrincipalContext(ContextType.Domain, ActiveDirectoryDomain)) { isValid = pc.ValidateCredentials(user.UserName, password); } From 03d53737a04c8cd2bbe0acddc48d82629a07741e Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 31 May 2016 13:39:14 +0200 Subject: [PATCH 017/389] Fixes issue with a zero offset time - which in JS also means false, so need to check for undefined explicitly --- .../views/propertyeditors/datepicker/datepicker.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js index 95d0a1bdf6..bb12f0404d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js @@ -68,7 +68,7 @@ function dateTimePickerController($scope, notificationsService, assetsService, a var elementData = $element.find("div:first").data().DateTimePicker; if ($scope.model.config.pickTime) { //check if we are supposed to offset the time - if ($scope.model.value && $scope.model.config.offsetTime === "1" && Umbraco.Sys.ServerVariables.application.serverTimeOffset) { + if ($scope.model.value && $scope.model.config.offsetTime === "1" && Umbraco.Sys.ServerVariables.application.serverTimeOffset !== undefined) { $scope.model.value = dateHelper.convertToServerStringTime(elementData.getDate(), Umbraco.Sys.ServerVariables.application.serverTimeOffset); $scope.serverTime = dateHelper.convertToServerStringTime(elementData.getDate(), Umbraco.Sys.ServerVariables.application.serverTimeOffset, "YYYY-MM-DD HH:mm:ss Z"); } From 3f5729767d362c86011865845cebea28f1ea5c92 Mon Sep 17 00:00:00 2001 From: Oliver Philpott Date: Thu, 9 Jun 2016 10:08:05 +0100 Subject: [PATCH 018/389] U4-8509 - Added woff2 mime type --- src/Umbraco.Web.UI/web.Template.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI/web.Template.config b/src/Umbraco.Web.UI/web.Template.config index dd66460408..1d170be7b1 100644 --- a/src/Umbraco.Web.UI/web.Template.config +++ b/src/Umbraco.Web.UI/web.Template.config @@ -317,6 +317,8 @@ + + From 952ff74899377b85d5767b670fc35abb64c329b4 Mon Sep 17 00:00:00 2001 From: Oliver Philpott Date: Thu, 9 Jun 2016 10:26:03 +0100 Subject: [PATCH 019/389] Fixed formatting for woff2 mimetype --- src/Umbraco.Web.UI/web.Template.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI/web.Template.config b/src/Umbraco.Web.UI/web.Template.config index 1d170be7b1..ffc8889625 100644 --- a/src/Umbraco.Web.UI/web.Template.config +++ b/src/Umbraco.Web.UI/web.Template.config @@ -317,8 +317,8 @@ - - + + From a099c40d9322d81ef0914b25c5c638094e357ea1 Mon Sep 17 00:00:00 2001 From: Claus Date: Sun, 12 Jun 2016 20:39:53 +0200 Subject: [PATCH 020/389] U4-8584 Starterkit thumbnails are not really thumbnails adding a parameter to the image url to avoid loading a massive image when its only rendered in a thumbnail size. --- src/Umbraco.Web.UI/umbraco/Install/Legacy/loadStarterKits.ascx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/Install/Legacy/loadStarterKits.ascx b/src/Umbraco.Web.UI/umbraco/Install/Legacy/loadStarterKits.ascx index aadd444a42..f509ab96f6 100644 --- a/src/Umbraco.Web.UI/umbraco/Install/Legacy/loadStarterKits.ascx +++ b/src/Umbraco.Web.UI/umbraco/Install/Legacy/loadStarterKits.ascx @@ -42,7 +42,7 @@
  • ">
    - <%# ((Package)Container.DataItem).Text %> + <%# ((Package)Container.DataItem).Text %>

    <%# ((Package)Container.DataItem).Text %>

    <%# ((Package)Container.DataItem).Description %> From 2e9e8f23e96f0b785e5d0af48592c71ef71de2c9 Mon Sep 17 00:00:00 2001 From: Claus Date: Sun, 12 Jun 2016 20:40:39 +0200 Subject: [PATCH 021/389] adjustments for paddings. --- src/Umbraco.Web.UI/umbraco/Install/Legacy/loadStarterKits.ascx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/Install/Legacy/loadStarterKits.ascx b/src/Umbraco.Web.UI/umbraco/Install/Legacy/loadStarterKits.ascx index f509ab96f6..490ff5b3ba 100644 --- a/src/Umbraco.Web.UI/umbraco/Install/Legacy/loadStarterKits.ascx +++ b/src/Umbraco.Web.UI/umbraco/Install/Legacy/loadStarterKits.ascx @@ -42,7 +42,7 @@
  • ">
    - <%# ((Package)Container.DataItem).Text %> + <%# ((Package)Container.DataItem).Text %>

    <%# ((Package)Container.DataItem).Text %>

    <%# ((Package)Container.DataItem).Description %> From b0a4cc33339312df06feedf107b5bf0ab3ae6a61 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 13 Jun 2016 22:43:05 +1000 Subject: [PATCH 022/389] Updates ImageProcessor :boom: - Update versions - Add ImageProcessorLogger --- build/NuSpecs/UmbracoCms.Core.nuspec | 4 +- .../Logging/ImageProcessorLogger.cs | 46 +++++++++++++++++++ src/Umbraco.Core/packages.config | 1 + src/Umbraco.Web.UI/packages.config | 4 +- 4 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 src/Umbraco.Core/Logging/ImageProcessorLogger.cs diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 72733c5800..077bafa760 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -32,8 +32,8 @@ - - + + diff --git a/src/Umbraco.Core/Logging/ImageProcessorLogger.cs b/src/Umbraco.Core/Logging/ImageProcessorLogger.cs new file mode 100644 index 0000000000..ac18f1d384 --- /dev/null +++ b/src/Umbraco.Core/Logging/ImageProcessorLogger.cs @@ -0,0 +1,46 @@ +namespace Umbraco.Core.Logging +{ + using System; + using System.Runtime.CompilerServices; + + using ImageProcessor.Common.Exceptions; + + /// + /// A logger for explicitly logging ImageProcessor exceptions. + /// + /// Creating this logger is enough for ImageProcessor to find and replace its in-built debug logger + /// without any additional configuration required. This class currently has to be public in order + /// to do so. + /// + /// + public sealed class ImageProcessorLogger : ImageProcessor.Common.Exceptions.ILogger + { + /// + /// Logs the specified message as an error. + /// + /// The type calling the logger. + /// The message to log. + /// The property or method name calling the log. + /// The line number where the method is called. + public void Log(string text, [CallerMemberName] string callerName = null, [CallerLineNumber] int lineNumber = 0) + { + // Using LogHelper since the ImageProcessor logger expects a parameterless constructor. + var message = string.Format("{0} {1}:{2}", callerName, lineNumber, text); + LogHelper.Error(message, new ImageProcessingException(message)); + } + + /// + /// Logs the specified message as an error. + /// + /// The type calling the logger. + /// The message to log. + /// The property or method name calling the log. + /// The line number where the method is called. + public void Log(Type type, string text, [CallerMemberName] string callerName = null, [CallerLineNumber] int lineNumber = 0) + { + // Using LogHelper since the ImageProcessor logger expects a parameterless constructor. + var message = string.Format("{0} {1}:{2}", callerName, lineNumber, text); + LogHelper.Error(type, message, new ImageProcessingException(message)); + } + } +} diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config index b69be5b5cc..784daf9ea0 100644 --- a/src/Umbraco.Core/packages.config +++ b/src/Umbraco.Core/packages.config @@ -2,6 +2,7 @@ + diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 160a82c5fb..ae045c0ece 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -5,8 +5,8 @@ - - + + From 33cbcbb7d49036fbd4a847c175caf78bad95d644 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 13 Jun 2016 22:47:49 +1000 Subject: [PATCH 023/389] Save projects. --- src/Umbraco.Core/Umbraco.Core.csproj | 5 +++++ src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 141622687e..4f6fc38c1d 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -52,6 +52,10 @@ ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll + + ..\packages\ImageProcessor.2.4.0.0\lib\net45\ImageProcessor.dll + True + False ..\packages\log4net-mediumtrust.2.0.0\lib\log4net.dll @@ -352,6 +356,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 4bd0be1308..2b7d01a46e 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -136,12 +136,12 @@ False ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll - - ..\packages\ImageProcessor.2.3.3.0\lib\net45\ImageProcessor.dll + + ..\packages\ImageProcessor.2.4.0.0\lib\net45\ImageProcessor.dll True - - ..\packages\ImageProcessor.Web.4.5.3.0\lib\net45\ImageProcessor.Web.dll + + ..\packages\ImageProcessor.Web.4.6.1.0\lib\net45\ImageProcessor.Web.dll True From c9c1aea8ab750f9417d5641e7ddcc20065bacd58 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 14 Jun 2016 04:54:26 +1000 Subject: [PATCH 024/389] Update ImageProcessor, clean up log message --- build/NuSpecs/UmbracoCms.Core.nuspec | 2 +- src/Umbraco.Core/Logging/ImageProcessorLogger.cs | 8 ++++---- src/Umbraco.Core/Umbraco.Core.csproj | 4 ++-- src/Umbraco.Core/packages.config | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- src/Umbraco.Web.UI/packages.config | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 077bafa760..4d6e1b3c6c 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -32,7 +32,7 @@ - + diff --git a/src/Umbraco.Core/Logging/ImageProcessorLogger.cs b/src/Umbraco.Core/Logging/ImageProcessorLogger.cs index ac18f1d384..02ca9c2949 100644 --- a/src/Umbraco.Core/Logging/ImageProcessorLogger.cs +++ b/src/Umbraco.Core/Logging/ImageProcessorLogger.cs @@ -25,8 +25,8 @@ public void Log(string text, [CallerMemberName] string callerName = null, [CallerLineNumber] int lineNumber = 0) { // Using LogHelper since the ImageProcessor logger expects a parameterless constructor. - var message = string.Format("{0} {1}:{2}", callerName, lineNumber, text); - LogHelper.Error(message, new ImageProcessingException(message)); + var message = string.Format("{0} {1} : {2}", callerName, lineNumber, text); + LogHelper.Error(string.Empty, new ImageProcessingException(message)); } /// @@ -39,8 +39,8 @@ public void Log(Type type, string text, [CallerMemberName] string callerName = null, [CallerLineNumber] int lineNumber = 0) { // Using LogHelper since the ImageProcessor logger expects a parameterless constructor. - var message = string.Format("{0} {1}:{2}", callerName, lineNumber, text); - LogHelper.Error(type, message, new ImageProcessingException(message)); + var message = string.Format("{0} {1} : {2}", callerName, lineNumber, text); + LogHelper.Error(type, string.Empty, new ImageProcessingException(message)); } } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4f6fc38c1d..47dd8b69b6 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -52,8 +52,8 @@ ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll - - ..\packages\ImageProcessor.2.4.0.0\lib\net45\ImageProcessor.dll + + ..\packages\ImageProcessor.2.4.1.0\lib\net45\ImageProcessor.dll True diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config index 784daf9ea0..a9241f46c8 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 2b7d01a46e..817900c36c 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -136,8 +136,8 @@ False ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll - - ..\packages\ImageProcessor.2.4.0.0\lib\net45\ImageProcessor.dll + + ..\packages\ImageProcessor.2.4.1.0\lib\net45\ImageProcessor.dll True diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index ae045c0ece..69aecec6a1 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -5,7 +5,7 @@ - + From 05ed8e95307f6f517b995c8776c3686104e62c46 Mon Sep 17 00:00:00 2001 From: Jeavon Date: Mon, 20 Jun 2016 13:07:16 +0100 Subject: [PATCH 025/389] Fix for U4-8612 so that Icon shows when installing a local package --- .../src/views/packager/views/install-local.html | 4 ++-- src/Umbraco.Web/Editors/PackageInstallController.cs | 1 + src/Umbraco.Web/Models/LocalPackageInstallModel.cs | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html index b25facae7f..1900863cfd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html @@ -53,8 +53,8 @@
    - - + +
    diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index 41f183eb8c..0d814c8f7f 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -233,6 +233,7 @@ namespace Umbraco.Web.Editors model.Name = ins.Name; model.Author = ins.Author; model.AuthorUrl = ins.AuthorUrl; + model.IconUrl = ins.IconUrl; model.License = ins.License; model.LicenseUrl = ins.LicenseUrl; model.ReadMe = ins.ReadMe; diff --git a/src/Umbraco.Web/Models/LocalPackageInstallModel.cs b/src/Umbraco.Web/Models/LocalPackageInstallModel.cs index abb7c85cec..819674d3f2 100644 --- a/src/Umbraco.Web/Models/LocalPackageInstallModel.cs +++ b/src/Umbraco.Web/Models/LocalPackageInstallModel.cs @@ -83,5 +83,8 @@ namespace Umbraco.Web.Models [DataMember(Name = "author")] public string Author { get; set; } + + [DataMember(Name = "iconUrl")] + public string IconUrl { get; set; } } } \ No newline at end of file From fed718efb5d8fc4e8d65bb7028efc939ba01ef1f Mon Sep 17 00:00:00 2001 From: Jeavon Date: Mon, 20 Jun 2016 13:17:09 +0100 Subject: [PATCH 026/389] Fix Author link in Local package installer --- .../src/views/packager/views/install-local.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html index 1900863cfd..62768d7f78 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html @@ -63,7 +63,7 @@
    From 53fbf04a6c3ab02bd7343717bfade94886c2a01f Mon Sep 17 00:00:00 2001 From: Jeavon Date: Mon, 20 Jun 2016 13:20:24 +0100 Subject: [PATCH 027/389] Add target=_blank on license url in local package installer so user doesn't loose their installer if they click the link --- .../src/views/packager/views/install-local.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html index 62768d7f78..97601d5438 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html @@ -73,7 +73,7 @@
    From 5b417f624138ac3adc5085d3d211f45b522b4a31 Mon Sep 17 00:00:00 2001 From: Danny Drogt Date: Mon, 20 Jun 2016 15:32:27 +0200 Subject: [PATCH 028/389] U4-8613 Link to DocType from "Properties" tab is not localized - Added call to TextService --- src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index efc9b7ced6..dba7f5bf18 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -221,7 +221,7 @@ namespace Umbraco.Web.Models.Mapping && UmbracoContext.Current.Security.CurrentUser.AllowedSections.Any(x => x.Equals(Constants.Applications.Settings))) { var currentDocumentType = contentTypeService.GetContentType(display.ContentTypeAlias); - var currentDocumentTypeName = currentDocumentType == null ? string.Empty : currentDocumentType.Name; + var currentDocumentTypeName = currentDocumentType == null ? string.Empty : localizedText.UmbracoDictionaryTranslate(currentDocumentType.Name); var currentDocumentTypeId = currentDocumentType == null ? string.Empty : currentDocumentType.Id.ToString(CultureInfo.InvariantCulture); //TODO: Hard coding this is not good From f247e1b66f3a119743fc6789bb853b6c2f7d5ff5 Mon Sep 17 00:00:00 2001 From: bjarnef Date: Mon, 20 Jun 2016 19:54:46 +0200 Subject: [PATCH 029/389] Document getByQuery --- .../src/common/resources/entity.resource.js | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) 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 a9296acc37..914b601249 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 @@ -109,16 +109,6 @@ function entityResource($q, $http, umbRequestHelper) { [{ id: id}, {type: type }])), 'Failed to retrieve entity data for id ' + id); }, - - getByQuery: function (query, nodeContextId, type) { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "entityApiBaseUrl", - "GetByQuery", - [{query: query},{ nodeContextId: nodeContextId}, {type: type }])), - 'Failed to retrieve entity data for query ' + query); - }, /** * @ngdoc method @@ -168,7 +158,41 @@ function entityResource($q, $http, umbRequestHelper) { /** * @ngdoc method - * @name umbraco.resources.entityResource#getEntityById + * @name umbraco.resources.entityResource#getByQuery + * @methodOf umbraco.resources.entityResource + * + * @description + * Gets an entity from a given xpath + * + * ##usage + *
    +         * //get content by xpath
    +         * entityResource.getByQuery("$current", -1, "Document")
    +         *    .then(function(ent) {
    +         *        var myDoc = ent; 
    +         *        alert('its here!');
    +         *    });
    +         * 
    + * + * @param {string} query xpath to use in query + * @param {Int} nodeContextId id id to start from + * @param {string} type Object type name + * @returns {Promise} resourcePromise object containing the entity. + * + */ + getByQuery: function (query, nodeContextId, type) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "entityApiBaseUrl", + "GetByQuery", + [{ query: query }, { nodeContextId: nodeContextId }, { type: type }])), + 'Failed to retrieve entity data for query ' + query); + }, + + /** + * @ngdoc method + * @name umbraco.resources.entityResource#getAll * @methodOf umbraco.resources.entityResource * * @description @@ -260,7 +284,7 @@ function entityResource($q, $http, umbRequestHelper) { /** * @ngdoc method - * @name umbraco.resources.entityResource#searchMedia + * @name umbraco.resources.entityResource#search * @methodOf umbraco.resources.entityResource * * @description From 944898d6e5fe9c736aec65ae334b5a1f54159648 Mon Sep 17 00:00:00 2001 From: Wincent Date: Tue, 21 Jun 2016 14:18:55 +0200 Subject: [PATCH 030/389] http://issues.umbraco.org/issue/U4-8530 Adds Preview property to ContentItemDisplay --- src/Umbraco.Web.UI.Client/src/views/content/edit.html | 2 +- .../Models/ContentEditing/ContentItemDisplay.cs | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/edit.html b/src/Umbraco.Web.UI.Client/src/views/content/edit.html index be79f797a0..c07dad7555 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/edit.html @@ -58,7 +58,7 @@ { + public ContentItemDisplay() + { + Preview = true; + } + [DataMember(Name = "publishDate")] public DateTime? PublishDate { get; set; } @@ -34,7 +39,10 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "urls")] public string[] Urls { get; set; } - + + [DataMember( Name = "preview" )] + public bool Preview { get; set; } + /// /// The allowed 'actions' based on the user's permissions - Create, Update, Publish, Send to publish /// From abd2e70b6aed8d4c681977f3648adbfed6f6a7ef Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 22 Jun 2016 17:19:24 +0200 Subject: [PATCH 031/389] Make sure to hide the timezone offset message when no offsetting is needed (server and client are in the same timezone) --- .../datepicker/datepicker.controller.js | 15 ++++++++++++++- .../propertyeditors/datepicker/datepicker.html | 5 +++-- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 2 ++ src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml | 2 ++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js index bb12f0404d..2c3495120a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js @@ -63,7 +63,7 @@ function dateTimePickerController($scope, notificationsService, assetsService, a //sets the scope model value accordingly - this is the value to be sent up to the server and depends on // if the picker is configured to offset time. We always format the date/time in a specific format for sending // to the server, this is different from the format used to display the date/time. - function setModelValue() { + function setModelValue() { if ($scope.hasDatetimePickerValue) { var elementData = $element.find("div:first").data().DateTimePicker; if ($scope.model.config.pickTime) { @@ -95,6 +95,19 @@ function dateTimePickerController($scope, notificationsService, assetsService, a } $scope.serverTime = null; + $scope.serverTimeNeedsOffsetting = false; + if (Umbraco.Sys.ServerVariables.application.serverTimeOffset !== undefined) { + // Will return something like 120 + var serverOffset = Umbraco.Sys.ServerVariables.application.serverTimeOffset; + + // Will return something like -120 + var localOffset = new Date().getTimezoneOffset(); + + // If these aren't equal then offsetting is needed + // note the minus in front of serverOffset needed + // because C# and javascript return the inverse offset + $scope.serverTimeNeedsOffsetting = (-serverOffset !== localOffset); + } //get the current user to see if we can localize this picker userService.getCurrentUser().then(function (user) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html index c9b83cc8ed..472c0a478b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html @@ -18,8 +18,9 @@ {{datePickerForm.datepicker.errorMsg}} Invalid date -

    - Server time: {{serverTime}} +

    + The time you picked is your local time. This translates to the following time on the server: {{serverTime}}
    + What does this mean?

    Clear date diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 84dedbb364..0c35794913 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -169,6 +169,8 @@ Not a member of group(s) Child items Target + This translates to the following time on the server:]]> + What does this mean?]]> Click to upload 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 ec41299b25..c85053191f 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -170,6 +170,8 @@ Not a member of group(s) Child items Target + This translates to the following time on the server:]]> + What does this mean?]]> Click to upload From b75578c889fc84dced7be8190daef4221009b83b Mon Sep 17 00:00:00 2001 From: Claus Date: Wed, 22 Jun 2016 19:30:31 +0200 Subject: [PATCH 032/389] U4-8640 Deleting a content type with children only emits a deleted event on the parent content type service now includes all deleted content types including children, in the DeletedContentType event arguments. tests to ensure the correct number of deleted entities are sent in events. --- .../Services/ContentTypeService.cs | 21 +++++-- .../Services/ContentTypeServiceTests.cs | 62 ++++++++++++++++++- 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 34dcdc01a9..1ea43c0f08 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -803,10 +803,13 @@ namespace Umbraco.Core.Services var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) { + var deletedContentTypes = new List() {contentType}; + deletedContentTypes.AddRange(contentType.Descendants().OfType()); + repository.Delete(contentType); uow.Commit(); - DeletedContentType.RaiseEvent(new DeleteEventArgs(contentType, false), this); + DeletedContentType.RaiseEvent(new DeleteEventArgs(deletedContentTypes.DistinctBy(x => x.Id), false), this); } Audit(AuditType.Delete, string.Format("Delete ContentType performed by user"), userId, contentType.Id); @@ -838,14 +841,18 @@ namespace Umbraco.Core.Services var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) { + var deletedContentTypes = new List(); + deletedContentTypes.AddRange(asArray); + foreach (var contentType in asArray) { + deletedContentTypes.AddRange(contentType.Descendants().OfType()); repository.Delete(contentType); } uow.Commit(); - DeletedContentType.RaiseEvent(new DeleteEventArgs(asArray, false), this); + DeletedContentType.RaiseEvent(new DeleteEventArgs(deletedContentTypes.DistinctBy(x => x.Id), false), this); } Audit(AuditType.Delete, string.Format("Delete ContentTypes performed by user"), userId, -1); @@ -1238,11 +1245,13 @@ namespace Umbraco.Core.Services var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) { + var deletedMediaTypes = new List() {mediaType}; + deletedMediaTypes.AddRange(mediaType.Descendants().OfType()); repository.Delete(mediaType); uow.Commit(); - DeletedMediaType.RaiseEvent(new DeleteEventArgs(mediaType, false), this); + DeletedMediaType.RaiseEvent(new DeleteEventArgs(deletedMediaTypes.DistinctBy(x => x.Id), false), this); } Audit(AuditType.Delete, string.Format("Delete MediaType performed by user"), userId, mediaType.Id); @@ -1271,13 +1280,17 @@ namespace Umbraco.Core.Services var uow = UowProvider.GetUnitOfWork(); using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) { + var deletedMediaTypes = new List(); + deletedMediaTypes.AddRange(asArray); + foreach (var mediaType in asArray) { + deletedMediaTypes.AddRange(mediaType.Descendants().OfType()); repository.Delete(mediaType); } uow.Commit(); - DeletedMediaType.RaiseEvent(new DeleteEventArgs(asArray, false), this); + DeletedMediaType.RaiseEvent(new DeleteEventArgs(deletedMediaTypes.DistinctBy(x => x.Id), false), this); } Audit(AuditType.Delete, string.Format("Delete MediaTypes performed by user"), userId, -1); diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index 742205904f..921dea16d9 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -7,7 +7,7 @@ using Umbraco.Core; using Umbraco.Core.Exceptions; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; - +using Umbraco.Core.Services; using Umbraco.Tests.CodeFirst.TestModels.Composition; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -275,6 +275,66 @@ namespace Umbraco.Tests.Services Assert.That(success, Is.False); } + [Test] + public void Deleting_ContentType_Sends_Correct_Number_Of_DeletedEntities_In_Events() + { + var cts = ServiceContext.ContentTypeService; + var deletedEntities = 0; + var contentType = MockedContentTypes.CreateSimpleContentType("page", "Page"); + cts.Save(contentType); + + ContentTypeService.DeletedContentType += (sender, args) => + { + deletedEntities += args.DeletedEntities.Count(); + }; + + cts.Delete(contentType); + + Assert.AreEqual(deletedEntities, 1); + } + + [Test] + public void Deleting_Multiple_ContentTypes_Sends_Correct_Number_Of_DeletedEntities_In_Events() + { + var cts = ServiceContext.ContentTypeService; + var deletedEntities = 0; + var contentType = MockedContentTypes.CreateSimpleContentType("page", "Page"); + cts.Save(contentType); + var contentType2 = MockedContentTypes.CreateSimpleContentType("otherPage", "Other page"); + cts.Save(contentType2); + + ContentTypeService.DeletedContentType += (sender, args) => + { + deletedEntities += args.DeletedEntities.Count(); + }; + + cts.Delete(contentType); + cts.Delete(contentType2); + + Assert.AreEqual(deletedEntities, 2); + } + + [Test] + public void Deleting_ContentType_With_Child_Sends_Correct_Number_Of_DeletedEntities_In_Events() + { + var cts = ServiceContext.ContentTypeService; + var deletedEntities = 0; + var contentType = MockedContentTypes.CreateSimpleContentType("page", "Page"); + cts.Save(contentType); + var contentType2 = MockedContentTypes.CreateSimpleContentType("subPage", "Sub page"); + contentType2.ParentId = contentType.Id; + cts.Save(contentType2); + + ContentTypeService.DeletedContentType += (sender, args) => + { + deletedEntities += args.DeletedEntities.Count(); + }; + + cts.Delete(contentType); + + Assert.AreEqual(deletedEntities, 2); + } + [Test] public void Can_Remove_ContentType_Composition_From_ContentType() { From 5b28260f96fef5eb44f456a52828ea1a55135ed7 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 23 Jun 2016 09:39:31 +0200 Subject: [PATCH 033/389] U4-8469 - xml cache TLC --- .../PublishedContentCache.cs | 6 +- .../XmlPublishedCache/XmlPublishedContent.cs | 188 ++++++++---------- .../XmlPublishedCache/XmlPublishedProperty.cs | 43 ++-- 3 files changed, 121 insertions(+), 116 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs index 8e9b529b7f..f2ef0e348f 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs @@ -277,15 +277,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private static IPublishedContent ConvertToDocument(XmlNode xmlNode, bool isPreviewing) { - return xmlNode == null - ? null - : (new XmlPublishedContent(xmlNode, isPreviewing)).CreateModel(); + return xmlNode == null ? null : XmlPublishedContent.Get(xmlNode, isPreviewing); } private static IEnumerable ConvertToDocuments(XmlNodeList xmlNodes, bool isPreviewing) { return xmlNodes.Cast() - .Select(xmlNode => (new XmlPublishedContent(xmlNode, isPreviewing)).CreateModel()); + .Select(xmlNode => XmlPublishedContent.Get(xmlNode, isPreviewing)); } #endregion diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs index 4bc2f2388a..5129542e46 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using System.Xml; using System.Xml.Serialization; @@ -26,44 +25,26 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache ///

    /// The Xml node. /// A value indicating whether the published content is being previewed. - public XmlPublishedContent(XmlNode xmlNode, bool isPreviewing) + private XmlPublishedContent(XmlNode xmlNode, bool isPreviewing) { _xmlNode = xmlNode; _isPreviewing = isPreviewing; - InitializeStructure(); - Initialize(); - InitializeChildren(); - } - - /// - /// Initializes a new instance of the XmlPublishedContent class with an Xml node, - /// and a value indicating whether to lazy-initialize the instance. - /// - /// The Xml node. - /// A value indicating whether the published content is being previewed. - /// A value indicating whether to lazy-initialize the instance. - /// Lazy-initializationg is NOT thread-safe. - internal XmlPublishedContent(XmlNode xmlNode, bool isPreviewing, bool lazyInitialize) - { - _xmlNode = xmlNode; - _isPreviewing = isPreviewing; - InitializeStructure(); - if (lazyInitialize == false) - { - Initialize(); - InitializeChildren(); - } } private readonly XmlNode _xmlNode; - - private bool _initialized; + private readonly bool _isPreviewing; + + private bool _nodeInitialized; + private bool _parentInitialized; private bool _childrenInitialized; - private readonly ICollection _children = new Collection(); + private IEnumerable _children = Enumerable.Empty(); private IPublishedContent _parent; - private int _id; + private PublishedContentType _contentType; + private Dictionary _properties; + + private int _id; private Guid _key; private int _template; private string _name; @@ -78,28 +59,24 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private DateTime _createDate; private DateTime _updateDate; private Guid _version; - private IPublishedProperty[] _properties; private int _sortOrder; private int _level; private bool _isDraft; - private readonly bool _isPreviewing; - private PublishedContentType _contentType; public override IEnumerable Children { get { - if (_initialized == false) - Initialize(); - if (_childrenInitialized == false) - InitializeChildren(); - return _children.OrderBy(x => x.SortOrder); + if (_nodeInitialized == false) InitializeNode(); + if (_childrenInitialized == false) InitializeChildren(); + return _children; } } public override IPublishedProperty GetProperty(string alias) { - return Properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias)); + IPublishedProperty property; + return _properties.TryGetValue(alias, out property) ? property : null; } // override to implement cache @@ -135,8 +112,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); + if (_parentInitialized == false) InitializeParent(); return _parent; } } @@ -145,8 +122,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _id; } } @@ -155,8 +131,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _key; } } @@ -165,8 +140,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _template; } } @@ -175,8 +149,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _sortOrder; } } @@ -185,8 +158,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _name; } } @@ -195,8 +167,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _docTypeAlias; } } @@ -205,8 +176,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _docTypeId; } } @@ -215,8 +185,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _writerName; } } @@ -225,8 +194,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _creatorName; } } @@ -235,8 +203,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _writerId; } } @@ -245,8 +212,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _creatorId; } } @@ -255,8 +221,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _path; } } @@ -265,8 +230,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _createDate; } } @@ -275,8 +239,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _updateDate; } } @@ -285,8 +248,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _version; } } @@ -295,8 +257,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _urlName; } } @@ -305,8 +266,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _level; } } @@ -315,8 +275,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _isDraft; } } @@ -325,9 +284,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); - return _properties; + if (_nodeInitialized == false) InitializeNode(); + return _properties.Values; } } @@ -335,24 +293,26 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { get { - if (_initialized == false) - Initialize(); + if (_nodeInitialized == false) InitializeNode(); return _contentType; } } - private void InitializeStructure() + private void InitializeParent() { - // load parent if it exists and is a node + if (_xmlNode == null) return; - var parent = _xmlNode == null ? null : _xmlNode.ParentNode; + var parent = _xmlNode.ParentNode; if (parent == null) return; if (parent.Name == "node" || (parent.Attributes != null && parent.Attributes.GetNamedItem("isDoc") != null)) - _parent = (new XmlPublishedContent(parent, _isPreviewing, true)).CreateModel(); + _parent = Get(parent, _isPreviewing); + + // warn: this is not thread-safe... + _parentInitialized = true; } - private void Initialize() + private void InitializeNode() { if (_xmlNode == null) return; @@ -423,22 +383,27 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache if (nodes != null) foreach (XmlNode n in nodes) { + var attrs = n.Attributes; + if (attrs == null) continue; var alias = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema - ? n.Attributes.GetNamedItem("alias").Value + ? attrs.GetNamedItem("alias").Value : n.Name; propertyNodes[alias.ToLowerInvariant()] = n; } - _properties = _contentType.PropertyTypes.Select(p => - { - XmlNode n; - return propertyNodes.TryGetValue(p.PropertyTypeAlias.ToLowerInvariant(), out n) - ? new XmlPublishedProperty(p, _isPreviewing, n) - : new XmlPublishedProperty(p, _isPreviewing); - }).Cast().ToArray(); + _properties = _contentType.PropertyTypes.Select(p => + { + XmlNode n; + return propertyNodes.TryGetValue(p.PropertyTypeAlias.ToLowerInvariant(), out n) + ? new XmlPublishedProperty(p, _isPreviewing, n) + : new XmlPublishedProperty(p, _isPreviewing); + }).Cast().ToDictionary( + x => x.PropertyTypeAlias, + x => x, + StringComparer.OrdinalIgnoreCase); // warn: this is not thread-safe... - _initialized = true; + _nodeInitialized = true; } private void InitializeChildren() @@ -449,14 +414,37 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache var childXPath = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema ? "node" : "* [@isDoc]"; var nav = _xmlNode.CreateNavigator(); var expr = nav.Compile(childXPath); - expr.AddSort("@sortOrder", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number); + //expr.AddSort("@sortOrder", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number); var iterator = nav.Select(expr); - while (iterator.MoveNext()) - _children.Add( - (new XmlPublishedContent(((IHasXmlNode)iterator.Current).GetNode(), _isPreviewing, true)).CreateModel()); - + + _children = iterator.Cast() + .Select(n => Get(((IHasXmlNode) n).GetNode(), _isPreviewing)) + .OrderBy(x => x.SortOrder) + .ToList(); + // warn: this is not thread-safe _childrenInitialized = true; } + + /// + /// Gets an IPublishedContent corresponding to an Xml cache node. + /// + /// The Xml node. + /// A value indicating whether we are previewing or not. + /// The IPublishedContent corresponding to the Xml cache node. + /// Maintains a per-request cache of IPublishedContent items in order to make + /// sure that we create only one instance of each for the duration of a request. The + /// returned IPublishedContent is a model, if models are enabled. + public static IPublishedContent Get(XmlNode node, bool isPreviewing) + { + // only 1 per request + + var attrs = node.Attributes; + var id = attrs == null ? null : attrs.GetNamedItem("id").Value; + if (id.IsNullOrWhiteSpace()) throw new InvalidOperationException("Node has no ID attribute."); + var cache = ApplicationContext.Current.ApplicationCache.RequestCache; + var key = "XMLPUBLISHEDCONTENT_" + id; // dont bother with preview, wont change during request in v7 + return (IPublishedContent) cache.GetCacheItem(key, () => (new XmlPublishedContent(node, isPreviewing)).CreateModel()); + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs index d7caf7a83f..704a6a9bc9 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedProperty.cs @@ -2,9 +2,7 @@ using System; using System.Xml; using System.Xml.Serialization; using Umbraco.Core; -using Umbraco.Core.Configuration; using Umbraco.Core.Models.PublishedContent; -using Umbraco.Web.Models; namespace Umbraco.Web.PublishedCache.XmlPublishedCache { @@ -17,10 +15,15 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache internal class XmlPublishedProperty : PublishedPropertyBase { private readonly string _xmlValue; // the raw, xml node value - private readonly Lazy _sourceValue; - private readonly Lazy _objectValue; - private readonly Lazy _xpathValue; - private readonly bool _isPreviewing; + + // in v7 we're not using XPath value so don't allocate that Lazy. + // as for the rest... we're single threaded here, keep it simple + //private readonly Lazy _sourceValue; + //private readonly Lazy _objectValue; + //private readonly Lazy _xpathValue; + private object _objectValue; + private bool _objectValueComputed; + private readonly bool _isPreviewing; /// /// Gets the raw value of the property. @@ -34,10 +37,26 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache get { return _xmlValue.Trim().Length > 0; } } - public override object Value { get { return _objectValue.Value; } } - public override object XPathValue { get { return _xpathValue.Value; } } + public override object Value + { + get + { + // NOT caching the source (intermediate) value since we'll never need it + // everything in Xml cache in v7 is per-request anyways + // also, properties should not be shared between requests and therefore + // are single threaded, so the following code should be safe & fast - public XmlPublishedProperty(PublishedPropertyType propertyType, bool isPreviewing, XmlNode propertyXmlData) + if (_objectValueComputed) return _objectValue; + var sourceValue = PropertyType.ConvertDataToSource(_xmlValue, _isPreviewing); + _objectValue = PropertyType.ConvertSourceToObject(sourceValue, _isPreviewing); + _objectValueComputed = true; + return _objectValue; + } + } + + public override object XPathValue { get { throw new NotImplementedException(); } } + + public XmlPublishedProperty(PublishedPropertyType propertyType, bool isPreviewing, XmlNode propertyXmlData) : this(propertyType, isPreviewing) { if (propertyXmlData == null) @@ -59,9 +78,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _xmlValue = string.Empty; _isPreviewing = isPreviewing; - _sourceValue = new Lazy(() => PropertyType.ConvertDataToSource(_xmlValue, _isPreviewing)); - _objectValue = new Lazy(() => PropertyType.ConvertSourceToObject(_sourceValue.Value, _isPreviewing)); - _xpathValue = new Lazy(() => PropertyType.ConvertSourceToXPath(_sourceValue.Value, _isPreviewing)); + //_sourceValue = new Lazy(() => PropertyType.ConvertDataToSource(_xmlValue, _isPreviewing)); + //_objectValue = new Lazy(() => PropertyType.ConvertSourceToObject(_sourceValue.Value, _isPreviewing)); + //_xpathValue = new Lazy(() => PropertyType.ConvertSourceToXPath(_sourceValue.Value, _isPreviewing)); } } } \ No newline at end of file From f3f53896b7249ea6dff15b25c34f7df18d848e13 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 23 Jun 2016 11:31:32 +0200 Subject: [PATCH 034/389] U4-8469 - fixing --- .../XmlPublishedCache/XmlPublishedContent.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs index 5129542e46..ca7c28dc7e 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs @@ -75,7 +75,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache public override IPublishedProperty GetProperty(string alias) { - IPublishedProperty property; + if (_nodeInitialized == false) InitializeNode(); + IPublishedProperty property; return _properties.TryGetValue(alias, out property) ? property : null; } @@ -93,7 +94,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache var key = string.Format("RECURSIVE_PROPERTY::{0}::{1}", Id, alias.ToLowerInvariant()); var value = cache.GetOrAdd(key, k => base.GetProperty(alias, true)); - if (value == null) + if (value == null) return null; var property = value as IPublishedProperty; @@ -297,12 +298,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return _contentType; } } - + private void InitializeParent() { if (_xmlNode == null) return; - var parent = _xmlNode.ParentNode; + var parent = _xmlNode.ParentNode; if (parent == null) return; if (parent.Name == "node" || (parent.Attributes != null && parent.Attributes.GetNamedItem("isDoc") != null)) @@ -341,7 +342,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _creatorName = _writerName; } - //Added the actual userID, as a user cannot be looked up via full name only... + //Added the actual userID, as a user cannot be looked up via full name only... if (_xmlNode.Attributes.GetNamedItem("creatorID") != null) _creatorId = int.Parse(_xmlNode.Attributes.GetNamedItem("creatorID").Value); if (_xmlNode.Attributes.GetNamedItem("writerID") != null) From 7d25723afd044b35990aeab8cd676600e29a1313 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 23 Jun 2016 13:33:54 +0200 Subject: [PATCH 035/389] U4-8469 - media cache TLC --- .../XmlPublishedCache/PublishedMediaCache.cs | 58 +++++++++++++++---- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 79806b3273..a7ee7aafff 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -224,10 +224,12 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache "Could not retrieve media {0} from Examine index, reverting to looking up media via legacy library.GetMedia method", () => id); - var media = global::umbraco.library.GetMedia(id, false); + //var media = global::umbraco.library.GetMedia(id, false); + //return ConvertFromXPathNodeIterator(media, id); - return ConvertFromXPathNodeIterator(media, id); - } + var media = ApplicationContext.Current.Services.MediaService.GetById(id); + return ConvertFromIMedia(media); + } internal CacheValues ConvertFromXPathNodeIterator(XPathNodeIterator media, int id) { @@ -364,14 +366,46 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache //return content.CreateModel(); } - /// - /// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists - /// in the results, if it does not, then we'll have to revert to looking up in the db. - /// - /// - /// - /// - private IPublishedProperty GetProperty(DictionaryPublishedContent dd, string alias) + internal CacheValues ConvertFromIMedia(IMedia media) + { + var values = new Dictionary(); + + values["id"] = media.Id.ToString(); + values["key"] = media.Key.ToString(); + values["parentID"] = media.ParentId.ToString(); + values["level"] = media.Level.ToString(); + values["creatorID"] = media.CreatorId.ToString(); + values["creatorName"] = ""; + values["writerID"] = "0"; + values["writerName"] = ""; + values["template"] = "0"; + values["urlName"] = ""; + values["sortOrder"] = media.SortOrder.ToString(); + values["createDate"] = media.CreateDate.ToString("yyyy-MM-dd HH:mm:ss"); + values["updateDate"] = media.UpdateDate.ToString("yyyy-MM-dd HH:mm:ss"); + values["nodeName"] = media.Name; + values["path"] = media.Path; + values["nodeType"] = media.ContentType.Id.ToString(); + values["nodeTypeAlias"] = media.ContentType.Alias; + + // add the user props + foreach (var prop in media.Properties) + values[prop.Alias] = prop.Value == null ? null : prop.Value.ToString(); + + return new CacheValues + { + Values = values + }; + } + + /// + /// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists + /// in the results, if it does not, then we'll have to revert to looking up in the db. + /// + /// + /// + /// + private IPublishedProperty GetProperty(DictionaryPublishedContent dd, string alias) { //lets check if the alias does not exist on the document. //NOTE: Examine will not index empty values and we do not output empty XML Elements to the cache - either of these situations @@ -601,7 +635,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _keysAdded.Add(alias); string value; const bool isPreviewing = false; // false :: never preview a media - var property = valueDictionary.TryGetValue(alias, out value) == false + var property = valueDictionary.TryGetValue(alias, out value) == false || value == null ? new XmlPublishedProperty(propertyType, isPreviewing) : new XmlPublishedProperty(propertyType, isPreviewing, value); _properties.Add(property); From 1bfb32dcdcd4e748964dcc5f68be9c8216cfcbc6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 22 Jun 2016 12:08:51 +0200 Subject: [PATCH 036/389] Merge branch 'U4-8588' of https://github.com/JimBobSquarePants/Umbraco-CMS into JimBobSquarePants-U4-8588 # Conflicts: # build/NuSpecs/UmbracoCms.Core.nuspec # src/Umbraco.Web.UI/packages.config --- build/NuSpecs/UmbracoCms.Core.nuspec | 4 +- .../Logging/ImageProcessorLogger.cs | 46 +++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 5 ++ src/Umbraco.Core/packages.config | 1 + src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 8 ++-- src/Umbraco.Web.UI/packages.config | 4 +- 6 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 src/Umbraco.Core/Logging/ImageProcessorLogger.cs diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index a486f8a58b..cbabc01bb7 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -32,8 +32,8 @@ - - + + diff --git a/src/Umbraco.Core/Logging/ImageProcessorLogger.cs b/src/Umbraco.Core/Logging/ImageProcessorLogger.cs new file mode 100644 index 0000000000..02ca9c2949 --- /dev/null +++ b/src/Umbraco.Core/Logging/ImageProcessorLogger.cs @@ -0,0 +1,46 @@ +namespace Umbraco.Core.Logging +{ + using System; + using System.Runtime.CompilerServices; + + using ImageProcessor.Common.Exceptions; + + /// + /// A logger for explicitly logging ImageProcessor exceptions. + /// + /// Creating this logger is enough for ImageProcessor to find and replace its in-built debug logger + /// without any additional configuration required. This class currently has to be public in order + /// to do so. + /// + /// + public sealed class ImageProcessorLogger : ImageProcessor.Common.Exceptions.ILogger + { + /// + /// Logs the specified message as an error. + /// + /// The type calling the logger. + /// The message to log. + /// The property or method name calling the log. + /// The line number where the method is called. + public void Log(string text, [CallerMemberName] string callerName = null, [CallerLineNumber] int lineNumber = 0) + { + // Using LogHelper since the ImageProcessor logger expects a parameterless constructor. + var message = string.Format("{0} {1} : {2}", callerName, lineNumber, text); + LogHelper.Error(string.Empty, new ImageProcessingException(message)); + } + + /// + /// Logs the specified message as an error. + /// + /// The type calling the logger. + /// The message to log. + /// The property or method name calling the log. + /// The line number where the method is called. + public void Log(Type type, string text, [CallerMemberName] string callerName = null, [CallerLineNumber] int lineNumber = 0) + { + // Using LogHelper since the ImageProcessor logger expects a parameterless constructor. + var message = string.Format("{0} {1} : {2}", callerName, lineNumber, text); + LogHelper.Error(type, string.Empty, new ImageProcessingException(message)); + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 3e14fc7758..e3cf2206e2 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -52,6 +52,10 @@ ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll + + ..\packages\ImageProcessor.2.4.1.0\lib\net45\ImageProcessor.dll + True + False ..\packages\log4net-mediumtrust.2.0.0\lib\log4net.dll @@ -353,6 +357,7 @@ + diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config index b69be5b5cc..a9241f46c8 100644 --- a/src/Umbraco.Core/packages.config +++ b/src/Umbraco.Core/packages.config @@ -2,6 +2,7 @@ + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 004b791890..6911829bb8 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -136,12 +136,12 @@ False ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll - - ..\packages\ImageProcessor.2.3.3.0\lib\net45\ImageProcessor.dll + + ..\packages\ImageProcessor.2.4.1.0\lib\net45\ImageProcessor.dll True - - ..\packages\ImageProcessor.Web.4.5.3.0\lib\net45\ImageProcessor.Web.dll + + ..\packages\ImageProcessor.Web.4.6.1.0\lib\net45\ImageProcessor.Web.dll True diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index e85831fd82..7891f928e8 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -5,8 +5,8 @@ - - + + From 1c40d9d0d840fcf1cfc334f8be540aa902ab226f Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 23 Jun 2016 16:53:50 +0200 Subject: [PATCH 037/389] U4-8627 Umbraco.Core.Sync.DatabaseServerMessenger - No last synced Id found when '0' is stored and there are no instructions --- .../Sync/DatabaseServerMessenger.cs | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index d8d828b23e..153b90ba5a 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -344,19 +344,39 @@ namespace Umbraco.Core.Sync /// /// Ensure that the last instruction that was processed is still in the database. /// - /// If the last instruction is not in the database anymore, then the messenger + /// + /// If the last instruction is not in the database anymore, then the messenger /// should not try to process any instructions, because some instructions might be lost, - /// and it should instead cold-boot. + /// and it should instead cold-boot. + /// However, if the last synced instruction id is '0' and there are '0' records, then this indicates + /// that it's a fresh site and no user actions have taken place, in this circumstance we do not want to cold + /// boot. See: http://issues.umbraco.org/issue/U4-8627 + /// private void EnsureInstructions() { - var sql = new Sql().Select("*") + if (_lastId == 0) + { + var sql = new Sql().Select("COUNT(*)") + .From(_appContext.DatabaseContext.SqlSyntax); + + var count = _appContext.DatabaseContext.Database.ExecuteScalar(sql); + + //if there are instructions but we haven't synced, then a cold boot is necessary + if (count > 0) + _lastId = -1; + } + else + { + var sql = new Sql().Select("*") .From(_appContext.DatabaseContext.SqlSyntax) .Where(dto => dto.Id == _lastId); - var dtos = _appContext.DatabaseContext.Database.Fetch(sql); + var dtos = _appContext.DatabaseContext.Database.Fetch(sql); - if (dtos.Count == 0) - _lastId = -1; + //if the last synced instruction is not found in the db, then a cold boot is necessary + if (dtos.Count == 0) + _lastId = -1; + } } /// From 8d55a55a80ee79d4e628b736e0374d300658a7a3 Mon Sep 17 00:00:00 2001 From: Anders Bjerner Date: Thu, 23 Jun 2016 17:45:01 +0200 Subject: [PATCH 038/389] Added a tooltip with icon alias on mouseover --- .../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 46c666b8cc..ed96587cb0 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 @@ -6,7 +6,7 @@
    -
    +
    From 1e12cb8ac5eafc5f734e1a81ff084a680c85e128 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 23 Jun 2016 17:58:01 +0200 Subject: [PATCH 039/389] U4-8649 - fix tests --- .../XmlPublishedCache/PublishedMediaCache.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index a7ee7aafff..47879f2c18 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -228,7 +228,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache //return ConvertFromXPathNodeIterator(media, id); var media = ApplicationContext.Current.Services.MediaService.GetById(id); - return ConvertFromIMedia(media); + return media == null ? null : ConvertFromIMedia(media); } internal CacheValues ConvertFromXPathNodeIterator(XPathNodeIterator media, int id) @@ -370,14 +370,17 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { var values = new Dictionary(); + var creator = _applicationContext.Services.UserService.GetProfileById(media.CreatorId); + var creatorName = creator == null ? "" : creator.Name; + values["id"] = media.Id.ToString(); values["key"] = media.Key.ToString(); values["parentID"] = media.ParentId.ToString(); values["level"] = media.Level.ToString(); values["creatorID"] = media.CreatorId.ToString(); - values["creatorName"] = ""; - values["writerID"] = "0"; - values["writerName"] = ""; + values["creatorName"] = creatorName; + values["writerID"] = media.CreatorId.ToString(); + values["writerName"] = creatorName; values["template"] = "0"; values["urlName"] = ""; values["sortOrder"] = media.SortOrder.ToString(); From 889ef47fd2bb8d0767bbabfcd49ee21db2744acd Mon Sep 17 00:00:00 2001 From: bjarnef Date: Fri, 24 Jun 2016 00:44:45 +0200 Subject: [PATCH 040/389] Update localization of listview bulk actions --- .../listview/listview.controller.js | 95 ++++++++----------- 1 file changed, 41 insertions(+), 54 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 5f3810723a..e68cc71010 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -371,41 +371,34 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie applySelected( function (selected, index) { return deleteItemCallback(getIdCallback(selected[index])); }, function (count, total) { - return "Deleted " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); - //var key = (total === 1 ? "bulk_deletedItemOfItem" : "bulk_deletedItemOfItems"); - //localizationService.localize(key, [count, total]).then(function (value) { - // return value; - //}); + var key = (total === 1 ? "bulk_deletedItemOfItem" : "bulk_deletedItemOfItems"); + localizationService.localize(key, [count, total]).then(function (value) { + return value; + }); }, function (total) { - return "Deleted " + total + " item" + (total > 1 ? "s" : ""); - //var key = (total === 1 ? "bulk_deletedItem" : "bulk_deletedItems"); - //localizationService.localize(key, [total]).then(function (value) { - // return value; - //}); + var key = (total === 1 ? "bulk_deletedItem" : "bulk_deletedItems"); + localizationService.localize(key, [total]).then(function (value) { + return value; + }); }, - //localizationService.localize("defaultdialogs_confirmdelete") + "?" - "Sure you want to delete?"); + localizationService.localize("defaultdialogs_confirmdelete") + "?"); }; $scope.publish = function () { applySelected( function (selected, index) { return contentResource.publishById(getIdCallback(selected[index])); }, function (count, total) { - return "Published " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); - //var key = (total === 1 ? "bulk_publishedItemOfItem" : "bulk_publishedItemOfItems"); - //localizationService.localize(key, [count, total]).then(function (value) { - // console.log(key, value); - // return value; - //}); + var key = (total === 1 ? "bulk_publishedItemOfItem" : "bulk_publishedItemOfItems"); + localizationService.localize(key, [count, total]).then(function (value) { + return value; + }); }, function (total) { - return "Published " + total + " item" + (total > 1 ? "s" : ""); - //var key = (total === 1 ? "bulk_publishedItem" : "bulk_publishedItems"); - //localizationService.localize(key, [total]).then(function (value) { - // console.log(key, value); - // return value; - //}); + var key = (total === 1 ? "bulk_publishedItem" : "bulk_publishedItems"); + localizationService.localize(key, [total]).then(function (value) { + return value; + }); }); }; @@ -413,18 +406,16 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie applySelected( function (selected, index) { return contentResource.unPublish(getIdCallback(selected[index])); }, function (count, total) { - return "Unpublished " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); - //var key = (total === 1 ? "bulk_unpublishedItemOfItem" : "bulk_unpublishedItemOfItems"); - //localizationService.localize(key, [count, total]).then(function (value) { - // return value; - //}); + var key = (total === 1 ? "bulk_unpublishedItemOfItem" : "bulk_unpublishedItemOfItems"); + localizationService.localize(key, [count, total]).then(function (value) { + return value; + }); }, function (total) { - return "Unpublished " + total + " item" + (total > 1 ? "s" : ""); - //var key = (total === 1 ? "bulk_unpublishedItem" : "bulk_unpublishedItems"); - //localizationService.localize(key, [total]).then(function (value) { - // return value; - //}); + var key = (total === 1 ? "bulk_unpublishedItem" : "bulk_unpublishedItems"); + localizationService.localize(key, [total]).then(function (value) { + return value; + }); }); }; @@ -458,18 +449,16 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie applySelected( function (selected, index) { return contentResource.move({ parentId: target.id, id: getIdCallback(selected[index]) }); }, function (count, total) { - return "Moved " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); - //var key = (total === 1 ? "bulk_movedItemOfItem" : "bulk_movedItemOfItems"); - //localizationService.localize(key, [count, total]).then(function (value) { - // return value; - //}); + var key = (total === 1 ? "bulk_movedItemOfItem" : "bulk_movedItemOfItems"); + localizationService.localize(key, [count, total]).then(function (value) { + return value; + }); }, function (total) { - return "Moved " + total + " item" + (total > 1 ? "s" : ""); - //var key = (total === 1 ? "bulk_movedItem" : "bulk_movedItems"); - //localizationService.localize(key, [total]).then(function (value) { - // return value; - //}); + var key = (total === 1 ? "bulk_movedItem" : "bulk_movedItems"); + localizationService.localize(key, [total]).then(function (value) { + return value; + }); }); } @@ -501,18 +490,16 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie applySelected( function (selected, index) { return contentResource.copy({ parentId: target.id, id: getIdCallback(selected[index]), relateToOriginal: relateToOriginal }); }, function (count, total) { - return "Copied " + count + " out of " + total + " item" + (total > 1 ? "s" : ""); - //var key = (total === 1 ? "bulk_copiedItemOfItem" : "bulk_copiedItemOfItems"); - //localizationService.localize(key, [count, total]).then(function (value) { - // return value; - //}); + var key = (total === 1 ? "bulk_copiedItemOfItem" : "bulk_copiedItemOfItems"); + localizationService.localize(key, [count, total]).then(function (value) { + return value; + }); }, function (total) { - return "Copied " + total + " item" + (total > 1 ? "s" : ""); - //var key = (total === 1 ? "bulk_copiedItem" : "bulk_copiedItems"); - //localizationService.localize(key, [total]).then(function (value) { - // return value; - //}); + var key = (total === 1 ? "bulk_copiedItem" : "bulk_copiedItems"); + localizationService.localize(key, [total]).then(function (value) { + return value; + }); }); } From 2e2d0e506fc444bda7d99bf8ac13815d1463a960 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 24 Jun 2016 10:49:07 +0200 Subject: [PATCH 041/389] Properly URL Encodes stylesheet properties --- .../umbraco/Trees/loadStylesheetProperty.cs | 4 ++-- .../umbraco/create/stylesheetPropertyTasks.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadStylesheetProperty.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadStylesheetProperty.cs index 1be4d03f58..197c52925e 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadStylesheetProperty.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadStylesheetProperty.cs @@ -58,12 +58,12 @@ namespace umbraco { var sheetId = sheet.Path.TrimEnd(".css"); var xNode = XmlTreeNode.Create(this); - xNode.NodeID = sheetId + "_" + prop.Name; + xNode.NodeID = sheetId + "_" + HttpUtility.UrlEncode(prop.Name); xNode.Text = prop.Name; xNode.Action = "javascript:openStylesheetProperty('" + //Needs to be escaped for JS HttpUtility.UrlEncode(sheet.Path) + - "','" + prop.Name + "');"; + "','" + HttpUtility.UrlEncode(prop.Name) + "');"; xNode.Icon = "icon-brackets"; xNode.OpenIcon = "icon-brackets"; diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/stylesheetPropertyTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/stylesheetPropertyTasks.cs index 76f1b83190..d1d607a7d8 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/stylesheetPropertyTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/stylesheetPropertyTasks.cs @@ -26,7 +26,7 @@ namespace umbraco s.AddProperty(new StylesheetProperty(Alias, "." + Alias.ToSafeAlias(), "")); Umbraco.Core.ApplicationContext.Current.Services.FileService.SaveStylesheet(s); - _returnUrl = string.Format("settings/stylesheet/property/EditStyleSheetProperty.aspx?id={0}&prop={1}", HttpUtility.UrlEncode(s.Path), Alias); + _returnUrl = string.Format("settings/stylesheet/property/EditStyleSheetProperty.aspx?id={0}&prop={1}", HttpUtility.UrlEncode(s.Path), HttpUtility.UrlEncode(Alias)); return true; } From f420960eea81e18c4091dfac05f3855fffb6c6a6 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 27 Jun 2016 09:50:35 +0200 Subject: [PATCH 042/389] add umb-avatar component + documentation --- .../components/umbavatar.directive.js | 72 +++++++++++++++++++ src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../src/less/components/umb-avatar.less | 30 ++++++++ .../src/views/components/umb-avatar.html | 1 + 4 files changed, 104 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/umbavatar.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/umb-avatar.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/umb-avatar.html diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbavatar.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbavatar.directive.js new file mode 100644 index 0000000000..c9a45fd316 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbavatar.directive.js @@ -0,0 +1,72 @@ +/** +@ngdoc directive +@name umbraco.directives.directive:umbAvatar +@restrict E +@scope + +@description +Use this directive to render an avatar. + +

    Markup example

    +
    +	
    + + + + +
    +
    + +

    Controller example

    +
    +	(function () {
    +		"use strict";
    +
    +		function Controller() {
    +
    +            var vm = this;
    +
    +            vm.avatar = [
    +                { value: "assets/logo.png" },
    +                { value: "assets/logo@2x.png" },
    +                { value: "assets/logo@3x.png" }
    +            ];
    +
    +        }
    +
    +		angular.module("umbraco").controller("My.Controller", Controller);
    +
    +	})();
    +
    + +@param {string} size (attribute): The size of the avatar (xs, s, m, l, xl). +@param {string} src (attribute): The image source to the avatar. +@param {string} srcset (atribute): Reponsive support for the image source. +**/ + +(function() { + 'use strict'; + + function AvatarDirective() { + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-avatar.html', + scope: { + size: "@", + src: "@", + srcset: "@" + } + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbAvatar', AvatarDirective); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index dfc275c4b0..efb677adb8 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -111,6 +111,7 @@ @import "components/umb-packages.less"; @import "components/umb-package-local-install.less"; @import "components/umb-lightbox.less"; +@import "components/umb-avatar.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-avatar.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-avatar.less new file mode 100644 index 0000000000..c4414c2880 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-avatar.less @@ -0,0 +1,30 @@ +.umb-avatar { + border-radius: 50%; + width: 50px; + height: 50px; +} + +.umb-avatar.-xs { + width: 30px; + height: 30px; +} + +.umb-avatar.-s { + width: 40px; + height: 40px; +} + +.umb-avatar.-m { + width: 50px; + height: 50px; +} + +.umb-avatar.-l { + width: 70px; + height: 70px; +} + +.umb-avatar.-xl { + width: 100px; + height: 100px; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-avatar.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-avatar.html new file mode 100644 index 0000000000..4fc0085988 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-avatar.html @@ -0,0 +1 @@ + From f6c74eedf678c083ce809b291932644510c05d51 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 27 Jun 2016 09:52:29 +0200 Subject: [PATCH 043/389] use avatar component in package details --- .../src/less/components/umb-packages.less | 7 ------- .../src/views/packager/views/repo.html | 7 ++++++- 2 files changed, 6 insertions(+), 8 deletions(-) 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 6278e78a6e..d602c24524 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 @@ -453,13 +453,6 @@ border-color: @blue; } -/* Avatar */ - -.umb-avatar { - border-radius: 50%; - width: 50px; -} - /* Progress bar */ .umb-progress-bar { diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html index 28d345ac85..6e0f40d501 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html @@ -143,9 +143,14 @@
    +
    - + +
    +
    {{ vm.package.ownerInfo.owner }}
    From 3773c55633632f5bc3e828f4e09a5489ed0c93c5 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 27 Jun 2016 09:53:40 +0200 Subject: [PATCH 044/389] use avatar component in sections --- .../src/controllers/main.controller.js | 27 ++++++++----------- .../components/application/umb-sections.html | 12 ++++++--- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js index 0d3b56991e..74eb872aa2 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js @@ -97,23 +97,18 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $ function successCallback(response) { // if we can't download the gravatar for some reason, an null gets returned, we cannot do anything if (response.data !== "null") { - $("#avatar-img").fadeTo(1000, 0, function () { - $scope.$apply(function () { - //this can be null if they time out - if ($scope.user && $scope.user.emailHash) { - var avatarBaseUrl = "https://www.gravatar.com/avatar/", - hash = $scope.user.emailHash; + if ($scope.user && $scope.user.emailHash) { + var avatarBaseUrl = "https://www.gravatar.com/avatar/"; + var hash = $scope.user.emailHash; - $scope.avatar = [ - { value: avatarBaseUrl + hash + ".jpg?s=30&d=mm" }, - { value: avatarBaseUrl + hash + ".jpg?s=60&d=mm" }, - { value: avatarBaseUrl + hash + ".jpg?s=90&d=mm" } - ]; - } - }); - $("#avatar-img").fadeTo(1000, 1); - }); + $scope.avatar = [ + { value: avatarBaseUrl + hash + ".jpg?s=30&d=mm" }, + { value: avatarBaseUrl + hash + ".jpg?s=60&d=mm" }, + { value: avatarBaseUrl + hash + ".jpg?s=90&d=mm" } + ]; + } } + }, function errorCallback(response) { //cannot load it from the server so we cannot do anything }); @@ -143,4 +138,4 @@ angular.module('umbraco').controller("Umbraco.MainController", MainController). config(function (tmhDynamicLocaleProvider) { //Set url for locale files tmhDynamicLocaleProvider.localeLocationPattern('lib/angular/1.1.5/i18n/angular-locale_{{locale}}.js'); - }); \ No newline at end of file + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-sections.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-sections.html index 05eb30c242..43af692d3a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-sections.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-sections.html @@ -1,9 +1,13 @@
    -
    +
    • - + +
    • @@ -11,11 +15,11 @@ ng-dblclick="sectionDblClick(section)" ng-click="sectionClick($event, section)" prevent-default> - + {{section.name}}
    • - +
    • From 37f67b505c764921c09de7591d8180b2440161dd Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 27 Jun 2016 14:14:49 +0200 Subject: [PATCH 045/389] Changes the greymed color to be a bit darker (after discussing with mads), makes the forms styles use greymed instead of a custom one, removes the first line from the help (too long) --- src/Umbraco.Web.UI.Client/src/less/forms.less | 2 +- src/Umbraco.Web.UI.Client/src/less/panel.less | 4 ++-- src/Umbraco.Web.UI.Client/src/less/variables.less | 2 +- .../src/views/propertyeditors/datepicker/datepicker.html | 2 +- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 2 +- src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index f9dedf5902..d046ac7104 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -9,7 +9,7 @@ small.umb-detail, label small, .guiDialogTiny { - color: #b3b3b3 !important; + color: @grayMed !important; text-decoration: none; display: block; font-weight: normal; diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index a1776bf12d..c7bb5263de 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -286,14 +286,14 @@ // form styles .umb-dialog .muted, .umb-panel .muted { - color: @grayLight; + color: @grayMed; } .umb-dialog a.muted:hover, .umb-dialog a.muted:focus, .umb-panel a.muted:hover, .umb-panel a.muted:focus { - color: darken(@grayLight, 10%); + color: darken(@grayMed, 10%); } .umb-dialog .text-warning, diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index 5bddbd8022..8ebb0f08ef 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -14,7 +14,7 @@ @grayDarker: #222; @grayDark: #343434; @gray: #555; -@grayMed: #999; +@grayMed: #7f7f7f; @grayLight: #d9d9d9; @grayLighter: #f8f8f8; @white: #fff; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html index 472c0a478b..003e2ada60 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html @@ -19,7 +19,7 @@ Invalid date

      - The time you picked is your local time. This translates to the following time on the server: {{serverTime}}
      + This translates to the following time on the server: {{serverTime}}
      What does this mean?

      diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 0c35794913..7964b4ffcb 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -169,7 +169,7 @@ Not a member of group(s) Child items Target - This translates to the following time on the server:]]> + This translates to the following time on the server: What does this mean?]]> 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 c85053191f..225ee935a4 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -170,7 +170,7 @@ Not a member of group(s) Child items Target - This translates to the following time on the server:]]> + This translates to the following time on the server: What does this mean?]]> From 09526e6db4b04feec8e0e7594bd05c0a8471aa80 Mon Sep 17 00:00:00 2001 From: Stephan Date: Mon, 27 Jun 2016 15:21:21 +0200 Subject: [PATCH 046/389] U4-8592 - fix property conversions --- src/Umbraco.Core/Models/Property.cs | 76 +++---- src/Umbraco.Core/Models/PropertyType.cs | 93 ++++---- src/Umbraco.Core/ObjectExtensions.cs | 207 +++++++++++------- src/Umbraco.Tests/AttemptTests.cs | 14 ++ .../Repositories/ContentRepositoryTest.cs | 69 ++++++ .../TestHelpers/Entities/MockedContent.cs | 15 ++ .../Entities/MockedPropertyTypes.cs | 66 ++++++ src/Umbraco.Tests/TryConvertToTests.cs | 105 +++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 2 + 9 files changed, 470 insertions(+), 177 deletions(-) create mode 100644 src/Umbraco.Tests/TestHelpers/Entities/MockedPropertyTypes.cs create mode 100644 src/Umbraco.Tests/TryConvertToTests.cs diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index 6d7cafe7c9..89f9307ff7 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -133,6 +133,13 @@ namespace Umbraco.Core.Models set { SetPropertyValueAndDetectChanges(value, ref _version, Ps.Value.VersionSelector); } } + private static void ThrowTypeException(object value, Type expected, string alias) + { + throw new Exception(string.Format("Value \"{0}\" of type \"{1}\" could not be converted" + + " to type \"{2}\" which is expected by property type \"{3}\".", + value, value.GetType(), expected, alias)); + } + ///

      /// Gets or Sets the value of the Property /// @@ -146,55 +153,34 @@ namespace Umbraco.Core.Models get { return _value; } set { - bool typeValidation = _propertyType.IsPropertyTypeValid(value); + var isOfExpectedType = _propertyType.IsPropertyTypeValid(value); - if (typeValidation == false) + if (isOfExpectedType == false) // isOfExpectedType is true if value is null - so if false, value is *not* null { - // Normally we'll throw an exception here. However if the property is of a type that can have it's data field (dataInt, dataVarchar etc.) - // changed, we might have a value of the now "wrong" type. As of May 2016 Label is the only built-in property editor that supports this. - // In that case we should try to parse the value and return null if that's not possible rather than throwing an exception. - if (value != null && _propertyType.CanHaveDataValueTypeChanged()) - { - var stringValue = value.ToString(); - switch (_propertyType.DataTypeDatabaseType) - { - case DataTypeDatabaseType.Nvarchar: - case DataTypeDatabaseType.Ntext: - value = stringValue; - break; - case DataTypeDatabaseType.Integer: - int integerValue; - if (int.TryParse(stringValue, out integerValue) == false) - { - // Edge case, but if changed from decimal --> integer, the above tryparse will fail. So we'll try going - // via decimal too to return the integer value rather than zero. - decimal decimalForIntegerValue; - if (decimal.TryParse(stringValue, out decimalForIntegerValue)) - { - integerValue = (int)decimalForIntegerValue; - } - } + // "garbage-in", accept what we can & convert + // throw only if conversion is not possible - value = integerValue; - break; - case DataTypeDatabaseType.Decimal: - decimal decimalValue; - decimal.TryParse(stringValue, out decimalValue); - value = decimalValue; - break; - case DataTypeDatabaseType.Date: - DateTime dateValue; - DateTime.TryParse(stringValue, out dateValue); - value = dateValue; - break; - } - } - else + switch (_propertyType.DataTypeDatabaseType) { - throw new Exception( - string.Format( - "Type validation failed. The value type: '{0}' does not match the DataType in PropertyType with alias: '{1}'", - value == null ? "null" : value.GetType().Name, Alias)); + case DataTypeDatabaseType.Nvarchar: + case DataTypeDatabaseType.Ntext: + value = value.ToString(); + break; + case DataTypeDatabaseType.Integer: + var convInt = value.TryConvertTo(); + if (convInt == false) ThrowTypeException(value, typeof(int), _propertyType.Alias); + value = convInt.Result; + break; + case DataTypeDatabaseType.Decimal: + var convDecimal = value.TryConvertTo(); + if (convDecimal == false) ThrowTypeException(value, typeof(decimal), _propertyType.Alias); + value = convDecimal.Result; + break; + case DataTypeDatabaseType.Date: + var convDateTime = value.TryConvertTo(); + if (convDateTime == false) ThrowTypeException(value, typeof(DateTime), _propertyType.Alias); + value = convDateTime.Result; + break; } } diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index cab2861e28..0aebcb6544 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -48,7 +48,7 @@ namespace Umbraco.Core.Models { _alias = GetAlias(propertyTypeAlias); } - + public PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType) : this(propertyEditorAlias, dataTypeDatabaseType, false) { @@ -56,7 +56,7 @@ namespace Umbraco.Core.Models public PropertyType(string propertyEditorAlias, DataTypeDatabaseType dataTypeDatabaseType, string propertyTypeAlias) : this(propertyEditorAlias, dataTypeDatabaseType, false, propertyTypeAlias) - { + { } /// @@ -298,18 +298,21 @@ namespace Umbraco.Core.Models } /// - /// Validates the Value from a Property according to its type + /// Gets a value indicating whether the value is of the expected type + /// for the property, and can be assigned to the property "as is". /// - /// - /// True if valid, otherwise false + /// The value. + /// True if the value is of the expected type for the property, + /// and can be assigned to the property "as is". Otherwise, false, to indicate + /// that some conversion is required. public bool IsPropertyTypeValid(object value) { - //Can't validate null values, so just allow it to pass the current validation + // null values are assumed to be ok if (value == null) return true; - //Check type if the type of the value match the type from the DataType/PropertyEditor - Type type = value.GetType(); + // check if the type of the value matches the type from the DataType/PropertyEditor + var valueType = value.GetType(); //TODO Add PropertyEditor Type validation when its relevant to introduce /*bool isEditorModel = value is IEditorModel; @@ -326,50 +329,34 @@ namespace Umbraco.Core.Models return argument == type; }*/ - if (PropertyEditorAlias.IsNullOrWhiteSpace() == false) + if (PropertyEditorAlias.IsNullOrWhiteSpace() == false) // fixme - always true? { - //Find DataType by Id - //IDataType dataType = DataTypesResolver.Current.GetById(DataTypeControlId); - //Check if dataType is null (meaning that the ControlId is valid) ? - //Possibly cast to BaseDataType and get the DbType from there (which might not be possible because it lives in umbraco.cms.businesslogic.datatype) ? - - //Simple validation using the DatabaseType from the DataTypeDefinition and Type of the passed in value - if (DataTypeDatabaseType == DataTypeDatabaseType.Integer && type == typeof(int)) - return true; - - if (DataTypeDatabaseType == DataTypeDatabaseType.Decimal && type == typeof(decimal)) - return true; - - if (DataTypeDatabaseType == DataTypeDatabaseType.Date && type == typeof(DateTime)) - return true; - - if (DataTypeDatabaseType == DataTypeDatabaseType.Nvarchar && type == typeof(string)) - return true; - - if (DataTypeDatabaseType == DataTypeDatabaseType.Ntext && type == typeof(string)) - return true; + // simple validation using the DatabaseType from the DataTypeDefinition + // and the Type of the passed in value + switch (DataTypeDatabaseType) + { + // fixme breaking! + case DataTypeDatabaseType.Integer: + return valueType == typeof(int); + case DataTypeDatabaseType.Decimal: + return valueType == typeof(decimal); + case DataTypeDatabaseType.Date: + return valueType == typeof(DateTime); + case DataTypeDatabaseType.Nvarchar: + return valueType == typeof(string); + case DataTypeDatabaseType.Ntext: + return valueType == typeof(string); + } } - //Fallback for simple value types when no Control Id or Database Type is set - if (type.IsPrimitive || value is string) + // fixme - never reached + makes no sense? + // fallback for simple value types when no Control Id or Database Type is set + if (valueType.IsPrimitive || value is string) return true; return false; } - /// - /// Checks the underlying property editor prevalues to see if the one that allows changing of the database field - /// to which data is saved (dataInt, dataVarchar etc.) is included. If so that means the field could be changed when the data - /// type is saved. - /// - /// - internal bool CanHaveDataValueTypeChanged() - { - var propertyEditor = PropertyEditorResolver.Current.GetByAlias(_propertyEditorAlias); - return propertyEditor.PreValueEditor.Fields - .SingleOrDefault(x => x.Key == Constants.PropertyEditors.PreValueKeys.DataValueType) != null; - } - /// /// Validates the Value from a Property according to the validation settings /// @@ -389,15 +376,15 @@ namespace Umbraco.Core.Models var regexPattern = new Regex(ValidationRegExp); return regexPattern.IsMatch(value.ToString()); } - catch + catch { throw new Exception(string .Format("Invalid validation expression on property {0}",this.Alias)); } - + } - + //TODO: We must ensure that the property value can actually be saved based on the specified database type - + //TODO Add PropertyEditor validation when its relevant to introduce /*if (value is IEditorModel && DataTypeControlId != Guid.Empty) { @@ -415,19 +402,19 @@ namespace Umbraco.Core.Models { if (base.Equals(other)) return true; - //Check whether the PropertyType's properties are equal. + //Check whether the PropertyType's properties are equal. return Alias.InvariantEquals(other.Alias); } public override int GetHashCode() { - //Get hash code for the Name field if it is not null. + //Get hash code for the Name field if it is not null. int baseHash = base.GetHashCode(); - //Get hash code for the Alias field. + //Get hash code for the Alias field. int hashAlias = Alias.ToLowerInvariant().GetHashCode(); - //Calculate the hash code for the product. + //Calculate the hash code for the product. return baseHash ^ hashAlias; } @@ -439,7 +426,7 @@ namespace Umbraco.Core.Models //need to manually assign the Lazy value as it will not be automatically mapped if (PropertyGroupId != null) { - clone._propertyGroupId = new Lazy(() => PropertyGroupId.Value); + clone._propertyGroupId = new Lazy(() => PropertyGroupId.Value); } //this shouldn't really be needed since we're not tracking clone.ResetDirtyProperties(false); diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs index a8a1e80eb7..6ed424c232 100644 --- a/src/Umbraco.Core/ObjectExtensions.cs +++ b/src/Umbraco.Core/ObjectExtensions.cs @@ -9,15 +9,28 @@ using System.Xml; namespace Umbraco.Core { + /// + /// Provides object extension methods. + /// public static class ObjectExtensions { //private static readonly ConcurrentDictionary> ObjectFactoryCache = new ConcurrentDictionary>(); + /// + /// + /// + /// + /// + /// public static IEnumerable AsEnumerableOfOne(this T input) { return Enumerable.Repeat(input, 1); } + /// + /// + /// + /// public static void DisposeIfDisposable(this object input) { var disposable = input as IDisposable; @@ -25,7 +38,8 @@ namespace Umbraco.Core } /// - /// Provides a shortcut way of safely casting an input when you cannot guarantee the is an instance type (i.e., when the C# AS keyword is not applicable) + /// Provides a shortcut way of safely casting an input when you cannot guarantee the is + /// an instance type (i.e., when the C# AS keyword is not applicable). /// /// /// The input. @@ -63,30 +77,30 @@ namespace Umbraco.Core } /// - /// Tries to convert the input object to the output type using TypeConverters. If the destination type is a superclass of the input type, - /// if will use . + /// Tries to convert the input object to the output type using TypeConverters. If the destination + /// type is a superclass of the input type, if will use . /// /// The input. /// Type of the destination. /// public static Attempt TryConvertTo(this object input, Type destinationType) { - //if it is null and it is nullable, then return success with null - if (input == null && destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof (Nullable<>)) - { - return Attempt.Succeed(null); + // if null... + if (input == null) + { + // nullable is ok + if (destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof(Nullable<>)) + return Attempt.Succeed(null); + + // value type is nok, else can be null, so is ok + return Attempt.SucceedIf(destinationType.IsValueType == false, null); } - - //if its not nullable and it is a value type - if (input == null && destinationType.IsValueType) return Attempt.Fail(); - //if the type can be null, then no problem - if (input == null && destinationType.IsValueType == false) return Attempt.Succeed(null); + // easy if (destinationType == typeof(object)) return Attempt.Succeed(input); - if (input.GetType() == destinationType) return Attempt.Succeed(input); - //check for string so that overloaders of ToString() can take advantage of the conversion. + // check for string so that overloaders of ToString() can take advantage of the conversion. if (destinationType == typeof(string)) return Attempt.Succeed(input.ToString()); // if we've got a nullable of something, we try to convert directly to that thing. @@ -116,9 +130,10 @@ namespace Umbraco.Core { if (input is string) { + // try convert from string, returns an Attempt if the string could be + // processed (either succeeded or failed), else null if we need to try + // other methods var result = TryConvertToFromString(input as string, destinationType); - - // if we processed the string (succeed or fail), we're done if (result.HasValue) return result.Value; } @@ -201,90 +216,124 @@ namespace Umbraco.Core return Attempt.Fail(); } - private static Nullable> TryConvertToFromString(this string input, Type destinationType) + // returns an attempt if the string has been processed (either succeeded or failed) + // returns null if we need to try other methods + private static Attempt? TryConvertToFromString(this string input, Type destinationType) { + // easy if (destinationType == typeof(string)) return Attempt.Succeed(input); - if (string.IsNullOrEmpty(input)) + // null, empty, whitespaces + if (string.IsNullOrWhiteSpace(input)) { - if (destinationType == typeof(Boolean)) - return Attempt.Succeed(false); // special case for booleans, null/empty == false - if (destinationType == typeof(DateTime)) + if (destinationType == typeof(bool)) // null/empty = bool false + return Attempt.Succeed(false); + if (destinationType == typeof(DateTime)) // null/empty = min DateTime value return Attempt.Succeed(DateTime.MinValue); + + // cannot decide here, + // any of the types below will fail parsing and will return a failed attempt + // but anything else will not be processed and will return null + // so even though the string is null/empty we have to proceed } - // we have a non-empty string, look for type conversions in the expected order of frequency of use... + // look for type conversions in the expected order of frequency of use... if (destinationType.IsPrimitive) { - if (destinationType == typeof(Int32)) + if (destinationType == typeof(int)) // aka Int32 { - Int32 value; - return Int32.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); - } - if (destinationType == typeof(Int64)) - { - Int64 value; - return Int64.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); - } - if (destinationType == typeof(Boolean)) - { - Boolean value; - if (Boolean.TryParse(input, out value)) - return Attempt.Succeed(value); // don't declare failure so the CustomBooleanTypeConverter can try - } - else if (destinationType == typeof(Int16)) - { - Int16 value; - return Int16.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); - } - else if (destinationType == typeof(Double)) - { - Double value; - var input2 = NormalizeNumberDecimalSeparator(input); - return Double.TryParse(input2, out value) ? Attempt.Succeed(value) : Attempt.Fail(); - } - else if (destinationType == typeof(Single)) - { - Single value; + int value; + if (int.TryParse(input, out value)) return Attempt.Succeed(value); + + // because decimal 100.01m will happily convert to integer 100, it + // makes sense that string "100.01" *also* converts to integer 100. + decimal value2; var input2 = NormalizeNumberDecimalSeparator(input); - return Single.TryParse(input2, out value) ? Attempt.Succeed(value) : Attempt.Fail(); - } - else if (destinationType == typeof(Char)) + return Attempt.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt32(value2)); + } + + if (destinationType == typeof(long)) // aka Int64 { - Char value; - return Char.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + long value; + if (long.TryParse(input, out value)) return Attempt.Succeed(value); + + // same as int + decimal value2; + var input2 = NormalizeNumberDecimalSeparator(input); + return Attempt.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt64(value2)); } - else if (destinationType == typeof(Byte)) + + // fixme - should we do the decimal trick for short, byte, unsigned? + + if (destinationType == typeof(bool)) // aka Boolean { - Byte value; - return Byte.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + bool value; + if (bool.TryParse(input, out value)) return Attempt.Succeed(value); + // don't declare failure so the CustomBooleanTypeConverter can try + return null; } - else if (destinationType == typeof(SByte)) + + if (destinationType == typeof(short)) // aka Int16 { - SByte value; - return SByte.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + short value; + return Attempt.SucceedIf(short.TryParse(input, out value), value); } - else if (destinationType == typeof(UInt32)) + + if (destinationType == typeof(double)) // aka Double { - UInt32 value; - return UInt32.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + double value; + var input2 = NormalizeNumberDecimalSeparator(input); + return Attempt.SucceedIf(double.TryParse(input2, out value), value); } - else if (destinationType == typeof(UInt16)) + + if (destinationType == typeof(float)) // aka Single { - UInt16 value; - return UInt16.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + float value; + var input2 = NormalizeNumberDecimalSeparator(input); + return Attempt.SucceedIf(float.TryParse(input2, out value), value); } - else if (destinationType == typeof(UInt64)) + + if (destinationType == typeof(char)) // aka Char { - UInt64 value; - return UInt64.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + char value; + return Attempt.SucceedIf(char.TryParse(input, out value), value); + } + + if (destinationType == typeof(byte)) // aka Byte + { + byte value; + return Attempt.SucceedIf(byte.TryParse(input, out value), value); + } + + if (destinationType == typeof(sbyte)) // aka SByte + { + sbyte value; + return Attempt.SucceedIf(sbyte.TryParse(input, out value), value); + } + + if (destinationType == typeof(uint)) // aka UInt32 + { + uint value; + return Attempt.SucceedIf(uint.TryParse(input, out value), value); + } + + if (destinationType == typeof(ushort)) // aka UInt16 + { + ushort value; + return Attempt.SucceedIf(ushort.TryParse(input, out value), value); + } + + if (destinationType == typeof(ulong)) // aka UInt64 + { + ulong value; + return Attempt.SucceedIf(ulong.TryParse(input, out value), value); } } else if (destinationType == typeof(Guid)) { Guid value; - return Guid.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + return Attempt.SucceedIf(Guid.TryParse(input, out value), value); } else if (destinationType == typeof(DateTime)) { @@ -307,30 +356,30 @@ namespace Umbraco.Core else if (destinationType == typeof(DateTimeOffset)) { DateTimeOffset value; - return DateTimeOffset.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + return Attempt.SucceedIf(DateTimeOffset.TryParse(input, out value), value); } else if (destinationType == typeof(TimeSpan)) { TimeSpan value; - return TimeSpan.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + return Attempt.SucceedIf(TimeSpan.TryParse(input, out value), value); } - else if (destinationType == typeof(Decimal)) + else if (destinationType == typeof(decimal)) // aka Decimal { - Decimal value; + decimal value; var input2 = NormalizeNumberDecimalSeparator(input); - return Decimal.TryParse(input2, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + return Attempt.SucceedIf(decimal.TryParse(input2, out value), value); } else if (destinationType == typeof(Version)) { Version value; - return Version.TryParse(input, out value) ? Attempt.Succeed(value) : Attempt.Fail(); + return Attempt.SucceedIf(Version.TryParse(input, out value), value); } // E_NOTIMPL IPAddress, BigInteger return null; // we can't decide... } - private static readonly char[] NumberDecimalSeparatorsToNormalize = new[] {'.', ','}; + private static readonly char[] NumberDecimalSeparatorsToNormalize = {'.', ','}; private static string NormalizeNumberDecimalSeparator(string s) { diff --git a/src/Umbraco.Tests/AttemptTests.cs b/src/Umbraco.Tests/AttemptTests.cs index 5fd4da7f8e..a912fdf2a7 100644 --- a/src/Umbraco.Tests/AttemptTests.cs +++ b/src/Umbraco.Tests/AttemptTests.cs @@ -37,5 +37,19 @@ namespace Umbraco.Tests i => Assert.AreEqual("finished", i), exception => Assert.Fail("Should have been successful.")); } + + [Test] + public void AttemptIf() + { + // just making sure that it is ok to use TryParse as a condition + + int value; + var attempt = Attempt.If(int.TryParse("1234", out value), value); + Assert.IsTrue(attempt.Success); + Assert.AreEqual(1234, attempt.Result); + + attempt = Attempt.If(int.TryParse("12xxx34", out value), value); + Assert.IsFalse(attempt.Success); + } } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index ce3ce3f22a..abcb5b3d6a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -44,6 +44,14 @@ namespace Umbraco.Tests.Persistence.Repositories base.TearDown(); } + private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out DataTypeDefinitionRepository dtdRepository) + { + TemplateRepository tr; + var ctRepository = CreateRepository(unitOfWork, out contentTypeRepository, out tr); + dtdRepository = new DataTypeDefinitionRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, contentTypeRepository); + return ctRepository; + } + private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository) { TemplateRepository tr; @@ -223,6 +231,67 @@ namespace Umbraco.Tests.Persistence.Repositories } } + /// + /// This test ensures that when property values using special database fields are saved, the actual data in the + /// object being stored is also transformed in the same way as the data being stored in the database is. + /// Before you would see that ex: a decimal value being saved as 100 or "100", would be that exact value in the + /// object, but the value saved to the database was actually 100.000000. + /// When querying the database for the value again - the value would then differ from what is in the object. + /// This caused inconsistencies between saving+publishing and simply saving and then publishing, due to the former + /// sending the non-transformed data directly on to publishing. + /// + [Test] + public void Property_Values_With_Special_DatabaseTypes_Are_Equal_Before_And_After_Being_Persisted() + { + var provider = new PetaPocoUnitOfWorkProvider(Logger); + var unitOfWork = provider.GetUnitOfWork(); + ContentTypeRepository contentTypeRepository; + DataTypeDefinitionRepository dataTypeDefinitionRepository; + using (var repository = CreateRepository(unitOfWork, out contentTypeRepository, out dataTypeDefinitionRepository)) + { + // Setup + var dtd = new DataTypeDefinition(-1, Constants.PropertyEditors.DecimalAlias) { Name = "test", DatabaseType = DataTypeDatabaseType.Decimal }; + dataTypeDefinitionRepository.AddOrUpdate(dtd); + unitOfWork.Commit(); + + const string decimalPropertyAlias = "decimalProperty"; + const string intPropertyAlias = "intProperty"; + const string dateTimePropertyAlias = "datetimeProperty"; + var dateValue = new DateTime(2016, 1, 6); + + var propertyTypeCollection = new PropertyTypeCollection( + new List + { + MockedPropertyTypes.CreateDecimalProperty(decimalPropertyAlias, "Decimal property", dtd.Id), + MockedPropertyTypes.CreateIntegerProperty(intPropertyAlias, "Integer property"), + MockedPropertyTypes.CreateDateTimeProperty(dateTimePropertyAlias, "DateTime property") + }); + var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage1", "Textpage", propertyTypeCollection); + contentTypeRepository.AddOrUpdate(contentType); + unitOfWork.Commit(); + + // Int and decimal values are passed in as strings as they would be from the backoffice UI + var textpage = MockedContent.CreateSimpleContentWithSpecialDatabaseTypes(contentType, "test@umbraco.org", -1, "100", "150", dateValue); + + // Act + repository.AddOrUpdate(textpage); + unitOfWork.Commit(); + + // Assert + Assert.That(contentType.HasIdentity, Is.True); + Assert.That(textpage.HasIdentity, Is.True); + + var persistedTextpage = repository.Get(textpage.Id); + Assert.That(persistedTextpage.Name, Is.EqualTo(textpage.Name)); + Assert.AreEqual(100m, persistedTextpage.GetValue(decimalPropertyAlias)); + Assert.AreEqual(persistedTextpage.GetValue(decimalPropertyAlias), textpage.GetValue(decimalPropertyAlias)); + Assert.AreEqual(150, persistedTextpage.GetValue(intPropertyAlias)); + Assert.AreEqual(persistedTextpage.GetValue(intPropertyAlias), textpage.GetValue(intPropertyAlias)); + Assert.AreEqual(dateValue, persistedTextpage.GetValue(dateTimePropertyAlias)); + Assert.AreEqual(persistedTextpage.GetValue(dateTimePropertyAlias), textpage.GetValue(dateTimePropertyAlias)); + } + } + [Test] public void Ensures_Permissions_Are_Set_If_Parent_Entity_Permissions_Exist() { diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs index 3452b55f39..db6919b24e 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs @@ -79,6 +79,21 @@ namespace Umbraco.Tests.TestHelpers.Entities return content; } + public static Content CreateSimpleContentWithSpecialDatabaseTypes(IContentType contentType, string name, int parentId, string decimalValue, string intValue, DateTime datetimeValue) + { + var content = new Content(name, parentId, contentType) { Language = "en-US", CreatorId = 0, WriterId = 0 }; + object obj = new + { + decimalProperty = decimalValue, + intProperty = intValue, + datetimeProperty = datetimeValue + }; + + content.PropertyValues(obj); + content.ResetDirtyProperties(false); + return content; + } + public static Content CreateAllTypesContent(IContentType contentType, string name, int parentId) { var content = new Content("Random Content Name", parentId, contentType) { Language = "en-US", Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 }; diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedPropertyTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedPropertyTypes.cs new file mode 100644 index 0000000000..b60afe77c9 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedPropertyTypes.cs @@ -0,0 +1,66 @@ +using Umbraco.Core.Models; + +namespace Umbraco.Tests.TestHelpers.Entities +{ + public class MockedPropertyTypes + { + /// + /// Returns a decimal property. + /// Requires a datatype definition Id, since this is not one of the pre-populated datatypes + /// + /// Alias of the created property type + /// Name of the created property type + /// Integer Id of a decimal datatype to use + /// Property type storing decimal value + public static PropertyType CreateDecimalProperty(string alias, string name, int dtdId) + { + return + new PropertyType("test", DataTypeDatabaseType.Decimal, alias) + { + Name = name, + Description = "Decimal property type", + Mandatory = false, + SortOrder = 4, + DataTypeDefinitionId = dtdId + }; + } + + /// + /// Returns a integer property. + /// + /// Alias of the created property type + /// Name of the created property type + /// Property type storing integer value + public static PropertyType CreateIntegerProperty(string alias, string name) + { + return + new PropertyType("test", DataTypeDatabaseType.Integer, alias) + { + Name = name, + Description = "Integer property type", + Mandatory = false, + SortOrder = 4, + DataTypeDefinitionId = -51 + }; + } + + /// + /// Returns a DateTime property. + /// + /// Alias of the created property type + /// Name of the created property type + /// Property type storing DateTime value + public static PropertyType CreateDateTimeProperty(string alias, string name) + { + return + new PropertyType("test", DataTypeDatabaseType.Date, alias) + { + Name = name, + Description = "DateTime property type", + Mandatory = false, + SortOrder = 4, + DataTypeDefinitionId = -36 + }; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TryConvertToTests.cs b/src/Umbraco.Tests/TryConvertToTests.cs new file mode 100644 index 0000000000..7116d98038 --- /dev/null +++ b/src/Umbraco.Tests/TryConvertToTests.cs @@ -0,0 +1,105 @@ +using System; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.ObjectResolution; +using Umbraco.Core.Strings; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests +{ + [TestFixture] + public class TryConvertToTests + { + [SetUp] + public void SetUp() + { + var settings = SettingsForTests.GetDefault(); + ShortStringHelperResolver.Current = new ShortStringHelperResolver(new DefaultShortStringHelper(settings).WithDefaultConfig()); + Resolution.Freeze(); + } + + [TearDown] + public void TearDown() + { + ShortStringHelperResolver.Reset(); + } + + [Test] + public void ConvertToIntegerTest() + { + var conv = "100".TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100, conv.Result); + + conv = "100.000".TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100, conv.Result); + + conv = "100,000".TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100, conv.Result); + + // oops + conv = "100.001".TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100, conv.Result); + + conv = 100m.TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100, conv.Result); + + conv = 100.000m.TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100, conv.Result); + + // oops + conv = 100.001m.TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100, conv.Result); + } + + [Test] + public void ConvertToDecimalTest() + { + var conv = "100".TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100m, conv.Result); + + conv = "100.000".TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100m, conv.Result); + + conv = "100,000".TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100m, conv.Result); + + conv = "100.001".TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100.001m, conv.Result); + + conv = 100m.TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100m, conv.Result); + + conv = 100.000m.TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100m, conv.Result); + + conv = 100.001m.TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100.001m, conv.Result); + + conv = 100.TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(100m, conv.Result); + } + + [Test] + public void ConvertToDateTimeTest() + { + var conv = "2016-06-07".TryConvertTo(); + Assert.IsTrue(conv); + Assert.AreEqual(new DateTime(2016, 6, 7), conv.Result); + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 1959330487..8514378b53 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -178,6 +178,8 @@ + + From 29e45534c51f61439606eeaa9188f97fd49bb80b Mon Sep 17 00:00:00 2001 From: Marc Goodson Date: Sat, 25 Jun 2016 10:54:10 +0100 Subject: [PATCH 047/389] Add an overload to GetDictionaryValue to provide a fallback - U4-8657 --- src/Umbraco.Web/UmbracoHelper.cs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 6d5e33b239..098e56f0dd 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -375,7 +375,21 @@ namespace Umbraco.Web { return CultureDictionary[key]; } - + /// + /// Returns the dictionary value for the key specified, and if empty returns the specified default fall back value + /// + /// key of dictionary item + /// fall back text if dictionary item is empty - Name altText to match Umbraco.Field + /// + public string GetDictionaryValue(string key, string altText) + { + var dictionaryValue = GetDictionaryValue(key); + if (String.IsNullOrWhiteSpace(dictionaryValue)) + { + dictionaryValue = altText; + } + return dictionaryValue; + } /// /// Returns the ICultureDictionary for access to dictionary items /// From 0e61eb79a81b83883e902c9a49eb936ff2289eff Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 27 Jun 2016 18:27:49 +0200 Subject: [PATCH 048/389] U4-8416 Empty recycle bin does not refresh or gives feedback to user --- .../content.emptyrecyclebin.controller.js | 21 ++++++++++++++++--- .../src/views/content/emptyrecyclebin.html | 17 +++++++++++---- .../src/views/media/emptyrecyclebin.html | 15 +++++++++++-- .../media/media.emptyrecyclebin.controller.js | 21 ++++++++++++++++--- src/Umbraco.Web/Editors/ContentController.cs | 3 ++- src/Umbraco.Web/Editors/MediaController.cs | 3 ++- .../WebApi/HttpRequestMessageExtensions.cs | 17 +++++++++++++++ 7 files changed, 83 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.emptyrecyclebin.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.emptyrecyclebin.controller.js index c81f2906d4..9077598424 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.emptyrecyclebin.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.emptyrecyclebin.controller.js @@ -6,18 +6,33 @@ * @description * The controller for deleting content */ -function ContentEmptyRecycleBinController($scope, contentResource, treeService, navigationService) { +function ContentEmptyRecycleBinController($scope, contentResource, treeService, navigationService, notificationsService, $route) { + + $scope.busy = false; $scope.performDelete = function() { //(used in the UI) + $scope.busy = true; $scope.currentNode.loading = true; - contentResource.emptyRecycleBin($scope.currentNode.id).then(function () { + contentResource.emptyRecycleBin($scope.currentNode.id).then(function (result) { + + $scope.busy = false; $scope.currentNode.loading = false; - //TODO: Need to sync tree, etc... + + //show any notifications + if (angular.isArray(result.notifications)) { + for (var i = 0; i < result.notifications.length; i++) { + notificationsService.showNotification(result.notifications[i]); + } + } + treeService.removeChildNodes($scope.currentNode); navigationService.hideMenu(); + + //reload the current view + $route.reload(); }); }; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/emptyrecyclebin.html b/src/Umbraco.Web.UI.Client/src/views/content/emptyrecyclebin.html index bf2d7af543..0416b25520 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/emptyrecyclebin.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/emptyrecyclebin.html @@ -1,8 +1,17 @@
      -
      - - - +
      + +
      +
      +
      +

      + When items are deleted from the recycle bin, they will be gone forever. + Are you sure? +

      + + + +
      diff --git a/src/Umbraco.Web.UI.Client/src/views/media/emptyrecyclebin.html b/src/Umbraco.Web.UI.Client/src/views/media/emptyrecyclebin.html index 5a05e9ca85..4558a21e2a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/emptyrecyclebin.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/emptyrecyclebin.html @@ -1,8 +1,19 @@
      - - + +
      +
      +
      + +

      + When items are deleted from the recycle bin, they will be gone forever. + Are you sure? +

      + + + +
      diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.emptyrecyclebin.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.emptyrecyclebin.controller.js index dd6ceb369e..218654b464 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/media.emptyrecyclebin.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/media/media.emptyrecyclebin.controller.js @@ -6,18 +6,33 @@ * @description * The controller for deleting media */ -function MediaEmptyRecycleBinController($scope, mediaResource, treeService, navigationService) { +function MediaEmptyRecycleBinController($scope, mediaResource, treeService, navigationService, notificationsService, $route) { + + $scope.busy = false; $scope.performDelete = function() { //(used in the UI) + $scope.busy = true; $scope.currentNode.loading = true; - mediaResource.emptyRecycleBin($scope.currentNode.id).then(function () { + mediaResource.emptyRecycleBin($scope.currentNode.id).then(function (result) { + + $scope.busy = false; $scope.currentNode.loading = false; - //TODO: Need to sync tree, etc... + + //show any notifications + if (angular.isArray(result.notifications)) { + for (var i = 0; i < result.notifications.length; i++) { + notificationsService.showNotification(result.notifications[i]); + } + } + treeService.removeChildNodes($scope.currentNode); navigationService.hideMenu(); + + //reload the current view + $route.reload(); }); }; diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 7abdaf7e69..d003636aac 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -462,7 +462,8 @@ namespace Umbraco.Web.Editors public HttpResponseMessage EmptyRecycleBin() { Services.ContentService.EmptyRecycleBin(); - return Request.CreateResponse(HttpStatusCode.OK); + + return Request.CreateNotificationSuccessResponse(Services.TextService.Localize("defaultdialogs/recycleBinIsEmpty")); } /// diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index c718f9ff38..4e3bf1a0ec 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -365,7 +365,8 @@ namespace Umbraco.Web.Editors public HttpResponseMessage EmptyRecycleBin() { Services.MediaService.EmptyRecycleBin(); - return Request.CreateResponse(HttpStatusCode.OK); + + return Request.CreateNotificationSuccessResponse(Services.TextService.Localize("defaultdialogs/recycleBinIsEmpty")); } /// diff --git a/src/Umbraco.Web/WebApi/HttpRequestMessageExtensions.cs b/src/Umbraco.Web/WebApi/HttpRequestMessageExtensions.cs index 04035c0017..00b66094e8 100644 --- a/src/Umbraco.Web/WebApi/HttpRequestMessageExtensions.cs +++ b/src/Umbraco.Web/WebApi/HttpRequestMessageExtensions.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using System.Web; using System.Web.Http; using System.Web.Http.ModelBinding; +using System.Web.Http.Results; using Microsoft.Owin; using Umbraco.Core; using Umbraco.Web.Models.ContentEditing; @@ -124,6 +125,22 @@ namespace Umbraco.Web.WebApi return request.CreateValidationErrorResponse(notificationModel); } + /// + /// Creates a succressful response with notifications in the result to be displayed in the UI + /// + /// + /// + /// + public static HttpResponseMessage CreateNotificationSuccessResponse(this HttpRequestMessage request, string successMessage) + { + var notificationModel = new SimpleNotificationModel + { + Message = successMessage + }; + notificationModel.AddSuccessNotification(successMessage, string.Empty); + return request.CreateResponse(HttpStatusCode.OK, notificationModel); + } + /// /// Create a 400 response message indicating that a validation error occurred /// From db796905012844351e193d85a17e87c53c697d8d Mon Sep 17 00:00:00 2001 From: Stephan Date: Tue, 28 Jun 2016 10:09:11 +0200 Subject: [PATCH 049/389] U4-8592 - fix decimal scaling factor issue --- src/Umbraco.Core/DecimalExtensions.cs | 23 +++++++++++++++++++ src/Umbraco.Core/Models/Property.cs | 4 +++- .../Models/Rdbms/PropertyDataDto.cs | 16 ++++++++++++- .../Persistence/Factories/PropertyFactory.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + 5 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/Umbraco.Core/DecimalExtensions.cs diff --git a/src/Umbraco.Core/DecimalExtensions.cs b/src/Umbraco.Core/DecimalExtensions.cs new file mode 100644 index 0000000000..b4d74fdb2e --- /dev/null +++ b/src/Umbraco.Core/DecimalExtensions.cs @@ -0,0 +1,23 @@ +namespace Umbraco.Core +{ + /// + /// Provides extension methods for System.Decimal. + /// + /// See System.Decimal on MSDN and also + /// http://stackoverflow.com/questions/4298719/parse-decimal-and-filter-extra-0-on-the-right/4298787#4298787. + /// + public static class DecimalExtensions + { + /// + /// Gets the normalized value. + /// + /// The value to normalize. + /// The normalized value. + /// Normalizing changes the scaling factor and removes trailing zeroes, + /// so 1.2500m comes out as 1.25m. + public static decimal Normalize(this decimal value) + { + return value / 1.000000000000000000000000000000000m; + } + } +} diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs index 89f9307ff7..49f336226b 100644 --- a/src/Umbraco.Core/Models/Property.cs +++ b/src/Umbraco.Core/Models/Property.cs @@ -174,7 +174,9 @@ namespace Umbraco.Core.Models case DataTypeDatabaseType.Decimal: var convDecimal = value.TryConvertTo(); if (convDecimal == false) ThrowTypeException(value, typeof(decimal), _propertyType.Alias); - value = convDecimal.Result; + // need to normalize the value (change the scaling factor and remove trailing zeroes) + // because the underlying database is going to mess with the scaling factor anyways. + value = convDecimal.Result.Normalize(); break; case DataTypeDatabaseType.Date: var convDateTime = value.TryConvertTo(); diff --git a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs index 63e3104d12..380eaa3034 100644 --- a/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/PropertyDataDto.cs @@ -33,9 +33,23 @@ namespace Umbraco.Core.Models.Rdbms [NullSetting(NullSetting = NullSettings.Null)] public int? Integer { get; set; } + private decimal? _decimalValue; + [Column("dataDecimal")] [NullSetting(NullSetting = NullSettings.Null)] - public decimal? Decimal { get; set; } + public decimal? Decimal + { + get + { + return _decimalValue; + } + set + { + // need to normalize the value (change the scaling factor and remove trailing zeroes) + // because the underlying database probably has messed with the scaling factor. + _decimalValue = value.HasValue ? (decimal?) value.Value.Normalize() : null; + } + } [Column("dataDate")] [NullSetting(NullSetting = NullSettings.Null)] diff --git a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs index a69539e9ea..446bd426ad 100644 --- a/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/PropertyFactory.cs @@ -99,7 +99,7 @@ namespace Umbraco.Core.Persistence.Factories decimal val; if (decimal.TryParse(property.Value.ToString(), out val)) { - dto.Decimal = val; + dto.Decimal = val; // property value should be normalized already } } else if (property.DataTypeDatabaseType == DataTypeDatabaseType.Date && property.Value != null && string.IsNullOrWhiteSpace(property.Value.ToString()) == false) diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 3e14fc7758..e303ef4993 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -305,6 +305,7 @@ + From 72a319572a6c01d9b931b0b9ca3f8f96c160be10 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 27 Jun 2016 10:36:22 +0200 Subject: [PATCH 050/389] add umb-progress-bar component --- .../components/umbprogressbar.directive.js | 41 +++++++++++++++++++ src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../src/less/components/umb-progress-bar.less | 20 +++++++++ .../views/components/umb-progress-bar.html | 3 ++ 4 files changed, 65 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/common/directives/components/umbprogressbar.directive.js create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/umb-progress-bar.less create mode 100644 src/Umbraco.Web.UI.Client/src/views/components/umb-progress-bar.html diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbprogressbar.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbprogressbar.directive.js new file mode 100644 index 0000000000..77bab9f023 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbprogressbar.directive.js @@ -0,0 +1,41 @@ + +/** +@ngdoc directive +@name umbraco.directives.directive:umbProgressBar +@restrict E +@scope + +@description +Use this directive to generate a progress bar. + +

      Markup example

      +
      +    
      +    
      +
      + +@param {number} percentage (attribute): The progress in percentage. +**/ + +(function() { + 'use strict'; + + function ProgressBarDirective() { + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-progress-bar.html', + scope: { + percentage: "@" + } + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbProgressBar', ProgressBarDirective); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index efb677adb8..8700bb8f36 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -112,6 +112,7 @@ @import "components/umb-package-local-install.less"; @import "components/umb-lightbox.less"; @import "components/umb-avatar.less"; +@import "components/umb-progress-bar.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-progress-bar.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-progress-bar.less new file mode 100644 index 0000000000..e7d54bca59 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-progress-bar.less @@ -0,0 +1,20 @@ +.umb-progress-bar { + background: @grayLight; + width: 100%; + display: block; + height: 10px; + border-radius: 10px; + box-sizing: border-box; + position: relative; + overflow: hidden; +} + +.umb-progress-bar__progress { + background: #50C878; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 100%; + border-radius: 10px; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-progress-bar.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-progress-bar.html new file mode 100644 index 0000000000..d3f7c2f2d1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-progress-bar.html @@ -0,0 +1,3 @@ +
      + +
      From 63b90ad0c9e354268416a64cb89abf70ac88a3c1 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Mon, 27 Jun 2016 10:37:00 +0200 Subject: [PATCH 051/389] use umb-progress-bar in package details --- .../src/less/components/umb-packages.less | 23 ------------------- .../src/views/packager/views/repo.html | 8 ++++--- 2 files changed, 5 insertions(+), 26 deletions(-) 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 d602c24524..6c8a7156bc 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 @@ -453,29 +453,6 @@ border-color: @blue; } -/* Progress bar */ - -.umb-progress-bar { - background: @grayLight; - width: 100%; - display: block; - height: 10px; - border-radius: 10px; - box-sizing: border-box; - position: relative; - overflow: hidden; -} - -.umb-progress-bar__progress { - background: #50C878; - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: 100%; - border-radius: 10px; -} - /* PACKAGE LIST */ .umb-package-list { diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html index 6e0f40d501..1849b3694f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html @@ -217,9 +217,11 @@ {{compatibility.version}} ({{compatibility.percentage}}%)
      -
      - -
      + + + + From e12b9665ab9b29c62d39321bfccc86bea9c8f188 Mon Sep 17 00:00:00 2001 From: Simon Busborg Date: Wed, 22 Jun 2016 12:54:08 +0200 Subject: [PATCH 052/389] Added better ui for multiple screen resolutions, added success screens for installer, added better ui for uninstall and accept term screens --- src/Umbraco.Web.UI.Client/src/less/belle.less | 5 ++ .../components/umb-package-local-install.less | 32 +++++++---- .../src/less/components/umb-packages.less | 53 +++++++++++++++++-- .../src/less/utilities/umb-utility_flex.less | 20 +++++++ .../less/utilities/umb-utility_spacing.less | 17 ++++++ .../less/utilities/umb-utility_widths.less | 4 ++ .../views/install-local.controller.js | 19 +++---- .../views/packager/views/install-local.html | 35 +++++++++--- .../src/views/packager/views/installed.html | 18 +++---- .../views/packager/views/repo.controller.js | 35 ++++++------ .../src/views/packager/views/repo.html | 44 +++++++++++---- 11 files changed, 212 insertions(+), 70 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_flex.less create mode 100644 src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_spacing.less create mode 100644 src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_widths.less diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 8700bb8f36..ea7f3a2354 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -120,6 +120,11 @@ @import "components/notifications/umb-notifications.less"; @import "components/umb-file-dropzone.less"; +// Utilities +@import "utilities/umb-utility_flex.less"; +@import "utilities/umb-utility_spacing.less"; +@import "utilities/umb-utility_widths.less"; + //page specific styles @import "pages/document-type-editor.less"; @import "pages/login.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-package-local-install.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-package-local-install.less index a272a40c90..c36184394f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-package-local-install.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-package-local-install.less @@ -7,16 +7,7 @@ color: @grayMed; } -.inline-flex { - display: inline-flex; -} - -.mt-3 { - margin-top: 30px; -} - // Upload state - .umb-upload-local, .umb-info-local { display: flex; @@ -36,6 +27,7 @@ margin: 0; max-width: 640px; + width: 100%; } .umb-upload-local__dropzone { @@ -151,3 +143,25 @@ .umb-info-local-items .umb-package-installer-label { margin-left: 10px; } + +.umb-info-success { + +} + +.umb-info-success .icon { + font-size: 62px; + + width: 35px; + height: 35px; + padding: 35px; + margin-bottom: 35px; + + background-color: #50C878; + color: white; + + border-radius: 50%; + + display: flex; + justify-content: center; + align-items: center; +} 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 6c8a7156bc..caeff6507d 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 @@ -52,11 +52,55 @@ .umb-package { padding: 10px; box-sizing: border-box; - width: 25%; + flex: 0 0 100%; + max-width: 100%; +} + +@media (min-width: 768px) { + .umb-package { + flex: 0 0 50%; + max-width: 50%; + } +} + +@media (min-width: 1200px) { + .umb-package { + flex: 0 0 33.33%; + max-width: 33.33%; + } +} + +@media (min-width: 1400px) { + .umb-package { + flex: 0 0 25%; + max-width: 25%; + } +} + +@media (min-width: 1700px) { + .umb-package { + flex: 0 0 20%; + max-width: 20%; + } +} + + +@media (min-width: 1900px) { + .umb-package { + flex: 0 0 16.66%; + max-width: 16.66%; + } +} + +@media (min-width: 2200px) { + .umb-package { + flex: 0 0 14.28%; + max-width: 14.28%; + } } .umb-package-link { - display: flex; + display: block; flex-wrap: wrap; flex-direction: column; justify-content: center; @@ -104,6 +148,7 @@ .umb-package-icon img { max-width: 70px; + width: 70px; height: auto; } @@ -304,6 +349,7 @@ padding: 10px 0; } + .umb-packages-category:hover, .umb-packages-category.-active { text-decoration: none; @@ -440,12 +486,13 @@ } .umb-gallery__thumbnail { - flex: 1 1 100px; + flex: 0 1 100px; border: 1px solid @grayLight; border-radius: 3px; margin: 5px; padding: 10px; box-sizing: border-box; + max-width: 100px; } .umb-gallery__thumbnail:hover { diff --git a/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_flex.less b/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_flex.less new file mode 100644 index 0000000000..747b4047d0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_flex.less @@ -0,0 +1,20 @@ +// Flex +.flex { + display: flex; +} + +.flex-inline { + display: inline-flex; +} + +.flex-direction-column { + flex-direction: column; +} + +.flex-justify-center { + justify-content: center; +} + +.flex-align-center { + align-items: center; +} diff --git a/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_spacing.less b/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_spacing.less new file mode 100644 index 0000000000..c05dd99a2a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_spacing.less @@ -0,0 +1,17 @@ +// Margin +.ma-center { + margin-left: auto; + margin-right: auto; +} + +.mt-3 { + margin-top: 30px; +} + +.mt-2 { + margin-top: 20px; +} + +.mt-1 { + margin-top: 10px; +} diff --git a/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_widths.less b/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_widths.less new file mode 100644 index 0000000000..99ae7da380 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_widths.less @@ -0,0 +1,4 @@ +// Widths +.width-100 { + width: 100%; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js index c68b31038f..f96df4d2f1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.controller.js @@ -16,6 +16,7 @@ uploadProgress: 0, serverErrorMessage: null }; + vm.reloadView = reloadView; $scope.handleFiles = function (files, event) { for (var i = 0; i < files.length; i++) { @@ -121,23 +122,17 @@ }, installError) .then(function(result) { - - if (result.postInstallationPath) { - //Put the redirect Uri in a cookie so we can use after reloading - window.localStorage.setItem("packageInstallUri", result.postInstallationPath); - } - //reload on next digest (after cookie) - $timeout(function() { - window.location.reload(true); - }); - - + vm.state = "packageInstalledSucces"; }, installError); } + function reloadView() { + window.location.reload(true); + } + function installError() { - //TODO: Need to do something about this? + //TODO: Need to do something about this? } } diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html index b25facae7f..25901d5e75 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html @@ -82,18 +82,18 @@ -
      +
      + -
      This package cannot be installed, it requires a minimum Umbraco version of {{vm.localPackage.umbracoVersion}} @@ -109,4 +109,27 @@
      + +
      +
      + +
      + +
      + +

      Packed was installed

      +

      + Yippy, the package was installed successfully – now let's take it to use. +

      + + + +
      +
      + +
      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 403406545c..307b6fdb02 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 @@ -43,7 +43,7 @@
      - +
      @@ -74,18 +74,18 @@ {{ vm.package.readme }}
      -
      - +
      +

      {{vm.installState.status}}

      diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js index a28f1a761b..6188f365cd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.controller.js @@ -26,11 +26,12 @@ vm.installPackage = installPackage; vm.downloadPackage = downloadPackage; vm.openLightbox = openLightbox; + vm.reloadView = reloadView; vm.closeLightbox = closeLightbox; //used to cancel any request in progress if another one needs to take it's place var canceler = null; - + function getActiveCategory() { if (vm.searchQuery !== "") { return ""; @@ -38,7 +39,7 @@ for (var i = 0; i < vm.categories.length; i++) { if (vm.categories[i].active === true) { return vm.categories[i].name; - } + } } return ""; } @@ -73,7 +74,7 @@ if (vm.searchQuery) { if (newVal !== null && newVal !== undefined && newVal !== oldVal) { vm.loading = true; - + //a canceler exists, so perform the cancelation operation and reset if (canceler) { canceler.resolve(); @@ -97,7 +98,7 @@ canceler = null; }); } - } + } }); }, 200)); @@ -121,7 +122,7 @@ searchCategory = ""; } - $q.all([ + $q.all([ ourPackageRepositoryResource.getPopular(8, searchCategory) .then(function(pack) { vm.popular = pack.packages; @@ -187,21 +188,21 @@ vm.packageViewState = "packageInstall"; vm.loading = false; vm.localPackage = pack; - vm.localPackage.allowed = true; + vm.localPackage.allowed = true; }, error); } function error(e, args) { - + } function installPackage(selectedPackage) { - + vm.installState.status = "importing..."; packageResource - .import(selectedPackage) + .import(selectedPackage) .then(function(pack) { vm.installState.status = "Installing..."; return packageResource.installFiles(pack); @@ -218,21 +219,15 @@ }, error) .then(function(result) { - - if (result.postInstallationPath) { - //Put the redirect Uri in a cookie so we can use after reloading - window.localStorage.setItem("packageInstallUri", result.postInstallationPath); - } - - //reload on next digest (after cookie) - $timeout(function () { - window.location.reload(true); - }); - + vm.packageViewState = "packageInstalledSucces"; }, error); } + function reloadView() { + window.location.reload(true); + } + function openLightbox(itemIndex, items) { vm.lightbox = { show: true, diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html index 1849b3694f..ec7c5d039b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html @@ -4,7 +4,7 @@
      - +
      +
      @@ -242,7 +242,7 @@
      - +
      @@ -278,18 +278,18 @@
      -
      - +
      +
      This package cannot be installed, it requires a minimum Umbraco version of {{vm.localPackage.umbracoVersion}} @@ -304,4 +304,26 @@
      + +
      +
      + +
      + +
      + +

      Packed was installed

      +

      + Yippy, the package was installed successfully – now let's take it to use. +

      + + + +
      +
      +
      From dbec2c207f8bce6ba5cdbce6a3c9d4b32cf0a3f7 Mon Sep 17 00:00:00 2001 From: Simon Busborg Date: Wed, 22 Jun 2016 13:05:19 +0200 Subject: [PATCH 053/389] Package category navigation fixed for all screen resolutions --- .../src/less/components/umb-packages.less | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) 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 caeff6507d..2bff88758e 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 @@ -350,6 +350,43 @@ } +@media (max-width: 768px) { + .umb-packages-category { + width: 100%; + } +} + +@media (max-width: 992px) { + .umb-packages-category { + border: 1px solid @grayLight; + margin: 5px; + flex: 0 0 auto; + + text-align: center; + padding: 10px; + + max-width: 100%; + + border-radius: 3px; + } +} + +@media (min-width: 1100px) and (max-width: 1300px) { + .umb-packages-category { + border: 1px solid @grayLight; + margin: 5px; + flex: 0 0 auto; + + text-align: center; + padding: 10px; + + max-width: 100%; + + border-radius: 3px; + } +} + + .umb-packages-category:hover, .umb-packages-category.-active { text-decoration: none; @@ -358,12 +395,14 @@ .umb-packages-category.-first { border-left: 1px solid @grayLight; - border-radius: 3px 0 0 3px; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; } .umb-packages-category.-last { border-right: 1px solid @grayLight; - border-radius: 0 3px 3px 0; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; } /* PACKAGE DETAILS */ From c280a0a5abb3db5d5483dbd94a496c122f031c90 Mon Sep 17 00:00:00 2001 From: Simon Busborg Date: Wed, 22 Jun 2016 13:40:01 +0200 Subject: [PATCH 054/389] Added new utility in text-align and added empty message for search --- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../src/less/utilities/umb-utility_text-align.less | 4 ++++ .../src/views/packager/views/repo.html | 9 ++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_text-align.less diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index ea7f3a2354..c19eb4c847 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 @@ // Utilities @import "utilities/umb-utility_flex.less"; @import "utilities/umb-utility_spacing.less"; +@import "utilities/umb-utility_text-align.less"; @import "utilities/umb-utility_widths.less"; //page specific styles diff --git a/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_text-align.less b/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_text-align.less new file mode 100644 index 0000000000..c25f1c5cff --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_text-align.less @@ -0,0 +1,4 @@ +// Text align +.tl { text-align: left; } +.tr { text-align: right; } +.tc { text-align: center; } diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html index ec7c5d039b..00f301f5d0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/repo.html @@ -12,6 +12,7 @@
      +
      -
      +

      Latest

      @@ -96,6 +97,12 @@
      + + +
      +

      We couldn't find anything for '{{ vm.searchQuery }}'

      +

      Please try searching for another package or browse through the categories.

      +
      From 9602a52801cca01d2e64058f6d4be330d5f75c42 Mon Sep 17 00:00:00 2001 From: Simon Busborg Date: Wed, 22 Jun 2016 13:52:49 +0200 Subject: [PATCH 055/389] added better utility for flexbox and changed classes in views --- .../src/less/utilities/umb-utility_flex.less | 55 +++++++++++++------ .../less/utilities/umb-utility_widths.less | 23 ++++++++ .../views/packager/views/install-local.html | 4 +- .../src/views/packager/views/installed.html | 4 +- .../src/views/packager/views/repo.html | 10 ++-- 5 files changed, 70 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_flex.less b/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_flex.less index 747b4047d0..73f58dde8d 100644 --- a/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_flex.less +++ b/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_flex.less @@ -1,20 +1,39 @@ -// Flex -.flex { - display: flex; +.flex { display: flex } +.flex-inline { display: flex-inline } + +.flex-column { flex-direction: column; } +.flex-wrap { flex-wrap: wrap; } + +.items-start { align-items: flex-start; } +.items-end { align-items: flex-end; } +.items-center { align-items: center; } +.items-baseline { align-items: baseline; } +.items-stretch { align-items: stretch; } + +.self-start { align-self: flex-start; } +.self-end { align-self: flex-end; } +.self-center { align-self: center; } +.self-baseline { align-self: baseline; } +.self-stretch { align-self: stretch; } + +.justify-start { justify-content: flex-start; } +.justify-end { justify-content: flex-end; } +.justify-center { justify-content: center; } +.justify-between { justify-content: space-between; } +.justify-around { justify-content: space-around; } + +.content-start { align-content: flex-start; } +.content-end { align-content: flex-end; } +.content-center { align-content: center; } +.content-between { align-content: space-between; } +.content-around { align-content: space-around; } +.content-stretch { align-content: stretch; } + +/* 1. Fix for Chrome 44 bug. https://code.google.com/p/chromium/issues/detail?id=506893 */ +.flex-auto { + flex: 1 1 auto; + min-width: 0; /* 1 */ + min-height: 0; /* 1 */ } -.flex-inline { - display: inline-flex; -} - -.flex-direction-column { - flex-direction: column; -} - -.flex-justify-center { - justify-content: center; -} - -.flex-align-center { - align-items: center; -} +.flex-none { flex: none; } diff --git a/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_widths.less b/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_widths.less index 99ae7da380..c9fbba3798 100644 --- a/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_widths.less +++ b/src/Umbraco.Web.UI.Client/src/less/utilities/umb-utility_widths.less @@ -1,4 +1,27 @@ // Widths +.width-1 { width: calc(1/12 * 100%); } +.width-2 { width: calc(2/12 * 100%); } +.width-3 { width: calc(3/12 * 100%); } +.width-4 { width: calc(4/12 * 100%); } +.width-5 { width: calc(5/12 * 100%); } +.width-6 { width: calc(6/12 * 100%); } +.width-7 { width: calc(7/12 * 100%); } +.width-8 { width: calc(8/12 * 100%); } +.width-9 { width: calc(9/12 * 100%); } +.width-10 { width: calc(10/12 * 100%); } +.width-11 { width: calc(11/12 * 100%); } +.width-12 { width: 100%; } + .width-100 { width: 100%; } + +.width { + float: left; + box-sizing: border-box; +} + +.width-right { + float: right; + box-sizing: border-box; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html index 25901d5e75..64e37713e9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html +++ b/src/Umbraco.Web.UI.Client/src/views/packager/views/install-local.html @@ -82,7 +82,7 @@
      -
      +
      terms of use @@ -90,7 +90,7 @@ 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 307b6fdb02..25cf187e67 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 @@ -74,7 +74,7 @@ {{ vm.package.readme }}
      -
      +
      - -
      + +

      We couldn't find anything for '{{ vm.searchQuery }}'

      Please try searching for another package or browse through the categories.

      + +
      @@ -285,7 +287,7 @@
      -
      +