diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d1efb01a6..3037fb4132 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,11 @@ jobs: # node install - run: yarn install --frozen-lockfile + # node code analysis + - run: yarn run prettier + - run: yarn run lint + - run: yarn run i18n:ci + # node test - run: yarn test:ci diff --git a/CHANGELOG.md b/CHANGELOG.md index adb8673551..7432421e69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## 10.0.1 (unreleased) +## 10.2.1 (unreleased) ### Breaking @@ -12,8 +12,34 @@ ### Internal +## 10.2.0 (2020-12-04) + +### Feature + +- Generate language file of added missing German translations by @tisto. @ksuess +- Add emailSend action @nzambello + +### Bugfix + +- Fix regression in the `getContent` action with the expandable missing @sneridagh + +- For python3.9 compatibility, install wheel package in build-backend targets @tiberiuichim + +- lazy load react-dropzone @nileshgulia1 + +## 10.1.0 (2020-11-30) + +### Feature + +- Add missing German translations @tisto + ## 10.0.0 (2020-11-30) +### Feature + +- Provide operations on multiple-selected blocks: delete, cut/copy and paste. You can trigger the "multiselected blocks" by holding the shift key and clicking on another block. You can add/remove blocks to the selection with the Control key. Holding Control when you click on the Paste button doesn't clear the clipboard, so you can paste multiple times. The blocks clipboard uses the browser's local storage to synchronize between tabs. @tiberiuichim +- Allow reducers to be persisted using localstorage @tiberiuichim + ### Breaking - Removal of the Razzle patch that was introduced in 9.0.0 @sneridagh diff --git a/Makefile b/Makefile index 71da3446c8..e87c2509e2 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ build-frontend: build-backend: ## Build Plone 5.2 (cd api && python3 -m venv .) (cd api && bin/pip install --upgrade pip) + (cd api && bin/pip install --upgrade wheel) (cd api && bin/pip install -r requirements.txt) (cd api && bin/buildout) @@ -34,6 +35,7 @@ build-backend: ## Build Plone 5.2 build-backend-withport: ## Build Plone 5.2 with port (cd api && python3 -m venv .) (cd api && bin/pip install --upgrade pip) + (cd api && bin/pip install --upgrade wheel) (cd api && bin/pip install -r requirements.txt) (cd api && bin/buildout instance:http-address=$(INSTANCE_PORT)) diff --git a/api/buildout.cfg b/api/buildout.cfg index 99534f10db..6f43a2fe8d 100644 --- a/api/buildout.cfg +++ b/api/buildout.cfg @@ -80,3 +80,4 @@ robotframework-selenium2library = robotframework-seleniumlibrary = robotframework-webpack= selenium = +feedparser = diff --git a/cypress/integration/blocks-copypaste.js b/cypress/integration/blocks-copypaste.js new file mode 100644 index 0000000000..d38a7a4f68 --- /dev/null +++ b/cypress/integration/blocks-copypaste.js @@ -0,0 +1,152 @@ +if (Cypress.env('API') !== 'guillotina') { + describe('Blocks copy/paste', () => { + beforeEach(() => { + cy.autologin(); + cy.createContent({ + contentType: 'Document', + contentId: 'my-page', + contentTitle: 'My Page', + }); + cy.visit('/my-page'); + cy.waitForResourceToLoad('@navigation'); + cy.waitForResourceToLoad('@breadcrumbs'); + cy.waitForResourceToLoad('@actions'); + cy.waitForResourceToLoad('@types'); + cy.waitForResourceToLoad('my-page'); + cy.navigate('/my-page/edit'); + }); + + it('Copy/paste multiple blocks', () => { + // GIVEN: A page with multiple blocks + cy.get('.block.inner.text .public-DraftEditor-content').click(); + cy.get('button.block-add-button').click(); + cy.get('.blocks-chooser .title').contains('Common').click(); + cy.get('.blocks-chooser .common').contains('Maps').click(); + cy.get(`.block.maps .toolbar-inner .ui.input input`) + .type( + '', + ) + .type('{enter}'); + + cy.get('.block.inner.text .public-DraftEditor-content') + .click() + .type('Noam Avram Chomsky') + .get('span[data-text]') + .contains('Noam Avram Chomsky'); + + cy.get('.block.text [contenteditable]').click(); + + cy.get('#toolbar-save').click(); + + cy.get('a[aria-label="Edit"]').click(); + + // WHEN: I copy paste them + cy.get('.documentFirstHeading > .public-DraftStyleDefault-block') + .click() + .type('{shift}', { release: false }); + cy.get('.block-editor-maps').click(); + cy.get('#toolbar-copy-blocks').click(); + + cy.get('.block.inner.text .public-DraftEditor-content') + .type('{shift}') + .click(); + cy.get('#toolbar-paste-blocks').click(); + + // THEN: the page will contain duplicated blocks + cy.get('.documentFirstHeading').should(($blocks) => { + expect($blocks).to.have.length(2); + }); + + cy.get('.block-editor-maps').should(($blocks) => { + expect($blocks).to.have.length(2); + }); + }); + + it('Cut/paste multiple blocks', () => { + // GIVEN: A page with multiple blocks + cy.get('.block.inner.text .public-DraftEditor-content').click(); + cy.get('button.block-add-button').click(); + cy.get('.blocks-chooser .title').contains('Common').click(); + cy.get('.blocks-chooser .common').contains('Maps').click(); + cy.get(`.block.maps .toolbar-inner .ui.input input`) + .type( + '', + ) + .type('{enter}'); + + cy.get('.block.inner.text .public-DraftEditor-content') + .click() + .type('Noam Avram Chomsky') + .get('span[data-text]') + .contains('Noam Avram Chomsky'); + + cy.get('.block.text [contenteditable]').click(); + + cy.get('#toolbar-save').click(); + + cy.get('a[aria-label="Edit"]').click(); + + // WHEN: I cut paste them + cy.get('.documentFirstHeading > .public-DraftStyleDefault-block') + .click() + .type('{shift}', { release: false }); + cy.get('.block-editor-maps').click(); + cy.get('#toolbar-cut-blocks').click(); + + cy.get('.block.inner.text .public-DraftEditor-content') + .type('{shift}') + .click(); + cy.get('#toolbar-paste-blocks').click(); + + // THEN: the page will contain only one of each blocks + cy.get('.documentFirstHeading').should(($blocks) => { + expect($blocks).to.have.length(1); + }); + + cy.get('.block-editor-maps').should(($blocks) => { + expect($blocks).to.have.length(1); + }); + }); + + it('Delete multiple blocks', () => { + // GIVEN: A page with multiple blocks + cy.get('.block.inner.text .public-DraftEditor-content').click(); + cy.get('button.block-add-button').click(); + cy.get('.blocks-chooser .title').contains('Common').click(); + cy.get('.blocks-chooser .common').contains('Maps').click(); + cy.get(`.block.maps .toolbar-inner .ui.input input`) + .type( + '', + ) + .type('{enter}'); + + cy.get('.block.inner.text .public-DraftEditor-content') + .click() + .type('Noam Avram Chomsky') + .get('span[data-text]') + .contains('Noam Avram Chomsky'); + + cy.get('.block.text [contenteditable]').click(); + + cy.get('#toolbar-save').click(); + + cy.get('a[aria-label="Edit"]').click(); + + // WHEN: I delete them + cy.get('.documentFirstHeading > .public-DraftStyleDefault-block') + .click() + .type('{shift}', { release: false }); + cy.get('.block-editor-maps').click(); + cy.get('#toolbar-delete-blocks').click(); + + // THEN: the page will contain none of the blocks + cy.get('.documentFirstHeading').should(($blocks) => { + expect($blocks).to.have.length(0); + }); + + cy.get('.block-editor-maps').should(($blocks) => { + expect($blocks).to.have.length(0); + }); + }); + }); +} diff --git a/docs/source/configuration/settings-reference.md b/docs/source/configuration/settings-reference.md index 2ea6bc6801..1600fef5c9 100644 --- a/docs/source/configuration/settings-reference.md +++ b/docs/source/configuration/settings-reference.md @@ -91,3 +91,10 @@ This list is still incomplete, contributions are welcomed! (for example content with lots of text) and you need to batch requests anyway, if you want to be sure to display all the children. + +## persistentReducers + +!!! block "" + + A list of reducer names that should use the browser's localstorage to + persist their data. diff --git a/locales/de.json b/locales/de.json index 7bbc7a57fd..d49ef50f03 100644 --- a/locales/de.json +++ b/locales/de.json @@ -1 +1 @@ -{"

