Roadiz is a polymorphic CMS based on a node system which can handle many types of services. This is v1 repository, for v2 and newer releases check https://github.com/roadiz/skeleton.
This version should be the last one using Symfony components and Pimple as a dependency injection container. Last versions pushed forward compatibility with Symfony framework by making all Roadiz components more independant from the DI container. And this release keeps on decoupling our architecture.
If you want to move to our next Roadix major version using Symfony framework, you should upgrade to v1.7 first and resolve any deprecations. Use Controller services instead of using $this->get('service')
or worse $this->container['service']
syntax.
This update requires composer.json changes and performing database migrations, make sure to backup your websites before upgrading.
Roadiz can now send webhook at content update to any location (we provide Gitlab CI and Netlify webhook consumers). Webhooks can be trigger manually, at content update or scoped for any node inside a given root node. This new feature relies on Symfony Messenger and RateLimiter components.
GenericJsonPostMessage
Backoffice theme has been removed from Roadiz main repository and it is a Composer dependency in order to allow using it into next Roadiz versions with Symfony framework. If you are using standard-edition or headless-edition make sure to require it in your project: composer require roadiz/rozier
RozierServiceProvider
EventSubscriber
from backoffice theme back to core because they're essential to Roadiz Core behaviourRZ/Roadiz/Explorer
namespace, make sure to update your project if you use/implement explorersHeadless edition has now a dedicated Docker image to use it with no code at-all! This is handy if you do not need any custom features and a quick start:
https://github.com/roadiz/headless-edition/tree/develop#usage
isCsrfTokenValid(string $id, ?string $token): bool
getSettingsBag(): Settings
dispatchEvent($event)
redirectToRoute($route, array $parameters = [], int $status = 302): RedirectResponse
Psr\Container\ContainerInterface
every time it's possible instead of Pimple\Container
to reuse Roadiz components in Symfony Framework contextNodeTypeField
Serializable fields and NodeType
searchable field to prevent a node-source title to be indexed in Solr.NodeTypeFieldSerializationType
and translation messagesNodeUpdatedEvent
and NodeStatusChangedEvent
are no longer dispatch when changing node status. You must rely on workflow.node.*
events.ManagerRegistry
to replace EntityManager
and allow smoother transition to Symfony framework. Classes constructor signatures may change.Symfony\Component\HttpKernel\KernelInterface
createForm
signature to match Symfony FrameworkContainer
dependency for Models, inject services directly.ObjectManager
when possible instead of EntityManager
- public static $priority = 0;
+ public static int $priority = 0;
- protected static $themeName = '';
+ protected static string $themeName = '';
- protected static $themeAuthor = '';
+ protected static string $themeAuthor = '';
- protected static $themeCopyright = '';
+ protected static string $themeCopyright = '';
- protected static $themeDir = '';
+ protected static string $themeDir = '';
- protected static $backendTheme = false;
+ protected static bool $backendTheme = false;
SettingTypeResolver
to remove Symfony Form types definitions from Entitymeta
as Twig Globals, fixed Install setupstatic_domain_name
database setting, use staticDomainName
in YAML configuration to prevent database access to initialize assets packagesem
service usage as much as possible, i.e. in TreeWidgets. ConsoleCommandsroadiz/document
packagedocuments:prune:orphans
command to remove document from database when no file has been found on file-system (except for embeds)ExceptionSubscriber
to be executed in debug mode and JSON format responsesDocument::isLocal
method to test if filename and folder are not emptycustom_public_scheme
setting for headless projectsDisplayableInterface
RandomImageFinder
service to allow different random image service in your projectAbstractSplashbasePictureFinder
is deprecated as splashbase.co is not available anymoresettingsBag
usage for main configuration, Openid provider throws UsernameNotFoundException
to allow ChainUserProvider to test other user providers.commonmark
v2roadiz/dts-generator
to generate Typescript type definition from your node-types.Roadiz v2 will be a symfony-pack to be added to any existing Symfony 5.3+ application. It will composed of a roadiz/core-bundle
and roadiz/compat-bundle
+ roadiz/rozier-bundle
to ensure compatibility with existing themes (such as our Backoffice theme) in the first time. Then new projects will be able to use App
logic instead of themes and even API-platform.
All Roadiz sub-packages: models
, documents
, entity-generator
, markdown
, nodetype-contracts
, openid
, random
will still be used as framework agnostic package between v1.x and v2.x until v1.x is obsolete.
Our next steps are to migrate latest projects and our most important middleware themes (AbstractBlogTheme, AbstractApiTheme) to this new architecture. This will allow Roadiz to benefit from the awesome Symfony bundle community. Then in the long-term we plan to re-develop our backoffice theme with new user-interface and better features, taking advantage of headless architecture.
Collapsed items won't expand again when you:
Renamed userProvider
service to UserProviderInterface::class
in order to use ChainUserProvider
.
If you theme uses or override userProvider
service, make sure to rename it to UserProviderInterface::class
. Or to append new UserProvider to ChainUserProvider
through userProviders
array service.
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);
CREATE INDEX node_status_parent ON nodes (status, parent_node_id);
CREATE INDEX IDX_1D3D05FC7AB0E8593445EB91 ON nodes (visible, parent_node_id);
CREATE INDEX node_visible_parent_position ON nodes (visible, parent_node_id, position);
ALTER TABLE nodes RENAME INDEX nodetype_id TO node_nodetype_status_parent;
CREATE INDEX tag_parent_visible_position ON tags (parent_tag_id, visible, position);
CREATE INDEX ns_translation_publishedat ON nodes_sources (translation_id, published_at);
This hotfix requires a migration: bin/roadiz migrations:migrate
custom_public_scheme
setting to replace node URL in backoffice with a different domain.mediaDuration
field in Document. Require migration
After many months and long projects, here is Roadiz v1.6 which embodies 3 goals for our team:
But for today, let's list our numerous changes, we need Le table of content…
Roadiz requires php 7.4 minimum
gelf
monolog handler to packages suggestionslcobucci/jwt
4.0.xJwtAccountToken
RavenHandler
spaceless
tag has been deleted and must be replaced with apply spaceless
localizeddate
filter has been deleted and must be replaced with format_datetime
localizednumber
filter has been deleted and must be replaced with format_number
localizedcurrency
filter has been deleted and must be replaced with format_currency
truncate
filter has been deleted and must be replaced with u.truncate
wordwrap
filter has been deleted and must be replaced with u.wordwrap
centralTruncate
filter must be renamed to central_truncate
cache
tag has been removed
allow_h2: true
allow_h3: true
allow_h4: true
allow_h5: true
allow_h6: true
allow_bold: true
allow_italic: true
allow_blockquote: true
allow_image: false
allow_list: true
allow_nbsp: true
allow_return: true
allow_link: true
allow_hr: true
allow_preview: true
Roadiz can handle .env
variables inside your app/conf/config.yml
. We are using the same syntax as Symfony in order to get rid of config.yml
shared volume on production and allow Roadiz to be used as a Kubernetes Pod.
Here is an example of Roadiz configuration using dot-env:
appNamespace: my_website
timezone: Europe/Paris
doctrine:
driver: pdo_mysql
host: '%env(string:MYSQL_HOST)%'
user: '%env(string:MYSQL_USER)%'
password: '%env(string:MYSQL_PASSWORD)%'
dbname: '%env(string:MYSQL_USER)%'
charset: utf8mb4
default_table_options:
charset: utf8mb4
collate: utf8mb4_unicode_ci
inheritance:
type: single_table
themes:
-
classname: \Themes\MyTheme\MyThemeApp
hostname: '*'
routePrefix: ''
cacheDriver:
type: apcu
host: null
port: null
security:
secret: '%env(string:APP_SECURITY_SECRET)%'
session_name: my_website_token
session_cookie_secure: '%env(bool:APP_SECURITY_COOKIE_SECURE)%'
mailer:
type: '%env(string:MAILER_TYPE)%'
host: '%env(string:MAILER_HOST)%'
port: '%env(int:MAILER_PORT)%'
encryption: '%env(default::MAILER_ENCRYPTION)%'
username: '%env(string:MAILER_USER)%'
password: '%env(string:MAILER_PASSWORD)%'
assetsProcessing:
driver: gd
defaultQuality: '%env(int:APP_ASSETS_DEFAULT_QUALITY)%'
maxPixelSize: '%env(int:APP_ASSETS_MAX_PIXEL_SIZE)%'
jpegoptimPath: /usr/bin/jpegoptim
pngquantPath: /usr/bin/pngquant
This is the last time we dump a manual SQL migration in our changelog… promised!
ALTER TABLE user_log_entries CHANGE object_class object_class VARCHAR(191) NOT NULL, CHANGE username username VARCHAR(191) DEFAULT NULL;
RENAME TABLE `groups` TO `usergroups`;
ALTER TABLE groups_roles DROP FOREIGN KEY FK_E79D4963FE54D947;
ALTER TABLE groups_roles ADD CONSTRAINT FK_E79D4963FE54D947 FOREIGN KEY (group_id) REFERENCES usergroups (id);
ALTER TABLE users_groups DROP FOREIGN KEY FK_FF8AB7E0FE54D947;
ALTER TABLE users_groups ADD CONSTRAINT FK_FF8AB7E0FE54D947 FOREIGN KEY (group_id) REFERENCES usergroups (id);
ALTER TABLE usergroups RENAME INDEX uniq_f06d39705e237e06 TO UNIQ_98972EB45E237E06;
…Because we finally added Doctrine Migrations tool for next Roadiz and middleware updates. You'll be able to perform application upgrades with:
bin/roadiz migrations:migrate --allow-no-migration
Only static updates will be migrated with this tool, node-types and node-type fields won't generate any Doctrine Migration because if you create your node-type schema on a production environment this will lead into unsynchronized migrations
table (unless you can commit your file changes from your production server which is non-sense in a Docker environment). We prefer to keep using .json
exports and import. But for any other static databases changes, we invite you to generate migration diffs (filtering ns_
tables):
bin/roadiz migrations:diff --allow-empty-diff --filter-expression="#^(?\!ns_)[^\\s]*\$#"
By default, migration files will be generated in app/migrations
folder unless you specify a namespace. Each registered theme can have its own Migrations/
folder, check at your doctrine migration status to see active namespaces:
bin/roadiz migrations:status
After upgrading Roadiz, Doctrine may complain that one migration is skipped, this is the initialization migration. You can mark it as resolved by adding it to migration table:
# [error] Migration RZ\Roadiz\Migrations\Version20201203004857 skipped during Execution. Reason: "Database has been initialized before Doctrine Migration tool."
# Manually add init migration
bin/roadiz migrations:version --add "RZ\Roadiz\Migrations\Version20201203004857"
Roadiz can now route nodes-sources without any theme registered until you setup all your services, routes, … directly into a custom service-provider registered in your AppKernel.
?_preview=1
will trigger preview any time you need. No more need to use preview.php
entry-point. This may ease up previewing APIs and non-HTML responses.
Of course, security is still enforce if Preview mode is enabled.
BC break: all Roadiz services that depended on Kernel::isPreview()
now depend on PreviewResolverInterface
which allows late-binding and abstracting preview mode logic.
roadiz/abstract-api-theme
for creating a JAM stack and use whatever frontend framework.src
folder, no need to register theme(s), just register your service provider into AppKernel
. Do you see it? We are slowly going to a more standard Symfony app architecture.AutoChildrenNodeSourceWalker
which reads your node-types configuration to build a data-graph against your children node-types fields.roadiz/abstract-api-theme
becomes default Roadiz public endpoint to expose your CMS content as JSON. This a read-only REST API engine which will automatically expose your node-types and will filter them. It uses JSON-LD like syntax and Hydra schema for collections and outputs simple JSON entity for detailled requests. Rezo Zero' TreeWalker is also serialized into detailled requests to be able to build an entire website page using only one API request.
See detailled documentation
Not-inlined SVG documents are now rendered as <img>
for a better compatibility with CSS and object-fit
properties.
<img src="/files/folder/file.svg" />
We are trying to split Roadiz logic into non-dependent modules. In order to acheive this, we need more independent interfaces
to be able to couple loosely components, especially for Entities
relationships. So Roadiz requires now:
RZ\Roadiz\Core\Entities\Translation
to RZ\Roadiz\Core\AbstractEntities\TranslationInterface
.indexAction
signatures must comply with TranslationInterface
All Roadiz form types were written using $options
as a dependency injector 🤦. We now use constructor to inject services and dependencies into AbstractType
classes thanks to RZ\Roadiz\CMS\Forms\Extension\ContainerFormExtension
.
Same refactoring for every ConstraintValidators thanks to ContainerConstraintValidatorFactory
and Psr11.
This update is introducing some BC breaks with existing setups. But we shall keep on this way to allow Roadiz logic to be split into independent components. Roadiz back-office will still be rendered with Twig environment but we expect headless-edition to become the main flavor for creating new frontend projects. We will keep maintaining Twig theming but Roadiz' BaseTheme repository won't be updated frequently (on the JS/CSS side) as our team will focus on headless architecture. Headless-edition will allow more developers and teams to use Roadiz with their favorite front-end framework such as Gatsby or NuxtJS for building API-driven experiences. Even using Netlify-like platforms and static-generated pages.
We hope that this new release will bring Roadiz closer to frontend developers and open up our CMS to many workflows while keeping Roadiz core philosophy: a content-centric approach to building websites.
See you for next release… until this time, give your feedback on our dedicated forum post.
*_txt_{locale}
dynamic fieldsReindex your website content to see changes. This should improve localized searches on Page contents and Tags.
This release is focused on performances and OpenID authentication. After many big websites, we needed to look further into Doctrine and the way we store your nodes content and how we can improve that. Especially when you have to deal with more than 30 node-types, your MySQL server is crying because of too long queries (too many INNER JOIN
). So we let developers choose Doctrine inheritance mapping strategy according to each website needs.
In the same time, we keep on developing our themes and new sidekick libraries in order to use Roadiz as a headless CMS in the next releases.
Roadiz is now supporting OpenID authentication scheme for backoffice users as well as custom authentication for your website visitors.
We chose OpenId because it is based on OAuth2 and JWT and use auto-discovery which make it really easy to configure. You can them choose to attach a Role to all external accounts, or implement your own JwtRoleStrategy
to decide how to authorize your SSO users based on their JWT claims.
Due to many performance limitations using Doctrine class (or joined) table inheritance (too many INNER JOIN when lots of node-types, in fact, one additional inner join for each new node-type), we added the choice, at first install, to switch to Doctrine single-table inheritance. This means that all node-types data will be stored in a unique table with all fields as columns. There are several drawbacks with this mode:
So if you need less than 20 node-types but complex ones and indexed fields, juste stay using joined table inheritance type. If you need to create lots of node-types but with very little fields or with the same fields over and over (excerpt
, content
, …), go for the single table inheritance type.
Sadly, there is no migration tool to switch an existing website to single-table inheritance because we would have to move every data to a single table and lose some during the process.
This strategy is very efficient for querying across all types in the hierarchy or for specific types. No table joins are required, only a WHERE clause listing the type identifiers. In particular, relationships involving types that employ this mapping strategy are very performing.
This strategy inherently requires multiple JOIN operations to perform just about any query which can have a negative impact on performance, especially with large tables and/or large hierarchies.
UrlGeneratorInterface
signature: you cannot generate URL from an object anymore. https://github.com/roadiz/roadiz/issues/365
Twig path
and url
methods still use the same signature to keep the ease of use: {{ path(nodeSource) }}
is internally interpreted as:
$this->get('urlGenerator')->generate(
RouteObjectInterface::OBJECT_BASED_ROUTE_NAME,
array_merge($parameters, [RouteObjectInterface::ROUTE_OBJECT => $nodeSource]),
Router::ABSOLUTE_PATH
);
array
and json
types. This makes these fields searchable using the MySQL 5.7 JSON_
functionsJSON_CONTAINS
verb to build Doctrine simple queries, for example to filter out nodes using a multiple choice field (without needing any relation table and heavy SQL query :$this->get('nodeSourceApi')->getBy([
'node.nodeType' => $this->get('nodeTypesBag')->get(NSBlogPost::class),
'places' => ['JSON_CONTAINS', 'Paris']
])
camelCase
in generated PHP classes. If you use Doctrine filters on these fields, you must convert them from snake_case
to camelCase
.ALTER TABLE newsletter_subscriber DROP FOREIGN KEY FK_401562C322DB1917;
ALTER TABLE newsletter_subscriber DROP FOREIGN KEY FK_401562C37808B1AD;
DROP TABLE newsletter_subscriber;
DROP TABLE newsletters;
DROP TABLE subscribers;
DROP INDEX IDX_409B1BCC845B4F32 ON node_types;
ALTER TABLE node_types DROP newsletter_type;
theme:migrate
command to be able to run it at any Docker image startup. Here is an exemple of ./docker/php74-nginx-alpine/before_launch.sh
script that you could add in your project Docker image:# Fix volume permissions
/bin/chown -R www-data:www-data /var/www/html/files;
/bin/chown -R www-data:www-data /var/www/html/web;
/bin/chown -R www-data:www-data /var/www/html/app;
# This line ensure that your project data structure is always up to date
/usr/bin/sudo -u www-data bin/roadiz themes:migrate -n Awesome
Then you will be able to setup automatic docker container restart with tools like Watchtower
documents:prune:unused
command to delete unused documents from CLI (when more than 200 documents can be too long to prune from web-server)nodes:empty-trash
command to empty deleted nodes from CLI (if it takes too long time to run it from web-server)Combine TreeWalker and AbstractApiTheme and transform Roadiz into a headless CMS.
AbstractApiTheme now supports listing and detail views for each node-types. TreeWalker is a standalone PHP library made to create data graphs from Data type definitions, in other terms, you could create an entire JSON page+blocks graph just by tell him how to fetch children nodes for each node-types. For the moment we are using it to build website navigation (to loop over pages without getting blocks), breadcrumb (just rewind your navigation TreeWalker) and to build pages by looping on inner blocks.
ROLE_ACCESS_LOGS
role to give unit access to CMS logs without needing an administrator accountEquatableInterface
for Roadiz User and OpenIdAccount for better control over authentication and token validationusername
to Roadiz log, in order to track SSO backend users history toobin/roadiz orm:schema-tool:update --dump-sql --force
should give you to following migration:
ALTER TABLE newsletter_subscriber DROP FOREIGN KEY FK_401562C322DB1917;
ALTER TABLE newsletter_subscriber DROP FOREIGN KEY FK_401562C37808B1AD;
CREATE TABLE custom_form_answers_documents (customformfieldattribute_id INT NOT NULL, document_id INT NOT NULL, INDEX IDX_E979F877C84CA2FC (customformfieldattribute_id), INDEX IDX_E979F877C33F7837 (document_id), PRIMARY KEY(customformfieldattribute_id, document_id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB;
ALTER TABLE custom_form_answers_documents ADD CONSTRAINT FK_E979F877C84CA2FC FOREIGN KEY (customformfieldattribute_id) REFERENCES custom_form_field_attributes (id) ON DELETE CASCADE;
ALTER TABLE custom_form_answers_documents ADD CONSTRAINT FK_E979F877C33F7837 FOREIGN KEY (document_id) REFERENCES documents (id) ON DELETE CASCADE;
DROP TABLE newsletter_subscriber;
DROP TABLE newsletters;
DROP TABLE subscribers;
ALTER TABLE documents DROP FOREIGN KEY FK_A2B0728826CBD5A5;
ALTER TABLE documents ADD CONSTRAINT FK_A2B0728826CBD5A5 FOREIGN KEY (raw_document) REFERENCES documents (id) ON DELETE SET NULL;
CREATE INDEX IDX_C6B7DA87A58FA4853F824FD6 ON translations (available, override_locale);
ALTER TABLE log ADD username VARCHAR(255) DEFAULT NULL;
CREATE INDEX IDX_8F3F68C5F85E0677 ON log (username);
CREATE INDEX IDX_7C7DED6D4AD26064 ON nodes_sources (discr);
CREATE INDEX IDX_7C7DED6D4AD260649CAA2B25 ON nodes_sources (discr, translation_id);
CREATE INDEX IDX_7C7DED6DE0D4FDE14AD260649CAA2B25 ON nodes_sources (published_at, discr, translation_id);
DROP INDEX IDX_409B1BCC845B4F32 ON node_types;
ALTER TABLE node_types DROP newsletter_type;
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.
For more detailled questions and feedback, you can use Roadiz dedicated forum: https://ask.roadiz.io/
Needs Doctrine migration
LoginRequest
and LoginReset
so that you can customize it for your public users if you are using AbstractUserTheme
username
and / or ClientIp
if username does not exists
username
, blocking is applyied on username
and ClientIp
not to block the legitimate user against the attackerlogin-attempts:clean
login-attempts:purge
LoggerFactory
to separate log files and channels and easily create custom loggers for your themes matters.RZ\Roadiz\Core\Authentification
namespace to RZ\Roadiz\Core\Authentication
AuthenticationProviderManager
configuration extensible into a new authenticationProviderList
service.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')) %}
hide_title
field will become hideTitle
.getNodeTypeName()
public method in your node-sources classes to prevent using nodeSource.node.nodeType.name
and triggering DB relations if your entity is not fully hydrated. Make sure to run bin/roadiz generate:nsentities
once before using it. This method will be serialized with name: @type
.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 namenodes_sources_$canonicalNodeTypeFieldName
will be added so that you can enable or disable node-source fields based on their node-type field group
nameapplication/json
, no more need to fake a XmlHttpRequest when using window.fetch
to send your form in JS.Symfony\Bridge\Twig\AppVariable
to handle session, request and user
session
object is deprecated: use app.session
session.user
is deprecated: use app.user
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 %}
Node-source edition form and tree widget have been improved to hide and display children nodes based on your node-type configuration.
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.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.
Needs Doctrine migration
AttributeGroup
Needs Doctrine migration
|display
Twig filter will automatically fallback on thumbnail if original document is not displayable.Tag
and Attribute
documents, so unused documents view is now taking these usages into account.Font
creation form (only keep .woff
and .woff2
files) and added font-display: swap;
into generated font-face
file.RZ\Roadiz\Core\Events\NodesSources\NodesSourcesIndexingEvent
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.setHighlightingFragmentSize
RZ\Roadiz\Utils\Theme\ThemeGenerator
documents:clear-folder
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.
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
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.
AbstractBlogTheme
has been upgraded to take advantage of all Roadiz v1.4 features and with more configuration options.
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);
CREATE INDEX IDX_761F9A91FC7ADECE47705282 ON nodes_to_nodes (node_a_id, node_type_field_id);
CREATE INDEX IDX_761F9A91FC7ADECE47705282462CE4F5 ON nodes_to_nodes (node_a_id, node_type_field_id, position);
CREATE INDEX IDX_761F9A91EECF712047705282 ON nodes_to_nodes (node_b_id, node_type_field_id);
CREATE INDEX IDX_761F9A91EECF712047705282462CE4F5 ON nodes_to_nodes (node_b_id, node_type_field_id, position);
CREATE INDEX IDX_7C7DED6DE0D4FDE19CAA2B25 ON nodes_sources (published_at, translation_id);
CREATE INDEX IDX_1D3D05FC7AB0E8593445EB91 ON nodes (visible, parent_node_id);
🥔
We begin 2020 with an awesome Roadiz update. v1.3 will bring many big new features such as versioning, encrypted settings and Symfony 4.4 LTS.
Lots of new features mean some changes to apply in your themes, check the following changelog to upgrade smoothly. Our forum is still available to discuss and share on your upgrade experience.
Parsedown
in favor of thephpleague/commonmark
choices_as_values
form option was removed, check your Form types$form->isValid()
must be called after $form->isSubmitted()
Role
class deprecationDoctrine\Common\Persistence
namespacemerge()
methodintervention-request
libstrict_types
everywhere: pay more attention at your type especially when using native PHP function
with wrong argument types (ie. null
instead of string
with explode()
method).EventDispatcher::dispatch
deprecation when using multiple arguments. See documentation for updating your events.Parsedown
in favor of thephpleague/commonmarkDue to PHP 7.4 and the lack of update from erusev/parsedown-extra
maintainer we decided to switch to
Commonmark
library with a custom made extension for Markdown footnotes support.
In order to anticipate any library change in the future we split the Markdown feature in a dedicated component: roadiz/markdown. This service provides a MarkdownInterface
with text
, textExtra
and line
methods, it’s up to you to wire any Markdown converter to these 3 methods.
Parsedown maintainers have updated parsedown-extra lib since we switched to commonmark. But, guess what, we made roadiz/markdown library extensible so you can revert using parsedown instead of commonmark without changing any code in Roadiz or your theme.
redirectUri
and timestampable fields to control redirection by their dateALTER TABLE redirections ADD created_at DATETIME NOT NULL, ADD updated_at DATETIME NOT NULL, CHANGE redirectUri redirectUri VARCHAR(255) DEFAULT NULL;
CREATE INDEX IDX_38F5ECE48B8E8428 ON redirections (created_at);
CREATE INDEX IDX_38F5ECE443625D9F ON redirections (updated_at);
Roadiz now takes advantage of rezozero/crypto
and paragonie/halite
libraries to perform modern cryptographic operations (this requires lib-sodium which should be available since PHP 7.2).
ALTER TABLE settings ADD encrypted TINYINT(1) DEFAULT '0' NOT NULL;
bin/roadiz generate:private-key
security.private_key_path
with default value: conf/default.key
Be careful to copy and back-up your private key when deploying your website data or your setting values will be lost for ever… ever. If you enabled encryption on a setting while having no private key, data will be saved as a clear string into your database.
We added node-source content versioning on scalar fields (not on relations and children nodes) to be able to view and revert to previous changes. Each node-source has its own history, so reverting changes on a parent node won’t affect its children.
A new DB table is required for holding versioning data:
CREATE TABLE user_log_entries (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, action VARCHAR(8) NOT NULL, logged_at DATETIME NOT NULL, object_id VARCHAR(64) DEFAULT NULL, object_class VARCHAR(255) NOT NULL, version INT NOT NULL, data LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)', username VARCHAR(255) DEFAULT NULL, INDEX IDX_BC2E42C7A76ED395 (user_id), INDEX log_class_lookup_idx (object_class), INDEX log_date_lookup_idx (logged_at), INDEX log_user_lookup_idx (username), INDEX log_version_lookup_idx (object_id, object_class, version), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB ROW_FORMAT = DYNAMIC;
ALTER TABLE user_log_entries ADD CONSTRAINT FK_BC2E42C7A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE SET NULL;
bin/roadiz generate:nsentities
to add Versioned
flag on your node-sources fields.ROLE_ACCESS_VERSIONS
to allowed back-office users to manage versions.Roadiz now stores document file size and image average color for any processable formats.
ALTER TABLE documents ADD filesize INT DEFAULT NULL;
ALTER TABLE documents ADD average_color VARCHAR(7) DEFAULT NULL;
v1.2.14
changes, see https://github.com/roadiz/roadiz/releases/tag/v1.2.14
RZ\Roadiz\Core\SearchEngine\SolariumFactoryInterface
service. Following code:$solarium = new SolariumNodeSource(
$nodeSource,
$this->solr,
$this->dispatcher,
$this->handlerFactory,
$this->logger,
$this->markdown
);
can be replaced by:
/** @var SolariumNodeSource $solarium */
$solarium = $solariumFactory->createWithNodesSources($nodeSource);
You’ll find docker-compose.yml
examples in our standard edition and ready-to-build development docker images based on our production-ready Roadiz stack to develop with docker and get production-ish performances (on Linux and Windows, macOS still sucks with docker shared volumes).
.rzt
format from back-office.font-face
CSS file generated by Roadiz
phpstan
to run static analyse with Travis CI for a better maintenance.sentry/sentry
Monolog handler, make sure to require these librairies before using it in your configuration file: composer require sentry/sentry php-http/curl-client guzzlehttp/psr7
ALTER TABLE redirections ADD created_at DATETIME NOT NULL, ADD updated_at DATETIME NOT NULL, CHANGE redirectUri redirectUri VARCHAR(255) DEFAULT NULL;
CREATE INDEX IDX_38F5ECE48B8E8428 ON redirections (created_at);
CREATE INDEX IDX_38F5ECE443625D9F ON redirections (updated_at);
ALTER TABLE settings ADD encrypted TINYINT(1) DEFAULT '0' NOT NULL;
ALTER TABLE documents ADD filesize INT DEFAULT NULL;
ALTER TABLE documents ADD average_color VARCHAR(7) DEFAULT NULL;
CREATE TABLE user_log_entries (id INT AUTO_INCREMENT NOT NULL, user_id INT DEFAULT NULL, action VARCHAR(8) NOT NULL, logged_at DATETIME NOT NULL, object_id VARCHAR(64) DEFAULT NULL, object_class VARCHAR(255) NOT NULL, version INT NOT NULL, data LONGTEXT DEFAULT NULL COMMENT '(DC2Type:array)', username VARCHAR(255) DEFAULT NULL, INDEX IDX_BC2E42C7A76ED395 (user_id), INDEX log_class_lookup_idx (object_class), INDEX log_date_lookup_idx (logged_at), INDEX log_user_lookup_idx (username), INDEX log_version_lookup_idx (object_id, object_class, version), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB ROW_FORMAT = DYNAMIC;
ALTER TABLE user_log_entries ADD CONSTRAINT FK_BC2E42C7A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE SET NULL;
ALTER TABLE fonts CHANGE created_at created_at DATETIME DEFAULT NULL, CHANGE updated_at updated_at DATETIME DEFAULT NULL;
ALTER TABLE tags CHANGE created_at created_at DATETIME DEFAULT NULL, CHANGE updated_at updated_at DATETIME DEFAULT NULL;
ALTER TABLE subscribers CHANGE created_at created_at DATETIME DEFAULT NULL, CHANGE updated_at updated_at DATETIME DEFAULT NULL;
ALTER TABLE folders CHANGE created_at created_at DATETIME DEFAULT NULL, CHANGE updated_at updated_at DATETIME DEFAULT NULL;
ALTER TABLE translations CHANGE created_at created_at DATETIME DEFAULT NULL, CHANGE updated_at updated_at DATETIME DEFAULT NULL;
ALTER TABLE users CHANGE created_at created_at DATETIME DEFAULT NULL, CHANGE updated_at updated_at DATETIME DEFAULT NULL;
ALTER TABLE newsletters CHANGE created_at created_at DATETIME DEFAULT NULL, CHANGE updated_at updated_at DATETIME DEFAULT NULL;
ALTER TABLE custom_forms CHANGE created_at created_at DATETIME DEFAULT NULL, CHANGE updated_at updated_at DATETIME DEFAULT NULL;
ALTER TABLE nodes CHANGE created_at created_at DATETIME DEFAULT NULL, CHANGE updated_at updated_at DATETIME DEFAULT NULL;
ALTER TABLE documents CHANGE created_at created_at DATETIME DEFAULT NULL, CHANGE updated_at updated_at DATETIME DEFAULT NULL;