From b215c6b271677e47f7fab5f1aa126c893adc0f43 Mon Sep 17 00:00:00 2001 From: Tomasz Dziuda Date: Tue, 22 Mar 2022 19:07:53 +0100 Subject: [PATCH 001/126] fix: solve issue with SFTP crash on upload error (#294pvj3) --- app/back-end/builddata.json | 2 +- app/back-end/modules/deploy/sftp.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/back-end/builddata.json b/app/back-end/builddata.json index 89b43feb8..556b85441 100644 --- a/app/back-end/builddata.json +++ b/app/back-end/builddata.json @@ -1 +1 @@ -{"version":"0.39.1","build":15483} \ No newline at end of file +{"version":"0.39.1","build":15484} \ No newline at end of file diff --git a/app/back-end/modules/deploy/sftp.js b/app/back-end/modules/deploy/sftp.js index e3c5b8785..fc8e980fb 100644 --- a/app/back-end/modules/deploy/sftp.js +++ b/app/back-end/modules/deploy/sftp.js @@ -220,7 +220,7 @@ class SFTP { self.deployment.uploadFile(); }).catch(err => { - console.log(`[${ new Date().toUTCString() }] ERROR UPLOAD FILE: ${normalizePath(path.join(self.outputDir, input))}`); + console.log(`[${ new Date().toUTCString() }] ERROR UPLOAD FILE: ${normalizePath(input)}`); console.log(`[${ new Date().toUTCString() }] ${err}`); self.deployment.uploadFile(); }); From af88f1f23888bd89a464c44228731a3c845f77d4 Mon Sep 17 00:00:00 2001 From: Tomasz Dziuda Date: Sat, 2 Apr 2022 10:49:18 +0200 Subject: [PATCH 002/126] feat: add `is-outdated` css class for languages list (#28z4qtt) --- app/back-end/builddata.json | 2 +- app/src/components/LanguagesListItem.vue | 3 ++- package-lock.json | 20 +++++++------------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/app/back-end/builddata.json b/app/back-end/builddata.json index 556b85441..3eefd48ef 100644 --- a/app/back-end/builddata.json +++ b/app/back-end/builddata.json @@ -1 +1 @@ -{"version":"0.39.1","build":15484} \ No newline at end of file +{"version":"0.39.1","build":15490} \ No newline at end of file diff --git a/app/src/components/LanguagesListItem.vue b/app/src/components/LanguagesListItem.vue index 62b90d419..5044ef0b5 100644 --- a/app/src/components/LanguagesListItem.vue +++ b/app/src/components/LanguagesListItem.vue @@ -3,7 +3,8 @@ @click="activateLanguage(directory, type)" :class="{ 'language': true, - 'is-active': isActiveLanguage + 'is-active': isActiveLanguage, + 'is-outdated': isOutdated }"> Date: Sat, 2 Apr 2022 10:57:06 +0200 Subject: [PATCH 003/126] fix: solve issue with missing translation while removing language (#294p6en) --- app/back-end/builddata.json | 2 +- app/src/components/LanguagesListItem.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/back-end/builddata.json b/app/back-end/builddata.json index 3eefd48ef..555b56f9e 100644 --- a/app/back-end/builddata.json +++ b/app/back-end/builddata.json @@ -1 +1 @@ -{"version":"0.39.1","build":15490} \ No newline at end of file +{"version":"0.39.1","build":15493} \ No newline at end of file diff --git a/app/src/components/LanguagesListItem.vue b/app/src/components/LanguagesListItem.vue index 5044ef0b5..33f87c1ec 100644 --- a/app/src/components/LanguagesListItem.vue +++ b/app/src/components/LanguagesListItem.vue @@ -90,7 +90,7 @@ export default { methods: { deleteLanguage (languageName, languageDirectory) { let confirmConfig = { - message: this.$t('langs.removeLanguageMessage', languageName), + message: this.$t('langs.removeLanguageMessage', { languageName }), okClick: function() { mainProcessAPI.send('app-language-delete', { name: languageName, From 79cda6bcbddbe94fc53ebf163e977dafab29d49b Mon Sep 17 00:00:00 2001 From: Tomasz Dziuda Date: Sat, 2 Apr 2022 10:59:30 +0200 Subject: [PATCH 004/126] fix: remove unnecessary character in the block editor link popup (#294pq32) --- app/src/components/block-editor/components/BlockLinkPopup.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/components/block-editor/components/BlockLinkPopup.vue b/app/src/components/block-editor/components/BlockLinkPopup.vue index 893eb8047..80418ce0f 100644 --- a/app/src/components/block-editor/components/BlockLinkPopup.vue +++ b/app/src/components/block-editor/components/BlockLinkPopup.vue @@ -83,7 +83,7 @@ {{ $t('link.openInNewTab') }} -= +
Date: Sat, 2 Apr 2022 11:02:24 +0200 Subject: [PATCH 005/126] feat: publii UI translations are now core feature not experimental (#294p6j9) --- app/back-end/builddata.json | 2 +- app/config/AST.app.config.js | 1 - .../default-languages/en-gb/translations.json | 2 -- .../default-languages/pl/translations.json | 2 -- app/src/components/AppSettings.vue | 17 ----------------- app/src/components/TopBarDropDown.vue | 1 - 6 files changed, 1 insertion(+), 24 deletions(-) diff --git a/app/back-end/builddata.json b/app/back-end/builddata.json index 555b56f9e..6baebb6fe 100644 --- a/app/back-end/builddata.json +++ b/app/back-end/builddata.json @@ -1 +1 @@ -{"version":"0.39.1","build":15493} \ No newline at end of file +{"version":"0.39.1","build":15494} \ No newline at end of file diff --git a/app/config/AST.app.config.js b/app/config/AST.app.config.js index 60e03a1e3..1971bd240 100644 --- a/app/config/AST.app.config.js +++ b/app/config/AST.app.config.js @@ -22,7 +22,6 @@ const AstAppConfig = { enableAdvancedPreview: false, editorFontSize: 18, editorFontFamily: 'sans-serif', - experimentalFeatureAppUiLanguages: false, experimentalFeatureAppAutoBeautifySourceCode: false }; diff --git a/app/default-files/default-languages/en-gb/translations.json b/app/default-files/default-languages/en-gb/translations.json index 358423ba5..e8fa0c055 100644 --- a/app/default-files/default-languages/en-gb/translations.json +++ b/app/default-files/default-languages/en-gb/translations.json @@ -604,8 +604,6 @@ "excludeFeaturedPosts": "Exclude featured posts", "excludeFilesFromSitemapInfo": "Type a comma-separated list of HTML files or folders to exclude from the sitemap.
For example: avoid-this-file.html,avoid-this-catalog-too.", "experimentalFeaturesWarning": "We're always looking for ways to improve our app. This section contains experimental features designed to extend Publii's functionality. Since they're experimental we can't guarantee they will work properly; activate them at your own risk.", - "experimentalFeatureAppUiLanguages": "Publii UI translations", - "experimentalFeatureAppUiLanguagesDesc": "This function allows you to manage Publii UI translations in the 'Languages' section which is available in the application menu (three dots menu).", "experimentalFeatureAppAutoBeautifySourceCode": "Auto-beautify source code in WYSIWYG editor", "experimentalFeatureAppAutoBeautifySourceCodeDesc": "This function enable automatic beautify for source code in the WYSIWYG editor", "externalImages": "External images", diff --git a/app/default-files/default-languages/pl/translations.json b/app/default-files/default-languages/pl/translations.json index bc320b9eb..44628df99 100644 --- a/app/default-files/default-languages/pl/translations.json +++ b/app/default-files/default-languages/pl/translations.json @@ -604,8 +604,6 @@ "excludeFeaturedPosts": "Wyklucz wyróżnione wpisy", "excludeFilesFromSitemapInfo": "Wpisz rozdzieloną przecinkami listę plików HTML lub katalogów, które mają być wykluczone z mapy witryny.
Na przykład: avoid-this-file.html,avoid-this-catalog-too.", "experimentalFeaturesWarning": "Zawsze szukamy sposobów na ulepszenie naszej aplikacji. Ta sekcja zawiera eksperymentalne funkcje zaprojektowane w celu rozszerzenia funkcjonalności Publii. Nie możemy zagwarantować ich poprawnego działania; aktywujesz je na własne ryzyko.", - "experimentalFeatureAppUiLanguages": "Tłumaczenia interfejsu Publii", - "experimentalFeatureAppUiLanguagesDesc": "Ta funkcja umożliwia zarządzanie tłumaczeniami interfejsu Publii w sekcji 'Języki' dostępnej w menu aplikacji (trzy kropki).", "experimentalFeatureAppAutoBeautifySourceCode": "Automatycznie upiększaj kod źródłowy w edytorze WYSIWYG", "experimentalFeatureAppAutoBeautifySourceCodeDesc": "Ta funkcja włącza automatyczne upiększanie kodu w edytorze kodu źródłowego edytora WYSIWYG", "externalImages": "Zewnętrzne obrazy", diff --git a/app/src/components/AppSettings.vue b/app/src/components/AppSettings.vue index 8990aed5e..c7dfcfb29 100644 --- a/app/src/components/AppSettings.vue +++ b/app/src/components/AppSettings.vue @@ -291,20 +291,6 @@ customHeight="28" />
- - - - {{ $t('settings.experimentalFeatureAppUiLanguagesDesc') }} - - From 947f46e6cb565c067dd5c54b6a780d6d4608f63d Mon Sep 17 00:00:00 2001 From: Tomasz Dziuda Date: Sat, 2 Apr 2022 16:38:12 +0200 Subject: [PATCH 006/126] feat: added support for optgroup in select field type for theme/plugin settings (#28duuv8) --- app/back-end/builddata.json | 2 +- app/src/components/ThemeSettings.vue | 32 ++++++++- app/src/components/ToolsPlugin.vue | 32 ++++++++- .../components/basic-elements/Dropdown.vue | 66 ++++++++++++++----- 4 files changed, 109 insertions(+), 23 deletions(-) diff --git a/app/back-end/builddata.json b/app/back-end/builddata.json index 6baebb6fe..4608438ab 100644 --- a/app/back-end/builddata.json +++ b/app/back-end/builddata.json @@ -1 +1 @@ -{"version":"0.39.1","build":15494} \ No newline at end of file +{"version":"0.39.1","build":15499} \ No newline at end of file diff --git a/app/src/components/ThemeSettings.vue b/app/src/components/ThemeSettings.vue index db10c181b..1539c222f 100644 --- a/app/src/components/ThemeSettings.vue +++ b/app/src/components/ThemeSettings.vue @@ -578,9 +578,37 @@ export default { }, getDropdownOptions (inputOptions) { let options = {}; + let hasGroups = !!inputOptions.filter(option => typeof option.group !== 'undefined').length; - for (let i = 0; i < inputOptions.length; i++) { - options[inputOptions[i].value] = inputOptions[i].label; + if (hasGroups) { + options.hasGroups = true; + let groups = { + ungrouped: {} + }; + + for (let i = 0; i < inputOptions.length; i++) { + let groupName = inputOptions[i].group; + + if (groupName && !groups[groupName]) { + groups[groupName] = {}; + } + } + + for (let i = 0; i < inputOptions.length; i++) { + let inputGroupName = inputOptions[i].group; + + if (inputGroupName) { + groups[inputGroupName][inputOptions[i].value] = inputOptions[i].label; + } else { + groups['ungrouped'][inputOptions[i].value] = inputOptions[i].label; + } + } + + options.groups = groups; + } else { + for (let i = 0; i < inputOptions.length; i++) { + options[inputOptions[i].value] = inputOptions[i].label; + } } return options; diff --git a/app/src/components/ToolsPlugin.vue b/app/src/components/ToolsPlugin.vue index 964358dcc..a65d3b4d3 100644 --- a/app/src/components/ToolsPlugin.vue +++ b/app/src/components/ToolsPlugin.vue @@ -511,9 +511,37 @@ export default { }, getDropdownOptions (inputOptions) { let options = {}; + let hasGroups = !!inputOptions.filter(option => typeof option.group !== 'undefined').length; - for (let i = 0; i < inputOptions.length; i++) { - options[inputOptions[i].value] = inputOptions[i].label; + if (hasGroups) { + options.hasGroups = true; + let groups = { + ungrouped: {} + }; + + for (let i = 0; i < inputOptions.length; i++) { + let groupName = inputOptions[i].group; + + if (groupName && !groups[groupName]) { + groups[groupName] = {}; + } + } + + for (let i = 0; i < inputOptions.length; i++) { + let inputGroupName = inputOptions[i].group; + + if (inputGroupName) { + groups[inputGroupName][inputOptions[i].value] = inputOptions[i].label; + } else { + groups['ungrouped'][inputOptions[i].value] = inputOptions[i].label; + } + } + + options.groups = groups; + } else { + for (let i = 0; i < inputOptions.length; i++) { + options[inputOptions[i].value] = inputOptions[i].label; + } } return options; diff --git a/app/src/components/basic-elements/Dropdown.vue b/app/src/components/basic-elements/Dropdown.vue index 9bc46b604..224f8f889 100644 --- a/app/src/components/basic-elements/Dropdown.vue +++ b/app/src/components/basic-elements/Dropdown.vue @@ -7,24 +7,54 @@ :class="{ 'no-border': noBorder }" @change="onChangeEvent"> - - + + + + + From ec8e753f5c6b7711cf3a5f1897eea6de83e2440a Mon Sep 17 00:00:00 2001 From: Tomasz Dziuda Date: Sun, 3 Apr 2022 17:06:36 +0200 Subject: [PATCH 007/126] feat: improve error handling for the manual deploy (#294puc3) --- app/back-end/builddata.json | 2 +- app/back-end/modules/deploy/manual.js | 21 ++++++++++++++++--- .../default-languages/en-gb/translations.json | 1 + .../default-languages/pl/translations.json | 9 ++++---- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/app/back-end/builddata.json b/app/back-end/builddata.json index 4608438ab..65a0f23dd 100644 --- a/app/back-end/builddata.json +++ b/app/back-end/builddata.json @@ -1 +1 @@ -{"version":"0.39.1","build":15499} \ No newline at end of file +{"version":"0.39.1","build":15505} \ No newline at end of file diff --git a/app/back-end/modules/deploy/manual.js b/app/back-end/modules/deploy/manual.js index b648020a1..43d030e16 100644 --- a/app/back-end/modules/deploy/manual.js +++ b/app/back-end/modules/deploy/manual.js @@ -31,13 +31,28 @@ class ManualDeployment { } returnCatalog() { + let outputBaseDir = this.deployment.siteConfig.deployment.manual.outputDirectory; let outputDirName = slug(this.deployment.siteName) + '-files'; - if (!this.deployment.siteConfig.deployment.manual.outputDirectory) { - this.deployment.siteConfig.deployment.manual.outputDirectory = path.join(this.deployment.sitesDir, this.deployment.siteName); + if (outputBaseDir && !Utils.dirExists(outputBaseDir)) { + process.send({ + type: 'web-contents', + message: 'app-connection-error', + value: { + additionalMessage: { + translation: 'core.archive.destinationNotExists' + } + } + }); + + return; + } + + if (!outputBaseDir) { + outputBaseDir = path.join(this.deployment.sitesDir, this.deployment.siteName); } - let outputPath = path.join(this.deployment.siteConfig.deployment.manual.outputDirectory, outputDirName); + let outputPath = path.join(outputBaseDir, outputDirName); if (outputPath !== '') { if (Utils.dirExists(outputPath)) { diff --git a/app/default-files/default-languages/en-gb/translations.json b/app/default-files/default-languages/en-gb/translations.json index e8fa0c055..f6fc79383 100644 --- a/app/default-files/default-languages/en-gb/translations.json +++ b/app/default-files/default-languages/en-gb/translations.json @@ -36,6 +36,7 @@ }, "core": { "archive": { + "destinationNotExists": "Specified output destination not exists", "errorDuringCreatingTAR": "An error occurred during TAR archive creation. Please try again.", "errorDuringCreatingZIP": "An error occurred during ZIP archive creation. Please try again." }, diff --git a/app/default-files/default-languages/pl/translations.json b/app/default-files/default-languages/pl/translations.json index 44628df99..773c799d8 100644 --- a/app/default-files/default-languages/pl/translations.json +++ b/app/default-files/default-languages/pl/translations.json @@ -36,8 +36,9 @@ }, "core": { "archive": { - "errorDuringCreatingTAR": "An error occurred during creating TAR archive. Please try again.", - "errorDuringCreatingZIP": "An error occurred during creating ZIP archive. Please try again." + "destinationNotExists": "Wybrana lokalizacja plików wynikowych nie istnieje", + "errorDuringCreatingTAR": "Wystąpił błąd podczas tworzenia archiwum TAR. Spróbuj ponownie.", + "errorDuringCreatingZIP": "Wystąpił błąd podczas tworzenia archiwum ZIP. Spróbuj ponownie." }, "backup": { "destinationDirectoryDoesNotExists": "Katalog docelowy nie istnieje", @@ -859,7 +860,7 @@ "ftps": "FTPS", "ftpWithSSLTLS": "FTP z SSL/TLS", "generatePreviewFiles": "Wygeneruj pliki podglądu", - "getWebsiteFiles": "Pobierz pliki strony internetowej", + "getWebsiteFiles": "Zobacz pliki strony", "githubPages": "Strony Github", "githubSyncedPart1": "Zmiany na Github Pages mogą być widoczne po kilku minutach od wysłania, ", "githubSyncedPart2": "zatem należy odczekać chwilę przed weryfikacją zmian.", @@ -873,7 +874,7 @@ "keyFile": "Plik klucza", "lastRendered": "Ostatnie renderowanie", "lastSync": "Ostatnia synchronizacja", - "leaveBlankToUseDefaultOutputDirectory": "Pozostaw puste aby użyć domyślnego kata;pgu docelowego", + "leaveBlankToUseDefaultOutputDirectory": "Pozostaw puste aby użyć domyślnego katalogu docelowego", "manualDeployment": "Wdrażanie ręczne", "manualOutputFieldCantBeEmpty": "Ręczny wybór wyjścia nie może być pusty", "ManualUpload": "Przesyłanie ręczne", From 50f97c410e6d932f34f8a2eed5c41a8c8860214f Mon Sep 17 00:00:00 2001 From: Tomasz Dziuda Date: Sun, 3 Apr 2022 18:51:20 +0200 Subject: [PATCH 008/126] feat: unification and improvements of the supportedFeatures for error/search/tag/author pages and page settings improvements according to these option values (#28duum3) --- app/back-end/builddata.json | 2 +- app/back-end/modules/render-html/renderer.js | 28 ++++ .../default-languages/en-gb/translations.json | 2 + .../default-languages/pl/translations.json | 8 +- .../default-themes/simple/config.json | 4 +- app/src/components/AuthorForm.vue | 4 + app/src/components/Settings.vue | 138 +++++++++++++++--- app/src/components/TagForm.vue | 4 + 8 files changed, 165 insertions(+), 25 deletions(-) diff --git a/app/back-end/builddata.json b/app/back-end/builddata.json index 65a0f23dd..c0b1d0b61 100644 --- a/app/back-end/builddata.json +++ b/app/back-end/builddata.json @@ -1 +1 @@ -{"version":"0.39.1","build":15505} \ No newline at end of file +{"version":"0.39.1","build":15512} \ No newline at end of file diff --git a/app/back-end/modules/render-html/renderer.js b/app/back-end/modules/render-html/renderer.js index 081b7206a..650678789 100644 --- a/app/back-end/modules/render-html/renderer.js +++ b/app/back-end/modules/render-html/renderer.js @@ -1007,6 +1007,13 @@ class Renderer { * Generate tag pages */ generateTags(tagID = false, ampMode = false) { + if ( + (this.themeConfig.supportedFeatures && this.themeConfig.supportedFeatures.tagPages === false) || + (this.themeConfig.renderer && this.themeConfig.renderer.createTagPages === false) + ) { + return false; + } + console.time(ampMode ? 'TAGS-AMP' : 'TAGS'); // Get tags let inputFile = ampMode ? 'amp-tag.hbs' : 'tag.hbs'; @@ -1206,6 +1213,13 @@ class Renderer { * Generate author pages */ generateAuthors(authorID = false, ampMode = false) { + if ( + (this.themeConfig.supportedFeatures && this.themeConfig.supportedFeatures.authorPages === false) || + (this.themeConfig.renderer && this.themeConfig.renderer.createAuthorPages === false) + ) { + return false; + } + console.time(ampMode ? 'AUTHORS-AMP' : 'AUTHORS'); // Create directory for authors let authorsDirPath = path.join(this.outputDir, this.siteConfig.advanced.urls.authorsPrefix); @@ -1405,6 +1419,13 @@ class Renderer { * Generate the 404 error page (if supported in the theme) */ generate404s() { + if ( + (this.themeConfig.supportedFeatures && this.themeConfig.supportedFeatures.errorPage === false) || + (this.themeConfig.renderer && this.themeConfig.renderer.create404Page === false) + ) { + return false; + } + console.time("404"); // Load template let inputFile = '404.hbs'; @@ -1429,6 +1450,13 @@ class Renderer { * Generate the 404 error page (if supported in the theme) */ generateSearch() { + if ( + (this.themeConfig.supportedFeatures && this.themeConfig.supportedFeatures.searchPage === false) || + (this.themeConfig.renderer && this.themeConfig.renderer.createSearchPage === false) + ) { + return false; + } + console.time("SEARCH"); // Load template let inputFile = 'search.hbs'; diff --git a/app/default-files/default-languages/en-gb/translations.json b/app/default-files/default-languages/en-gb/translations.json index f6fc79383..d56f772c3 100644 --- a/app/default-files/default-languages/en-gb/translations.json +++ b/app/default-files/default-languages/en-gb/translations.json @@ -749,8 +749,10 @@ "tagsPrefixCannotBeEmpty": "Tags prefix cannot be empty if pretty URLs are enabled.", "themeDoesNotHaveSupportedFeaturesList": "

Your theme's config.json file does not contain a supportedFeatures section. Please update or modify your theme to include accurate messaging about features which are not supported by your currently used theme. Read more about supported features.

", "themeDoesNotSupport404ErrorPage": "Your theme does not support a 404 Error page.", + "themeDoesNotSupportAuthorPages": "Your theme does not support author pages.", "themeDoesNotSupportErrorPages": "Your theme does not support error pages", "themeDoesNotSupportSearchPages": "Your theme does not support search pages", + "themeDoesNotSupportTagPages": "Your theme does not support tag pages.", "themeDoesNotSupportTagsListPage": "Your theme does not support a Tags list page.", "themePrimaryColor": "Theme primary color", "timeFormat": "Time format:", diff --git a/app/default-files/default-languages/pl/translations.json b/app/default-files/default-languages/pl/translations.json index 773c799d8..03953ff23 100644 --- a/app/default-files/default-languages/pl/translations.json +++ b/app/default-files/default-languages/pl/translations.json @@ -25,7 +25,7 @@ "noAuthorsMatchingYourCriteria": "Brak autorów spełniających Twoje kryteria.", "removeAuthorsMessage": "Na pewno chcesz usunąć wybranych autorów?", "removeAuthorsSuccessMessage": "Wybrani autorzy zostali usunięci", - "saveAndPreviewNotAvailableDueToNoSupportForAuthorPagesInTheme": "Opcja \"Zapis i Podgląd\" nie jest dostępna z powodu braku wspracia dla stron autorów w Twoim motywie.", + "saveAndPreviewNotAvailableDueToNoSupportForAuthorPagesInTheme": "Opcja \"Zapis i Podgląd\" nie jest dostępna z powodu braku wsparcia dla stron autorów w Twoim motywie.", "selectAuthor": "Wybierz autora", "selectAuthorPage": "Wybierz stronę autora", "themeDoesNotSupportFeaturedImagesForAuthors": "Twój motyw nie wspiera wyróżnionych obrazków dla autorów.", @@ -749,8 +749,10 @@ "tagsPrefixCannotBeEmpty": "Prefiks tagów nie może być pusty, jeśli włączone są ładne adresy URL.", "themeDoesNotHaveSupportedFeaturesList": "

Plik Twojego motywu config.json nie zawiera sekcji supportedFeatures. Proszę zaktualizować lub zmodyfikować motyw aby uzyskać dokładny komunikat o funkcjach, które nie są wspierane przez aktualnie używany motyw. Czytaj więcej o wspieranych funkcjach.

", "themeDoesNotSupport404ErrorPage": "Twój motyw nie obsługuje strony błędu 404.", - "themeDoesNotSupportErrorPages": "Twój motyw nie obsługuje stron błędów", + "themeDoesNotSupportAuthorPages": "Twój motyw nie obsługuje stron autorów.", + "themeDoesNotSupportErrorPages": "Twój motyw nie obsługuje stron błędów.", "themeDoesNotSupportSearchPages": "Twój motyw nie obsługuje stron wyszukiwania.", + "themeDoesNotSupportTagPages": "Twój motyw nie obsługuje stron tagów.", "themeDoesNotSupportTagsListPage": "Twój motyw nie wspiera stron listy tagów.", "themePrimaryColor": "Kolor przewodni motywu", "timeFormat": "Format czasu:", @@ -978,7 +980,7 @@ "noTagsMatchingYourCriteria": "Brak tagów spełniających Twoje kryteria.", "removeTagMessage": "Na pewno chcesz usunąć wybrane tagi?", "removeTagSuccessMessage": "Wybrane tagi zostały usunięte", - "saveAndPreviewNotAvailableDueToNoSupportForTagPagesInTheme": "Opcja \"Zapis i Podgląd\" nie jest dostępna z powodu braku wspracia dla stron tagów w Twoim motywie.", + "saveAndPreviewNotAvailableDueToNoSupportForTagPagesInTheme": "Opcja \"Zapis i Podgląd\" nie jest dostępna z powodu braku wsparcia dla stron tagów w Twoim motywie.", "selectTag": "Wybierz tag", "selectTagPage": "Wybierz stronę taga", "tag": "Tag", diff --git a/app/default-files/default-themes/simple/config.json b/app/default-files/default-themes/simple/config.json index 8182404b1..a185d73ea 100755 --- a/app/default-files/default-themes/simple/config.json +++ b/app/default-files/default-themes/simple/config.json @@ -26,7 +26,9 @@ "tagImages": true, "authorImages": true, "searchPage": true, - "errorPage": true + "errorPage": true, + "tagPages": true, + "authorPages": true }, "config": [ diff --git a/app/src/components/AuthorForm.vue b/app/src/components/AuthorForm.vue index 56ad60221..111b63c74 100644 --- a/app/src/components/AuthorForm.vue +++ b/app/src/components/AuthorForm.vue @@ -358,6 +358,10 @@ export default { return this.$store.state.currentSite.themeSettings.supportedFeatures && this.$store.state.currentSite.themeSettings.supportedFeatures.authorImages; }, currentThemeHasSupportForAuthorPages () { + if (this.$store.state.currentSite.themeSettings.supportedFeatures && this.$store.state.currentSite.themeSettings.supportedFeatures.authorPages === false) { + return false; + } + return this.$store.state.currentSite.themeSettings.renderer.createAuthorPages; }, metaFieldAttrs: function() { diff --git a/app/src/components/Settings.vue b/app/src/components/Settings.vue index 844427f40..e6b671054 100644 --- a/app/src/components/Settings.vue +++ b/app/src/components/Settings.vue @@ -307,8 +307,15 @@ type="medium" :label="$t('tag.tagPage')" /> +
+ +

{{ $t('settings.themeDoesNotSupportTagPages') }}

+
+ @@ -327,7 +334,7 @@ +
+ +

{{ $t('settings.themeDoesNotSupportAuthorPages') }}

+
+ @@ -419,7 +434,7 @@ @@ -518,7 +534,7 @@

{{ $t('settings.themeDoesNotSupportSearchPages') }}

- + @@ -574,7 +590,7 @@ + {{ $t('settings.themeDoesNotSupportTagPages') }} + + @@ -652,8 +676,16 @@ @click.native="clearErrors('authors-prefix')" v-model="advanced.urls.authorsPrefix" :spellcheck="false" + :disabled="!currentThemeSupportsAuthorPages" slot="field" /> + {{ $t('settings.themeDoesNotSupportAuthorPages') }} + + @@ -687,6 +719,7 @@ :readonly="!themeHasSupportForErrorPage" v-model="advanced.urls.errorPage" :spellcheck="false" + :disabled="!currentThemeSupportsErrorPage" slot="field" /> - + {{ $t('settings.tagPages') }} @@ -1648,11 +1686,62 @@ export default { currentThemeSupportsTagsList () { return this.$store.state.currentSite.themeSettings.supportedFeatures && this.$store.state.currentSite.themeSettings.supportedFeatures.tagsList; }, + currentThemeSupportsTagPages () { + return ( + this.$store.state.currentSite.themeSettings.supportedFeatures && + ( + this.$store.state.currentSite.themeSettings.supportedFeatures.tagPages || + typeof this.$store.state.currentSite.themeSettings.supportedFeatures.tagPages === 'undefined' + ) + ) || ( + !this.$store.state.currentSite.themeSettings.supportedFeatures && + this.$store.state.currentSite.themeSettings.renderer && + this.$store.state.currentSite.themeSettings.renderer.createTagPages + ) || ( + this.$store.state.currentSite.themeSettings.supportedFeatures && + ( + this.$store.state.currentSite.themeSettings.supportedFeatures.tagPages || + typeof this.$store.state.currentSite.themeSettings.supportedFeatures.tagPages === 'undefined' + ) && + this.$store.state.currentSite.themeSettings.renderer && + this.$store.state.currentSite.themeSettings.renderer.createTagPages + ); + }, + currentThemeSupportsAuthorPages () { + return ( + this.$store.state.currentSite.themeSettings.supportedFeatures && + ( + this.$store.state.currentSite.themeSettings.supportedFeatures.authorPages || + typeof this.$store.state.currentSite.themeSettings.supportedFeatures.authorPages === 'undefined' + ) + ) || ( + !this.$store.state.currentSite.themeSettings.supportedFeatures && + this.$store.state.currentSite.themeSettings.renderer && + this.$store.state.currentSite.themeSettings.renderer.createAuthorPages + ) || ( + this.$store.state.currentSite.themeSettings.supportedFeatures && + ( + this.$store.state.currentSite.themeSettings.supportedFeatures.authorPages || + typeof this.$store.state.currentSite.themeSettings.supportedFeatures.authorPages === 'undefined' + ) && + this.$store.state.currentSite.themeSettings.renderer && + this.$store.state.currentSite.themeSettings.renderer.createAuthorPages + ); + }, currentThemeSupportsSearchPage () { return ( this.$store.state.currentSite.themeSettings.supportedFeatures && this.$store.state.currentSite.themeSettings.supportedFeatures.searchPage ) || ( + !this.$store.state.currentSite.themeSettings.supportedFeatures && + this.$store.state.currentSite.themeSettings.renderer && + this.$store.state.currentSite.themeSettings.renderer.createSearchPage + ) || ( + this.$store.state.currentSite.themeSettings.supportedFeatures && + ( + this.$store.state.currentSite.themeSettings.supportedFeatures.searchPage || + typeof this.$store.state.currentSite.themeSettings.supportedFeatures.searchPage === 'undefined' + ) && this.$store.state.currentSite.themeSettings.renderer && this.$store.state.currentSite.themeSettings.renderer.createSearchPage ); @@ -1662,8 +1751,17 @@ export default { this.$store.state.currentSite.themeSettings.supportedFeatures && this.$store.state.currentSite.themeSettings.supportedFeatures.errorPage ) || ( + !this.$store.state.currentSite.themeSettings.supportedFeatures && + this.$store.state.currentSite.themeSettings.renderer && + this.$store.state.currentSite.themeSettings.renderer.create404Page + ) || ( + this.$store.state.currentSite.themeSettings.supportedFeatures && + ( + this.$store.state.currentSite.themeSettings.supportedFeatures.errorPage || + typeof this.$store.state.currentSite.themeSettings.supportedFeatures.errorPage === 'undefined' + ) && this.$store.state.currentSite.themeSettings.renderer && - this.$store.state.currentSite.themeSettings.renderer.create404page + this.$store.state.currentSite.themeSettings.renderer.create404Page ); }, advancedTabs () { diff --git a/app/src/components/TagForm.vue b/app/src/components/TagForm.vue index b9927633f..ea0a5730e 100644 --- a/app/src/components/TagForm.vue +++ b/app/src/components/TagForm.vue @@ -331,6 +331,10 @@ export default { return this.$store.state.currentSite.themeSettings.supportedFeatures && this.$store.state.currentSite.themeSettings.supportedFeatures.tagImages; }, currentThemeHasSupportForTagPages () { + if (this.$store.state.currentSite.themeSettings.supportedFeatures && this.$store.state.currentSite.themeSettings.supportedFeatures.tagPages === false) { + return false; + } + return this.$store.state.currentSite.themeSettings.renderer.createTagPages; }, metaFieldAttrs: function() { From dc14fef13f9c733919366565f7a14dbfa085c2dd Mon Sep 17 00:00:00 2001 From: Tomasz Dziuda Date: Wed, 6 Apr 2022 20:59:30 +0200 Subject: [PATCH 009/126] feat: add options to simplify the posts listing (#270hppj) --- app/back-end/builddata.json | 2 +- app/config/AST.app.config.js | 2 ++ .../default-languages/en-gb/translations.json | 2 ++ .../default-languages/pl/translations.json | 2 ++ app/src/components/AppSettings.vue | 27 +++++++++++++++++++ app/src/components/Posts.vue | 16 +++++++---- 6 files changed, 45 insertions(+), 6 deletions(-) diff --git a/app/back-end/builddata.json b/app/back-end/builddata.json index c0b1d0b61..9e219af02 100644 --- a/app/back-end/builddata.json +++ b/app/back-end/builddata.json @@ -1 +1 @@ -{"version":"0.39.1","build":15512} \ No newline at end of file +{"version":"0.39.1","build":15518} \ No newline at end of file diff --git a/app/config/AST.app.config.js b/app/config/AST.app.config.js index 1971bd240..e0b04b1de 100644 --- a/app/config/AST.app.config.js +++ b/app/config/AST.app.config.js @@ -11,8 +11,10 @@ const AstAppConfig = { timeFormat: 12, closeEditorOnSave: true, wideScrollbars: false, + showModificationDate: true, showModificationDateAsColumn: false, showPostSlugs: false, + showPostTags: true, postsOrdering: 'id DESC', tagsOrdering: 'id DESC', authorsOrdering: 'id DESC', diff --git a/app/default-files/default-languages/en-gb/translations.json b/app/default-files/default-languages/en-gb/translations.json index d56f772c3..acf9bc320 100644 --- a/app/default-files/default-languages/en-gb/translations.json +++ b/app/default-files/default-languages/en-gb/translations.json @@ -723,8 +723,10 @@ "showFeaturedImageInfo": "Display a post's featured image in the feed.", "showFullText": "Show full text", "showFullTextInFeedInfo": "Display full text of the post in the feed.", + "showModificationDate": "Show modification date", "showModificationDateAsColumn": "Show modification date as column", "showOnlyFeaturedPosts": "Show only featured posts", + "showPostTagsOnTheListing": "Show post tags on the listing", "showPostSlugsOnTheListing": "Show post slugs on the listing", "sitemap": "Sitemap", "sitemapLinkInfo": "You can find the XML sitemap here: sitemap.xml", diff --git a/app/default-files/default-languages/pl/translations.json b/app/default-files/default-languages/pl/translations.json index 03953ff23..496ffb0d0 100644 --- a/app/default-files/default-languages/pl/translations.json +++ b/app/default-files/default-languages/pl/translations.json @@ -723,9 +723,11 @@ "showFeaturedImageInfo": "Wyświetl w kanale obraz wyróżniony wpisu.", "showFullText": "Pokaż pełen tekst", "showFullTextInFeedInfo": "Wyświetl w kanale pełny tekst wpisu.", + "showModificationDate": "Pokaż datę modyfikacji", "showModificationDateAsColumn": "Pokaż datę modyfikacji jako kolumnę", "showOnlyFeaturedPosts": "Pokaż tylko wyróżnione wpisy", "showPostSlugsOnTheListing": "Pokaż slug wpisu na liście", + "showPostTagsOnTheListing": "Pokaż tagi wpisu na liście", "sitemap": "Mapa witryny", "sitemapLinkInfo": "Mapę XML można znaleźć tutaj: sitemap.xml", "siteSettings": "Ustawienia strony", diff --git a/app/src/components/AppSettings.vue b/app/src/components/AppSettings.vue index c7dfcfb29..861e737fa 100644 --- a/app/src/components/AppSettings.vue +++ b/app/src/components/AppSettings.vue @@ -78,6 +78,17 @@ + + + + @@ -97,6 +108,16 @@ v-model="showPostSlugs" /> + + + + + :itemsCount="showModificationDate && showModificationDateAsColumn ? 6 : 5"> + v-if="showModificationDate && showModificationDateAsColumn"> @@ -333,7 +333,7 @@ @@ -614,9 +617,8 @@ export default { } .note { - display: block; - font-style: italic; - line-height: 1.4; - margin: 2rem 0; + margin-top: 2rem; + position: relative; + z-index: 1; } From ca4c5ecbaca5d488e3cbfb6975ae72f2916d6a0d Mon Sep 17 00:00:00 2001 From: Tomasz Dziuda Date: Wed, 1 Jun 2022 19:02:56 +0200 Subject: [PATCH 058/126] feat: gdpr - options for the new cookie banner (#28z19ma) --- app/back-end/builddata.json | 2 +- app/config/AST.currentSite.config.js | 24 ++- .../default-languages/en-gb/translations.json | 19 +++ .../default-languages/pl/translations.json | 21 ++- app/src/components/Settings.vue | 153 ++++++++++++++++++ .../components/basic-elements/GdprGroups.vue | 17 ++ .../components/basic-elements/TextArea.vue | 5 + 7 files changed, 233 insertions(+), 8 deletions(-) diff --git a/app/back-end/builddata.json b/app/back-end/builddata.json index 692073c91..38ffbcd63 100644 --- a/app/back-end/builddata.json +++ b/app/back-end/builddata.json @@ -1 +1 @@ -{"version":"0.39.1","build":15614} \ No newline at end of file +{"version":"0.39.1","build":15620} \ No newline at end of file diff --git a/app/config/AST.currentSite.config.js b/app/config/AST.currentSite.config.js index f0475b48b..e37bba9be 100644 --- a/app/config/AST.currentSite.config.js +++ b/app/config/AST.currentSite.config.js @@ -117,17 +117,29 @@ const AstCurrentSiteConfig = { articleLinkType: 'internal', articleExternalUrl: '', groups: [ - { 'name': 'Required', 'id': '-' }, - { 'name': 'Functionality', 'id': 'functions' }, - { 'name': 'Analytical', 'id': 'analytics' }, - { 'name': 'Marketing', 'id': 'marketing' } + { 'name': 'Required', 'id': '-', 'description': '' }, + { 'name': 'Functionality', 'id': 'functions', 'description': '' }, + { 'name': 'Analytical', 'id': 'analytics', 'description': '' }, + { 'name': 'Marketing', 'id': 'marketing', 'description': '' } ], - saveButtonLabel: 'Save', + saveButtonLabel: 'Accept all', behaviour: 'badge', badgeLabel: 'Cookie Policy', behaviourLink: '#cookie-settings', vimeoNoTrack: false, - ytNoCookies: false + ytNoCookies: false, + bannerPosition: 'cloud', + showRejectButton: false, + rejectButtonLabel: 'Reject', + allowAdvancedConfiguration: false, + advancedConfigurationLinkLabel: 'Manage preferences', + advancedConfigurationAcceptButtonLabel: 'Accept all', + advancedConfigurationRejectButtonLabel: 'Reject all', + advancedConfigurationSaveButtonLabel: 'Save settings', + advancedConfigurationTitle: 'Cookie settings', + advancedConfigurationDescription: 'We use cookies to enhance your browsing experience, serve personalized ads or content, and analyze our traffic. By clicking "Accept All", you consent to our use of cookies.', + advancedConfigurationShowDescriptionLink: true, + debugMode: false }, relatedPostsOrder: 'default', relatedPostsCriteria: 'titles-and-tags', diff --git a/app/default-files/default-languages/en-gb/translations.json b/app/default-files/default-languages/en-gb/translations.json index 9ea8a7ef4..552fe29b2 100644 --- a/app/default-files/default-languages/en-gb/translations.json +++ b/app/default-files/default-languages/en-gb/translations.json @@ -297,6 +297,7 @@ }, "gdpr": { "addGroup": "Add group", + "groupDescriptionPlaceholder": "Insert cookies group description here", "groupID": "Group ID", "groupName": "Group name", "state": "State" @@ -557,6 +558,7 @@ "badge": "Badge", "badgeAndCustomLink": "Badge + custom link", "badgeLabel": "Badge label", + "bannerPosition": "Cookie banner position", "basicSettings": "Basic settings", "byIDAscending": "By ID ascending", "byIDDescending": "By ID descending", @@ -638,7 +640,24 @@ "footerText": "Footer text", "frontpage": "Frontpage", "GDPR": "GDPR", + "gdprBannerPosition": { + "centered": "Centered", + "left": "Left", + "right": "Right", + "bar": "Bar" + }, + "gdprAllowAdvancedConfiguration": "Allow advanced cookies configuration", + "gdprAdvancedConfigurationLinkLabel": "Label for advanced configuration link", + "gdprAdvancedConfigurationAcceptButtonLabel": "Accept button label", + "gdprAdvancedConfigurationRejectButtonLabel": "Rejection button label", + "gdprAdvancedConfigurationSaveButtonLabel": "Save button label", + "gdprAdvancedConfigurationTitle": "Popup title", + "gdprAdvancedConfigurationDescription": "Popup description", + "gdprAdvancedConfigurationShowDescriptionLink": "Show link to privacy policy in the popup description", "gdprBehaviourInfo": "Remember to place a link with the above anchor in your website navigation or the website content (e.g. Cookie preferences). Otherwise, your website's users might not be able to change their individual cookie preferences.", + "gdprDebugMode": "Enable debug mode", + "gdprShowRejectButton": "Show reject button", + "gdprRejectButtonLabel": "Reject button label", "gdprTitle": "Title", "generateOpenGraphTags": "Generate Open Graph tags", "generateTwitterCards": "Generate Twitter cards", diff --git a/app/default-files/default-languages/pl/translations.json b/app/default-files/default-languages/pl/translations.json index 73fd79f87..404d43f69 100644 --- a/app/default-files/default-languages/pl/translations.json +++ b/app/default-files/default-languages/pl/translations.json @@ -297,6 +297,7 @@ }, "gdpr": { "addGroup": "Dodaj grupę", + "groupDescriptionPlaceholder": "Dodaj tutaj opis grupy plików cookies", "groupID": "ID grupy", "groupName": "Nazwa grupy", "state": "Stan" @@ -557,6 +558,7 @@ "badge": "Badge", "badgeAndCustomLink": "Badge + link niestandardowy", "badgeLabel": "Etykieta plakietki", + "bannerPosition": "Pozycja bannera cookies", "basicSettings": "Ustawienia podstawowe", "byIDAscending": "Według ID rosnąco", "byIDDescending": "Według ID malejąco", @@ -638,7 +640,24 @@ "footerText": "Tekst w stopce", "frontpage": "Pierwsza strona", "GDPR": "GDPR", + "gdprBannerPosition": { + "centered": "Wycentrowany", + "left": "Z lewej", + "right": "Z prawej", + "bar": "Pasek" + }, + "gdprAllowAdvancedConfiguration": "Dopuszczaj zaawansowaną konfigurację plików cookies", + "gdprAdvancedConfigurationLinkLabel": "Etykieta dla linka zaawansowanej konfiguracji", + "gdprAdvancedConfigurationAcceptButtonLabel": "Etykieta przycisku akceptacji", + "gdprAdvancedConfigurationRejectButtonLabel": "Etykieta przycisku odrzucenia", + "gdprAdvancedConfigurationSaveButtonLabel": "Etykieta przycisku zapisu", + "gdprAdvancedConfigurationTitle": "Tytuł w popupie", + "gdprAdvancedConfigurationDescription": "Opis w popupie", + "gdprAdvancedConfigurationShowDescriptionLink": "Pokaż link do polityki prywatności w opisie popupu", "gdprBehaviourInfo": "Pamiętaj o umieszczeniu linku z powyższą kotwicą w nawigacji Twojej strony lub w treści strony (np. Ustawienia plików cookies). W przeciwnym razie użytkownicy Twojej witryny mogą nie mieć możliwości zmiany swoich indywidualnych preferencji dotyczących plików cookie.", + "gdprDebugMode": "Włącz tryb debugowania", + "gdprShowRejectButton": "Pokaż przycisk odrzucania", + "gdprRejectButtonLabel": "Etykieta przycisku odrzucania", "gdprTitle": "Tytuł", "generateOpenGraphTags": "Generuj tagi Open Graph", "generateTwitterCards": "Generuj karty Twitter'a", @@ -728,7 +747,7 @@ "removeHTMLComments": "Usuń komentarze HTML", "responsiveImagesQuality": "Jakość responsywnych obrazów", "RSSJSONFeed": "Kanał RSS/JSON", - "saveButtonLabel": "Etykieta przyciku zapisu", + "saveButtonLabel": "Etykieta przycisku zapisu", "saveSearchStateInfo": "Po włączeniu Publii zapisze bieżące wyniki wyszukiwania, nawet podczas tworzenia nowego wpisu, umożliwiając powrót do listy. Domyślnie Publii zapisuje wyniki wyszukiwania tylko podczas otwierania wpisu do edycji.", "requiresRestartingApp": "Wymaga ponowengo uruchomienia aplikacji.", "saveSettings": "Zapisz Ustawienia", diff --git a/app/src/components/Settings.vue b/app/src/components/Settings.vue index d574be16d..020ae426e 100644 --- a/app/src/components/Settings.vue +++ b/app/src/components/Settings.vue @@ -1269,6 +1269,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Fri, 3 Jun 2022 20:05:16 +0200 Subject: [PATCH 059/126] feat: new cookie banner - initial implementation (#28z19ma) --- app/back-end/builddata.json | 2 +- .../modules/render-html/helpers/gdpr.js | 427 +++---------- app/config/AST.currentSite.config.js | 16 +- app/default-files/gdpr-assets/gdpr.css | 604 ++++++++++++++++++ app/default-files/gdpr-assets/gdpr.js | 394 ++++++++++++ app/default-files/gdpr-assets/template.html | 123 ++++ app/src/components/Settings.vue | 68 +- package.json | 1 + 8 files changed, 1280 insertions(+), 355 deletions(-) create mode 100644 app/default-files/gdpr-assets/gdpr.css create mode 100644 app/default-files/gdpr-assets/gdpr.js create mode 100644 app/default-files/gdpr-assets/template.html diff --git a/app/back-end/builddata.json b/app/back-end/builddata.json index 38ffbcd63..9e296fb35 100644 --- a/app/back-end/builddata.json +++ b/app/back-end/builddata.json @@ -1 +1 @@ -{"version":"0.39.1","build":15620} \ No newline at end of file +{"version":"0.39.1","build":15625} \ No newline at end of file diff --git a/app/back-end/modules/render-html/helpers/gdpr.js b/app/back-end/modules/render-html/helpers/gdpr.js index 2369de51c..49c146a55 100644 --- a/app/back-end/modules/render-html/helpers/gdpr.js +++ b/app/back-end/modules/render-html/helpers/gdpr.js @@ -1,366 +1,131 @@ +const fs = require('fs-extra'); class Gdpr { static popupHtmlOutput (configuration, renderer) { + let template = fs.readFileSync(__dirname + '/../../../../default-files/gdpr-assets/template.html', 'utf8'); + let output = Gdpr.parseTemplate(configuration, template, renderer); + return output; + } + + static prepareCookieGroups (configuration) { let groups = ``; - let privacyPolicyLink = ``; for (let i = 0; i < configuration.groups.length; i++) { if (configuration.groups[i].id === '-' || configuration.groups[i].id === '') { - groups += ` - - `; + groups += `
  • +
    + ${configuration.groups[i].name} +

    ${configuration.groups[i].description}

    +
    +
    + + +
    +
  • `; continue; } groups += ` - - `; - } - - if (Gdpr.getPrivacyPolicyUrl(configuration, renderer)) { - privacyPolicyLink = `${configuration.readMoreLinkLabel}`; - } - - let output = ` - `; - - return output; +
  • +
    + ${configuration.groups[i].name} +

    ${configuration.groups[i].description}

    +
    +
    + + +
    +
  • `; + } + + return groups; } - static popupCssOutput () { - let output = ` - .cookie-popup { - background: #fff; - border-radius: 2px; - bottom: 1rem; - -webkit-box-shadow: 0 0 12px rgba(0, 0, 0, 0.15); - box-shadow: 0 0 12px rgba(0, 0, 0, 0.15); - font-size: 15px; - left: 16px; - right: 16px; - max-width: 600px; - padding: 2rem; - position: fixed; - -webkit-transform: translateY(100%); - -ms-transform: translateY(100%); - transform: translateY(100%); - -webkit-transition: -webkit-transform 0.8s ease 0s; - transition: -webkit-transform 0.8s ease 0s; - -o-transition: transform 0.8s ease 0s; - transition: transform 0.8s ease 0s; - transition: transform 0.8s ease 0s, -webkit-transform 0.8s ease 0s; - will-change: transform; - z-index: 1000; - } - - .cookie-popup--uses-badge { - background: #24a931; - border-radius: 6px 6px 0 0; - bottom: 0.6rem; - height: 2rem; - padding: 0; - -webkit-transition: all 0.24s ease-out; - transition: all 0.24s ease-out; - width: 6.4rem; - } - - .cookie-popup.cookie-popup--uses-badge:hover, - .cookie-popup.cookie-popup--uses-badge.cookie-popup--uses-link:hover { - bottom: 2rem; - } - - .cookie-popup.cookie-popup--uses-badge.cookie-popup--uses-link.cookie-popup--is-sticky { - bottom: .6rem; - } - - .cookie-popup--uses-badge > h2, - .cookie-popup--uses-badge > p, - .cookie-popup--uses-badge > form { - display: none; - } - - .cookie-popup--is-sticky { - border-radius: 2px; - -webkit-transform: translateY(0); - -ms-transform: translateY(0); - transform: translateY(0); - -webkit-transition: transform .8s ease 0s; - transition: transform .8s ease 0s; - } - - .cookie-popup--uses-badge.cookie-popup--is-sticky { - background: #ffffff; - bottom: 1rem; - height: auto; - padding: 2rem; - width: 100%; - } - - @media (max-width:600px) { - .cookie-popup--uses-badge.cookie-popup--is-sticky { - bottom: 0 !important; - left: 0; - right: 0; - } - } - - @media (min-width:600px) { - .cookie-popup--uses-badge.cookie-popup--is-sticky:hover { - bottom: 1rem; - } - } - - .cookie-popup--uses-badge.cookie-popup--is-sticky > h2, - .cookie-popup--uses-badge.cookie-popup--is-sticky > p, - .cookie-popup--uses-badge.cookie-popup--is-sticky > form { - display: block; - } - - .cookie-popup.cookie-popup--uses-link { - background: #fff; - bottom: 0; - } - - .cookie-popup.cookie-popup--uses-badge.cookie-popup--uses-link { - background: #24a931; - bottom: 0.6rem; + static parseTemplate (configuration, template, renderer) { + // Remove unnecessary code fragments from template + if (!configuration.popupShowRejectButton) { + template = template.replace(/\{\{\#showRejectButton\}\}[\s\S]*?\{\{\/showRejectButton\}\}/gmi, ''); + } else { + template = template.replace(/\{\{\#showRejectButton\}\}/gmi, ''); + template = template.replace(/\{\{\/showRejectButton\}\}/gmi, ''); } - .cookie-popup.cookie-popup--uses-badge.cookie-popup--uses-link.cookie-popup--is-sticky { - background: #fff; + if (!configuration.allowAdvancedConfiguration) { + template = template.replace(/\{\{\#allowAdvancedConfiguration\}\}[\s\S]*?\{\{\/allowAdvancedConfiguration\}\}/gmi, ''); + } else { + template = template.replace(/\{\{\#allowAdvancedConfiguration\}\}/gmi, ''); + template = template.replace(/\{\{\/allowAdvancedConfiguration\}\}/gmi, ''); } - .cookie-popup--is-sticky.cookie-popup--uses-link { - -webkit-transform: translateY(-1rem); - -ms-transform: translateY(-1rem); - transform: translateY(-1rem); + if (!configuration.advancedConfigurationShowDescriptionLink) { + template = template.replace(/\{\{\#advancedConfigurationShowDescriptionLink\}\}[\s\S]*?\{\{\/advancedConfigurationShowDescriptionLink\}\}/gmi, ''); + } else { + template = template.replace(/\{\{\#advancedConfigurationShowDescriptionLink\}\}/gmi, ''); + template = template.replace(/\{\{\/advancedConfigurationShowDescriptionLink\}\}/gmi, ''); } - .cookie-popup > h2 { - font-size: 20px; - margin: 0; + if (configuration.behaviour === 'link') { + template = template.replace(/\{\{\#showBadge\}\}[\s\S]*?\{\{\/showBadge\}\}/gmi, ''); + } else { + template = template.replace(/\{\{\#showBadge\}\}/gmi, ''); + template = template.replace(/\{\{\/showBadge\}\}/gmi, ''); } - .cookie-popup > p { - margin: 1rem 0 0; - } - - .cookie-popup input[type="checkbox"] + label { - margin-right: 0.53333rem; - white-space: nowrap; - } + // Replace variables + let bannerPositionCssClass = ''; - .cookie-popup input[type="checkbox"] + label::before { - height: 20px; - line-height: 20px; - width: 20px; - margin-right: 0.26667rem; - } - - .cookie-popup input[type="checkbox"]:disabled + label:before { - content: ""; - background-color: #f6f6f6; - background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 11 8'%3e%3cpolygon points='9.53 0 4.4 5.09 1.47 2.18 0 3.64 2.93 6.54 4.4 8 5.87 6.54 11 1.46 9.53 0' fill='%23999999'/%3e%3c/svg%3e"); - } + if (['left', 'right', 'bar'].indexOf(configuration.popupPosition) > -1) { + bannerPositionCssClass = 'cb__banner--' + configuration.popupPosition; + } - .cookie-popup__save { - background: #24a931; - border: none !important; - border-radius: 3px; - -webkit-box-shadow: none; - box-shadow: none; - color: #ffffff !important; - font-size: 13px; - padding: 0.666rem 1.2rem; - } - - .cookie-popup > form, - .cookie-popup__save-wrapper { - margin-top: 1.06667rem - } - - .cookie-popup__save:hover, - .cookie-popup__save:active { - opacity: 0.85; - } + let privacyPolicyLink = ``; - .cookie-popup-label { - color: #fff; - cursor: pointer; - left: 50%; - position: absolute; - top: 50%; - -webkit-transform: translateX(-50%) translateY(-50%); - -ms-transform: translateX(-50%) translateY(-50%); - transform: translateX(-50%) translateY(-50%); - white-space: nowrap; + if (Gdpr.getPrivacyPolicyUrl(configuration, renderer)) { + privacyPolicyLink = `${configuration.readMoreLinkLabel}`; } - .cookie-popup--is-sticky .cookie-popup-label { - display: none; - } - `; + template = template.replace(/\{\{behaviour\}\}/gmi, configuration.behaviour); + template = template.replace(/\{\{behaviourLink\}\}/gmi, configuration.behaviourLink); + template = template.replace(/\{\{badgeLabel\}\}/gmi, configuration.badgeLabel); + template = template.replace(/\{\{bannerPositionCssClass\}\}/gmi, bannerPositionCssClass); + template = template.replace(/\{\{popupTitlePrimary\}\}/gmi, configuration.popupTitlePrimary); + template = template.replace(/\{\{popupDesc\}\}/gmi, configuration.popupDesc); + template = template.replace(/\{\{privacyPolicyLink\}\}/gmi, privacyPolicyLink); + template = template.replace(/\{\{advancedConfigurationLinkLabel\}\}/gmi, configuration.advancedConfigurationLinkLabel); + template = template.replace(/\{\{rejectButtonLabel\}\}/gmi, configuration.popupRejectButtonLabel); + template = template.replace(/\{\{saveButtonLabel\}\}/gmi, configuration.saveButtonLabel); + template = template.replace(/\{\{advancedConfigurationTitle\}\}/gmi, configuration.advancedConfigurationTitle); + template = template.replace(/\{\{advancedConfigurationDescription\}\}/gmi, configuration.advancedConfigurationDescription); + template = template.replace(/\{\{cbGroups\}\}/gmi, Gdpr.prepareCookieGroups(configuration)); + template = template.replace(/\{\{advancedConfigurationAcceptButtonLabel\}\}/gmi, configuration.advancedConfigurationAcceptButtonLabel); + template = template.replace(/\{\{advancedConfigurationRejectButtonLabel\}\}/gmi, configuration.advancedConfigurationRejectButtonLabel); + template = template.replace(/\{\{advancedConfigurationSaveButtonLabel\}\}/gmi, configuration.advancedConfigurationSaveButtonLabel); + template = template.replace(/\{\{cookieSettingsTTL\}\}/gmi, configuration.cookieSettingsTTL); + template = template.replace(/\{\{cookieSettingsRevision\}\}/gmi, configuration.cookieSettingsRevision); + template = template.replace(/\{\{debugMode\}\}/gmi, configuration.debugMode); + + return template; + } + static popupCssOutput () { + let output = fs.readFileSync(__dirname + '/../../../../default-files/gdpr-assets/gdpr.css', 'utf8'); return output; } static popupJsOutput (configuration) { - let linkCode = ``; - - if (configuration.behaviour !== 'badge') { - linkCode = ` - var triggerLinks = document.querySelectorAll('a[href*="${configuration.behaviourLink}"]'); - - for (var i = 0; i < triggerLinks.length; i++) { - triggerLinks[i].addEventListener('click', function(e) { - e.preventDefault(); - popup.classList.add('cookie-popup--is-sticky'); - }); - } - `; - } - + let scriptCode = fs.readFileSync(__dirname + '/../../../../default-files/gdpr-assets/gdpr.js', 'utf8'); let output = ` `; return output; diff --git a/app/config/AST.currentSite.config.js b/app/config/AST.currentSite.config.js index e37bba9be..1ab8280f5 100644 --- a/app/config/AST.currentSite.config.js +++ b/app/config/AST.currentSite.config.js @@ -126,12 +126,10 @@ const AstCurrentSiteConfig = { behaviour: 'badge', badgeLabel: 'Cookie Policy', behaviourLink: '#cookie-settings', - vimeoNoTrack: false, - ytNoCookies: false, - bannerPosition: 'cloud', - showRejectButton: false, - rejectButtonLabel: 'Reject', - allowAdvancedConfiguration: false, + popupPosition: 'centered', + popupShowRejectButton: false, + popupRejectButtonLabel: 'Reject', + allowAdvancedConfiguration: true, advancedConfigurationLinkLabel: 'Manage preferences', advancedConfigurationAcceptButtonLabel: 'Accept all', advancedConfigurationRejectButtonLabel: 'Reject all', @@ -139,7 +137,11 @@ const AstCurrentSiteConfig = { advancedConfigurationTitle: 'Cookie settings', advancedConfigurationDescription: 'We use cookies to enhance your browsing experience, serve personalized ads or content, and analyze our traffic. By clicking "Accept All", you consent to our use of cookies.', advancedConfigurationShowDescriptionLink: true, - debugMode: false + cookieSettingsRevision: '1', + cookieSettingsTTL: '90', + debugMode: false, + vimeoNoTrack: false, + ytNoCookies: false }, relatedPostsOrder: 'default', relatedPostsCriteria: 'titles-and-tags', diff --git a/app/default-files/gdpr-assets/gdpr.css b/app/default-files/gdpr-assets/gdpr.css new file mode 100644 index 000000000..e3c60a53d --- /dev/null +++ b/app/default-files/gdpr-assets/gdpr.css @@ -0,0 +1,604 @@ +:root { + --cb-width: 42em; + --cb-popup-height: 36em; + --cb-font-base-size: 17px; + --cb-font-weight-normal: 400; + --cb-font-weight-bold: 700; + --cb-border-radius: 4px; + --cb-badge-border-radius: 100%; + /* light */ + --cb-bg: #ffffff; + --cb-overlay: rgba(12, 13, 16, .8); + --cb-text-color: #343435; + --cb-headings-color: #283149; + --cb-border-color: #eee; + --cb-bg-light: #f6f6f9; + --cb-btn-primary-bg: #e7e8ea; + --cb-btn-primary-bg-hover: #d7d8dc; + --cb-btn-primary-text: #283149; + --cb-btn-primary-text-hover: #283149; + --cb-btn-secondary-bg: #1089ff; + --cb-btn-secondary-bg-hover: #0079f2; + --cb-btn-secondary-text: #ffffff; + --cb-btn-secondary-text-hover: #ffffff; + --cb-btn-link: #283149; + --cb-btn-link-hover: #0079f2; + --cb-badge-bg: #1089ff; + --cb-badge-bg-hover: #0079f2; + --cb-badge-color: #fff; + --cb-badge-color-hover: #fff; + /* dark */ + --cb-bg: #191a1f; + --cb-overlay: rgba(10, 10, 12, .8); + --cb-text-color: #a5a7b7; + --cb-headings-color: #d9d9e0; + --cb-border-color: #393a47; + --cb-bg-light: #f6f6f9; + --cb-btn-primary-bg: #32343e; + --cb-btn-primary-bg-hover: #3c3f4b; + --cb-btn-primary-text: #d9d9e0; + --cb-btn-primary-text-hover: #ffffff; + --cb-btn-secondary-bg: #1089ff; + --cb-btn-secondary-bg-hover: #0079f2; + --cb-btn-secondary-text: #ffffff; + --cb-btn-secondary-text-hover: #ffffff; + --cb-btn-link: #d9d9e0; + --cb-btn-link-hover: #42a0ff; + --cb-badge-bg: #1089ff; + --cb-badge-bg-hover: #0079f2; + --cb-badge-color: #fff; + --cb-badge-color-hover: #fff; +} + +.cb { + color: var(--cb-text-color); + font-weight: 400; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-smooth: always; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + scroll-behavior: smooth; + text-rendering: optimizeLegibility; +} + +.cb *, +.cb::after, +.cb::before { + -webkit-animation: none; + animation: none; + background: 0; + border: none; + -webkit-box-shadow: none; + box-shadow: none; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: inherit; + float: none; + font-style: normal; + font-variant: normal; + font-weight: inherit; + font-family: inherit; + line-height: 1; + font-size: 1em; + margin: 0; + padding: 0; + text-transform: none; + letter-spacing: unset; + visibility: unset; + -webkit-transform: none; + transform: none; + -webkit-transition: none; + transition: none; + text-decoration: none; + text-align: left; +} + +.cb__banner { + background: var(--cb-bg); + -webkit-box-shadow: 0 0.625em 1.875em rgba(2, 2, 3, 0.28); + box-shadow: 0 0.625em 1.875em rgba(2, 2, 3, 0.28); + border-radius: calc(1.5 * var(--cb-border-radius)); + bottom: 1.25em; + font-size: var(--cb-font-base-size); + left: 1em; + max-width: var(--cb-width); + margin: 0 auto; + overflow: hidden; + padding: 1.5em 2em; + opacity: 0; + position: fixed; + right: 1em; + text-align: center; + -webkit-transform: translateY(1.5em); + transform: translateY(1.5em); + -webkit-transition: all 0.25s ease-out; + transition: all 0.25s ease-out; + visibility: hidden; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + z-index: 1; +} + +.cb__banner--left { + margin-left: 0; +} + +.cb__banner--right { + margin-left: auto; + margin-right: 0; +} + +.cb__banner--bar { + max-width: 100%; + border-radius: 0; + left: 0; + right: 0; + bottom: 0; + padding: 2.5em 2em; +} + +@media all and (min-width: 37.5em) { + .cb__banner--bar { + font-size: 17px; + } +} + +.cb__banner.is-visible { + opacity: 1; + -webkit-transform: translateY(0); + transform: translateY(0); + visibility: visible; +} + +.cb__inner { + max-width: var(--cb-width); + margin: 0 auto; +} + +.cb__title { + color: var(--cb-headings-color); + font-size: 1.05em; + font-weight: var(--cb-font-weight-bold); + margin-bottom: 0.75em; +} + +.cb__txt { + font-size: 0.85em; + font-weight: var(--cb-font-weight-normal); + line-height: 1.5; +} + +.cb__txt a { + color: var(--cb-btn-link); + font-weight: var(--cb-font-weight-bold); + text-decoration: underline; + text-decoration-thickness: 1px; + text-underline-offset: 0.2em; + -webkit-text-decoration-skip: ink; + text-decoration-skip-ink: auto; +} + +.cb__txt a:active, .cb__txt a:hover, .cb__txt a:focus { + color: var(--cb-btn-link-hover); +} + +.cb__buttons { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin-top: 0.5em; +} + +@media all and (min-width: 30em) and (max-width: 37.4375em) { + .cb__buttons { + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; + } +} + +@media all and (min-width: 37.5em) { + .cb__buttons { + margin-top: 1.25em; + } +} + +.cb__btn { + color: var(--cb-btn-primary-text); + background: var(--cb-btn-primary-bg); + border: none; + border-radius: var(--cb-border-radius); + -webkit-box-shadow: none; + box-shadow: none; + cursor: pointer; + display: inline-block; + font-weight: var(--cb-font-weight-bold); + font-size: 0.825em; + -webkit-box-flex: 1; + -ms-flex: 1 1 100%; + flex: 1 1 100%; + padding: 1em 1.5em; + text-align: center; + -webkit-transition: all 0.12s linear; + transition: all 0.12s linear; + -webkit-transform: none; + transform: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +@media all and (min-width: 37.5em) { + .cb__btn { + -webkit-box-flex: 0; + -ms-flex: 0 10 auto; + flex: 0 10 auto; + } +} + +.cb__btn:active, .cb__btn:hover, .cb__btn:focus { + background: var(--cb-btn-primary-bg-hover); + border: inherit; + -webkit-box-shadow: inherit; + box-shadow: inherit; + color: var(--cb-btn-primary-text-hover); + -webkit-transform: inherit; + transform: inherit; +} + +.cb__btn--solid { + background: var(--cb-btn-secondary-bg); + color: var(--cb-btn-secondary-text); +} + +.cb__btn--solid:active, +.cb__btn--solid:hover, +.cb__btn--solid:focus { + background: var(--cb-btn-secondary-bg-hover); + color: var(--cb-btn-secondary-text-hover); +} + +.cb__btn--link { + background: none; + color: var(--cb-btn-link); + margin-right: auto; + padding-left: 0; + padding-right: 0; + text-align: left; + text-decoration: underline; + text-decoration-thickness: 1px; + text-underline-offset: 0.2em; + -webkit-text-decoration-skip: ink; + text-decoration-skip-ink: auto; +} + +@media all and (max-width: 37.4375em) { + .cb__btn--link { + margin-bottom: 0.25em; + } +} + +.cb__btn--link:active, +.cb__btn--link:hover, +.cb__btn--link:focus { + background: inherit; + color: var(--cb-btn-link-hover); +} + +.cb__btn + .cb__btn { + margin: 0.25em 0; +} + +@media all and (min-width: 37.5em) { + .cb__btn + .cb__btn { + margin: 0 0 0 0.5em; + } +} + +.cb__overlay { + background: var(--cb-overlay); + bottom: 0; + display: none; + left: 0; + position: fixed; + right: 0; + top: 0; + visibility: hidden; + z-index: 999999999; +} + +.cb__overlay.is-visible { + display: block; + opacity: 1; + visibility: visible; +} + +.cb__popup { + display: table; + height: 100%; + left: 0; + opacity: 0; + position: fixed; + right: 0; + top: 0; + width: 100%; + visibility: hidden; + z-index: 9999999999; + -webkit-transition: all 0.25s ease-out; + transition: all 0.25s ease-out; +} + + @media all and (min-width: 37.5em) { + .cb__popup { + left: 1em; + right: 1em; + -webkit-transform: translateY(1.5em); + transform: translateY(1.5em); + width: calc(100% - 2em); + } +} + +.cb__popup.is-visible { + visibility: visible; + opacity: 1; + -webkit-transform: translateY(0); + transform: translateY(0); +} + +.cb__popup__wrapper { + background: var(--cb-bg); + font-size: var(--cb-font-base-size); + height: 100%; + max-height: 100%; + max-width: 100%; + margin: 0 auto; + overflow: hidden; + padding: 1.5em 2em; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: 100%; + position: absolute; + top: 50%; + left: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); +} + +@media all and (min-width: 37.5em) { + .cb__popup__wrapper { + border-radius: calc(1.5 * var(--cb-border-radius)); + -webkit-box-shadow: 0 0.625em 1.875em rgba(2, 2, 3, 0.28); + box-shadow: 0 0.625em 1.875em rgba(2, 2, 3, 0.28); + max-height: var(--cb-popup-height); + max-width: var(--cb-width); + } +} + +.cb__popup__inner { + padding-top: 2.5em; + padding-bottom: 10em; + position: relative; + height: 100%; +} + +@media all and (min-width: 37.5em) { + .cb__popup__inner { + padding-bottom: 2.5em; + } +} + +.cb__popup__heading { + border-bottom: 1px solid var(--cb-border-color); + left: 0; + height: 2.5em; + padding-bottom: 2em; + position: absolute; + right: 0; + top: 0; +} + +.cb__popup__content { + display: block; + height: 100%; + overflow-y: auto; + overflow-x: hidden; + width: calc(100% + 1em); + margin-right: -1em; + padding-right: 1em; +} + +.cb__popup__txt { + margin-top: 2em; +} + +.cb__popup__buttons { + background-color: var(--cb-bg); + bottom: 0; + left: 0; + margin: 0; + padding: 1.25em 0 0; + position: absolute; + right: 0; +} + +.cb__popup__buttons::before { + content: ""; + border-top: 1px solid var(--cb-border-color); + height: 1px; + left: 0; + width: 100%; + position: absolute; + top: 0; +} + +.cb__popup__buttons .cb__btn:nth-child(3) { + margin-left: auto; +} + +.cb__popup__switch { + height: 100%; + position: absolute; + right: 1em; + top: 0; +} + +.cb__popup__switch input[type=checkbox] { + height: 0; + width: 0; + visibility: hidden; +} + +.cb__popup__switch label { + cursor: pointer; + text-indent: -9999px; + width: 44px; + height: 24px; + background: var(--cb-btn-primary-bg-hover); + display: block; + border-radius: 100px; + position: relative; +} + +.cb__popup__switch label::after { + content: ""; + position: absolute; + top: 2px; + left: 2px; + width: 20px; + height: 20px; + background: var(--cb-bg); + border-radius: 20px; + -webkit-transition: 0.3s; + transition: 0.3s; +} + +.cb__popup__switch input:checked + label { + background: var(--cb-btn-secondary-bg); +} + +.cb__popup__switch input:checked + label:after { + left: calc(100% - 2px); + -webkit-transform: translateX(-100%); + transform: translateX(-100%); +} + +.cb__popup__switch.is-checked label { + opacity: 0.5; + pointer-events: none; +} + +.cb__groups { + list-style: none; + margin: 2em 0 1em; +} + +.cb__group { + border: 1px solid var(--cb-border-color); + border-radius: var(--cb-border-radius); + margin-bottom: 0.5em; + padding: 0 1em; + position: relative; +} + +.cb__group__title { + font-weight: var(--cb-font-weight-bold); + font-size: 0.941em; + color: var(--cb-headings-color); + cursor: pointer; + display: block; + padding: 1.25em 3.5em 1.25em 1.25em; + position: relative; + -webkit-transition: all 0.12s linear; + transition: all 0.12s linear; +} + +.cb__group__title:hover { + color: var(--cb-btn-link-hover); +} + +.cb__group__title:focus { + outline: none; +} + +.cb__group__title::marker, .cb__group__title::-webkit-details-marker { + display: none; +} + +.cb__group__title::before { + content: ""; + border-right: 1px solid; + border-bottom: 1px solid; + border-color: inherit; + left: 0; + height: 5px; + display: block; + position: absolute; + -webkit-transform: translate(0, -50%) rotate(-45deg); + transform: translate(0, -50%) rotate(-45deg); + -webkit-transition: all 0.12s linear; + transition: all 0.12s linear; + top: 50%; + width: 5px; +} + +.cb__group__txt { + font-size: 0.85em; + font-weight: var(--cb-font-weight-normal); + line-height: 1.5; + margin-top: -0.5em; + padding: 0.5em 0 1.25em 1.25em; +} + +.cb__group > details[open] .cb-popup__group__title::before { + -webkit-transform: translate(0, -50%) rotate(45deg); + transform: translate(0, -50%) rotate(45deg); +} + +.cb__badge { + background: var(--cb-badge-bg); + border: none; + border-radius: var(--cb-badge-border-radius); + -webkit-box-shadow: none; + box-shadow: none; + bottom: 1.25em; + color: var(--cb-badge-color); + cursor: pointer; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + left: 1.25em; + padding: 0.55em; + position: fixed; + -webkit-transition: all 0.12s linear; + transition: all 0.12s linear; + -webkit-transform: none; + transform: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + visibility: hidden; +} + +.cb__badge:active, +.cb__badge:hover, +.cb__badge:focus { + background: var(--cb-badge-bg-hover); + border: inherit; + -webkit-box-shadow: inherit; + box-shadow: inherit; + color: var(--cb-badge-color-hover); + -webkit-transform: inherit; + transform: inherit; +} + +.cb__badge.is-visible { + visibility: visible; +} diff --git a/app/default-files/gdpr-assets/gdpr.js b/app/default-files/gdpr-assets/gdpr.js new file mode 100644 index 000000000..0055524eb --- /dev/null +++ b/app/default-files/gdpr-assets/gdpr.js @@ -0,0 +1,394 @@ +(function() { + if (!document.querySelector('.cb')) { + return; + } + + var cbConfig = { + behaviour: document.querySelector('.cb').getAttribute('data-behaviour'), + behaviourLink: document.querySelector('.cb').getAttribute('data-behaviour-link'), + revision: document.querySelector('.cb').getAttribute('data-revision'), + configTTL: parseInt(document.querySelector('.cb').getAttribute('data-config-ttl'), 10), + debugMode: document.querySelector('.cb').getAttribute('data-debug-mode') === 'true', + initialState: null + }; + + var cbUI = { + wrapper: document.querySelector('.cb'), + banner: { + element: null, + btnAccept: null, + btnReject: null, + btnConfigure: null + }, + popup: { + element: null, + btnSave: null, + btnAccept: null, + btnReject: null, + checkboxes: null + }, + overlay: null, + badge: null, + blockedScripts: document.querySelectorAll('script[type^="gdpr-blocker/"]'), + triggerLinks: cbConfig.behaviourLink ? document.querySelectorAll('a[href*="' + cbConfig.behaviourLink + '"]') : null + }; + + function initUI () { + // setup banner elements + cbUI.banner.element = cbUI.wrapper.querySelector('.cb__banner'); + cbUI.banner.btnAccept = cbUI.banner.element.querySelector('.cb__btn--accept'); + cbUI.banner.btnReject = cbUI.banner.element.querySelector('.cb__btn--reject'); + cbUI.banner.btnConfigure = cbUI.banner.element.querySelector('.cb__btn--configure'); + + // setup popup elements + if (cbUI.wrapper.querySelector('.cb__popup')) { + cbUI.popup.element = cbUI.wrapper.querySelector('.cb__popup'); + cbUI.popup.btnSave = cbUI.popup.element.querySelector('.cb__btn--save'); + cbUI.popup.btnAccept = cbUI.popup.element.querySelector('.cb__btn--accept'); + cbUI.popup.btnReject = cbUI.popup.element.querySelector('.cb__btn--reject'); + cbUI.popup.checkboxes = cbUI.popup.element.querySelector('input[type="checkbox"]'); + // setup overlay + cbUI.overlay = cbUI.wrapper.querySelector('.cb__overlay'); + } + + cbUI.badge = cbUI.wrapper.querySelector('.cb__badge'); + + if (cbConfig.behaviour.indexOf('link') > -1) { + for (var i = 0; i < cbUI.triggerLinks.length; i++) { + cbUI.triggerLinks[i].addEventListener('click', function(e) { + e.preventDefault(); + showBannerOrPopup(); + }); + } + } + } + + function initState () { + var lsKeyName = getConfigName(); + var currentConfig = localStorage.getItem(lsKeyName); + var configIsFresh = checkIfConfigIsFresh(); + + if (!configIsFresh || currentConfig === null) { + var checkedGroups = cbUI.popup.element.querySelectorAll('input[type="checkbox"]:checked'); + showBanner(); + + for (var i = 0; i < checkedGroups.length; i++) { + var allowedGroup = checkedGroups[i].getAttribute('data-group-name'); + + if (allowedGroup !== '-' && allowedGroup !== '') { + allowCookieGroup(allowedGroup); + } + } + } else if (typeof currentConfig === 'string') { + var allowedGroups = currentConfig.split(','); + var checkedCheckboxes = cbUI.popup.element.querySelectorAll('input[type="checkbox"]:checked'); + showBadge(); + + for (var j = 0; j < checkedCheckboxes.length; j++) { + var name = checkedCheckboxes[j].getAttribute('data-group-name'); + + if (name && name !== '-' && allowedGroups.indexOf(name) === -1) { + checkedCheckboxes[j].checked = false; + } + } + + for (var i = 0; i < allowedGroups.length; i++) { + var checkbox = cbUI.popup.element.querySelector('input[type="checkbox"][data-group-name="' + allowedGroups[i] + '"]'); + + if (checkbox) { + checkbox.checked = true; + } + + allowCookieGroup(allowedGroups[i]); + } + } + + setTimeout(function () { + cbConfig.initialState = getInitialStateOfConsents(); + }, 0); + } + + function checkIfConfigIsFresh () { + var lastConfigSave = localStorage.getItem('publii-gdpr-cookies-config-save-date'); + + if (lastConfigSave === null) { + return false; + } + + lastConfigSave = parseInt(lastConfigSave, 10); + + if (lastConfigSave === 0) { + return true; + } + + if (+new Date() - lastConfigSave < cbConfig.configTTL * 24 * 60 * 60 * 1000) { + return true; + } + + return false; + } + + function initBannerEvents () { + cbUI.banner.btnAccept.addEventListener('click', function (e) { + e.preventDefault(); + acceptAllCookies('banner'); + showBadge(); + }, false); + + cbUI.banner.btnReject.addEventListener('click', function (e) { + e.preventDefault(); + rejectAllCookies(); + showBadge(); + }, false); + + cbUI.banner.btnConfigure.addEventListener('click', function (e) { + e.preventDefault(); + hideBanner(); + showAdvancedPopup(); + showBadge(); + }, false); + } + + function initPopupEvents () { + if (!cbUI.popup.element) { + return; + } + + cbUI.overlay.addEventListener('click', function (e) { + hideAdvancedPopup(); + }, false); + + cbUI.popup.element.addEventListener('click', function (e) { + e.stopPropagation(); + }, false); + + cbUI.popup.btnAccept.addEventListener('click', function (e) { + e.preventDefault(); + acceptAllCookies('popup'); + }, false); + + cbUI.popup.btnReject.addEventListener('click', function (e) { + e.preventDefault(); + rejectAllCookies(); + }, false); + + cbUI.popup.btnSave.addEventListener('click', function (e) { + e.preventDefault(); + saveConfiguration(); + }, false); + } + + function initBadgeEvents () { + if (!cbUI.badge) { + return; + } + + cbUI.badge.addEventListener('click', function (e) { + showBannerOrPopup(); + }, false); + } + + initUI(); + initState(); + initBannerEvents(); + initPopupEvents(); + initBadgeEvents(); + + /** + * API + */ + function addScript (src, inline) { + var newScript = document.createElement('script'); + + if (src) { + newScript.setAttribute('src', src); + } + + if (inline) { + newScript.text = inline; + } + + document.body.appendChild(newScript); + } + + function allowCookieGroup (allowedGroup) { + var scripts = document.querySelectorAll('script[type="gdpr-blocker/' + allowedGroup + '"]'); + + for (var j = 0; j < scripts.length; j++) { + addScript(scripts[j].src, scripts[j].text); + } + + var groupEvent = new Event('publii-cookie-banner-unblock-' + allowedGroup); + document.body.dispatchEvent(groupEvent); + } + + function showBannerOrPopup () { + if (cbUI.popup.element) { + showAdvancedPopup(); + } else { + showBanner(); + } + } + + function showAdvancedPopup () { + cbUI.popup.element.classList.add('is-visible'); + cbUI.overlay.classList.add('is-visible'); + } + + function hideAdvancedPopup () { + cbUI.popup.element.classList.remove('is-visible'); + cbUI.overlay.classList.remove('is-visible'); + } + + function showBanner () { + cbUI.banner.element.classList.add('is-visible'); + } + + function hideBanner () { + cbUI.banner.element.classList.remove('is-visible'); + } + + function showBadge () { + if (!cbUI.badge) { + return; + } + + console.log(cbUI); + + cbUI.badge.classList.add('is-visible'); + } + + function getConfigName () { + var lsKeyName = 'publii-gdpr-allowed-cookies-v1'; + + if (cbConfig.revision) { + lsKeyName = lsKeyName + '-v' + parseInt(cbConfig.revision, 10); + } + + return lsKeyName; + } + + function storeConfiguration (allowedGroups) { + var lsKeyName = getConfigName(); + var dataToStore = allowedGroups.join(','); + localStorage.setItem(lsKeyName, dataToStore); + + if (cbConfig.configTTL === 0) { + localStorage.setItem('publii-gdpr-cookies-config-save-date', 0); + } else { + localStorage.setItem('publii-gdpr-cookies-config-save-date', +new Date()); + } + } + + function getInitialStateOfConsents () { + var checkedGroups = cbUI.popup.element.querySelectorAll('input[type="checkbox"]:checked'); + var groups = []; + + for (var i = 0; i < checkedGroups.length; i++) { + var allowedGroup = checkedGroups[i].getAttribute('data-group-name'); + + if (allowedGroup !== '') { + groups.push(allowedGroup); + } + } + + return groups; + } + + function getCurrentStateOfConsents () { + var checkedGroups = cbUI.popup.element.querySelectorAll('input[type="checkbox"]:checked'); + var groups = []; + + for (var i = 0; i < checkedGroups.length; i++) { + var allowedGroup = checkedGroups[i].getAttribute('data-group-name'); + + if (allowedGroup !== '') { + groups.push(allowedGroup); + } + } + + return groups; + } + + function getAllGroups () { + var checkedGroups = cbUI.popup.element.querySelectorAll('input[type="checkbox"]'); + var groups = []; + + for (var i = 0; i < checkedGroups.length; i++) { + var allowedGroup = checkedGroups[i].getAttribute('data-group-name'); + + if (allowedGroup !== '') { + groups.push(allowedGroup); + } + } + + return groups; + } + + function acceptAllCookies (source) { + var groupsToAccept = getAllGroups(); + storeConfiguration(groupsToAccept); + + for (var i = 0; i < groupsToAccept.length; i++) { + var group = groupsToAccept[i]; + + if (cbConfig.initialState.indexOf(group) > -1) { + continue; + } + + allowCookieGroup(group); + } + + if (source === 'popup') { + hideAdvancedPopup(); + } else if (source === 'banner') { + hideBanner(); + } + } + + function rejectAllCookies () { + storeConfiguration([]); + setTimeout(function () { + window.location.reload(); + }, 100); + } + + function saveConfiguration () { + var groupsToAccept = getCurrentStateOfConsents(); + storeConfiguration(groupsToAccept); + + if (reloadIsNeeded(groupsToAccept)) { + setTimeout(function () { + window.location.reload(); + }, 100); + return; + } + + for (var i = 0; i < groupsToAccept.length; i++) { + var group = groupsToAccept[i]; + + if (cbConfig.initialState.indexOf(group) > -1) { + continue; + } + + allowCookieGroup(group); + } + + hideAdvancedPopup(); + } + + function reloadIsNeeded (groupsToAccept) { + // check if user rejected consent for initial groups + var initialGroups = cbConfig.initialState; + + for (var i = 0; i < initialGroups.length; i++) { + var groupToCheck = initialGroups[i]; + + if (groupsToAccept.indexOf(groupToCheck) === -1) { + return true; + } + } + + return false; + } +})(); diff --git a/app/default-files/gdpr-assets/template.html b/app/default-files/gdpr-assets/template.html new file mode 100644 index 000000000..003c2d189 --- /dev/null +++ b/app/default-files/gdpr-assets/template.html @@ -0,0 +1,123 @@ + +
    + + + + + {{#allowAdvancedConfiguration}} + + + + +
    + + {{/allowAdvancedConfiguration}} + + {{#showBadge}} + + + + {{/showBadge}} +
    + diff --git a/app/src/components/Settings.vue b/app/src/components/Settings.vue index 020ae426e..53a049366 100644 --- a/app/src/components/Settings.vue +++ b/app/src/components/Settings.vue @@ -1239,7 +1239,11 @@ id="posts-listing-order-by" key="posts-listing-order-by" v-model="advanced.gdpr.behaviour" - :items="{ 'badge': $t('settings.badge'), 'link': $t('ui.customLink'), 'badge-link': $t('settings.badgeAndCustomLink') }">
    + :items="{ + 'badge': $t('settings.badge'), + 'link': $t('ui.customLink'), + 'badge-link': $t('settings.badgeAndCustomLink') + }">
    @@ -1322,7 +1326,7 @@ + + + + + + + + diff --git a/package.json b/package.json index e76d5427e..bd9ef556c 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "*.+(dylib|node|dll|aff|dic)", "default-files/default-themes/**/*", "default-files/default-languages/**/*", + "default-files/gdpr-assets/**/*", "node_modules/sharp/**/*", "node_modules/keytar/**/*", "node_modules/better-sqlite3/**/*", From 21969263936d93cc0cef0f9dce040ba9fd3fe911 Mon Sep 17 00:00:00 2001 From: Tomasz Dziuda Date: Fri, 3 Jun 2022 20:15:42 +0200 Subject: [PATCH 060/126] feat: new cookie banner - script improvements and improved ARIA support (#28z19ma) --- app/default-files/gdpr-assets/gdpr.css | 2 ++ app/default-files/gdpr-assets/gdpr.js | 35 +++++++++++++-------- app/default-files/gdpr-assets/template.html | 9 +++--- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/app/default-files/gdpr-assets/gdpr.css b/app/default-files/gdpr-assets/gdpr.css index e3c60a53d..d8465a605 100644 --- a/app/default-files/gdpr-assets/gdpr.css +++ b/app/default-files/gdpr-assets/gdpr.css @@ -323,6 +323,7 @@ height: 100%; left: 0; opacity: 0; + pointer-events: none; position: fixed; right: 0; top: 0; @@ -358,6 +359,7 @@ max-width: 100%; margin: 0 auto; overflow: hidden; + pointer-events: auto; padding: 1.5em 2em; -webkit-user-select: none; -moz-user-select: none; diff --git a/app/default-files/gdpr-assets/gdpr.js b/app/default-files/gdpr-assets/gdpr.js index 0055524eb..b2a4d4672 100644 --- a/app/default-files/gdpr-assets/gdpr.js +++ b/app/default-files/gdpr-assets/gdpr.js @@ -135,18 +135,22 @@ showBadge(); }, false); - cbUI.banner.btnReject.addEventListener('click', function (e) { - e.preventDefault(); - rejectAllCookies(); - showBadge(); - }, false); + if (!cbUI.banner.btnReject) { + cbUI.banner.btnReject.addEventListener('click', function (e) { + e.preventDefault(); + rejectAllCookies(); + showBadge(); + }, false); + } - cbUI.banner.btnConfigure.addEventListener('click', function (e) { - e.preventDefault(); - hideBanner(); - showAdvancedPopup(); - showBadge(); - }, false); + if (!cbUI.banner.btnConfigure) { + cbUI.banner.btnConfigure.addEventListener('click', function (e) { + e.preventDefault(); + hideBanner(); + showAdvancedPopup(); + showBadge(); + }, false); + } } function initPopupEvents () { @@ -233,19 +237,25 @@ function showAdvancedPopup () { cbUI.popup.element.classList.add('is-visible'); cbUI.overlay.classList.add('is-visible'); + cbUI.popup.element.setAttribute('aria-hidden', 'false'); + cbUI.overlay.setAttribute('aria-hidden', 'false'); } function hideAdvancedPopup () { cbUI.popup.element.classList.remove('is-visible'); cbUI.overlay.classList.remove('is-visible'); + cbUI.popup.element.setAttribute('aria-hidden', 'true'); + cbUI.overlay.setAttribute('aria-hidden', 'true'); } function showBanner () { cbUI.banner.element.classList.add('is-visible'); + cbUI.banner.element.setAttribute('aria-hidden', 'false'); } function hideBanner () { cbUI.banner.element.classList.remove('is-visible'); + cbUI.banner.element.setAttribute('aria-hidden', 'true'); } function showBadge () { @@ -253,9 +263,8 @@ return; } - console.log(cbUI); - cbUI.badge.classList.add('is-visible'); + cbUI.badge.setAttribute('aria-hidden', 'false'); } function getConfigName () { diff --git a/app/default-files/gdpr-assets/template.html b/app/default-files/gdpr-assets/template.html index 003c2d189..5e0ef418e 100644 --- a/app/default-files/gdpr-assets/template.html +++ b/app/default-files/gdpr-assets/template.html @@ -10,7 +10,7 @@