Created on 27 May 2025, 12 days ago

Problem/Motivation

When upgrading from 2.x -> 3.x extra settings (title_format, title_fallback) are added but we do not update existing content, leading to broken embeds that just output the raw text.

Steps to reproduce

  1. Install ckeditor5 and video_embed_field 2.x
  2. Set ckeditor5 as the text editor for an available format at /admin/config/content/formats
  3. Add the "Video Embed" button to the toolbar
  4. Create a new piece of content using the video embed tool
  5. Upgrade video_embed_field to 3.x
  6. Clear cache
  7. View content, note raw output

Proposed resolution

Provide an upgrade path that checks for text, text_long, or text_with_summary field configuration on nodes, paragraphs, and block_content. Then, check those fields in the database for video_embed_wysiwyg usage and add the missing configuration to the settings.

🐛 Bug report
Status

Needs review

Version

3.0

Component

Video Embed WYSWIYG

Created by

🇨🇦Canada cchiste

Live updates comments and jobs are added and updated live.
  • Upgrade path

    It affects the ability to upgrade Drupal sites to a new major version. Preferred over version-specific tags such as D7 upgrade path.

Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @cchiste
  • Merge request !84Resolve #3526845 "Upgrade path" → (Open) created by cchiste
  • Pipeline finished with Success
    12 days ago
    Total: 173s
    #507787
  • Pipeline finished with Success
    12 days ago
    Total: 223s
    #507795
  • Pipeline finished with Success
    12 days ago
    Total: 190s
    #507798
  • Pipeline finished with Success
    12 days ago
    Total: 171s
    #507802
  • I've made another update.
    - a bit cleaner,
    - very fast,
    - covers all multiline text fields and db revisions
    - does one database table per pass (not tested on that)
    - converts settings to json instead of manipulating strings
    Shouldn't do any harm, at least.

    <?php
    
    /**
     * @file
     * Install file for Video Embed WYSIWYG.
     */
    const VIDEO_EMBED_WYSIWYG_UPDATE_REGEX = '\{"preview_thumbnail"[^}]*"video_url"[^}]*"settings":\{[^}]*\}[^}]*\}';
    
    /**
     * Add title_format setting to video embed settings.
     */
    function video_embed_wysiwyg_update_10001(&$sandbox) {
      $database = Drupal::database();
      if (!isset($sandbox['embed_node_tables'])) {
        foreach (\Drupal::service('entity_field.manager')->getFieldMap() as $entity_type => $fields) {
          foreach ($fields as $field_name => $field_info) {
            if ($field_info['type'] == 'text_long' or $field_info['type'] == 'text_with_summary') {
              $sandbox['embed_node_tables'][$entity_type.'__'.$field_name] = 0;
              $sandbox['embed_node_tables'][$entity_type.'_revision__'.$field_name] = 0;
              $sandbox['finished'] = 0;
            }
          }
        }
      }
      $table_name = key($sandbox['embed_node_tables']);
      // Do one table each pass.
      video_embed_wysiwyg_update_text($table_name, $sandbox['embed_node_tables']);
    }
    
    /**
     * Updates up to 999 fields. Very fast.
     *
     * @param string $table_name
     * @param int $last_updated_id
     *   updated by reference
     */
    function video_embed_wysiwyg_update_text($table_name, array &$tables) {
      $last_updated_id = &$tables[$table_name];
      $col = substr($table_name, strpos($table_name, '__') + 2) . '_value';
      $cols = ['entity_id', $col];
      if (str_contains($table_name, '_revision__')) {
        $cols[] = 'vid';
      }
      $field_values = $database->select($table_name, 't')
        ->fields('t', )
        ->where($col . ' REGEXP :pattern', [':pattern' => VIDEO_EMBED_WYSIWYG_UPDATE_REGEX])
        ->condition('t.entity_id', $last_updated_id, '>')
        ->orderby('entity_id', 'DESC')
        ->execute()->fetchAllAssoc('entity_id');
    
      $count = 0;
      while ($res = array_pop($field_values)) {
        $count++;
        $id = $res->entity_id;
        $orig_text = $res->{$col};
        $replace_text = $orig_text;
        if (preg_match_all('/'.$regex_pattern.'/', $orig_text, $matches)) {
          foreach ($matches[0] as $match) {
            $embed_data = json_decode($match, TRUE);
            $embed_data['settings'] += ['title_fallback' => TRUE, 'title_format' => '@provider | @title'];
            $replace_text = str_replace($match, json_encode($embed_data, JSON_UNESCAPED_SLASHES), $replace_text);
          }
        }
        if ($replace_text and strlen($replace_text) > strlen($orig_text)) {
          $q= $database->update($table_name)
            ->fields([$col => $replace_text]);
          if (str_contains($table_name, '_revision__')) {
            $q->condition('vid', $last_updated_id = $res->vid);
          }
          else {
            $q->condition('entity_id', $last_updated_id = $res->entity_id);
          }
          $q->execute();
        }
        $tables[$table_name] = $last_updated_id;
        if ($count > 999) break;
      }
      if (empty($field_values)) {
        unset($tables[$table_name]);
        $sandbox['finished'] = empty($tables);
      }
    }
    
  • 🇫🇷France mably

    Shouldn't the rendering code also be fixed to avoid rendering raw content? 🤔

  • can you say a bit more about that?

  • 🇫🇷France mably

    Can't we just use some default value when the new configuration is missing?

  • 🇨🇦Canada cchiste

    @matslats Thank you for the improvement suggestions! I tried running this locally and noticed a couple of issues.

    • No `\` on Drupal for initial assignment of `$database` to `Drupal::database()
    • `$database` is not defined in `video_embed_wysiwyg_update_text`

    @mably I might be misunderstanding, but I believe we are providing the default value (as far as Video Embed Field goes) in the following. Let me know if I am off base though.
    ```
    // Check if title_format already exists.
    if (!preg_match('/"title_format"/', $settings)) {
    // Add title_format setting.
    $title_format = '"title_format":"@provider | @title"';
    $title_fallback = '"title_fallback":true';

    // If settings is empty, just add the title_format.
    if (trim($settings) === '') {
    $updated_settings = $title_format . ',' . $title_fallback;
    }
    else {
    // Add title_format at the end.
    $updated_settings = $settings . ',' . $title_format . ',' . $title_fallback;
    }

    \Drupal::logger('video_embed_wysiwyg')->notice('Added title_format and title_fallback to settings: @old -> @new', [
    '@old' => $settings,
    '@new' => $updated_settings,
    ]);

    return '"settings":{' . $updated_settings . '}';
    }
    ```

  • @mably That approach also seems valid, and would require less upgrade code, but its also cleaner in the long term to update the old data so that all the data managed by the module is consistent. So its up to the maintenance team to decide.

Production build 0.71.5 2024