Add some HTML here

":"","Accessibility":"Barrierefreiheit","Account Registration Completed":"Die Registrierung Ihres Zugangs wurde erfolgreich abgeschlossen.","Account activation completed":"Passwort gesetzt.","Action":"Aktion","Actions":"Aktionen","Activate and deactivate":"Aktiviern und Deaktivieren","Add":"Hinzufügen","Add Addons":"Add-on hinzufügen","Add Content":"Inhalte hinzufügen","Add Translation…":"Übersetzung hinzufügen…","Add User":"Benutzer hinzufügen","Add a description…":"Beschreibung hinzufügen…","Add block…":"Block hinzufügen","Add criteria":"Kriterien hinzufügen","Add date":"Datum hinzufügen","Add field":"Feld hinzufügen","Add fieldset":"Fieldset hinzufügen","Add group":"Gruppe hinzufügen","Add new content type":"Neuen Inhaltstypen hinzufügen","Add new group":"Neue Gruppe hinzufügen","Add new user":"Neuen Benutzer hinzufügen","Add to Groups":"Zu Gruppe hinzufügen","Add {type}":"{type} hinzufügen","Add-ons Settings":"Einstellungen Add-ons","Additional date":"Zusätzliches Datum","Alignment":"Ausrichtung","All":"Alle","Alphabetically":"alphabetisch","Alt text":"Alternative Text","Are you sure you want to delete this field?":"Sind Sie sicher, dass Sie dieses Feld löschen möchten?","Are you sure you want to delete this fieldset including all fields?":"Sind Sie sicher, dass Sie dieses Fieldset löschen möchten?","Ascending":"Aufsteigend","Available":"Verfügbar","Back":"Zurück","Base":"Basis","Block":"Block","Block style":"Block Darstellung","Both email address and password are case sensitive, check that caps lock is not enabled.":"Sowohl E-Mail Adresse als auch Passwort unterscheiden zwischen Groß- und Kleinschreibung, stellen Sie sicher dass die Hochstelltaste nicht aktiviert ist.","Breadcrumbs":"Brotkrumen","Browse":"Durchsuchen","Browse the site, drop an image, or type an URL":"Seite durchsuchen, Bild ablegen oder URL eingeben","By default, permissions from the container of this item are inherited. If you disable this, only the explicitly defined sharing permissions will be valid. In the overview, the symbol {inherited} indicates an inherited value. Similarly, the symbol {global} indicates a global role, which is managed by the site administrator.":"Standardmäßig werden die Berechtigungen von einem Ordner auf die in ihm befindlichen Artikel vererbt. Wenn Sie dies deaktivieren, sind nur die explizit definierten Zugriffsberechtigungen gültig. In der Übersicht zeigt das Symbol ${image_confirm_icon} einen ererbten Wert an. Das Symbol ${image_link_icon} zeigt eine globale Funktion an, die vom Administrator verwaltet wird.","Cache Name":"Cache Name","Can not edit Layout for {type} content-type as it doesn't have support for Volto Blocks enabled":"","Can not edit Layout for {type} content-type as the Blocks behavior is enabled and read-only":"","Cancel":"Abbrechen","Cell":"Zelle","Center":"Mittig","Change Note":"Änderungsnotiz","Change Password":"Passwort ändern","Change State":"Arbeitsablauf-Status ändern","Change workflow state recursively":"Arbeitlauf-Status für alle Unterobjekte ebenfalls ändern","Changes saved":"Änderungen gespeichert","Changes saved.":"Änderungen gespeichert.","Checkbox":"Checkbox","Choices":"Auswahlfeld","Choose Image":"Bild auswählen","Choose Target":"Ziel auswählen","Choose a file":"","Clear":"","Click to download full sized image":"Klicken um das Bild in der vollen Größe runterzuladen","Close":"Schließen","Close menu":"Menu schließen","Code":"","Collection":"Kollektion","Comment":"Kommentar","Commenter":"Kommentarautor","Compare":"Vergleichen","Confirm password":"Passwort bestätigen","Connection refused":"Verbindung abgelehnt","Contact":"Kontakt","Contact form":"Kontaktformular","Contained items":"Enthaltene Elemente","Content":"Inhalt","Content type created":"Inhaltstyp erstellt","Content type deleted":"Inhaltstyp gelöscht","Contents":"Inhaltsverzeichnis","Copy":"Kopieren","Copyright":"Urheberrecht","Copyright statement or other rights information on this item.":"Informationen über die Urheber- und Nutzungsrechte an diesem Artikel.","Created on":"Erstellt am","Creator":"Ersteller","Creators":"Ersteller","Criteria":"Kriterium","Current password":"Aktuelles Passwort","Cut":"Ausschneiden","Daily":"Täglich","Database Information":"Datenbankinformationen","Database Location":"Speicheort Datenbank","Database Size":"Größe Datenbank","Database main":"Datenbank","Date":"Datum","Date (newest first)":"Datum (neustes zuerst)","Default":"Standard","Delete":"Löschen","Delete Group":"Gruppe löschen","Delete Type":"Inhaltstype löschen","Delete User":"Benutzer löschen","Delete col":"Spalte löschen","Delete row":"Zeile löschen","Depth":"","Descending":"Absteigend","Description":"Beschreibung","Diff":"Unterschied","Difference between revision {one} and {two} of {title}":"Unterschied zwischen Version {one} and {two} von {title}","Distributed under the {license}.":"Lizensiert unter der {license}.","Divide each row into separate cells":"Zeile in zwei separate Zellen aufteilen","Do you really want to delete the following items?":"Möchten Sie den Artikel wirklich löschen?","Do you really want to delete the group {groupname}?":"Möchten Sie die Gruppe {groupname} wirklich löschen?","Do you really want to delete the type {typename}?":"Möchten Sie den Inhaltstyp {typename} wirklich löschen?","Do you really want to delete the user {username}?":"Möchten Sie den Nutzer {username} wirklich löschen?","Do you really want to delete this item?":"Möchten Sie den Artikel wirklich löschen?","Document":"Seite","Drag and drop files from your computer onto this area or click the “Browse” button.":"Ziehen Sie Dateien von Ihrem Computer auf diesen Bereich oder drücken Sie den “Durchsuchen”-Knopf.","Drop file here to replace the existing file":"","Drop file here to upload a new file":"","Drop files here ...":"","E-mail":"E-Mail","E-mail addresses do not match.":"E-Mail-Adressen stimmen nicht überein.","Edit":"Bearbeiten","Edit comment":"Kommentar bearbeiten","Edit field":"Feld bearbeiten","Edit fieldset":"Fieldset bearbeiten","Edit recurrence":"Wiederkehrende Einstellungen bearbeiten","Edit values":"Werte bearbeiten","Edit {title}":"{title} bearbeiten","Email":"E-Mail","Email sent":"E-Mail versendet","Embed code error, please follow the instructions and try again.":"Fehler beim Einbinden des Google Maps Codes. Bitte lesen Sie die Anweisungen und stellen Sie sicher dass Sie den korrekten Code verwenden.","Embeded Google Maps":"Eingebettete Google Maps Karte","Enable editable Blocks":"","End Date":"Enddatum","Enter URL or select an item":"","Enter a username above to search or click 'Show All'":"","Enter an email address. This will be your login name. We respect your privacy, and will not give the address away to any third parties or expose it anywhere.":"Tragen Sie Ihre E-Mail-Adresse ein, mit der Sie sich künftig anmelden müssen. Wir respektieren den Datenschutz und werden die E-Mail-Adresse nicht an Dritte weitergeben und auch nirgends anzeigen.","Enter full name, e.g. John Smith.":"Tragen Sie bitte Ihren vollen Namen ein.","Enter map Embed Code":"Karten-Einbettungscode eingeben","Enter your current password.":"Geben Sie Ihr aktuelles Passwort ein.","Enter your email address for verification.":"Tragen Sie zur Überprüfung Ihre E-Mail-Adresse ein.","Enter your new password. Minimum 5 characters.":"Geben Sie ihr neues Passwort ein. Mindestens 5 Zeichen.","Error":"Fehler","Exclude from navigation":"Von der Navigation ausschließen","Exclude this occurence":"Dieses Datum ausschließen","Excluded from navigation":"Von Navigation ausgeschlossen","Expand sidebar":"Sidebar vergrößern","Expiration Date":"Ablaufdatum","Expiration date":"Ablaufdatum","Expired":"Abgelaufen","External URL":"Externe URL","File":"Datei","File size":"Dateigröße","Filename":"Dateiname","Filter…":"Filter…","First":"Erster Tag des Monats","Fixed width table cells":"Tabellen-Zellen mit fester Breite","Folder":"Ordner","Forbidden":"Verboten","Fourth":"Vierter","From":"E-Mail","Full":"Volle Breite","Full Name":"Vor- und Nachname","Fullname":"Name","GNU GPL license":"GNU-GPL-Lizenz","Global role":"Globale Rolle","Google Maps Embedded Block":"Google Maps Block","Group":"Gruppe","Group created":"Gruppe erstellt","Groupname":"Gruppenname","Groups":"Gruppen","History":"Historie","History of {title}":"Historie von {title}","Home":"Startseite","Home page":"Homepage","ID":"ID","If selected, this item will not appear in the navigation tree":"Bestimmt, ob der Artikel nicht in der Navigation auftauchen soll.","If this date is in the future, the content will not show up in listings and searches until this date.":"Falls das Datum in der Zukunft liegt wird der Inhalt in Auflistungen und bei der Suche nicht auftauchen, bis zu dem Datum.","If you are certain you have the correct web address but are encountering an error, please contact the {site_admin}.":"Wenn Sie sicher sind, dass Sie die richtige Adresse eingegeben haben, kontaktieren Sie bitte den {site_admin}.","If you have forgotten your password, {forgotpassword}":"Klicken Sie bitte hier, falls Sie Ihr Passwort vergessen haben: {forgotpassword}","If you you do not have an account here, head over to the {registrationform}.":"Falls Sie noch keinen Zugang haben, {registrationform} Sie sich bitte zunächst.","Image":"Bild","Include this occurence":"Datum einbeziehen","Info":"Information","Inherit permissions from higher levels":"Berechtigungen von übergeordneten Ordnern übernehmen","Inherited value":"Geerbter Wert","Insert col after":"Spalte danach einfügen","Insert col before":"Spalte davor einfügen","Insert row after":"Zeile danach einfügen","Insert row before":"Zeile davor einfügen","Install":"Installieren","Installed":"Installiert","Installed version":"Installierte Version","Interval Daily":"Täglich","Interval Monthly":"Monatlich","Interval Weekly":"Wöchentlich","Interval Yearly":"Jährliches Intervall","Item batch size":"Batch-Anzahl","Item succesfully moved.":"","Item(s) copied.":"Artikel kopiert.","Item(s) cut.":"Artikel ausgeschnitten.","Item(s) has been updated.":"","Item(s) pasted.":"Artikel eingefügt.","Items":"Elemente","Items must be unique.":"Auswahl muss eindeutig sein.","Language":"Sprache","Last":"Letzter","Last comment date":"Letztes Kommentierdatum","Last modified":"Letzte Änderung","Latest version":"Letzte Version","Layout":"","Lead Image":"Leadbild","Left":"Links","Link":"Link","Link more":"'Mehr' Link","Link title":"Linktitel","Link to":"Link auf","Listing":"Auflistung","Location":"Ort","Log In":"Anmelden","Log in":"Anmelden","Login":"Einloggen","Login Failed":"Login fehlgeschlagen","Login Name":"Benutzername","Make the table compact":"Tabelle kompakt darstellen","Manage Translations":"","Manage translations for {title}":"","Map":"Karte","Maps URL":"Karten URL","Maximum length is {len}.":"","Maximum value is {len}.":"","Message":"Nachricht","Minimum length is {len}.":"Minimale Länge ist {len}","Minimum value is {len}.":"","Moderate Comments":"Kommentare moderieren","Moderate comments":"Kommentare moderieren","Monday and Friday":"Montag und Freitag","Month day":"Tag des Monats","Monthly":"Monatlich","More":"Mehr","Move to bottom of folder":"Ans Ende verschieben","Move to top of folder":"An den Anfang verschieben","My email address is":"Meine E-Mail-Adresse lautet","Name":"Name","Navigate back":"Zurück navigieren","New password":"Neues Passwort","News Item":"Nachricht","No":"Nein","No image selected":"Kein Bild ausgewählt","No image set in Lead Image content field":"Im Leadbild Feld wurde kein Bild gesetzt","No image set in image content field":"Im Bild Feld wurde kein Bild gesetzt","No items found in this container.":"Keine Elemente gefunden","No items selected":"Kein Element ausgewählt","No map selected":"Keine Karte ausgewählt","No occurences set":"Kein Datum gesetzt","No options":"Keine Option","No results found":"Keine Ergebnisse gefunden","No results found.":"Keine Ergebnisse gefunden.","No selection":"Keine Auswahl","No uninstall profile":"Kein Deinstallationsprofil","No value":"Kein Wert","No video selected":"Kein Video ausgewählt","No workflow":"Kein Workflow","None":"Nicht vorhanden","Number of active objects":"Anzahl aktive Objekte","Object Size":"Grösse","Occurences":"Vorkommen","Ok":"OK","Open in a new tab":"In neuem Browser-Tab öffnen","Open menu":"Menü öffnen","Origin":"Quelle","Page":"Seite","Parent fieldset":"","Password":"Passwort","Password reset":"Passwort zurücksetzen","Passwords do not match.":"Die Passwörter stimmen nicht überein.","Paste":"Einfügen","Personal Information":"Persönliche Informationen","Personal Preferences":"Meine Einstellungen","Personal tools":"Persönliche Einstellungen","Persons responsible for creating the content of this item. Please enter a list of user names, one per line. The principal creator should come first.":"Eine Liste von Personen, die an der Erstellung dieses Artikels beteiligt waren. Bitte geben Sie einen Benutzernamen pro Zeile ein. Der Hauptverantwortliche sollte zuerst genannt werden.","Please enter a valid URL by deleting the block and adding a new video block.":"Geben Sie eine gültige URL ","Please enter the Embed Code provided by Google Maps -> Share -> Embed map. It should contain the