From a4abfd4962814004d47360123828256693949459 Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Thu, 10 Sep 2020 08:52:18 +0200 Subject: [PATCH 01/73] Add Content with contentpicker test and bump helpers version --- .../cypress/integration/Content/content.ts | 99 ++++++++++++++++++- src/Umbraco.Tests.AcceptanceTest/package.json | 2 +- 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts index 23e97043b0..d2990a8635 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts @@ -1,5 +1,5 @@ /// -import { DocumentTypeBuilder, ContentBuilder } from 'umbraco-cypress-testhelpers'; +import { DocumentTypeBuilder, ContentBuilder, AliasHelper } from 'umbraco-cypress-testhelpers'; context('Content', () => { beforeEach(() => { @@ -499,4 +499,101 @@ context('Content', () => { // Clean up (content is automatically deleted when document types are gone) cy.umbracoEnsureDocumentTypeNameNotExists(rootDocTypeName); }); + + it('Content with contentpicker', () => { + const pickerDocTypeName = 'Content Picker Type'; + const groupName = 'ContentPickerGroup'; + const alias = AliasHelper.toAlias(pickerDocTypeName); + const pickedDocTypeName = 'Picked Content Type'; + const pickedNodeName = 'Content to pick'; + + cy.deleteAllContent(); + cy.umbracoEnsureDocumentTypeNameNotExists(pickerDocTypeName); + cy.umbracoEnsureTemplateNameNotExists(pickerDocTypeName); + cy.umbracoEnsureDocumentTypeNameNotExists(pickedDocTypeName); + + // Create the content type and content we'll be picking from. + const pickedDocType = new DocumentTypeBuilder() + .withName(pickedDocTypeName) + .withAllowAsRoot(true) + .addGroup() + .addTextBoxProperty() + .withAlias('text') + .done() + .done() + .build(); + + cy.saveDocumentType(pickedDocType).then((generatedType) => { + const pickedContentNode = new ContentBuilder() + .withContentTypeAlias(generatedType["alias"]) + .withAction("publishNew") + .addVariant() + .withName(pickedNodeName) + .withSave(true) + .withPublish(true) + .addProperty() + .withAlias('text') + .withValue('Acceptance test') + .done() + .withSave(true) + .withPublish(true) + .done() + .build(); + cy.saveContent(pickedContentNode); + }); + + // Create the doctype with a the picker + const pickerDocType = new DocumentTypeBuilder() + .withName(pickerDocTypeName) + .withAlias(alias) + .withAllowAsRoot(true) + .withDefaultTemplate(alias) + .addGroup() + .withName(groupName) + .addContentPickerProperty() + .withAlias('picker') + .done() + .done() + .build(); + + cy.saveDocumentType(pickerDocType); + + // Edit it the template to allow us to verify the rendered view. + cy.editTemplate(pickerDocTypeName, `@inherits Umbraco.Web.Mvc.UmbracoViewPage + @using ContentModels = Umbraco.Web.PublishedModels; + @{ + Layout = null; + } + + @{ + IPublishedContent typedContentPicker = Model.Value("picker"); + if (typedContentPicker != null) + { +

@typedContentPicker.Value("text")

