v1.4.0 is out, after a long stay-at-home period
TL;DR: Roadiz v1.4 is huge š¾ and will breaks some little things.
Make sure to upgrade to v1.3.x first and resolve every Roadiz deprecation notices before trying to upgrade to v1.4. If you are using our AbstractBlogTheme
or AbstractApiTheme
, you will need to upgraded them too.
Security
Needs Doctrine migration
- User creation does not send random password by email anymore. It blocks the new user until he or she chooses a new password. An email is at least sent to invite them to choose a new password. This invitation is now valid for 15 minutes. After that delay this user will have to use Forgot password feature.
- This new feature is built on
LoginRequest
and LoginReset
so that you can customize it for your public users if you are using AbstractUserTheme
- Add login attempts throttling policy using
username
and / or ClientIp
if username does not exists
- After 3 failed attemps, IP or username is blocked for 3 minutes, after any failed attempt
- After 6 failed attemps, IP or username is blocked for 15 minutes, after any failed attempt
- After 9 failed attemps, IP or username is blocked for 30 minutes, after any failed attempt
- If as login is attempted on existing
username
, blocking is applyied on username
and ClientIp
not to block the legitimate user against the attacker
- Added console commands to clear login attempts:
login-attempts:clean
login-attempts:purge
- Added new
LoggerFactory
to separate log files and channels and easily create custom loggers for your themes matters.
- Populate more information on authentication exceptions and to make it available from back-office dashboard history.
- Renamed
RZ\Roadiz\Core\Authentification
namespace to RZ\Roadiz\Core\Authentication
- Made
AuthenticationProviderManager
configuration extensible into a new authenticationProviderList
service.
URL Generation
- Made NodesSources path aggregation customizable and optimized,
RZ\Roadiz\Core\Routing\NodesSourcesPathAggregator
will reduce Doctrine object instanciation and will use partial array response to fetch all node-source ancestors.
asset
and absolute_url
Twig function are now available to use theme AssetPackages (this has been back-ported into Roadiz v1.3). This means no more ugly string concatenation like below:
{% set previewImageUrl = head.absoluteResourcesUrl ~ 'img/share.png' %}
becomes
{% set previewImageUrl = absolute_url(asset('img/share.png', 'DefaultTheme')) %}
- New Display locale when using url-aliases setting to force displaying translation locale in URL even if your node-source has an url-alias configured.
Serialization
- Added more detailled node-sources serailization groups
nodes_sources_base
for non specific node-sources data (title, publication date, SEO fields)
nodes_sources_default
for every node-type fields with no group name
nodes_sources_$canonicalNodeTypeFieldName
will be added so that you can enable or disable node-source fields based on their node-type field group
name
Contact form manager
- Switch contact form response to JSON if Accept header is set to
application/json
, no more need to fake a XmlHttpRequest when using window.fetch
to send your form in JS.
Sessions
- Added
Symfony\Bridge\Twig\AppVariable
to handle session, request and user
session
object is deprecated: use app.session
session.user
is deprecated: use app.user
- Session messages are no longer available, you must use new
app
object the same way you would use it in Symfony:
{% for label, messages in app.flashes(['warning', 'error']) %}
{% for message in messages %}
<p class="alert alert-{{ label }}">
{{- message -}}
</p>
{% endfor %}
{% endfor %}
Backoffice
Node-source edition form and tree widget have been improved to hide and display children nodes based on your node-type configuration.
- Children Node Widget now displays only node according to your node-type selection for this field. This goes with new feature below to support cases where you want to create pages nodes inside pages.
- A new node-type parameter
hidingNonReachableNodes
allows node-tree widget to display reachable children node without the non-reachable ones. This is really useful when your tree has pages in pages but your pages also have content blocks, so you need to see your pages tree without being polluted by content blocks.
- Main node / tag / folder tree widgets are not included in backoffice template rendering. This avoids numerous Doctrine queries to build tree graph on plain HTML request.
Markdown documentation generator
Roadiz can now export a documentation boilerplate for all your node-types. It will generate Markdown files for each node-type with a _sidebar.md
file ready to be compiled with Docsify tool.
Attributes
Needs Doctrine migration
- Attributes can be grouped inā¦
AttributeGroup
- Attributes can have documents now
- Nodeā attribute values can be reordered in the back-office (this has been back-ported in Roadiz v1.3 too)
Documents and fonts
Needs Doctrine migration
- Documents can now have one or more thumbnails documents. This is useful to illustrate non-image documents such as PDFs, native videos, archivesā¦
|display
Twig filter will automatically fallback on thumbnail if original document is not displayable.
- Usage tab now supports
Tag
and Attribute
documents, so unused documents view is now taking these usages into account.
- Support Deezer OEmbed, but without any thumbnails.
- Removed deprecated fonts files from
Font
creation form (only keep .woff
and .woff2
files) and added font-display: swap;
into generated font-face
file.
- Upgraded image manipulation library Intervention Request to v3.0.0 with improved performances
Solr
- Moved all Node-source indexing logic to an event-subscriber, even base indexation data so you can override it from your themes. FYI: event is
RZ\Roadiz\Core\Events\NodesSources\NodesSourcesIndexingEvent
- Added
SolrSearchResults
data transfer object to store Solr results and meta. Before we had to query once for the results and once for result count :shushing_face:
SolrSearchResults
implements \Iterator
to make nearly no breaking changes in your Controllers or Twig templates. This object handles your search result hydratation too, so we will be able to override it if you need to hydrate more than NodesSources
or Document
objects.
AbstractSearchHandler::search
and AbstractSearchHandler::searchWithHighlight
methods must now return a SolrSearchResults
. You should check at your custom Solr search handlers in your themes.
- Use regex to check if Solr query is single word
- Solarium deprecations have been addressed
- Improved Back-office search by grouping node-source results by node
- Node-type fields can be flagged in order to exclude them from Solr indexation. This is useful for technical string fields such as layout selector or external API IRI that could be indexed into Solr and pollute search results.
- Made Solr highlighting fragment size configurable via
setHighlightingFragmentSize
Commands
- Refactored all theme-related commands moving generation and path resolution logic away from commands to
RZ\Roadiz\Utils\Theme\ThemeGenerator
- Simplified theme commands: they only need theme name and not its class path anymore
- Added command to delete all documents from a folder:
documents:clear-folder
Translations
- Added new Simplified Chinese backoffice translations. Big up to JerryS
Standard Edition
Roadiz Standard Edition is the repository for creating new projects with Roadiz. With v1.4, we added a complete Docker development environment using Traefik labels. If you are working on Linux, MacOs or Windows this will offer the same experience and the same workspace for all your developers.
Docker images
- Our dedicated production Docker images are now usable in development with just a few modifications that will be built when youāll start a new Standard Edition project.
- For Linux users,
mysql
and solr
images will be built to use the same unix User ID as your current session. This will prevent shared volume files to be locked for your host machine.
crontab
is now running inside your Roadiz Docker image, so you can configure maintenance or custom CLI tasks to be performed without configuring cron
on your production server. Just edit crontab.txt
Other cool stuff on our abstract themesā¦
ApiTheme
AbstractApiTheme
is our middleware theme to expose your Roadiz contents as REST Json API with a simple API-Key+Referrer security. Since Roadiz v1.4 and AbstractApiTheme v2.0 all your node-types will be automatically exposed into /api/1.0/my-node-type
and /api/1.0/my-node-type/{id}
Hydra JSON responses.
AbstractApiTheme will become more important for Roadiz ecosystem in the future. As we want Roadiz to become a Content repository instead of a classic CMS. Exposing its content as API will make them available for frontend frameworks as well as Server-side rendering services. Maybe, one day we wonāt need Twig anymore⦠but for now we love it too much š to give it away.
BlogTheme
AbstractBlogTheme
has been upgraded to take advantage of all Roadiz v1.4 features and with more configuration options.
Migration
Here is a migration dump for this version.
bin/roadiz orm:schema-tool:upgrade --dump-sql --force
should make the changes below:
CREATE TABLE attributes_documents (id INT AUTO_INCREMENT NOT NULL, attribute_id INT DEFAULT NULL, document_id INT DEFAULT NULL, position DOUBLE PRECISION NOT NULL, INDEX IDX_67CCC9E0B6E62EFA (attribute_id), INDEX IDX_67CCC9E0C33F7837 (document_id), INDEX IDX_67CCC9E0462CE4F5 (position), INDEX IDX_67CCC9E0B6E62EFA462CE4F5 (attribute_id, position), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB;
CREATE TABLE attribute_group_translations (id INT AUTO_INCREMENT NOT NULL, attribute_group_id INT DEFAULT NULL, translation_id INT DEFAULT NULL, name VARCHAR(255) NOT NULL, INDEX IDX_5C704A6862D643B7 (attribute_group_id), INDEX IDX_5C704A689CAA2B25 (translation_id), INDEX IDX_5C704A685E237E06 (name), UNIQUE INDEX UNIQ_5C704A6862D643B79CAA2B25 (attribute_group_id, translation_id), UNIQUE INDEX UNIQ_5C704A685E237E069CAA2B25 (name, translation_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB;
CREATE TABLE login_attempts (id INT AUTO_INCREMENT NOT NULL, ip_address VARCHAR(50) DEFAULT NULL, date DATETIME NOT NULL COMMENT '(DC2Type:datetime_immutable)', blocks_login_until DATETIME DEFAULT NULL, username VARCHAR(255) NOT NULL, attempt_count INT DEFAULT NULL, INDEX IDX_9163C7FBF85E0677 (username), INDEX IDX_9163C7FBEFF8A4EEF85E0677 (blocks_login_until, username), INDEX IDX_9163C7FBEFF8A4EEF85E067722FFD58C (blocks_login_until, username, ip_address), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB;
CREATE TABLE attribute_groups (id INT AUTO_INCREMENT NOT NULL, canonical_name VARCHAR(255) NOT NULL, UNIQUE INDEX UNIQ_D28C172A674D812 (canonical_name), INDEX IDX_D28C172A674D812 (canonical_name), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB;
ALTER TABLE attributes_documents ADD CONSTRAINT FK_67CCC9E0B6E62EFA FOREIGN KEY (attribute_id) REFERENCES attributes (id) ON DELETE CASCADE;
ALTER TABLE attributes_documents ADD CONSTRAINT FK_67CCC9E0C33F7837 FOREIGN KEY (document_id) REFERENCES documents (id) ON DELETE CASCADE;
ALTER TABLE attribute_group_translations ADD CONSTRAINT FK_5C704A6862D643B7 FOREIGN KEY (attribute_group_id) REFERENCES attribute_groups (id) ON DELETE CASCADE;
ALTER TABLE attribute_group_translations ADD CONSTRAINT FK_5C704A689CAA2B25 FOREIGN KEY (translation_id) REFERENCES translations (id) ON DELETE CASCADE;
ALTER TABLE node_type_fields ADD exclude_from_search TINYINT(1) DEFAULT '0' NOT NULL;
CREATE INDEX IDX_6FBC9426F5C1A0D77AB0E859 ON tags (parent_tag_id, visible);
ALTER TABLE log ADD channel VARCHAR(255) DEFAULT NULL, ADD additional_data JSON DEFAULT NULL;
CREATE INDEX IDX_8F3F68C5A2F98E47 ON log (channel);
CREATE INDEX IDX_7C7DED6D460D9FD79CAA2B25E0D4FDE1 ON nodes_sources (node_id, translation_id, published_at);
CREATE INDEX IDX_7C7DED6D2B36786BE0D4FDE1 ON nodes_sources (title, published_at);
CREATE INDEX IDX_7C7DED6D2B36786BE0D4FDE19CAA2B25 ON nodes_sources (title, published_at, translation_id);
ALTER TABLE node_types ADD hiding_non_reachable_nodes TINYINT(1) DEFAULT '0' NOT NULL;
CREATE INDEX IDX_409B1BCC5A3C14C7 ON node_types (hiding_non_reachable_nodes);
CREATE INDEX IDX_6E886F1F22010F1462CE4F5 ON tags_translations_documents (tag_translation_id, position);
CREATE INDEX IDX_1D3D05FC9987F3907B00651C ON nodes (node_name, status);
CREATE INDEX IDX_1D3D05FC7AB0E8597B00651C ON nodes (visible, status);
CREATE INDEX IDX_1D3D05FC7AB0E8597B00651C3445EB91 ON nodes (visible, status, parent_node_id);
DROP INDEX IDX_184662BC462CE4F5 ON attribute_values;
CREATE INDEX IDX_184662BCB6E62EFA460D9FD7 ON attribute_values (attribute_id, node_id);
CREATE INDEX IDX_C6B7DA87A58FA485609A56D9 ON translations (available, default_translation);
CREATE INDEX IDX_C6B7DA87A58FA4854180C698 ON translations (available, locale);
ALTER TABLE attributes ADD group_id INT DEFAULT NULL, ADD color VARCHAR(7) DEFAULT NULL;
ALTER TABLE attributes ADD CONSTRAINT FK_319B9E70FE54D947 FOREIGN KEY (group_id) REFERENCES attribute_groups (id) ON DELETE SET NULL;
CREATE INDEX IDX_319B9E7094CD8C0D ON attributes (searchable);
CREATE INDEX IDX_319B9E70FE54D947 ON attributes (group_id);
ALTER TABLE attribute_value_translations DROP FOREIGN KEY FK_1293849BFF82614D;
DROP INDEX IDX_1293849BFF82614D ON attribute_value_translations;
ALTER TABLE attribute_value_translations CHANGE attributevalue_id attribute_value INT DEFAULT NULL;
ALTER TABLE attribute_value_translations ADD CONSTRAINT FK_1293849BFE4FBB82 FOREIGN KEY (attribute_value) REFERENCES attribute_values (id) ON DELETE CASCADE;
CREATE INDEX IDX_1293849BFE4FBB82 ON attribute_value_translations (attribute_value);
CREATE INDEX IDX_1293849B9CAA2B25FE4FBB82 ON attribute_value_translations (translation_id, attribute_value);
CREATE INDEX IDX_1CD104F7AA2D6147705282 ON nodes_sources_documents (ns_id, node_type_field_id);
CREATE INDEX IDX_1CD104F7AA2D6147705282462CE4F5 ON nodes_sources_documents (ns_id, node_type_field_id, position);
ALTER TABLE documents ADD original INT DEFAULT NULL;
ALTER TABLE documents ADD CONSTRAINT FK_A2B072882F727085 FOREIGN KEY (original) REFERENCES documents (id) ON DELETE SET NULL;
CREATE INDEX IDX_A2B072882F727085 ON documents (original);
CREATE INDEX IDX_A2B072881AB3DB55D206C1D1 ON documents (raw, private);
š„