Drupal 10 compatibility JS aggregation causes js error with aggregation enabled

Created on 5 July 2023, over 1 year ago
Updated 29 July 2024, 5 months ago

There is UI and functionality issue come when aggregation enabled

download H5P 2.x and apply patch for Drupal 10 combability and make sure "admin/config/development/performance" aggregation enabled.

May be this issue come with jQuery version 2.x using jQuery v1.9.1 need to upgrade jQuery 3.x or higher version with Drupal 10 compatibility

After enable aggregation js/css not apply into iframe content

🐛 Bug report
Status

Fixed

Version

2.0

Component

Code

Created by

🇮🇳India ajeet_kumar gurugaon

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @ajeet_kumar
  • 🇪🇪Estonia pjotr.savitski

    The assumption about this having something to do with jQuery is incorrect as H5P uses custom versions of everything, at least within internal iframes.

    This is the issue of createCachedPublicFiles method of the H5PDrupal class.

    This line will throw an error as the second argument is required (at least with PHP 8.1):

    $cachedAsset = $optimizer->optimize($assets);
    

    The logic tells that it should be changed to:

    $cachedAsset = $optimizer->optimize($assets, []);
    

    Which fixes the exception being thrown, but the end result is not what it is expected to be. This line will fail as there is no data key or it is empty, instead items are set and those point to the original files provided:

    return array_map(function($publicUrl){ return \Drupal::service('file_url_generator')->generateAbsoluteString($publicUrl); }, array_column($cachedAsset, 'data'));
    

    I'm not sure about the inner workings of the new JsCollectionOptimizerLazy class (or the one before that) and why this new lazy approach and creation of those files at the first request fails, but this is what is causing the issue. I've tried proving a library name and that gets the expected data key returned, but the files themselves are never created and the logger reports The libraries to include are encoded incorrectly.

    My guess is that something has changed and the data provided for optimization is formatted incorrectly, though I see no documentation on how that should be formatted or what changed with the move to the new lazy approach.

    The only really quick fix is to always disregard the JS and CSS preprocess configuration option, but that makes the whole optimization step meaningless. Just change the aggregateAssets method from this (this still keeps aggregation functional for everything else but the H5P):

      $jsAssetConfig = ['preprocess' => $systemPerformance->get('js.preprocess')];
      $cssAssetConfig = ['preprocess' => $systemPerformance->get('css.preprocess'), 'media' => 'css'];
    

    to this:

      $jsAssetConfig = ['preprocess' => FALSE];
      $cssAssetConfig = ['preprocess' => FALSE, 'media' => 'css'];
    
  • 🇪🇪Estonia pjotr.savitski

    I've checked the code and it seems that individual files are not really processed in any way, at least not with the type of file, unless at least one library is provided. This more or less means that the whole optimization code is useless, unless libraries are used, which is not really applicable in this case.

    Not sure is this is the new internal logic or not, but providing any library prevents the aggregated file from being created. The other issue is that there is no cache busting logic present like it used to be with the previous approach and recreation of the combined file using fresh versions of the resources each time new cache file is generated.

  • 🇪🇪Estonia pjotr.savitski

    It seems that a few modifications can help with getting the old way of dealing with assets running again. It requires redefining and using old versions of collection optimizer services. A few modifications to the current code would also be required as there are some changes in how new approach works, including changes to the dependencies (mostly affects the CSS, at least it was like that for me).

    First, a h5p.services.yml file needs to be added, having copies of services from Drupal version 9.5 with a legacy suffix:

    services:
      asset.css.collection_optimizer_legacy:
        class: Drupal\Core\Asset\CssCollectionOptimizer
        arguments: [ '@asset.css.collection_grouper', '@asset.css.optimizer', '@asset.css.dumper', '@state', '@file_system' ]
      asset.js.collection_optimizer_legacy:
        class: Drupal\Core\Asset\JsCollectionOptimizer
        arguments: [ '@asset.js.collection_grouper', '@asset.js.optimizer', '@asset.js.dumper', '@state', '@file_system' ]
    

    Secondyl H5PDrupal.php file has to be patched with changes to aggregatedAssets and createCachedPublicFiles methods:

    /**
       * Aggregate assets.
       *
       * @param array $scriptAssets
       *   JavaScript assets.
       * @param array $styleAssets
       *   Stylesheet assets.
       */
      public static function aggregatedAssets($scriptAssets, $styleAssets) {
        $jsOptimizer = \Drupal::service('asset.js.collection_optimizer_legacy'); // NB! Different service being used
        $cssOptimizer = \Drupal::service('asset.css.collection_optimizer_legacy'); // NB! Different service being used
        $systemPerformance = \Drupal::config('system.performance');
        $jsAssetConfig = ['preprocess' => $systemPerformance->get('js.preprocess')];
        $cssAssetConfig = ['preprocess' => $systemPerformance->get('css.preprocess'), 'media' => 'all']; // NB! Value of "media" changed from "css" to "all"
        $assets = ['scripts' => [], 'styles' => []];
        foreach ($scriptAssets as $jsFiles) {
          $assets['scripts'][] = self::createCachedPublicFiles($jsFiles, $jsOptimizer, $jsAssetConfig);
        }
        foreach ($styleAssets as $cssFiles) {
          $assets['styles'][] = self::createCachedPublicFiles($cssFiles, $cssOptimizer, $cssAssetConfig);
        }
        return $assets;
      }
    
      /**
       * Combines a set of files to a cached version, that is public available
       *
       * @param string[] $filePaths
       * @param AssetCollectionOptimizerInterface $optimizer
       * @param array $assetConfig
       *
       * @return string[]
       */
      private static function createCachedPublicFiles(array $filePaths, $optimizer, $assetConfig) {
        $assets = [];
    
        $defaultAssetConfig = [
          'type' => 'file',
          'group' => 'h5p',
          'cache' => TRUE,
          'attributes' => [],
          'version' => NULL,
          'browsers' => [],
          'license' => FALSE, // NB! Missing license key added to prevent missing key notices
        ];
    
        foreach ($filePaths as $index => $path) {
          $path = explode('?', $path)[0];
    
          $assets[$path] = [
            'weight' => count($filePaths) - $index,
            'data' => $path,
          ] + $assetConfig + $defaultAssetConfig;
        }
        $cachedAsset = $optimizer->optimize($assets, []);
    
        return array_map(function($publicUrl){ return \Drupal::service('file_url_generator')->generateAbsoluteString($publicUrl); }, array_column($cachedAsset, 'data'));
      }
    

    This seems to be making everything work as expected, at least my testing material looks the way it used to and has no JS errors. It might be able to work until Drupal version 11.

    The best approach would be to use the new logic and somehow dynamically define a library for each of the material, thus making the new lazy approach work as expected. One way would be to pass a library called h5p-content- and then dynamically define that library with the hook if the current context is one of the controllers for JS and CSS asset optimizers and the query string has some custom keys present. There might be a better approach, but this is what has come to my mind. Another possibility would be to mimic the new lazy approach and have a custom controllers that create optimized asset files or serve already existing ones.

  • 🇪🇪Estonia pjotr.savitski

    Here is the patch that uses old versions of JS and CSS optimisation services with a few additional fixes for related problems of missing keys and warnings.

  • Status changed to RTBC over 1 year ago
  • 🇺🇦Ukraine o_timoshchuk

    +1 RTBC I applied patch #6 with the Drupal 10 compatibility patch. It is functioning correctly.

  • 🇮🇳India StanleyFernandes Goa

    Patch #6 failed with the latest D10 compatibility patch i.e
    https://www.drupal.org/project/h5p/issues/3329297#comment-15218847 📌 Automated Drupal 10 compatibility fixes Needs review

    With the help of patch #6 created a new patch.
    Tested on Drupal 10.1.5 working as expected.

  • 🇫🇮Finland anaconda777

    This #8 patch does not apply with the latest dev.
    And with 2.0.0-alpha3 I am not able to get it working with D10 properly, cos I dont know which patches it is missinge compared to dev?

  • 🇬🇧United Kingdom SirClickALot Somerset

    I can confirm too that, when using the latest dev version "drupal/h5p": "2.0.x-dev@dev", that most, H5P creations DO NOT render when JavaScript aggregation is active.

    By contrast to @Anaconda777 's comment, mine DO all seem to work with CSS aggregation active; it is only the JavaScript aggregation.

  • 🇩🇪Germany ammaletu Bonn, Germany

    Against the current dev version patch #6 almost works. The changes in src/H5PDrupal/H5PDrupal.php in line 88, 89 and 94 don't apply, I think. After I changed these lines manually, displaying the H5P content worked again. Patch #8 contains a lot of the changes to get H5P to work with Drupal 10, which are in some way already part of the dev version, soon to be tagged as alpha4.

  • Status changed to Needs work about 1 year ago
  • 🇬🇧United Kingdom catch

    These needs a re-roll.

  • Status changed to Needs review about 1 year ago
  • 🇩🇰Denmark googletorp

    Rerolled the patch from #8 by manual comparing what was required.

  • 🇫🇮Finland anaconda777

    @googletrop Thanks, that patch applies but now when viewing H5P content I get wsod and this:

    Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: You have requested a non-existent service "asset.js.collection_optimizer_legacy". Did you mean one of these: "asset.css.collection_optimizer", "asset.js.collection_optimizer"? i Drupal\Component\DependencyInjection\Container->get() (rad 157 av /var/www/html/drupal10/web/core/lib/Drupal/Component/DependencyInjection/Container.php).

    I have Drupal 10.1.6

    Without this patch there is not error message, it just does not show the H5P content when js aggregation is on

  • Status changed to Needs work about 1 year ago
  • 🇬🇧United Kingdom catch

    The reroll is missing the two legacy services added in the previous patches.

  • 🇺🇸United States SocialNicheGuru

    Now that 📌 Automated Drupal 10 compatibility fixes Needs review has landed, the patch #6 should be re-rolled.

  • Status changed to Needs review about 1 year ago
  • 🇫🇮Finland jhuhta

    This is a reroll based on #8.

  • Status changed to Fixed about 1 year ago
  • 🇬🇧United Kingdom catch

    Committed/pushed to 2.0.x so that this change is available with the dev release. Thanks!

  • Status changed to Needs work about 1 year ago
  • 🇬🇧United Kingdom catch

    Actually no this isn't good either, it makes the H5P module only compatible with Drupal 10.1, not 9.5 or 10.0.x, because the legacy classes don't exist in those versions. Also using the legacy services is a stopgap anyway.

    Two options:

    1. Define the services in a service provider instead of YAML so it can check for the class existence first. Then in the actual logic, check the Drupal core version and only use the legacy services if we're on 10.1.x or higher. This adds more complexity and is still a stopgap.

    2. Refactor this entirely to either not aggregate, or to not use the the Drupal core aggregation API at all and just concatenate the files together instead - or at least use more targeted bits of the API.

  • 🇫🇮Finland anaconda777

    can confirm, patch #17 works with D10.1.6 and latest H5P dev

    Thanks / kiitos

  • Status changed to Fixed about 1 year ago
  • 🇬🇧United Kingdom catch

    I was wrong in #20, because the 'legacy' classes are just the old one shipped with 9.5/10.0, then this approach works fine as a stopgap and should be compatible with both 9.5/10.0/10.1.

    I opened 📌 Refactor asset aggregation to avoid using deprecated asset optimization classes Active to refactor things in time for Drupal 11 compatibility though. Moving this issue back to fixed.

  • Automatically closed - issue fixed for 2 weeks with no activity.

  • Status changed to Fixed 5 months ago
  • 🇨🇦Canada maursilveira Windsor, ON

    Hello,

    I'm experiencing this issue even after applying the patch #17. I'm getting a Uncaught TypeError: H5P.EventDispatcher is undefined, most likely coming from one of the content libraries located in the public folder (/sites/default/files/h5p/libraries).

    Any ideas or recommendations? Any help is much appreciated!

    Thank you!

  • 🇨🇦Canada maursilveira Windsor, ON

    Hello,

    One thing I noticed that I believe can be the cause of the error I'm having is that the content libraries, located in /sites/default/files/h5p/libraries, are being loaded before the main libraries h5p.js and jquery.js, located in the vendor directory in the module folder, ando also before the file h5p-event-dispatcher.js, which I believe is where the aforementioned function is defined and constructed. Please see the attached screenshot.

    Shouldn't the main libraries be loaded before, in order to ensure all the H5P core functionality is properly defined? Is this an issue you have experienced before? Any recommendations on how to solve this?

    Please let me know if this sounds to you a different issue, and I can open a new one.

    Thank you!

  • 🇨🇦Canada maursilveira Windsor, ON

    For visibility, I opened a new task https://www.drupal.org/project/h5p/issues/3464434 🐛 Errors due to content libraries being loaded before main H5P libraries Active .

    Thank you

Production build 0.71.5 2024