+ } + }`); + + // Create content with content picker + cy.get('.umb-tree-root-link').rightclick(); + cy.get('.-opens-dialog > .umb-action-link').click(); + cy.get('[data-element="action-create-contentPickerType"] > .umb-action-link').click(); + // Fill out content + cy.umbracoEditorHeaderName('ContentPickerContent'); + cy.get('.umb-node-preview-add').click(); + // Should really try and find a better way to do this, but umbracoTreeItem tries to click the content pane in the background + cy.get('[ng-if="vm.treeReady"] > .umb-tree > [ng-if="!tree.root.containsGroups"] > .umb-animated > .umb-tree-item__inner').click(); + // We have to wait for the picked content to show up or it wont be added. + cy.get('.umb-node-preview__description').should('be.visible'); + //save and publish + cy.umbracoButtonByLabelKey('buttons_saveAndPublish').click(); + cy.umbracoSuccessNotification().should('be.visible'); + + // Assert + const expectedContent = '

Acceptance test

' + cy.umbracoVerifyRenderedViewContent('contentpickercontent', expectedContent, true).should('be.true'); + // clean + cy.umbracoEnsureDocumentTypeNameNotExists(pickerDocTypeName); + cy.umbracoEnsureTemplateNameNotExists(pickerDocTypeName); + cy.umbracoEnsureDocumentTypeNameNotExists(pickedDocTypeName); + }); }); diff --git a/src/Umbraco.Tests.AcceptanceTest/package.json b/src/Umbraco.Tests.AcceptanceTest/package.json index 2017142d1e..f09313f2ad 100644 --- a/src/Umbraco.Tests.AcceptanceTest/package.json +++ b/src/Umbraco.Tests.AcceptanceTest/package.json @@ -7,7 +7,7 @@ "cross-env": "^7.0.2", "cypress": "^5.1.0", "ncp": "^2.0.0", - "umbraco-cypress-testhelpers": "^1.0.0-beta-50" + "umbraco-cypress-testhelpers": "^1.0.0-beta-51" }, "dependencies": { "typescript": "^3.9.2" From f82a4c19cbde8a2cd51e36e223a6b40f13cc8a99 Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Thu, 10 Sep 2020 09:30:07 +0200 Subject: [PATCH 02/73] Wait for content to be refreshed and switch order of rollback --- .../cypress/integration/Content/content.ts | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts index d2990a8635..bb70a1c858 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts @@ -6,6 +6,14 @@ context('Content', () => { cy.umbracoLogin(Cypress.env('username'), Cypress.env('password')); }); + function refreshContentTree(){ + // Refresh to update the tree + cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); + cy.umbracoContextMenuAction("action-refreshNode").click(); + // We have to wait in case the execution is slow, otherwise we'll try and click the item before it appears in the UI + cy.get('.umb-tree-item__inner').should('exist', {timeout: 10000}); + } + it('Copy content', () => { const rootDocTypeName = "Test document type"; const childDocTypeName = "Child test document type"; @@ -74,8 +82,7 @@ context('Content', () => { }); // Refresh to update the tree - cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); - cy.umbracoContextMenuAction("action-refreshNode").click(); + refreshContentTree(); // Copy node cy.umbracoTreeItem("content", [nodeName, childNodeName]).rightclick({ force: true }); @@ -159,8 +166,7 @@ context('Content', () => { }); // Refresh to update the tree - cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); - cy.umbracoContextMenuAction("action-refreshNode").click(); + refreshContentTree(); // Move node cy.umbracoTreeItem("content", [nodeName, childNodeName]).rightclick({ force: true }); @@ -247,8 +253,7 @@ context('Content', () => { }); // Refresh to update the tree - cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); - cy.umbracoContextMenuAction("action-refreshNode").click(); + refreshContentTree(); // Sort nodes cy.umbracoTreeItem("content", [nodeName]).rightclick({ force: true }); @@ -297,8 +302,7 @@ context('Content', () => { }); // Refresh to update the tree - cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); - cy.umbracoContextMenuAction("action-refreshNode").click(); + refreshContentTree(); // Access node cy.umbracoTreeItem("content", [initialNodeName]).click(); @@ -326,8 +330,8 @@ context('Content', () => { cy.reload(); // Assert - cy.get('.history').find('.umb-badge').eq(0).should('contain.text', "Save"); - cy.get('.history').find('.umb-badge').eq(1).should('contain.text', "Rollback"); + cy.get('.history').find('.umb-badge').eq(0).should('contain.text', "Rollback"); + cy.get('.history').find('.umb-badge').eq(1).should('contain.text', "Save"); cy.get('#headerName').should('have.value', initialNodeName); // Clean up (content is automatically deleted when document types are gone) @@ -365,8 +369,7 @@ context('Content', () => { }); // Refresh to update the tree - cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); - cy.umbracoContextMenuAction("action-refreshNode").click(); + refreshContentTree(); // Access node cy.umbracoTreeItem("content", [nodeName]).click(); @@ -407,8 +410,7 @@ context('Content', () => { }); // Refresh to update the tree - cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); - cy.umbracoContextMenuAction("action-refreshNode").click(); + refreshContentTree(); // Access node cy.umbracoTreeItem("content", [nodeName]).click(); @@ -446,8 +448,7 @@ context('Content', () => { }); // Refresh to update the tree - cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); - cy.umbracoContextMenuAction("action-refreshNode").click(); + refreshContentTree(); // Access node cy.umbracoTreeItem("content", [nodeName]).click(); @@ -487,8 +488,7 @@ context('Content', () => { }); // Refresh to update the tree - cy.get('li .umb-tree-root:contains("Content")').should("be.visible").rightclick(); - cy.umbracoContextMenuAction("action-refreshNode").click(); + refreshContentTree(); // Access node cy.umbracoTreeItem("content", [nodeName]).click(); From 6fb33043e5d29c8afe21fe4e76d521a4014e7438 Mon Sep 17 00:00:00 2001 From: Nikolaj Date: Thu, 10 Sep 2020 11:15:43 +0200 Subject: [PATCH 03/73] Add javascript tests --- .../cypress/integration/Settings/scripts.ts | 94 ++++++++++++++++++- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts index cce8a45da6..430f8aa108 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/scripts.ts @@ -1,18 +1,24 @@ /// +import { ScriptBuilder } from "umbraco-cypress-testhelpers"; + context('Scripts', () => { 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"); + } + it('Create new JavaScript file', () => { const name = "TestScript"; const fileName = name + ".js"; - cy.umbracoEnsureScriptNameNotExists(fileName); + cy.umbracoEnsureScriptNameNotExists(fileName); - cy.umbracoSection('settings'); - cy.get('li .umb-tree-root:contains("Settings")').should("be.visible"); + navigateToSettings() cy.umbracoTreeItem("settings", ["Scripts"]).rightclick(); @@ -27,9 +33,89 @@ context('Scripts', () => { //Assert cy.umbracoSuccessNotification().should('be.visible'); + cy.umbracoScriptExists(fileName).should('be.true'); + //Clean up cy.umbracoEnsureScriptNameNotExists(fileName); - }); + }); + it('Delete a JavaScript file', () => { + const name = "TestDeleteScriptFile"; + const fileName = name + ".js"; + + cy.umbracoEnsureScriptNameNotExists(fileName); + + const script = new ScriptBuilder() + .withName(name) + .withContent('alert("this is content");') + .build(); + + cy.saveScript(script); + + navigateToSettings() + + cy.umbracoTreeItem("settings", ["Scripts", fileName]).rightclick(); + cy.umbracoContextMenuAction("action-delete").click(); + cy.umbracoButtonByLabelKey("general_ok").click(); + + cy.contains(fileName).should('not.exist'); + cy.umbracoScriptExists(name).should('be.false'); + + cy.umbracoEnsureScriptNameNotExists(fileName); + }); + + it('Update JavaScript file', () => { + const name = "TestEditJavaScriptFile"; + const nameEdit = "Edited"; + let fileName = name + ".js"; + + const originalContent = 'console.log("A script);\n'; + const edit = 'alert("content");'; + const expected = originalContent + edit; + + cy.umbracoEnsureScriptNameNotExists(fileName); + + const script = new ScriptBuilder() + .withName(name) + .withContent(originalContent) + .build(); + cy.saveScript(script); + + navigateToSettings(); + cy.umbracoTreeItem("settings", ["Scripts", fileName]).click(); + + cy.get('.ace_text-input').type(edit, { force: true }); + + // Since scripts has no alias it should be safe to not use umbracoEditorHeaderName + // umbracoEditorHeaderName does not like {backspace} + cy.get('#headerName').type("{backspace}{backspace}{backspace}" + nameEdit).should('have.value', name+nameEdit); + fileName = name + nameEdit + ".js"; + cy.get('.btn-success').click(); + + cy.umbracoSuccessNotification().should('be.visible'); + cy.umbracoVerifyScriptContent(fileName, expected).should('be.true'); + + cy.umbracoEnsureScriptNameNotExists(fileName); + }); + + it('Can Delete folder', () => { + const folderName = "TestFolder"; + + // The way scripts and folders are fetched and deleted are identical + cy.umbracoEnsureScriptNameNotExists(folderName); + cy.saveFolder('scripts', folderName); + + navigateToSettings() + + cy.umbracoTreeItem("settings", ["Scripts", folderName]).rightclick(); + cy.umbracoContextMenuAction("action-delete").click(); + cy.umbracoButtonByLabelKey("general_ok").click(); + + cy.contains(folderName).should('not.exist'); + cy.umbracoScriptExists(folderName).should('be.false') + + // A script an a folder is the same thing in this case + cy.umbracoEnsureScriptNameNotExists(folderName); + }); }); From a7db1588947ecc1518c28d8988733d469f9d115a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 2 Oct 2020 16:12:24 +0200 Subject: [PATCH 04/73] ask user wether they like to stay in preview mode --- src/Umbraco.Web.UI.Client/gulp/config.js | 3 +- .../src/websitepreview/websitepreview.js | 146 +++++ .../config/umbracoSettings.config | 533 +++++++++--------- 3 files changed, 415 insertions(+), 267 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js diff --git a/src/Umbraco.Web.UI.Client/gulp/config.js b/src/Umbraco.Web.UI.Client/gulp/config.js index 3c32832ba0..9eb335a62c 100755 --- a/src/Umbraco.Web.UI.Client/gulp/config.js +++ b/src/Umbraco.Web.UI.Client/gulp/config.js @@ -30,6 +30,7 @@ module.exports = { // js files for backoffice // processed in the js task js: { + websitepreview: { files: "./src/websitepreview/**/*.js", out: "umbraco.websitepreview.js" }, preview: { files: "./src/preview/**/*.js", out: "umbraco.preview.js" }, installer: { files: "./src/installer/**/*.js", out: "umbraco.installer.js" }, filters: { files: "./src/common/filters/**/*.js", out: "umbraco.filters.js" }, @@ -56,7 +57,7 @@ module.exports = { ], out: "umbraco.directives.js" } - + }, //selectors for copying all views into the build diff --git a/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js b/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js new file mode 100644 index 0000000000..203748e96e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js @@ -0,0 +1,146 @@ + +/*********************************************************************************************************/ +/* website preview controller */ +/*********************************************************************************************************/ + +(function() { + + if ( window.location !== window.parent.location ) { + //we are in an iFrame, so lets skip the dialog. + return; + } + + function setCookie(cname, cvalue, exminutes) { + var d = new Date(); + d.setTime(d.getTime() + (exminutes * 60 * 1000)); + document.cookie = cname + "=" + cvalue + ";expires="+d.toUTCString() + ";path=/"; + } + + function getCookie(cname) { + var name = cname + "="; + var ca = document.cookie.split(';'); + for(var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == ' ') { + c = c.substring(1); + } + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + return ""; + } + + function exitPreviewMode() { + window.top.location.href = "../preview/end?redir=" + encodeURIComponent(window.location.pathname+window.location.search); + } + function continuePreviewMode() { + setCookie("UMB-WEBSITE-PREVIEW-ACCEPT", "true", 1); + } + + var user = getCookie("UMB-WEBSITE-PREVIEW-ACCEPT"); + if (user != "true") { + + var modelStyles = ` + .umbraco-preview-dialog { + position: fixed; + display: flex; + align-items: center; + justify-content: center; + z-index: 99999999; + top:0; + bottom:0; + left:0; + right:0; + overflow: auto; + background-color: rgba(0,0,0,0.6); + } + + .umbraco-preview-dialog__modal { + background-color: #fff; + border-radius: 6px; + box-shadow: 0 3px 7px rgba(0,0,0,0.3); + margin: auto; + padding: 30px 40px; + width: 100%; + max-width: 540px; + font-family: Lato,Helvetica Neue,Helvetica,Arial,sans-serif; + font-size: 15px; + } + + .umbraco-preview-dialog__headline { + font-weight: 700; + font-size: 22px; + color: #1b264f; + margin-top:10px; + margin-bottom:10px; + } + .umbraco-preview-dialog__question { + margin-bottom:30px; + } + .umbraco-preview-dialog__modal > button { + display: inline-block; + padding: 8px 18px; + text-align: center; + vertical-align: middle; + border-radius: 3px; + border:none; + font-weight: 700; + float:right; + margin-left:10px; + + color: #1b264f; + background-color: #f6f1ef; + } + .umbraco-preview-dialog__modal > button:hover { + color: #2152a3; + background-color: #f6f1ef; + } + .umbraco-preview-dialog__modal > button.umbraco-preview-dialog__continue { + color: #fff; + background-color: #2bc37c; + } + .umbraco-preview-dialog__modal > button.umbraco-preview-dialog__continue:hover { + background-color: #39d38b; + } + `; + + var fragment = document.createDocumentFragment(); + + var con = document.createElement("style"); + con.innerHTML = modelStyles; + fragment.appendChild(con); + + var con = document.createElement("div"); + con.className = "umbraco-preview-dialog"; + + + var model = document.createElement("div"); + model.className = "umbraco-preview-dialog"; + model.innerHTML = `
Preview mode
+
Do you want to continue viewing latest saved version?
`; + + var continueButton = document.createElement("button"); + continueButton.type = "button"; + continueButton.innerHTML = "Continue"; + model.appendChild(continueButton); + + continueButton.addEventListener("click", continuePreviewMode); + + var exitButton = document.createElement("button"); + exitButton.type = "button"; + exitButton.className = "umbraco-preview-dialog__continue"; + exitButton.innerHTML = "Exit preview mode"; + model.appendChild(exitButton); + + continueButton.addEventListener("click", exitPreviewMode); + + fragment.appendChild(con); + + document.getElementsByTagName("BODY")[0].appendChild(fragment); + } else { + continuePreviewMode(); + console.log("already set"); + } + +})(); diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index d723b9c7c0..43f239444f 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -1,266 +1,267 @@ - - - - - - - - - - - - - 1 - - - - - - - - your@email.here - - - - - - Preview mode - - … - - - Click to end - - - - ]]> - - - - throw - - - ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,xhtml,html,htm,php,htaccess - - - assets/img/login.jpg - - - - - - false - - true - - false - - - - - - - - - - - - - - + + + + + + + + + + + + + 1 + + + + + + + + your@email.here + + + + + + Preview mode + + … + + + Click to end + + + + + ]]> + + + + throw + + + ashx,aspx,ascx,config,cshtml,vbhtml,asmx,air,axd,swf,xml,xhtml,html,htm,php,htaccess + + + assets/img/login.jpg + + + + + + false + + true + + false + + + + + + + + + + + + + + From f20e322ff5adb454b71c830784e43b739ca6d343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 2 Oct 2020 20:46:54 +0200 Subject: [PATCH 05/73] apply functionality to modal. --- .../src/websitepreview/websitepreview.js | 53 ++++++++++--------- .../config/umbracoSettings.config | 2 +- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js b/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js index 203748e96e..be5c989f68 100644 --- a/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js +++ b/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js @@ -10,6 +10,8 @@ return; } + const scriptElement = document.currentScript; + function setCookie(cname, cvalue, exminutes) { var d = new Date(); d.setTime(d.getTime() + (exminutes * 60 * 1000)); @@ -18,24 +20,24 @@ function getCookie(cname) { var name = cname + "="; - var ca = document.cookie.split(';'); + var ca = document.cookie.split(";"); for(var i = 0; i < ca.length; i++) { var c = ca[i]; - while (c.charAt(0) == ' ') { - c = c.substring(1); + while (c.charAt(0) == " ") { + c = c.substring(1); } if (c.indexOf(name) == 0) { - return c.substring(name.length, c.length); + return c.substring(name.length, c.length); } } return ""; } function exitPreviewMode() { - window.top.location.href = "../preview/end?redir=" + encodeURIComponent(window.location.pathname+window.location.search); + window.top.location.href = scriptElement.getAttribute("data-umbraco-path")+"/preview/end?redir=" + encodeURIComponent(window.location.pathname+window.location.search); } function continuePreviewMode() { - setCookie("UMB-WEBSITE-PREVIEW-ACCEPT", "true", 1); + setCookie("UMB-WEBSITE-PREVIEW-ACCEPT", "true", 5); } var user = getCookie("UMB-WEBSITE-PREVIEW-ACCEPT"); @@ -105,42 +107,45 @@ } `; + var bodyEl = document.getElementsByTagName("BODY")[0]; + var fragment = document.createDocumentFragment(); - var con = document.createElement("style"); - con.innerHTML = modelStyles; - fragment.appendChild(con); + var style = document.createElement("style"); + style.innerHTML = modelStyles; + fragment.appendChild(style); var con = document.createElement("div"); con.className = "umbraco-preview-dialog"; + fragment.appendChild(con); - - var model = document.createElement("div"); - model.className = "umbraco-preview-dialog"; - model.innerHTML = `
Preview mode
+ var modal = document.createElement("div"); + modal.className = "umbraco-preview-dialog__modal"; + modal.innerHTML = `
Preview mode
Do you want to continue viewing latest saved version?
`; + con.appendChild(modal); var continueButton = document.createElement("button"); continueButton.type = "button"; + continueButton.className = "umbraco-preview-dialog__continue"; continueButton.innerHTML = "Continue"; - model.appendChild(continueButton); - - continueButton.addEventListener("click", continuePreviewMode); + continueButton.addEventListener("click", function() { + bodyEl.removeChild(style); + bodyEl.removeChild(con); + continuePreviewMode(); + }); + modal.appendChild(continueButton); var exitButton = document.createElement("button"); exitButton.type = "button"; - exitButton.className = "umbraco-preview-dialog__continue"; exitButton.innerHTML = "Exit preview mode"; - model.appendChild(exitButton); + exitButton.addEventListener("click", exitPreviewMode); + modal.appendChild(exitButton); - continueButton.addEventListener("click", exitPreviewMode); - - fragment.appendChild(con); - - document.getElementsByTagName("BODY")[0].appendChild(fragment); + bodyEl.appendChild(fragment); + continueButton.focus(); } else { continuePreviewMode(); - console.log("already set"); } })(); diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 43f239444f..2287464d07 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -170,7 +170,7 @@ width:1em; }} - + ]]> From 879d54b5259b237d7c6b82c285bee28c2f64dd7f Mon Sep 17 00:00:00 2001 From: Mike Masey Date: Tue, 6 Oct 2020 13:09:35 +0100 Subject: [PATCH 06/73] V8/feature/update icon picker to use umb icon (#9063) (cherry picked from commit 27751a4ae70b66e477aff554583e4261f5e4e94a) --- ...on-adressbook.svg => icon-addressbook.svg} | 0 .../components/umbicon.directive.js | 36 +++++++++++-------- .../iconpicker/iconpicker.controller.js | 22 +++++++++--- .../iconpicker/iconpicker.html | 8 ++--- 4 files changed, 44 insertions(+), 22 deletions(-) rename src/Umbraco.Web.UI.Client/src/assets/icons/{icon-adressbook.svg => icon-addressbook.svg} (100%) diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-adressbook.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-addressbook.svg similarity index 100% rename from src/Umbraco.Web.UI.Client/src/assets/icons/icon-adressbook.svg rename to src/Umbraco.Web.UI.Client/src/assets/icons/icon-addressbook.svg diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbicon.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbicon.directive.js index 517776388b..87d976f6d9 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbicon.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbicon.directive.js @@ -15,16 +15,7 @@ Simple icon Icon with additional attribute. It can be treated like any other dom element
-    
-
- -Manual svg string -This format is only used in the iconpicker.html -
-    
-    
+    
 
@example **/ @@ -43,13 +34,19 @@ This format is only used in the iconpicker.html svgString: "=?" }, - link: function (scope) { - + link: function (scope, element) { if (scope.svgString === undefined && scope.svgString !== null && scope.icon !== undefined && scope.icon !== null) { - var icon = scope.icon.split(" ")[0]; // Ensure that only the first part of the icon is used as sometimes the color is added too, e.g. see umbeditorheader.directive scope.openIconPicker + const observer = new IntersectionObserver(_lazyRequestIcon, {rootMargin: "100px"}); + const iconEl = element[0]; - _requestIcon(icon); + observer.observe(iconEl); + + // make sure to disconnect the observer when the scope is destroyed + scope.$on('$destroy', function () { + observer.disconnect(); + }); } + scope.$watch("icon", function (newValue, oldValue) { if (newValue && oldValue) { var newicon = newValue.split(" ")[0]; @@ -61,6 +58,17 @@ This format is only used in the iconpicker.html } }); + function _lazyRequestIcon(entries, observer) { + entries.forEach(entry => { + if (entry.isIntersecting === true) { + observer.disconnect(); + + var icon = scope.icon.split(" ")[0]; // Ensure that only the first part of the icon is used as sometimes the color is added too, e.g. see umbeditorheader.directive scope.openIconPicker + _requestIcon(icon); + } + }); + } + function _requestIcon(icon) { // Reset svg string before requesting new icon. scope.svgString = null; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.controller.js index 44b10b852d..59a9aed4bb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.controller.js @@ -43,10 +43,24 @@ function IconPickerController($scope, localizationService, iconHelper) { setTitle(); - iconHelper.getIcons().then(function (icons) { - vm.icons = icons; - vm.loading = false; - }); + iconHelper.getAllIcons() + .then(icons => { + vm.icons = icons; + + // Get's legacy icons, removes duplicates then maps them to IconModel + iconHelper.getIcons() + .then(icons => { + if (icons && icons.length > 0) { + let legacyIcons = icons + .filter(icon => !vm.icons.find(x => x.name == icon)) + .map(icon => { return { name: icon, svgString: null }; }); + + vm.icons = legacyIcons.concat(vm.icons); + } + + vm.loading = false; + }); + }); // set a default color if nothing is passed in vm.color = $scope.model.color ? findColor($scope.model.color) : vm.colors.find(x => x.default); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.html index dd2ff41efa..e364b29b3f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/iconpicker/iconpicker.html @@ -45,10 +45,10 @@
    -
  • -
From d7bf980f121995fd62aa38c4efce71c1deda209e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Kottal?= Date: Tue, 6 Oct 2020 15:06:03 +0200 Subject: [PATCH 07/73] Adds icons from App_Plugins/**/Icons/*.svg (#8884) (cherry picked from commit 3b551bdc5d01c2b28b66f670fc93da4a619c856e) --- src/Umbraco.Core/Services/IIconService.cs | 4 +- src/Umbraco.Web/Services/IconService.cs | 65 ++++++++++++++++++----- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Core/Services/IIconService.cs b/src/Umbraco.Core/Services/IIconService.cs index 963edb22a5..72174a0504 100644 --- a/src/Umbraco.Core/Services/IIconService.cs +++ b/src/Umbraco.Core/Services/IIconService.cs @@ -9,13 +9,13 @@ namespace Umbraco.Core.Services /// Gets an IconModel containing the icon name and SvgString according to an icon name found at the global icons path /// /// - /// + /// IconModel GetIcon(string iconName); /// /// Gets a list of all svg icons found at at the global icons path. /// - /// + /// A list of IList GetAllIcons(); } } diff --git a/src/Umbraco.Web/Services/IconService.cs b/src/Umbraco.Web/Services/IconService.cs index 206af296f9..ba6692c7b9 100644 --- a/src/Umbraco.Web/Services/IconService.cs +++ b/src/Umbraco.Web/Services/IconService.cs @@ -19,13 +19,11 @@ namespace Umbraco.Web.Services _globalSettings = globalSettings; } - /// public IList GetAllIcons() { var icons = new List(); - var directory = new DirectoryInfo(IOHelper.MapPath($"{_globalSettings.IconsPath}/")); - var iconNames = directory.GetFiles("*.svg"); + var iconNames = GetAllIconNames(); iconNames.OrderBy(f => f.Name).ToList().ForEach(iconInfo => { @@ -45,15 +43,15 @@ namespace Umbraco.Web.Services { return string.IsNullOrWhiteSpace(iconName) ? null - : CreateIconModel(iconName.StripFileExtension(), IOHelper.MapPath($"{_globalSettings.IconsPath}/{iconName}.svg")); + : CreateIconModel(iconName.StripFileExtension()); } /// /// Gets an IconModel using values from a FileInfo model /// /// - /// - private IconModel GetIcon(FileInfo fileInfo) + /// + private IconModel GetIcon(FileSystemInfo fileInfo) { return fileInfo == null || string.IsNullOrWhiteSpace(fileInfo.Name) ? null @@ -64,17 +62,37 @@ namespace Umbraco.Web.Services /// Gets an IconModel containing the icon name and SvgString /// /// - /// - /// - private IconModel CreateIconModel(string iconName, string iconPath) + /// + private IconModel CreateIconModel(string iconName) { - var sanitizer = new HtmlSanitizer(); - sanitizer.AllowedAttributes.UnionWith(Constants.SvgSanitizer.Attributes); - sanitizer.AllowedCssProperties.UnionWith(Constants.SvgSanitizer.Attributes); - sanitizer.AllowedTags.UnionWith(Constants.SvgSanitizer.Tags); + if (string.IsNullOrWhiteSpace(iconName)) + return null; + + var iconNames = GetAllIconNames(); + var iconPath = iconNames.FirstOrDefault(x => x.Name.InvariantEquals($"{iconName}.svg"))?.FullName; + return iconPath == null + ? null + : CreateIconModel(iconName, iconPath); + } + + /// + /// Gets an IconModel containing the icon name and SvgString + /// + /// + /// + /// + private static IconModel CreateIconModel(string iconName, string iconPath) + { + if (string.IsNullOrWhiteSpace(iconPath)) + return null; try { + var sanitizer = new HtmlSanitizer(); + sanitizer.AllowedAttributes.UnionWith(Constants.SvgSanitizer.Attributes); + sanitizer.AllowedCssProperties.UnionWith(Constants.SvgSanitizer.Attributes); + sanitizer.AllowedTags.UnionWith(Constants.SvgSanitizer.Tags); + var svgContent = System.IO.File.ReadAllText(iconPath); var sanitizedString = sanitizer.Sanitize(svgContent); @@ -91,5 +109,26 @@ namespace Umbraco.Web.Services return null; } } + + private IEnumerable GetAllIconNames() + { + // add icons from plugins + var appPlugins = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.AppPlugins)); + var pluginIcons = appPlugins.Exists == false + ? new List() + : appPlugins.GetDirectories() + // Find all directories in App_Plugins that are named "Icons" and get a list of SVGs from them + .SelectMany(x => x.GetDirectories("Icons", SearchOption.AllDirectories)) + .SelectMany(x => x.GetFiles("*.svg", SearchOption.TopDirectoryOnly)); + + // add icons from IconsPath if not already added from plugins + var directory = new DirectoryInfo(IOHelper.MapPath($"{_globalSettings.IconsPath}/")); + var iconNames = directory.GetFiles("*.svg") + .Where(x => pluginIcons.Any(i => i.Name == x.Name) == false); + + iconNames = iconNames.Concat(pluginIcons).ToList(); + + return iconNames; + } } } From df607dbef8bab97f668a9cf6bd894fc0e46e5302 Mon Sep 17 00:00:00 2001 From: Mole Date: Wed, 7 Oct 2020 11:20:40 +0200 Subject: [PATCH 08/73] Make rollback test pass again --- .../cypress/integration/Content/content.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts index bb70a1c858..9095ee0abb 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts @@ -317,11 +317,11 @@ context('Content', () => { // Rollback cy.get('.umb-box-header :button').click(); - cy.get('.umb-box-content > .ng-scope > .input-block-level') + cy.get('.umb-box-content > div > .input-block-level') .find('option[label*=' + new Date().getDate() + ']') .then(elements => { const option = elements[[elements.length - 1]].getAttribute('value'); - cy.get('.umb-box-content > .ng-scope > .input-block-level') + cy.get('.umb-box-content > div > .input-block-level') .select(option); }); @@ -330,8 +330,8 @@ context('Content', () => { cy.reload(); // Assert - cy.get('.history').find('.umb-badge').eq(0).should('contain.text', "Rollback"); - cy.get('.history').find('.umb-badge').eq(1).should('contain.text', "Save"); + cy.get('.history').find('.umb-badge').eq(0).should('contain.text', "Save"); + cy.get('.history').find('.umb-badge').eq(1).should('contain.text', "Rollback"); cy.get('#headerName').should('have.value', initialNodeName); // Clean up (content is automatically deleted when document types are gone) From 3137c01ea1b8b76917c93a1be08eefc21cc1e4a4 Mon Sep 17 00:00:00 2001 From: Mole Date: Wed, 7 Oct 2020 13:04:23 +0200 Subject: [PATCH 09/73] Assert success notification after publishign content in rollback test --- .../cypress/integration/Content/content.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts index 9095ee0abb..bb121b5f5b 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts @@ -313,6 +313,7 @@ context('Content', () => { // Save and publish cy.get('.btn-success').first().click(); + cy.umbracoSuccessNotification().should('be.visible'); // Rollback cy.get('.umb-box-header :button').click(); From 035b5beba737f97ed80c6eb473b18c7fc0247f3f Mon Sep 17 00:00:00 2001 From: Mole Date: Wed, 7 Oct 2020 15:19:56 +0200 Subject: [PATCH 10/73] Click the content area in create template test instead of making edit Tests no longer fail with a weird The process cannot access the file, or well at least not in the 10 recurrent tries i just did. --- .../cypress/integration/Settings/templates.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts index aff1c38093..e315a74c8a 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts @@ -27,12 +27,14 @@ context('Templates', () => { 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_text-input').type("var num = 5;", {force:true} ); - - //Save + /* + Click the content area, if we don't do this we wont get redirected + to the new edit url, this will cause the test to save two copies at best + worst case, we'll get an error "The process cannot access the file" on the + template. + */ + cy.get('.ace_content').click(); + // Save cy.get('.btn-success').click(); //Assert From 030928f3a6938bc409917a48c6896e43214ffe10 Mon Sep 17 00:00:00 2001 From: Mole Date: Thu, 8 Oct 2020 09:35:16 +0200 Subject: [PATCH 11/73] Unfocus and wait for save to be visible in templates --- .../cypress/integration/Settings/templates.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts index e315a74c8a..4cc990b762 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts @@ -21,20 +21,21 @@ context('Templates', () => { it('Create template', () => { - const name = "Test template test"; + const name = "Create Template test"; cy.umbracoEnsureTemplateNameNotExists(name); createTemplate(); //Type name cy.umbracoEditorHeaderName(name); - /* - Click the content area, if we don't do this we wont get redirected - to the new edit url, this will cause the test to save two copies at best - worst case, we'll get an error "The process cannot access the file" on the - template. - */ - cy.get('.ace_content').click(); // Save + // We must drop focus for the auto save event to occur. + cy.get('.btn-success').focus(); + // And then wait for the auto save event to finish but finding the page in the tree view + // This is a bit of a roundabout way to find items in a treev view since we dont use umbracoTreeItem + // But we must be able to wait for the save evnent to finish, and we can't do that with umbracoTreeItem + cy.get('[data-element="tree-item-templates"] > :nth-child(2) > .umb-animated > .umb-tree-item__inner > .umb-tree-item__label').contains(name).should('be.visible', {timeout:10000}); + // Now that the auto save event has finished we can save + // And there wont be any duplicates or file in use errors. cy.get('.btn-success').click(); //Assert From a5115757847ee96064b0a439c757bbe35cb3cc7a Mon Sep 17 00:00:00 2001 From: Mole Date: Thu, 8 Oct 2020 10:06:56 +0200 Subject: [PATCH 12/73] Assert that error notification does not come up on create template Had the test pass sometimes before the fix because the success notification appear, but the error notification appear at well, so the test shouldn't actually have passed --- .../cypress/integration/Settings/templates.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts index 4cc990b762..36fa447d1e 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts @@ -18,8 +18,6 @@ context('Templates', () => { cy.umbracoContextMenuAction("action-create").click(); } - - it('Create template', () => { const name = "Create Template test"; cy.umbracoEnsureTemplateNameNotExists(name); @@ -40,6 +38,9 @@ context('Templates', () => { //Assert cy.umbracoSuccessNotification().should('be.visible'); + // For some reason cy.umbracoErrorNotification tries to click the element which is not possible + // if it doesn't actually exist, making should('not.be.visible') impossible. + cy.get('.umb-notifications__notifications > .alert-error', {timeout: 5000}).should('not.be.visible'); //Clean up cy.umbracoEnsureTemplateNameNotExists(name); From 3c29982c0987baec55a3ed4ec74b40a10efb37c1 Mon Sep 17 00:00:00 2001 From: Mole Date: Thu, 8 Oct 2020 10:35:05 +0200 Subject: [PATCH 13/73] Don't expect a certain order from badges in rollback The badge order is not deterministic which means we can't expect them to be in a certain order. --- .../cypress/integration/Content/content.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts index bb121b5f5b..c63031532c 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts @@ -331,8 +331,8 @@ context('Content', () => { cy.reload(); // Assert - cy.get('.history').find('.umb-badge').eq(0).should('contain.text', "Save"); - cy.get('.history').find('.umb-badge').eq(1).should('contain.text', "Rollback"); + cy.get('.history').find('.umb-badge').contains('Save').should('be.visible'); + cy.get('.history').find('.umb-badge').contains('Rollback').should('be.visible'); cy.get('#headerName').should('have.value', initialNodeName); // Clean up (content is automatically deleted when document types are gone) From c8018ed00a941f4e16621968d45918a6970fdf47 Mon Sep 17 00:00:00 2001 From: Mole Date: Thu, 8 Oct 2020 15:19:54 +0200 Subject: [PATCH 14/73] Add a timeout when getting the progress percentage --- .../cypress/integration/Tour/backofficeTour.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tour/backofficeTour.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tour/backofficeTour.ts index 307da2518c..e7f250d2be 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tour/backofficeTour.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tour/backofficeTour.ts @@ -71,7 +71,7 @@ function resetTourData() { function runBackOfficeIntroTour(percentageComplete, buttonText, timeout) { cy.get('[data-element="help-tours"]').should("be.visible"); cy.get('[data-element="help-tours"]').click(); - cy.get('[data-element="help-tours"] .umb-progress-circle', { timeout: timeout }).get('[percentage]').contains(percentageComplete + '%'); + cy.get('[data-element="help-tours"] .umb-progress-circle', { timeout: timeout }).get('[percentage]').contains(percentageComplete + '%', {timeout: timeout}); cy.get('[data-element="help-tours"]').click(); cy.get('[data-element="tour-umbIntroIntroduction"] .umb-button').should("be.visible"); cy.get('[data-element="tour-umbIntroIntroduction"] .umb-button').contains(buttonText); From dc072293f9233bcab690b69d80ec02a919748856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 8 Oct 2020 16:36:25 +0200 Subject: [PATCH 15/73] New dialogs for asking about Preview mode --- src/Umbraco.Core/Constants-Web.cs | 4 + .../src/preview/preview.controller.js | 205 ++++++++++++++++-- .../src/websitepreview/websitepreview.js | 49 +++-- .../Umbraco/Views/Preview/Index.cshtml | 3 + .../config/umbracoSettings.config | 2 +- src/Umbraco.Web/Editors/PreviewController.cs | 17 +- 6 files changed, 240 insertions(+), 40 deletions(-) diff --git a/src/Umbraco.Core/Constants-Web.cs b/src/Umbraco.Core/Constants-Web.cs index 64216ba571..0e956cfd6a 100644 --- a/src/Umbraco.Core/Constants-Web.cs +++ b/src/Umbraco.Core/Constants-Web.cs @@ -17,6 +17,10 @@ /// The preview cookie name /// public const string PreviewCookieName = "UMB_PREVIEW"; + /// + /// Client-side cookie that determins wether the user has accepted to be in Preview Mode when visiting the website. + /// + public const string AcceptPreviewCookieName = "UMB-WEBSITE-PREVIEW-ACCEPT"; public const string InstallerCookieName = "umb_installId"; } diff --git a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js index 5ff8dd3633..412d0c9cc5 100644 --- a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js @@ -5,13 +5,13 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.services']) - .controller("previewController", function ($scope, $window, $location) { + .controller("previewController", function ($scope, $window, $location, $http) { $scope.currentCulture = { iso: '', title: '...', icon: 'icon-loading' } var cultures = []; $scope.tabbingActive = false; - // There are a number of ways to detect when a focus state should be shown when using the tab key and this seems to be the simplest solution. + // There are a number of ways to detect when a focus state should be shown when using the tab key and this seems to be the simplest solution. // For more information about this approach, see https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2 function handleFirstTab(evt) { if (evt.keyCode === 9) { @@ -89,7 +89,7 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi if (isInit === "true") { //do not continue, this is the first load of this new window, if this is passed in it means it's been //initialized by the content editor and then the content editor will actually re-load this window without - //this flag. This is a required trick to get around chrome popup mgr. + //this flag. This is a required trick to get around chrome popup mgr. return; } @@ -100,7 +100,7 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi $scope.valueAreLoaded = false; $scope.devices = [ - { name: "fullsize", css: "fullsize", icon: "icon-application-window-alt", title: "Browser" }, + { name: "fullsize", css: "fullsize", icon: "icon-application-window-alt", title: "Fit browser" }, { name: "desktop", css: "desktop shadow", icon: "icon-display", title: "Desktop" }, { name: "laptop - 1366px", css: "laptop shadow", icon: "icon-laptop", title: "Laptop" }, { name: "iPad portrait - 768px", css: "iPad-portrait shadow", icon: "icon-ipad", title: "Tablet portrait" }, @@ -136,17 +136,24 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi $scope.windowClickHandler = function () { closeOthers(); } + function windowBlurHandler() { closeOthers(); $scope.$digest(); } + window.addEventListener("blur", windowBlurHandler); - var win = $($window); - - win.on("blur", windowBlurHandler); + function windowVisibilityHandler(e) { + // When tab is visible again: + if(document.hidden === false) { + checkPreviewState(); + } + } + document.addEventListener("visibilitychange", windowVisibilityHandler); $scope.$on("$destroy", function () { - win.off("blur", handleBlwindowBlurHandlerur); + window.removeEventListener("blur", windowBlurHandler); + window.removeEventListener("visibilitychange", windowVisibilityHandler); }); @@ -162,6 +169,167 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi $scope.pageUrl = "frame?" + query; } } + function getCookie(cname) { + var name = cname + "="; + var ca = document.cookie.split(";"); + for(var i = 0; i < ca.length; i++) { + var c = ca[i]; + while (c.charAt(0) == " ") { + c = c.substring(1); + } + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + return null; + } + function setCookie(cname, cvalue, exminutes) { + var d = new Date(); + d.setTime(d.getTime() + (exminutes * 60 * 1000)); + document.cookie = cname + "=" + cvalue + ";expires="+d.toUTCString() + ";path=/"; + } + var hasPreviewDialog = false; + function checkPreviewState() { + if (getCookie("UMB_PREVIEW") === null) { + + if(hasPreviewDialog === true) return; + hasPreviewDialog = true; + + // Ask to re-enter preview mode? + + // This modal is also used in websitepreview.js + var modelStyles = ` + + /* Webfont: LatoLatin-Bold */ + @font-face { + font-family: 'Lato'; + src: url('https://fonts.googleapis.com/css2?family=Lato:wght@700&display=swap'); + font-style: normal; + font-weight: 700; + font-display: swap; + text-rendering: optimizeLegibility; + } + + .umbraco-preview-dialog { + position: fixed; + display: flex; + align-items: center; + justify-content: center; + z-index: 99999999; + top:0; + bottom:0; + left:0; + right:0; + overflow: auto; + background-color: rgba(0,0,0,0.6); + } + + .umbraco-preview-dialog__modal { + background-color: #fff; + border-radius: 6px; + box-shadow: 0 3px 7px rgba(0,0,0,0.3); + margin: auto; + padding: 30px 40px; + width: 100%; + max-width: 540px; + font-family: Lato,Helvetica Neue,Helvetica,Arial,sans-serif; + font-size: 15px; + } + + .umbraco-preview-dialog__headline { + font-weight: 700; + font-size: 22px; + color: #1b264f; + margin-top:10px; + margin-bottom:20px; + } + .umbraco-preview-dialog__question { + margin-bottom:30px; + } + .umbraco-preview-dialog__modal > button { + display: inline-block; + cursor: pointer; + padding: 8px 18px; + text-align: center; + vertical-align: middle; + border-radius: 3px; + border:none; + font-family: inherit; + font-weight: 700; + font-size: 15px; + float:right; + margin-left:10px; + + color: #1b264f; + background-color: #f6f1ef; + } + .umbraco-preview-dialog__modal > button:hover { + color: #2152a3; + background-color: #f6f1ef; + } + .umbraco-preview-dialog__modal > button.umbraco-preview-dialog__continue { + color: #fff; + background-color: #2bc37c; + } + .umbraco-preview-dialog__modal > button.umbraco-preview-dialog__continue:hover { + background-color: #39d38b; + } + `; + + var bodyEl = document.getElementsByTagName("BODY")[0]; + + var fragment = document.createElement("div"); + var shadowRoot = fragment.attachShadow({ mode: 'open' }); + + var style = document.createElement("style"); + style.innerHTML = modelStyles; + shadowRoot.appendChild(style); + + var con = document.createElement("div"); + con.className = "umbraco-preview-dialog"; + shadowRoot.appendChild(con); + + var modal = document.createElement("div"); + modal.className = "umbraco-preview-dialog__modal"; + modal.innerHTML = `
Preview content?
+
You have exited preview mode, do you want to continue previewing this content?
`; + con.appendChild(modal); + + var continueButton = document.createElement("button"); + continueButton.type = "button"; + continueButton.className = "umbraco-preview-dialog__continue"; + continueButton.innerHTML = "Preview content"; + continueButton.addEventListener("click", () => { + bodyEl.removeChild(fragment); + reenterPreviewMode(); + hasPreviewDialog = false; + }); + modal.appendChild(continueButton); + + bodyEl.appendChild(fragment); + continueButton.focus(); + + } + } + function reenterPreviewMode() { + //Re-enter Preview Mode + $http({ + method: 'POST', + url: '../preview/enterPreview', + params: { + id: $scope.pageId + } + }) + } + function getPageURL() { + var culture = $location.search().culture || getParameterByName("culture"); + var relativeUrl = "/" + $scope.pageId; + if (culture) { + relativeUrl += '?culture=' + culture; + } + return relativeUrl; + } + /*****************************************************************************/ /* Preview devices */ /*****************************************************************************/ @@ -171,18 +339,21 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi $scope.previewDevice = device; }; + /*****************************************************************************/ + /* Open website in preview mode */ + /*****************************************************************************/ + + $scope.openInBrowser = function () { + setCookie("UMB-WEBSITE-PREVIEW-ACCEPT", "true", 5); + window.open(getPageURL(), "_blank"); + }; + /*****************************************************************************/ /* Exit Preview */ /*****************************************************************************/ $scope.exitPreview = function () { - - var culture = $location.search().culture || getParameterByName("culture"); - var relativeUrl = "/" + $scope.pageId; - if (culture) { - relativeUrl += '?culture=' + culture; - } - window.top.location.href = "../preview/end?redir=" + encodeURIComponent(relativeUrl); + window.top.location.href = "../preview/end?redir=" + encodeURIComponent(getPageURL()); }; $scope.onFrameLoaded = function (iframe) { @@ -239,8 +410,8 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi var vm = this; vm.$postLink = function () { - var resultFrame = $element.find("#resultFrame"); - resultFrame.on("load", iframeReady); + var resultFrame = $element.find("#resultFrame").get(0); + resultFrame.addEventListener("load", iframeReady); }; function iframeReady() { diff --git a/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js b/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js index be5c989f68..cb76b47bb7 100644 --- a/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js +++ b/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js @@ -30,20 +30,31 @@ return c.substring(name.length, c.length); } } - return ""; + return null; } function exitPreviewMode() { window.top.location.href = scriptElement.getAttribute("data-umbraco-path")+"/preview/end?redir=" + encodeURIComponent(window.location.pathname+window.location.search); } - function continuePreviewMode() { - setCookie("UMB-WEBSITE-PREVIEW-ACCEPT", "true", 5); + function continuePreviewMode(minutsToExpire) { + setCookie("UMB-WEBSITE-PREVIEW-ACCEPT", "true", minutsToExpire || 2); } var user = getCookie("UMB-WEBSITE-PREVIEW-ACCEPT"); if (user != "true") { + // This modal is also used in preview.js var modelStyles = ` + /* Webfont: LatoLatin-Bold */ + @font-face { + font-family: 'Lato'; + src: url('https://fonts.googleapis.com/css2?family=Lato:wght@700&display=swap'); + font-style: normal; + font-weight: 700; + font-display: swap; + text-rendering: optimizeLegibility; + } + .umbraco-preview-dialog { position: fixed; display: flex; @@ -68,6 +79,7 @@ max-width: 540px; font-family: Lato,Helvetica Neue,Helvetica,Arial,sans-serif; font-size: 15px; + line-height: 1.5; } .umbraco-preview-dialog__headline { @@ -75,19 +87,22 @@ font-size: 22px; color: #1b264f; margin-top:10px; - margin-bottom:10px; + margin-bottom:20px; } .umbraco-preview-dialog__question { margin-bottom:30px; } .umbraco-preview-dialog__modal > button { display: inline-block; + cursor: pointer; padding: 8px 18px; text-align: center; vertical-align: middle; border-radius: 3px; border:none; + font-family: inherit; font-weight: 700; + font-size: 15px; float:right; margin-left:10px; @@ -109,37 +124,37 @@ var bodyEl = document.getElementsByTagName("BODY")[0]; - var fragment = document.createDocumentFragment(); + var fragment = document.createElement("div"); + var shadowRoot = fragment.attachShadow({ mode: 'open' }); var style = document.createElement("style"); style.innerHTML = modelStyles; - fragment.appendChild(style); + shadowRoot.appendChild(style); var con = document.createElement("div"); con.className = "umbraco-preview-dialog"; - fragment.appendChild(con); + shadowRoot.appendChild(con); var modal = document.createElement("div"); modal.className = "umbraco-preview-dialog__modal"; - modal.innerHTML = `
Preview mode
-
Do you want to continue viewing latest saved version?
`; + modal.innerHTML = `
View published version?
+
You are in Preview Mode, do you want exit in order to view the published version of your website?
`; con.appendChild(modal); var continueButton = document.createElement("button"); continueButton.type = "button"; continueButton.className = "umbraco-preview-dialog__continue"; - continueButton.innerHTML = "Continue"; - continueButton.addEventListener("click", function() { - bodyEl.removeChild(style); - bodyEl.removeChild(con); - continuePreviewMode(); - }); + continueButton.innerHTML = "View published version"; + continueButton.addEventListener("click", exitPreviewMode); modal.appendChild(continueButton); var exitButton = document.createElement("button"); exitButton.type = "button"; - exitButton.innerHTML = "Exit preview mode"; - exitButton.addEventListener("click", exitPreviewMode); + exitButton.innerHTML = "Stay in preview mode"; + exitButton.addEventListener("click", function() { + bodyEl.removeChild(fragment); + continuePreviewMode(5); + }); modal.appendChild(exitButton); bodyEl.appendChild(fragment); diff --git a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml index 4b7bcaee87..8b7d871ba4 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml @@ -65,6 +65,9 @@
} + + diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 2287464d07..086210e5a5 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -64,7 +64,7 @@ pointer-events:none; left: 50%; transform: translate(-50%, 40px); - animation: umbraco-preview-badge--effect 10s 100ms ease both; + animation: umbraco-preview-badge--effect 10s 1.2s ease both; border-radius: 3px 3px 0 0; }} @keyframes umbraco-preview-badge--effect {{ diff --git a/src/Umbraco.Web/Editors/PreviewController.cs b/src/Umbraco.Web/Editors/PreviewController.cs index f148655acd..c57968af06 100644 --- a/src/Umbraco.Web/Editors/PreviewController.cs +++ b/src/Umbraco.Web/Editors/PreviewController.cs @@ -101,11 +101,7 @@ namespace Umbraco.Web.Editors [UmbracoAuthorize] public ActionResult Frame(int id, string culture) { - var user = _umbracoContextAccessor.UmbracoContext.Security.CurrentUser; - - var previewToken = _publishedSnapshotService.EnterPreview(user, id); - - Response.Cookies.Set(new HttpCookie(Constants.Web.PreviewCookieName, previewToken)); + EnterPreview(id); // use a numeric url because content may not be in cache and so .Url would fail var query = culture.IsNullOrWhiteSpace() ? string.Empty : $"?culture={culture}"; @@ -113,7 +109,16 @@ namespace Umbraco.Web.Editors return null; } + public ActionResult EnterPreview(int id) + { + var user = _umbracoContextAccessor.UmbracoContext.Security.CurrentUser; + var previewToken = _publishedSnapshotService.EnterPreview(user, id); + + Response.Cookies.Set(new HttpCookie(Constants.Web.PreviewCookieName, previewToken)); + + return null; + } public ActionResult End(string redir = null) { var previewToken = Request.GetPreviewCookieValue(); @@ -121,6 +126,8 @@ namespace Umbraco.Web.Editors service.ExitPreview(previewToken); System.Web.HttpContext.Current.ExpireCookie(Constants.Web.PreviewCookieName); + // Expire Client-side cookie that determins wether the user has accepted to be in Preview Mode when visiting the website. + System.Web.HttpContext.Current.ExpireCookie(Constants.Web.AcceptPreviewCookieName); if (Uri.IsWellFormedUriString(redir, UriKind.Relative) && redir.StartsWith("//") == false From 3e77457b817232be9939cb29200d7dddcc30d4be Mon Sep 17 00:00:00 2001 From: Mole Date: Fri, 9 Oct 2020 09:49:09 +0200 Subject: [PATCH 16/73] Increase typing timeout in member tests and some minor cleaning --- .../cypress/integration/Members/members.js | 5 +++-- .../cypress/integration/Settings/templates.ts | 11 ++++++----- .../cypress/integration/Tour/backofficeTour.ts | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/members.js b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/members.js index aeb4576ccd..3d257cfa58 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/members.js +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Members/members.js @@ -9,6 +9,7 @@ context('Members', () => { const name = "Alice Bobson"; const email = "alice-bobson@acceptancetest.umbraco"; const password = "$AUlkoF*St0kgPiyyVEk5iU5JWdN*F7&@OSl5Y4pOofnidfifkBj5Ns2ONv%FzsTl36V1E924Gw97zcuSeT7UwK&qb5l&O9h!d!w"; + const passwordTimeout = 20000 cy.umbracoEnsureMemberEmailNotExists(email); cy.umbracoSection('member'); @@ -24,8 +25,8 @@ context('Members', () => { cy.get('input#_umb_login').clear().type(email); cy.get('input#_umb_email').clear().type(email); - cy.get('input#password').clear().type(password); - cy.get('input#confirmPassword').clear().type(password); + cy.get('input#password').clear().type(password, { timeout: passwordTimeout }); + cy.get('input#confirmPassword').clear().type(password, { timeout: passwordTimeout }); // Save cy.get('.btn-success').click(); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts index 36fa447d1e..39e303a486 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts @@ -28,19 +28,20 @@ context('Templates', () => { // Save // We must drop focus for the auto save event to occur. cy.get('.btn-success').focus(); - // And then wait for the auto save event to finish but finding the page in the tree view + // And then wait for the auto save event to finish by finding the page in the tree view. // This is a bit of a roundabout way to find items in a treev view since we dont use umbracoTreeItem - // But we must be able to wait for the save evnent to finish, and we can't do that with umbracoTreeItem - cy.get('[data-element="tree-item-templates"] > :nth-child(2) > .umb-animated > .umb-tree-item__inner > .umb-tree-item__label').contains(name).should('be.visible', {timeout:10000}); + // but we must be able to wait for the save evnent to finish, and we can't do that with umbracoTreeItem + cy.get('[data-element="tree-item-templates"] > :nth-child(2) > .umb-animated > .umb-tree-item__inner > .umb-tree-item__label') + .contains(name).should('be.visible', { timeout: 10000 }); // Now that the auto save event has finished we can save - // And there wont be any duplicates or file in use errors. + // and there wont be any duplicates or file in use errors. cy.get('.btn-success').click(); //Assert cy.umbracoSuccessNotification().should('be.visible'); // For some reason cy.umbracoErrorNotification tries to click the element which is not possible // if it doesn't actually exist, making should('not.be.visible') impossible. - cy.get('.umb-notifications__notifications > .alert-error', {timeout: 5000}).should('not.be.visible'); + cy.get('.umb-notifications__notifications > .alert-error').should('not.be.visible'); //Clean up cy.umbracoEnsureTemplateNameNotExists(name); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tour/backofficeTour.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tour/backofficeTour.ts index e7f250d2be..9bc1fff488 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tour/backofficeTour.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Tour/backofficeTour.ts @@ -71,7 +71,7 @@ function resetTourData() { function runBackOfficeIntroTour(percentageComplete, buttonText, timeout) { cy.get('[data-element="help-tours"]').should("be.visible"); cy.get('[data-element="help-tours"]').click(); - cy.get('[data-element="help-tours"] .umb-progress-circle', { timeout: timeout }).get('[percentage]').contains(percentageComplete + '%', {timeout: timeout}); + cy.get('[data-element="help-tours"] .umb-progress-circle', { timeout: timeout }).get('[percentage]').contains(percentageComplete + '%', { timeout: timeout }); cy.get('[data-element="help-tours"]').click(); cy.get('[data-element="tour-umbIntroIntroduction"] .umb-button').should("be.visible"); cy.get('[data-element="tour-umbIntroIntroduction"] .umb-button').contains(buttonText); From 2fcab52e7e17b28bcd38c2f0f323e5c68f356538 Mon Sep 17 00:00:00 2001 From: Mole Date: Fri, 9 Oct 2020 10:07:04 +0200 Subject: [PATCH 17/73] Indent nested builders Makes it easier to see what calls belongs where --- .../cypress/integration/Content/content.ts | 82 +++++++++---------- .../cypress/integration/Settings/templates.ts | 2 +- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts index c63031532c..e52f109b1a 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts @@ -48,8 +48,8 @@ context('Content', () => { .withContentTypeAlias(rootDocTypeAlias) .withAction("saveNew") .addVariant() - .withName(nodeName) - .withSave(true) + .withName(nodeName) + .withSave(true) .done() .build(); @@ -60,8 +60,8 @@ context('Content', () => { .withAction("saveNew") .withParent(contentNode["id"]) .addVariant() - .withName(childNodeName) - .withSave(true) + .withName(childNodeName) + .withSave(true) .done() .build(); @@ -72,8 +72,8 @@ context('Content', () => { .withContentTypeAlias(rootDocTypeAlias) .withAction("saveNew") .addVariant() - .withName(anotherNodeName) - .withSave(true) + .withName(anotherNodeName) + .withSave(true) .done() .build(); @@ -132,8 +132,8 @@ context('Content', () => { .withContentTypeAlias(rootDocTypeAlias) .withAction("saveNew") .addVariant() - .withName(nodeName) - .withSave(true) + .withName(nodeName) + .withSave(true) .done() .build(); @@ -144,8 +144,8 @@ context('Content', () => { .withAction("saveNew") .withParent(contentNode["id"]) .addVariant() - .withName(childNodeName) - .withSave(true) + .withName(childNodeName) + .withSave(true) .done() .build(); @@ -156,8 +156,8 @@ context('Content', () => { .withContentTypeAlias(rootDocTypeAlias) .withAction("saveNew") .addVariant() - .withName(anotherNodeName) - .withSave(true) + .withName(anotherNodeName) + .withSave(true) .done() .build(); @@ -215,8 +215,8 @@ context('Content', () => { .withContentTypeAlias(generatedRootDocType["alias"]) .withAction("saveNew") .addVariant() - .withName(nodeName) - .withSave(true) + .withName(nodeName) + .withSave(true) .done() .build(); @@ -229,8 +229,8 @@ context('Content', () => { .withAction("saveNew") .withParent(parentId) .addVariant() - .withName(firstChildNodeName) - .withSave(true) + .withName(firstChildNodeName) + .withSave(true) .done() .build(); @@ -242,8 +242,8 @@ context('Content', () => { .withAction("saveNew") .withParent(parentId) .addVariant() - .withName(secondChildNodeName) - .withSave(true) + .withName(secondChildNodeName) + .withSave(true) .done() .build(); @@ -293,8 +293,8 @@ context('Content', () => { const rootContentNode = new ContentBuilder() .withContentTypeAlias(generatedRootDocType["alias"]) .addVariant() - .withName(initialNodeName) - .withSave(true) + .withName(initialNodeName) + .withSave(true) .done() .build(); @@ -348,9 +348,9 @@ context('Content', () => { .withName(rootDocTypeName) .withAllowAsRoot(true) .addGroup() - .addTextBoxProperty() - .withLabel(labelName) - .done() + .addTextBoxProperty() + .withLabel(labelName) + .done() .done() .build(); @@ -361,8 +361,8 @@ context('Content', () => { const rootContentNode = new ContentBuilder() .withContentTypeAlias(generatedRootDocType["alias"]) .addVariant() - .withName(nodeName) - .withSave(true) + .withName(nodeName) + .withSave(true) .done() .build(); @@ -402,8 +402,8 @@ context('Content', () => { .withContentTypeAlias(generatedRootDocType["alias"]) .withAction("saveNew") .addVariant() - .withName(nodeName) - .withSave(true) + .withName(nodeName) + .withSave(true) .done() .build(); @@ -440,8 +440,8 @@ context('Content', () => { .withContentTypeAlias(generatedRootDocType["alias"]) .withAction("saveNew") .addVariant() - .withName(nodeName) - .withSave(true) + .withName(nodeName) + .withSave(true) .done() .build(); @@ -480,8 +480,8 @@ context('Content', () => { const rootContentNode = new ContentBuilder() .withContentTypeAlias(generatedRootDocType["alias"]) .addVariant() - .withName(nodeName) - .withSave(true) + .withName(nodeName) + .withSave(true) .done() .build(); @@ -502,10 +502,10 @@ context('Content', () => { }); it('Content with contentpicker', () => { - const pickerDocTypeName = 'Content Picker Type'; + const pickerDocTypeName = 'Content picker doc type'; const groupName = 'ContentPickerGroup'; const alias = AliasHelper.toAlias(pickerDocTypeName); - const pickedDocTypeName = 'Picked Content Type'; + const pickedDocTypeName = 'Picked content document type'; const pickedNodeName = 'Content to pick'; cy.deleteAllContent(); @@ -518,8 +518,8 @@ context('Content', () => { .withName(pickedDocTypeName) .withAllowAsRoot(true) .addGroup() - .addTextBoxProperty() - .withAlias('text') + .addTextBoxProperty() + .withAlias('text') .done() .done() .build(); @@ -533,8 +533,8 @@ context('Content', () => { .withSave(true) .withPublish(true) .addProperty() - .withAlias('text') - .withValue('Acceptance test') + .withAlias('text') + .withValue('Acceptance test') .done() .withSave(true) .withPublish(true) @@ -550,10 +550,10 @@ context('Content', () => { .withAllowAsRoot(true) .withDefaultTemplate(alias) .addGroup() - .withName(groupName) - .addContentPickerProperty() - .withAlias('picker') - .done() + .withName(groupName) + .addContentPickerProperty() + .withAlias('picker') + .done() .done() .build(); diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts index 39e303a486..da1adedaeb 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Settings/templates.ts @@ -19,7 +19,7 @@ context('Templates', () => { } it('Create template', () => { - const name = "Create Template test"; + const name = "Create template test"; cy.umbracoEnsureTemplateNameNotExists(name); createTemplate(); From 9039f3030bbeebd8e11605b1e843edc2736cb441 Mon Sep 17 00:00:00 2001 From: Mole Date: Fri, 9 Oct 2020 10:39:20 +0200 Subject: [PATCH 18/73] Fix content with contentpicker after renaming doctype --- .../cypress/integration/Content/content.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts index e52f109b1a..4422df9859 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts @@ -503,10 +503,8 @@ context('Content', () => { it('Content with contentpicker', () => { const pickerDocTypeName = 'Content picker doc type'; - const groupName = 'ContentPickerGroup'; - const alias = AliasHelper.toAlias(pickerDocTypeName); + const pickerDocTypeAlias = AliasHelper.toAlias(pickerDocTypeName); const pickedDocTypeName = 'Picked content document type'; - const pickedNodeName = 'Content to pick'; cy.deleteAllContent(); cy.umbracoEnsureDocumentTypeNameNotExists(pickerDocTypeName); @@ -529,7 +527,7 @@ context('Content', () => { .withContentTypeAlias(generatedType["alias"]) .withAction("publishNew") .addVariant() - .withName(pickedNodeName) + .withName('Content to pick') .withSave(true) .withPublish(true) .addProperty() @@ -546,11 +544,11 @@ context('Content', () => { // Create the doctype with a the picker const pickerDocType = new DocumentTypeBuilder() .withName(pickerDocTypeName) - .withAlias(alias) + .withAlias(pickerDocTypeAlias) .withAllowAsRoot(true) - .withDefaultTemplate(alias) + .withDefaultTemplate(pickerDocTypeAlias) .addGroup() - .withName(groupName) + .withName('ContentPickerGroup') .addContentPickerProperty() .withAlias('picker') .done() @@ -560,7 +558,7 @@ context('Content', () => { cy.saveDocumentType(pickerDocType); // Edit it the template to allow us to verify the rendered view. - cy.editTemplate(pickerDocTypeName, `@inherits Umbraco.Web.Mvc.UmbracoViewPage + cy.editTemplate(pickerDocTypeName, `@inherits Umbraco.Web.Mvc.UmbracoViewPage @using ContentModels = Umbraco.Web.PublishedModels; @{ Layout = null; @@ -577,7 +575,7 @@ context('Content', () => { // Create content with content picker cy.get('.umb-tree-root-link').rightclick(); cy.get('.-opens-dialog > .umb-action-link').click(); - cy.get('[data-element="action-create-contentPickerType"] > .umb-action-link').click(); + cy.get('[data-element="action-create-' + pickerDocTypeAlias + '"] > .umb-action-link').click(); // Fill out content cy.umbracoEditorHeaderName('ContentPickerContent'); cy.get('.umb-node-preview-add').click(); @@ -590,6 +588,7 @@ context('Content', () => { cy.umbracoSuccessNotification().should('be.visible'); // Assert + cy.log('Checking that content is rendered correctly.') const expectedContent = '

Acceptance test

' cy.umbracoVerifyRenderedViewContent('contentpickercontent', expectedContent, true).should('be.true'); // clean From 4485e76a28ed10dad70fce40ca5f438432b22005 Mon Sep 17 00:00:00 2001 From: Mole Date: Fri, 9 Oct 2020 10:44:52 +0200 Subject: [PATCH 19/73] Add forgotten indent --- .../cypress/integration/Content/content.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts index 4422df9859..68f31e80bb 100644 --- a/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts +++ b/src/Umbraco.Tests.AcceptanceTest/cypress/integration/Content/content.ts @@ -517,8 +517,8 @@ context('Content', () => { .withAllowAsRoot(true) .addGroup() .addTextBoxProperty() - .withAlias('text') - .done() + .withAlias('text') + .done() .done() .build(); From 6009acdfbf0558cbb8273d1589e57c0a7a05eaf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 9 Oct 2020 10:46:36 +0200 Subject: [PATCH 20/73] updated wording --- src/Umbraco.Web.UI.Client/src/preview/preview.controller.js | 2 +- .../src/websitepreview/websitepreview.js | 2 +- src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml | 4 ++-- src/Umbraco.Web.UI/config/umbracoSettings.config | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js index 412d0c9cc5..056ade090e 100644 --- a/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/preview/preview.controller.js @@ -292,7 +292,7 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi var modal = document.createElement("div"); modal.className = "umbraco-preview-dialog__modal"; modal.innerHTML = `
Preview content?
-
You have exited preview mode, do you want to continue previewing this content?
`; +
You have ended preview mode, do you want to continue previewing this content?
`; con.appendChild(modal); var continueButton = document.createElement("button"); diff --git a/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js b/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js index cb76b47bb7..43fd152e61 100644 --- a/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js +++ b/src/Umbraco.Web.UI.Client/src/websitepreview/websitepreview.js @@ -37,7 +37,7 @@ window.top.location.href = scriptElement.getAttribute("data-umbraco-path")+"/preview/end?redir=" + encodeURIComponent(window.location.pathname+window.location.search); } function continuePreviewMode(minutsToExpire) { - setCookie("UMB-WEBSITE-PREVIEW-ACCEPT", "true", minutsToExpire || 2); + setCookie("UMB-WEBSITE-PREVIEW-ACCEPT", "true", minutsToExpire || 4); } var user = getCookie("UMB-WEBSITE-PREVIEW-ACCEPT"); diff --git a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml index 8b7d871ba4..912470eb65 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml @@ -65,10 +65,10 @@ } - - diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index 086210e5a5..454da921e7 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -41,11 +41,11 @@ Preview mode - + - - Click to end + + Click to end preview mode