From 47986ec0f59a6f73544ac312739c4a79aa1fd66d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 30 Jul 2020 15:01:29 +0200 Subject: [PATCH 01/89] limit control-label effect to the first control-label, these 3 new lines covers all cases that i could find in CMS. --- src/Umbraco.Web.UI.Client/src/less/mixins.less | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/mixins.less b/src/Umbraco.Web.UI.Client/src/less/mixins.less index a87080a326cb..9739a90dae7a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/mixins.less +++ b/src/Umbraco.Web.UI.Client/src/less/mixins.less @@ -138,7 +138,9 @@ // additional targetting of the ng-invalid class. .formFieldState(@textColor: @gray-4, @borderColor: @gray-7, @backgroundColor: @gray-10) { // Set the text color - .control-label, + > .control-label, + > .umb-el-wrap > .control-label, + > .umb-el-wrap > .control-header > .control-label, .help-block, .help-inline { color: @textColor; From 55805eabc3ab87571f23b9f40e60cc8fa4b7ea6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 30 Jul 2020 15:26:40 +0200 Subject: [PATCH 02/89] make the required-star as bold as possible. --- src/Umbraco.Web.UI.Client/src/less/main.less | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less index ce20b8dc88f5..2354e96d38c7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/main.less +++ b/src/Umbraco.Web.UI.Client/src/less/main.less @@ -272,6 +272,7 @@ label:not([for]) { /* CONTROL VALIDATION */ .umb-control-required { color: @controlRequiredColor; + font-weight: 900; } .controls-row { From 33afbb8af6c4cb9127d46a36d447adb3e99b69b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 30 Jul 2020 16:13:22 +0200 Subject: [PATCH 03/89] added validation-error badge to navigation item --- .../src/less/components/umb-editor-navigation-item.less | 8 ++++++++ .../components/editor/umb-editor-navigation-item.html | 1 + 2 files changed, 9 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less index cb673e3c6f79..c2f3e0f04d15 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-editor-navigation-item.less @@ -101,6 +101,10 @@ height: 12px; min-width: 12px; } + &.--error-badge { + display: none; + font-weight: 900; + } } &-text { @@ -191,4 +195,8 @@ &::before { background-color: @red; } + .badge.--error-badge { + display: block; + } + } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html index 484e0175c560..9e5669f443e4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-navigation-item.html @@ -9,6 +9,7 @@ {{ vm.item.name }}
{{vm.item.badge.count}}
+
!
@param {string} name The content name. +@param {boolean=} nameRequired Require name to be defined. (True by default) @param {array=} tabs Array of tabs. See example above. @param {array=} navigation Array of sub views. See example above. @param {boolean=} nameLocked Set to true to lock the name. @@ -199,7 +200,7 @@ Use this directive to construct a header inside the main editor window. @param {boolean=} hideAlias Set to true to hide alias. @param {string=} description Add a description to the content. @param {boolean=} hideDescription Set to true to hide description. -@param {boolean=} setpagetitle If true the page title will be set to reflect the type of data the header is working with +@param {boolean=} setpagetitle If true the page title will be set to reflect the type of data the header is working with @param {string=} editorfor The localization to use to aid accessibility on the edit and create screen **/ @@ -207,7 +208,7 @@ Use this directive to construct a header inside the main editor window. 'use strict'; function EditorHeaderDirective(editorService, localizationService, editorState, $rootScope) { - + function link(scope, $injector) { scope.vm = {}; @@ -329,11 +330,11 @@ Use this directive to construct a header inside the main editor window. } scope.accessibility.a11yMessageVisible = !isEmptyOrSpaces(scope.accessibility.a11yMessage); scope.accessibility.a11yNameVisible = !isEmptyOrSpaces(scope.accessibility.a11yName); - + }); } - + function isEmptyOrSpaces(str) { return str === null || str===undefined || str.trim ===''; @@ -348,7 +349,7 @@ Use this directive to construct a header inside the main editor window. }); } - + var directive = { transclude: true, @@ -358,6 +359,7 @@ Use this directive to construct a header inside the main editor window. scope: { name: "=", nameLocked: "=", + nameRequired: "=?", menu: "=", hideActionsMenu: " From b42bd51392acf2b0f767d5aac878dc25fe382adc Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Thu, 13 Aug 2020 09:02:18 +0200 Subject: [PATCH 49/89] Add partial views tests --- .../integration/Settings/partialsViews.ts | 152 +++++++++++++++--- 1 file changed, 131 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViews.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViews.ts index b644c6642b51..5fa4e151089d 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViews.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViews.ts @@ -1,35 +1,145 @@ /// +import { PartialViewBuilder } from "umbraco-cypress-testhelpers"; + context('Partial Views', () => { - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); + beforeEach(() => { + cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); + }); + + function navigateToSettings() { + cy.umbracoSection('settings'); + cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); + } + + function openPartialViewsCreatePanel() { + navigateToSettings(); + cy.umbracoTreeItem("settings", ["Partial Views"]).rightclick(); + } + + it('Create new empty partial view', () => { + const name = "TestPartialView"; + const fileName = name + ".cshtml"; + + cy.umbracoEnsurePartialViewNameNotExists(fileName); + + openPartialViewsCreatePanel(); + + cy.umbracoContextMenuAction("action-create").click(); + cy.get('.menu-label').first().click(); // TODO: Fucked we cant use something like cy.umbracoContextMenuAction("action-mediaType").click(); + + //Type name + cy.umbracoEditorHeaderName(name); + + //Save + cy.get('.btn-success').click(); + + //Assert + cy.umbracoSuccessNotification().should('be.visible'); + cy.umbracoPartialViewExists(fileName).then(exists => { expect(exists).to.be.true; }); + + //Clean up + cy.umbracoEnsurePartialViewNameNotExists(fileName); + }); + + it('Create partial view from snippet', () => { + const name = "TestPartialViewFromSnippet"; + const fileName = name + ".cshtml"; + + cy.umbracoEnsurePartialViewNameNotExists(fileName); + + openPartialViewsCreatePanel(); + + cy.umbracoContextMenuAction("action-create").click(); + cy.get('.menu-label').eq(1).click(); + // Select snippet + cy.get('.menu-label').eq(2).click(); + + // Type name + cy.umbracoEditorHeaderName(name); + + // Save + cy.get('.btn-success').click(); + + // Assert + cy.umbracoSuccessNotification().should('be.visible'); + cy.umbracoPartialViewExists(fileName).then(exists => { expect(exists).to.be.true; }); + + // Clean up + cy.umbracoEnsurePartialViewNameNotExists(fileName); + }); + + it('Partial view with no name', () => { + openPartialViewsCreatePanel(); + + cy.umbracoContextMenuAction("action-create").click(); + cy.get('.menu-label').first().click(); + + // The test would fail intermittently, most likely because the editor didn't have time to load + // This should ensure that the editor is loaded and the test should no longer fail unexpectedly. + cy.get('.ace_content', {timeout: 5000}).should('exist'); + + // Click save + cy.get('.btn-success').click(); + + // Asserts + cy.umbracoErrorNotification().should('be.visible'); + }); + + it('Delete partial view', () => { + const name = "TestDeletePartialView"; + const fileName = name + ".cshtml"; + + cy.umbracoEnsurePartialViewNameNotExists(fileName); + + // Build and save partial view + const partialView = new PartialViewBuilder() + .withName(name) + .withContent("@inherits Umbraco.Web.Mvc.UmbracoViewPage") + .build(); + + cy.savePartialView(partialView); + + navigateToSettings(); + + // Delete partial view + cy.umbracoTreeItem("settings", ["Partial Views", fileName]).rightclick(); + cy.umbracoContextMenuAction("action-delete").click(); + cy.umbracoButtonByLabelKey("general_ok").click(); - it('Create new empty partial view', () => { - const name = "TestPartialView"; - const fileName = name + ".cshtml"; + // Assert + cy.contains(fileName).should('not.exist'); + cy.umbracoPartialViewExists(fileName).then(exists => { expect(exists).to.be.false; }); - cy.umbracoEnsurePartialViewNameNotExists(fileName); + // Clean + cy.umbracoEnsurePartialViewNameNotExists(fileName); + }); - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); + it('Edit partial view', () => { + const name = 'EditPartialView'; + const fileName = name + ".cshtml"; - cy.umbracoTreeItem("settings", ["Partial Views"]).rightclick(); + cy.umbracoEnsurePartialViewNameNotExists(fileName); - cy.umbracoContextMenuAction("action-create").click(); - cy.get('.menu-label').first().click(); // TODO: Fucked we cant use something like cy.umbracoContextMenuAction("action-mediaType").click(); + const partialView = new PartialViewBuilder() + .withName(name) + .withContent("@inherits Umbraco.Web.Mvc.UmbracoViewPage\n") + .build(); - //Type name - cy.umbracoEditorHeaderName(name); + cy.savePartialView(partialView); - //Save - cy.get('.btn-success').click(); + navigateToSettings(); + // Open partial view + cy.umbracoTreeItem("settings", ["Partial Views", fileName]).click(); + // Edit + cy.get('.ace_content').type("var num = 5;"); + cy.get('.btn-success').click(); - //Assert - cy.umbracoSuccessNotification().should('be.visible'); + // Assert + cy.umbracoSuccessNotification().should('be.visible'); + // Clean + cy.umbracoEnsurePartialViewNameNotExists(fileName); + }); - //Clean up - cy.umbracoEnsurePartialViewNameNotExists(fileName); - }); }); From cc696ade1c95ca76a5058781e3109eb58f40c9e5 Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Thu, 13 Aug 2020 09:02:54 +0200 Subject: [PATCH 50/89] Update npm packages --- src/Umbraco.Tests.AcceptanceTest/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/package.json b/src/Umbraco.Tests.AcceptanceTest/package.json index 5840518184b8..867b7f5cf389 100644 --- a/src/Umbraco.Tests.AcceptanceTest/package.json +++ b/src/Umbraco.Tests.AcceptanceTest/package.json @@ -5,9 +5,9 @@ }, "devDependencies": { "cross-env": "^7.0.2", - "cypress": "^4.12.0", + "cypress": "^4.12.1", "ncp": "^2.0.0", - "umbraco-cypress-testhelpers": "^1.0.0-beta-46" + "umbraco-cypress-testhelpers": "^1.0.0-beta-48" }, "dependencies": { "typescript": "^3.9.2" From e76ffc865ca0624bbfca994d7fc4cbec8c8be7c7 Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Thu, 13 Aug 2020 09:09:15 +0200 Subject: [PATCH 51/89] Ensure that the template is deleted when creating a new documentType --- .../cypress/integration/Settings/documentTypes.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts index e0a651731ac8..c40d65d54120 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/documentTypes.ts @@ -10,6 +10,7 @@ context('Document Types', () => { const name = "Test document type"; cy.umbracoEnsureDocumentTypeNameNotExists(name); + cy.umbracoEnsureTemplateNameNotExists(name); cy.umbracoSection('settings'); cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); @@ -44,6 +45,7 @@ context('Document Types', () => { //Assert cy.umbracoSuccessNotification().should('be.visible'); + cy.umbracoEnsureTemplateNameNotExists(name); //Clean up cy.umbracoEnsureDocumentTypeNameNotExists(name); From 9a2fb1e0d8b573885fc6789bfeb4f420a50df4bc Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Thu, 13 Aug 2020 09:11:58 +0200 Subject: [PATCH 52/89] Fix javascript test --- .../cypress/integration/Settings/partialsViewMacroFiles.ts | 2 +- .../cypress/integration/Settings/scripts.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViewMacroFiles.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViewMacroFiles.ts index 0a1fec7bc8b8..bf1c25c16dd9 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViewMacroFiles.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/partialsViewMacroFiles.ts @@ -119,7 +119,7 @@ context('Partial View Macro Files', () => { // Assert cy.contains(fullName).should('not.exist'); - // Clearn + // Clean cleanup(name); }); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts index 8cffd3e59bea..cce8a45da6af 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts @@ -14,7 +14,7 @@ context('Scripts', () => { cy.umbracoSection('settings'); cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - cy.umbracoTreeItem("settings", ["Stylesheets"]).rightclick(); + cy.umbracoTreeItem("settings", ["Scripts"]).rightclick(); cy.umbracoContextMenuAction("action-create").click(); cy.get('.menu-label').first().click(); // TODO: Fucked we cant use something like cy.umbracoContextMenuAction("action-mediaType").click(); From 1512496f4f6c82ebc67de0ea1ce34af584ba057c Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Thu, 13 Aug 2020 09:31:28 +0200 Subject: [PATCH 53/89] Add template tests --- .../cypress/integration/Settings/templates.ts | 147 +++++++++++++++--- 1 file changed, 128 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts index 6871db7ffed2..194419ee8889 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts @@ -1,57 +1,166 @@ /// -import {DocumentTypeBuilder, TemplateBuilder} from "umbraco-cypress-testhelpers"; +import { TemplateBuilder } from 'umbraco-cypress-testhelpers'; context('Templates', () => { - beforeEach(() => { - cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); - }); - - it('Create template', () => { - const name = "Test template"; - - cy.umbracoEnsureTemplateNameNotExists(name); + beforeEach(() => { + cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); + }); + function navigateToSettings() { cy.umbracoSection('settings'); cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); + } + function createTemplate() { + navigateToSettings(); cy.umbracoTreeItem("settings", ["Templates"]).rightclick(); - cy.umbracoContextMenuAction("action-create").click(); + } + + + it('Create template', () => { + const name = "Test template test"; + cy.umbracoEnsureTemplateNameNotExists(name); + + createTemplate(); //Type name cy.umbracoEditorHeaderName(name); + /* Make an edit, if you don't the file will be create twice, + only happens in testing though, probably because the test is too fast + Certifiably mega wonk regardless */ + cy.get('.ace_content').type("var num = 5;"); //Save - cy.get("form[name='contentForm']").submit(); + cy.get('.btn-success').click(); //Assert cy.umbracoSuccessNotification().should('be.visible'); //Clean up cy.umbracoEnsureTemplateNameNotExists(name); - }); + }); + + it('Unsaved changes stay', () => { + const name = "Templates Unsaved Changes Stay test"; + const edit = "var num = 5;"; + cy.umbracoEnsureTemplateNameNotExists(name); + + const template = new TemplateBuilder() + .withName(name) + .withContent('@inherits Umbraco.Web.Mvc.UmbracoViewPage\n') + .build(); + + cy.saveTemplate(template); + + navigateToSettings(); + + // Open partial view + cy.umbracoTreeItem("settings", ["Templates", name]).click(); + // Edit + cy.get('.ace_content').type(edit); + // Navigate away + cy.umbracoSection('content'); + // Click stay button + cy.get('umb-button[label="Stay"] button:enabled').click(); + + // Assert + // That the same document is open + cy.get('#headerName').should('have.value', name); + cy.get('.ace_content').contains(edit); + + cy.umbracoEnsureTemplateNameNotExists(name); + }); + + it('Discard unsaved changes', () => { + const name = "Discard changes test"; + const edit = "var num = 5;"; - it('Delete template', () => { - const name = "Test template"; cy.umbracoEnsureTemplateNameNotExists(name); const template = new TemplateBuilder() .withName(name) + .withContent('@inherits Umbraco.Web.Mvc.UmbracoViewPage\n') .build(); cy.saveTemplate(template); + navigateToSettings(); + + // Open partial view + cy.umbracoTreeItem("settings", ["Templates", name]).click(); + // Edit + cy.get('.ace_content').type(edit); + // Navigate away + cy.umbracoSection('content'); + // Click discard + cy.get('umb-button[label="Discard changes"] button:enabled').click(); + // Navigate back cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); - cy.umbracoTreeItem("settings", ["Templates", name]).rightclick(); - cy.umbracoContextMenuAction("action-delete").click(); + // Asserts + cy.get('.ace_content').should('not.contain', edit); + // cy.umbracoPartialViewExists(fileName).then(exists => { expect(exists).to.be.false; }); TODO: Switch to template + cy.umbracoEnsureTemplateNameNotExists(name); + }); + + it('Insert macro', () => { + const name = 'InsertMacroTest'; + + cy.umbracoEnsureTemplateNameNotExists(name); + cy.umbracoEnsureMacroNameNotExists(name); + + const template = new TemplateBuilder() + .withName(name) + .withContent('') + .build(); + + cy.saveTemplate(template); + + cy.saveMacro(name); + + navigateToSettings(); + cy.umbracoTreeItem("settings", ["Templates", name]).click(); + // Insert macro + cy.umbracoButtonByLabelKey('general_insert').click(); + cy.get('.umb-insert-code-box__title').contains('Macro').click(); + cy.get('.umb-card-grid-item').contains(name).click(); - cy.umbracoButtonByLabelKey("general_ok").click(); + // Assert + cy.get('.ace_content').contains('@Umbraco.RenderMacro("' + name + '")').should('exist'); - cy.contains(name).should('not.exist'); + // Clean + cy.umbracoEnsureTemplateNameNotExists(name); + cy.umbracoEnsureMacroNameNotExists(name); + }); + + it('Insert value', () => { + const name = 'Insert Value Test'; cy.umbracoEnsureTemplateNameNotExists(name); + + const partialView = new TemplateBuilder() + .withName(name) + .withContent('') + .build(); + + cy.saveTemplate(partialView); + + navigateToSettings(); + cy.umbracoTreeItem("settings", ["Templates", name]).click(); + + // Insert value + cy.umbracoButtonByLabelKey('general_insert').click(); + cy.get('.umb-insert-code-box__title').contains('Value').click(); + cy.get('select').select('umbracoBytes'); + cy.umbracoButtonByLabelKey('general_submit').click(); + + // assert + cy.get('.ace_content').contains('@Model.Value("umbracoBytes")').should('exist'); + + // Clean + cy.umbracoEnsureTemplateNameNotExists(name); }); + }); From 2b73b3ddadcb13520e2b6e9858d68b8360ffe70e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 13 Aug 2020 13:12:07 +0200 Subject: [PATCH 54/89] include PublishedPendingChanges as Published state. --- .../content/overlays/publish.controller.js | 21 ++++++++++--------- .../src/views/content/overlays/publish.html | 15 ++++++------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js index decf05be5f94..37f005cd4778 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js @@ -11,13 +11,13 @@ /** Returns true if publish meets the requirements of mandatory languages */ function canPublish() { - + var hasSomethingToPublish = false; for (var i = 0; i < vm.variants.length; i++) { var variant = vm.variants[i]; - // if varaint is mandatory and not already published: + // if varaint is mandatory and not already published, then we require it to be set to publish: if (variant.publish === false && notPublishedMandatoryFilter(variant)) { return false; } @@ -39,8 +39,9 @@ function hasAnyDataFilter(variant) { - if (variant.name == null || variant.name.length === 0) { - return false; + // if we have a name, then we have data. + if (variant.name != null && variant.name.length > 0) { + return true; } if(variant.isDirty === true) { @@ -77,7 +78,7 @@ } function notPublishedMandatoryFilter(variant) { - return variant.state !== "Published" && isMandatoryFilter(variant); + return variant.state !== "Published" && variant.state !== "PublishedPendingChanges" && isMandatoryFilter(variant); } function isMandatoryFilter(variant) { //determine a variant is 'dirty' (meaning it will show up as publish-able) if it's @@ -98,11 +99,11 @@ vm.variants = $scope.model.variants; _.each(vm.variants, (variant) => { - + // reset to not be published variant.publish = false; variant.save = false; - + variant.isMandatory = isMandatoryFilter(variant); // If we have a variant thats not in the state of NotCreated, then we know we have adata and its not a new content node. @@ -112,12 +113,12 @@ }); _.each(vm.variants, (variant) => { - + // if this is a new node and we have data on this variant. if(vm.isNew === true && hasAnyDataFilter(variant)) { variant.save = true; } - + }); vm.availableVariants = vm.variants.filter(publishableVariantFilter); @@ -139,7 +140,7 @@ if (a.language.name < b.language.name) { return 1; } - } + } if (a.segment && b.segment) { if (a.segment > b.segment) { return -1; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html index 71e19651d601..c3e0b90f5f09 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html @@ -21,19 +21,16 @@ on-change="vm.changeSelection(variant)" server-validation-field="{{variant.htmlId}}"> - - - * - - - - — {{variant.language.name}} - * + + + + — {{variant.language.name}} + * - - + {{publishVariantSelectorForm.publishVariantSelector.errorMsg}} From dde735f75cc96979f3707743fa5c1f1468906e79 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Thu, 13 Aug 2020 14:33:19 +0200 Subject: [PATCH 55/89] Sync some dependencies that have been updated in v8/contrib --- src/Umbraco.Web.UI.Client/package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 21445e3cb331..454d081fe793 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -38,11 +38,11 @@ "lazyload-js": "1.0.0", "moment": "2.22.2", "ng-file-upload": "12.2.13", - "nouislider": "14.4.0", + "nouislider": "14.6.0", "npm": "^6.14.7", "signalr": "2.4.0", "spectrum-colorpicker": "1.8.0", - "tinymce": "4.9.10", + "tinymce": "4.9.11", "typeahead.js": "0.11.1", "underscore": "1.9.1" }, @@ -78,7 +78,7 @@ "karma-phantomjs-launcher": "1.0.4", "karma-spec-reporter": "0.0.32", "less": "3.10.3", - "lodash": "4.17.15", + "lodash": "4.17.19", "marked": "^0.7.0", "merge-stream": "2.0.0", "run-sequence": "2.2.1" From 6d6f525343c9f3794063cd96e78a36b39893540c Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Tue, 18 Aug 2020 12:43:29 +0100 Subject: [PATCH 56/89] Fix to ensure we use virtual paths for any blocks with custom views (#8597) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Niels Lyngsø Co-authored-by: Shannon --- .../services/umbrequesthelper.service.js | 31 ++++++++++--------- ...t.blockconfiguration.overlay.controller.js | 7 +++-- .../blocklist/umbblocklistblock.component.js | 23 +++++++------- .../services/umb-request-helper.spec.js | 28 +++++++++++++++++ 4 files changed, 59 insertions(+), 30 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js index 7b43b239eadf..90125e7de642 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js @@ -15,7 +15,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe * * @description * This will convert a virtual path (i.e. ~/App_Plugins/Blah/Test.html ) to an absolute path - * + * * @param {string} a virtual path, if this is already an absolute path it will just be returned, if this is a relative path an exception will be thrown */ convertVirtualToAbsolutePath: function(virtualPath) { @@ -31,6 +31,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe return Umbraco.Sys.ServerVariables.application.applicationPath + virtualPath.trimStart("~/"); }, + /** * @ngdoc method * @name umbraco.services.umbRequestHelper#dictionaryToQueryString @@ -39,7 +40,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe * * @description * This will turn an array of key/value pairs or a standard dictionary into a query string - * + * * @param {Array} queryStrings An array of key/value pairs */ dictionaryToQueryString: function (queryStrings) { @@ -76,9 +77,9 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe * * @description * This will return the webapi Url for the requested key based on the servervariables collection - * + * * @param {string} apiName The webapi name that is found in the servervariables["umbracoUrls"] dictionary - * @param {string} actionName The webapi action name + * @param {string} actionName The webapi action name * @param {object} queryStrings Can be either a string or an array containing key/value pairs */ getApiUrl: function (apiName, actionName, queryStrings) { @@ -103,7 +104,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe * * @description * This returns a promise with an underlying http call, it is a helper method to reduce - * the amount of duplicate code needed to query http resources and automatically handle any + * the amount of duplicate code needed to query http resources and automatically handle any * Http errors. See /docs/source/using-promises-resources.md * * @param {object} opts A mixed object which can either be a string representing the error message to be @@ -117,7 +118,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe * The error callback must return an object containing: {errorMsg: errorMessage, data: originalData, status: status } */ resourcePromise: function (httpPromise, opts) { - + /** The default success callback used if one is not supplied in the opts */ function defaultSuccess(data, status, headers, config) { //when it's successful, just return the data @@ -151,7 +152,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe return httpPromise.then(function (response) { - //invoke the callback + //invoke the callback var result = callbacks.success.apply(this, [response.data, response.status, response.headers, response.config]); formHelper.showNotifications(response.data); @@ -183,7 +184,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe overlayService.ysod(error); } else { - //show a simple error notification + //show a simple error notification notificationsService.error("Server error", "Contact administrator, see log for full details.
" + result.errorMsg + ""); } @@ -209,7 +210,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe * * @description * Used for saving content/media/members specifically - * + * * @param {Object} args arguments object * @returns {Promise} http promise object. */ @@ -233,7 +234,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe if (args.showNotifications === null || args.showNotifications === undefined) { args.showNotifications = true; } - + //save the active tab id so we can set it when the data is returned. var activeTab = _.find(args.content.tabs, function (item) { return item.active; @@ -300,7 +301,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe overlayService.ysod(error); } else { - //show a simple error notification + //show a simple error notification notificationsService.error("Server error", "Contact administrator, see log for full details.
" + response.data.ExceptionMessage + ""); } @@ -331,7 +332,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe }); } else if (!jsonData.key || !jsonData.value) { throw "jsonData object must have both a key and a value property"; } - + return $http({ method: 'POST', url: url, @@ -366,7 +367,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe return $q.reject(response); }); }, - + /** * @ngdoc method * @name umbraco.resources.contentResource#downloadFile @@ -374,7 +375,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe * * @description * Downloads a file to the client using AJAX/XHR - * + * * @param {string} httpPath the path (url) to the resource being downloaded * @returns {Promise} http promise object. */ @@ -388,7 +389,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe // Use an arraybuffer return $http.get(httpPath, { responseType: 'arraybuffer' }) .then(function (response) { - + var octetStreamMime = 'application/octet-stream'; var success = false; diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js index b1937e718d76..48c21b4009ab 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/prevalue/blocklist.blockconfiguration.overlay.controller.js @@ -160,7 +160,7 @@ }, select: function (node) { const filepath = decodeURIComponent(node.id.replace(/\+/g, " ")); - block.view = filepath; + block.view = "~/" + filepath; editorService.close(); }, close: function () { @@ -206,7 +206,7 @@ }, select: function (node) { const filepath = decodeURIComponent(node.id.replace(/\+/g, " ")); - block.stylesheet = filepath; + block.stylesheet = "~/" + filepath; editorService.close(); }, close: function () { @@ -252,7 +252,8 @@ return !(i.name.indexOf(".jpg") !== -1 || i.name.indexOf(".jpeg") !== -1 || i.name.indexOf(".png") !== -1 || i.name.indexOf(".svg") !== -1 || i.name.indexOf(".webp") !== -1 || i.name.indexOf(".gif") !== -1); }, select: function (file) { - block.thumbnail = file.name; + const id = decodeURIComponent(file.id.replace(/\+/g, " ")); + block.thumbnail = "~/" + id; editorService.close(); }, close: function () { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbblocklistblock.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbblocklistblock.component.js index 4474dbd55c42..64fd40459dea 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbblocklistblock.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/blocklist/umbblocklistblock.component.js @@ -5,11 +5,11 @@ * @ngdoc directive * @name umbraco.directives.directive:umbBlockListBlock * @description - * The component to render the view for a block. + * The component to render the view for a block. * If a stylesheet is used then this uses a ShadowDom to make a scoped element. * This way the backoffice styling does not collide with the block style. */ - + angular .module("umbraco") .component("umbBlockListBlock", { @@ -29,16 +29,16 @@ } ); - function BlockListBlockController($scope, $compile, $element) { + function BlockListBlockController($scope, $compile, $element, umbRequestHelper) { var model = this; model.$onInit = function () { - // This is ugly and is only necessary because we are not using components and instead + // This is ugly and is only necessary because we are not using components and instead // relying on ng-include. It is definitely possible to compile the contents - // of the view into the DOM using $templateCache and $http instead of using - // ng - include which means that the controllerAs flows directly to the view. - // This would mean that any custom components would need to be updated instead of relying on $scope. - // Guess we'll leave it for now but means all things need to be copied to the $scope and then all + // of the view into the DOM using $templateCache and $http instead of using + // ng - include which means that the controllerAs flows directly to the view. + // This would mean that any custom components would need to be updated instead of relying on $scope. + // Guess we'll leave it for now but means all things need to be copied to the $scope and then all // primitives need to be watched. $scope.block = model.block; @@ -48,8 +48,7 @@ $scope.valFormManager = model.valFormManager; if (model.stylesheet) { - // TODO: Not sure why this needs a prefixed /? this means it will never work in a virtual directory - model.stylesheet = "/" + model.stylesheet; + model.stylesheet = umbRequestHelper.convertVirtualToAbsolutePath(model.stylesheet); var shadowRoot = $element[0].attachShadow({ mode: 'open' }); shadowRoot.innerHTML = `