Uploaded image for project: 'eZ Publish / Platform'
  1. eZ Publish / Platform
  2. EZP-29584

As a Maintainer I want multilingual URL Aliases to be archived properly

    XMLWordPrintable

Details

    Description

      When there is multilingual Content with the same name in more than one translation, when the name is changed, only path for one translation is properly archived (a.k.a. "historized"), meaning proper HTTP redirect 30X will happen only for one translation.

      It is caused by an architecture issue in a ezurlalias_ml database table. It has Primary Key set to {{(parent, text_md5), which excludes possibility to set more than one state (e.g. archived, not archived) for more than one language if the name is the same.

      In other words, if we have e.g. name: MyContent, which belongs to e.g. English and Polish translations, once we update that name in e.g. Polish we cannot say - "set this to archived in Polish, but keep as current in English" because that information is stored in a single database row constrained by the mentioned Primary Key. Please note that we can update (and archive) only one translation at a time.

      The most common use case for this feature is the fact that when creating new translation, we can set another one as base translation which by default will create the same name and break possibility to properly archive it.
      Another use case when we could end up with the same Content name in multiple languages is naming Content using proper name of a thing or person it describes.

      Spec

      Solution 1.

      One idea would be to redesign ezurlalias_ml (solving a few of other design issues):

      CREATE TABLE ezurlalias_ml (
        id int NOT NULL AUTO_INCREMENT,
        node_id int NULL, -- for global aliases or multilevel custom ones it will be NULL (previously "nop" action type)
        alias_redirects boolean NOT NULL DEFAULT false,
        is_global boolean NOT NULL DEFAULT false, -- previously "module" action_type
        is_alias boolean NOT NULL DEFAULT false,
        is_original boolean NOT NULL DEFAULT false,
        is_always_available boolean NOT NULL DEFAULT false,
        language_id bigint NOT NULL,
        link_id int NOT NULL DEFAULT 0, -- @todo check if link_id is needed
        parent_id int NOT NULL DEFAULT 0,
        text longtext NOT NULL,
        text_md5 char(32) NOT NULL,
        PRIMARY KEY (id),
        UNIQUE KEY ezurlalias_ml_lang_parent_text (language_id, parent_id, text_md5),
        FOREIGN KEY ezurlalias_ml_node (node_id) REFERENCES ezcontentobject_tree (node_id),
        FOREIGN KEY ezurlalias_ml_lang (language_id) REFERENCES ezcontent_language (id),
        FOREIGN KEY ezurlalias_ml_link (link_id) REFERENCES ezurlalias_ml (id),
        FOREIGN KEY ezurlalias_ml_parent (parent_id) REFERENCES ezurlalias_ml (id),
        -- Indices
        INDEX ezurlalias_ml_alias_parent (is_alias, parent_id),
        INDEX ezurlalias_ml_node_orig_alias (node_id, is_original, is_alias),
        INDEX ezurlalias_ml_node_orig_alias_lang (node_id, is_original, is_alias, language_id),
        INDEX ezurlalias_ml_node_orig_alias_link (node_id, is_original, is_alias, link_id),
        INDEX ezurlalias_ml_node_orig_alias_parent (node_id, is_original, is_alias, parent_id),
        INDEX ezurlalias_ml_node_orig_alias_lang_parent_textmd5 (node_id, is_original, is_alias, language_id, parent_id, text_md5),
        INDEX ezurlalias_ml_global_orig_alias (is_global, is_original, is_alias),
        INDEX ezurlalias_ml_parent_textmd5 (parent_id, text_md5),
        INDEX ezurlalias_ml_parent_textmd5_alias (parent_id, text_md5, is_alias)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
      
      CREATE TABLE ezurlalias_global (
        urlalias_id int NOT NULL,
        action longtext NOT NULL,
        PRIMARY KEY (urlalias_id),
        FOREIGN KEY ezurlalias_global_urlalias (urlalias_id) REFERENCES ezurlalias_ml (id)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
      

      The main disadvantage of this solution is that it introduces major BC break and immediate need for data migration

      Solution 2.

      Another idea would be to move archived auto-generated entries to a separate new table and align with that Gateway internal implementation:

      CREATE TABLE ezurlalias_archive (
        node_id int NOT NULL,
        alias_redirects boolean NOT NULL DEFAULT false,
        is_always_available boolean NOT NULL DEFAULT false,
        language_id bigint NOT NULL,
        link_id int NOT NULL DEFAULT 0,
        parent_id int NOT NULL DEFAULT 0,
        text longtext NOT NULL,
        text_md5 char(32) NOT NULL,
        PRIMARY KEY (language_id, parent_id, text_md5),
        FOREIGN KEY ezurlalias_archive_node (node_id) REFERENCES ezcontentobject_tree (node_id),
        FOREIGN KEY ezurlalias_ml_lang (language_id) REFERENCES ezcontent_language (id),
        FOREIGN KEY ezurlalias_ml_link (link_id) REFERENCES ezurlalias_ml (id),
        FOREIGN KEY ezurlalias_ml_parent (parent_id) REFERENCES ezurlalias_ml (id)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
      

      The advantage of this solution is possibility of introducing it in a BC-safe way, though some simple data migration (e.g incorporated into EZP-29139 command) would be recommended.
      The risk is that while internal implementation can be modified in a BC-safe way, it might become very complex (even more than now). It also might have an impact on a performance.

      Attachments

        Activity

          People

            Unassigned Unassigned
            andrew.longosz@ibexa.co Andrew Longosz
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: