Account created on 24 February 2010, over 15 years ago
#

Merge Requests

More

Recent comments

🇪🇸Spain isholgueras

This project is currently configuring XB under Drupal CMS: https://github.com/phenaproxima/xb-demo

To have XB working, you need to add a specific xb field type component_tree under article or xb_page. No other content types are currently supported, and this project does that.

Here is the ticket where the conversation about supporting other content types is happening: 📌 Allow XB to be used on all node types Active

🇪🇸Spain isholgueras

If the issue is the

          $component = Component::load($component_instance['component']);
          assert($component instanceof Component);

Could we change only this assert with a regular if if we're in preview? Something like this draft code:

          $component = Component::load($component_instance['component']);
          // Avoid asserts in preview with zend.assertions=1 to improve message to users.
          if (!$isPreview) {
            assert($component instanceof Component);
          }
          elseif (!$component instanceof Component) {
            // Or anything else.
            throw new ComponentNotFoundException($component_instance['component'] . ' doesn\'t exist.');
          }

I'm probably missing something but that's the only assert we need to sort, right?

🇪🇸Spain isholgueras

After reviewing both MR, I think I feel mor comfortable with MR 1351 (ignoring changed). https://git.drupalcode.org/project/experience_builder/-/merge_requests/1351.

It doesn't require any front-end changes but it would probably be good idea for the front-end to also stop sending changed but it could be in follow-up

Agree, but I think you're covering it well by ignoring changed.

🇪🇸Spain isholgueras

After debugging a bit, I think it's because of this piece of code in ClientDataToEntityConverter

https://git.drupalcode.org/issue/experience_builder-3536247/-/blob/35362...

      if ($entity instanceof EntityChangedInterface && $name === 'changed') {
        $changed_timestamp = $items->first()?->get('value');
        assert($changed_timestamp instanceof Timestamp);
        $changed_timestamp_int = $changed_timestamp->getCastedValue();
        assert(is_int($changed_timestamp_int));
        $form_updated_changed_field = $changed_timestamp_int !== ((int) $entity_form_fields['changed']);
      }
    }

    assert(!is_null($entity->id()));
    $original_entity = $this->entityTypeManager->getStorage($entity->getEntityTypeId())->loadUnchanged($entity->id());
    assert($original_entity instanceof FieldableEntityInterface);
    // Filter out form_build_id, form_id and form_token.
    $entity_form_fields = array_filter($entity_form_fields, static fn (string|int $key): bool => is_string($key) && $entity->hasField($key), ARRAY_FILTER_USE_KEY);
    // Copied from \Drupal\jsonapi\Controller\EntityResource::updateEntityField()
    // but with the additional special-casing for `changed`.
    foreach ($entity_form_fields as $field_name => $field_value) {
      \assert(\is_string($field_name));
      if ($field_name === 'changed' && $form_updated_changed_field) {
        continue;
      }
      try {
        $original_field = $original_entity->get($field_name);

There is a case when the $form_updated_changed_field value is not TRUE so is set to the original field, and the opposite. I still working on how to reproduce it reliably.

🇪🇸Spain isholgueras

I've left some minor comments. Overall, It looks good to me though

🇪🇸Spain isholgueras

We decided that we're going to validate only the entities ($entity->validate) right before the $entity->save() to still validate Constraints and don't execute the form validate.

I'll work on that and adapt the tests.

🇪🇸Spain isholgueras

Revert issue title thanks to that PathAlias is only validating on form submit.

🇪🇸Spain isholgueras

I've updated the title to surface the real issue.

🇪🇸Spain isholgueras

It's not a beta blocker, but it's pretty useful to help with testing different MRs functionality

It was just requiring default_content.

🇪🇸Spain isholgueras

I've left a couple of comments in Gitlab in case something is missing.

Everything else looks good to me

🇪🇸Spain isholgueras

I've created the StagedConfigUpdateValidationTest and moved the testValidations.

@wim, @ted, Is there anything else you wanted to validate here?

🇪🇸Spain isholgueras

I was wondering...
- If that's related with some kind of race condition with these code components
- If these components must render only in the client (not in SSR) (maybe with client:only="react"
- If we should detect/configure which code components have this client:only

BTW, while testing this I think I've reached the limit of nested code components.

I had 18 nested code components (with slot and prop), and it breaks in the 19th.
"Undo last action" worked though.

🇪🇸Spain isholgueras

I think that this line: https://git.drupalcode.org/project/experience_builder/-/merge_requests/1... from 🐛 User is able to create pages with same URL alias Active will report correctly all those 500 errors to the client because in this other line: https://git.drupalcode.org/project/experience_builder/-/merge_requests/1..., there is catching the violations and returning a proper JsonResponse.

Imho, we could postpone this issue, or include this issue into 🐛 User is able to create pages with same URL alias Active scope.

🇪🇸Spain isholgueras

I was able to replicate that yesterday while working on 🐛 User is able to create pages with same URL alias Active . I think the transaction doesn't start, but there is no JsonResponse with the error/exception returned, and the "2 Errors" with a dropdown appeared, but without message.

🇪🇸Spain isholgueras

I can confirm that's an issue.

In ApiAutoSaveController::post we're validating the entities to be saved against the existing entities, but not against each other.

IMHO, that should trigger an exception and rollback the saves

    // Either everything must be published, or nothing at all.
    try {
      $transaction = $this->database->startTransaction();
      foreach ($entities as $entity) {
        $entity->save();
        $this->autoSaveManager->delete($entity);
      }
    }
    catch (\Exception $e) {
      if (isset($transaction)) {
        $transaction->rollBack();
      }
      Error::logException($this->logger, $e);
      throw $e;
    }

I think that's not a data loss, but could it be close to?

🇪🇸Spain isholgueras

I've set

SYMFONY_DEPRECATIONS_HELPER=max[total]=99999

and it doesn't work either.

🇪🇸Spain isholgueras

I've configured it to just have deprecations we have in experience_builder.
I've also disabled the fail on deprecations with:
- SYMFONY_DEPRECATIONS_HELPER= "disabled&ignoreFile=$DRUPAL_PROJECT_FOLDER/.phpunit-deprecation-ignore.txt" (see more)
- PHPUNIT_FAIL_ON_PHPUNIT_DEPRECATION=0: (see more)

But the job is still failing. Any idea?

🇪🇸Spain isholgueras

I've tested it in this branch and in 0.x. In 0.x can reproduce the issue but in this branch the user is there. Even I created a new Content Editor user (nacho) with the right permissions and works well.

The code looks good to me too, so I think it's RTBC.

🇪🇸Spain isholgueras

… but let's land !1204 first, that is much harder to reroll.

Sure! feel free to throw it back to me if we need a reroll here.

🇪🇸Spain isholgueras

This is ready with all tests green. Let me know if we can create a follow-up ticket for tests or if we should include here the tests

🇪🇸Spain isholgueras

Thanks both for your feedback! I know exactly what needs to be done.

🇪🇸Spain isholgueras

@effulgentsia,

I'm missing something in your approach that is making me going back and forth in the ticket. I've pushed some code to help me explain that.

What you're proposing is to modify what is coming in the img.src with the url to the image itself, without any style, and as a query parameters alternateWidths send only the template (with the {width} and the itok) so the ui can react and modify.

The decoded url you've written is

    // /sites/default/files/foo.jpg?alternateWidths=/sites/default/files/styles/xb_parameterized_width--{width}/public/foo.jpg.webp?itok=1Rl59WAb

Is that exactly that we want to send in the src=""

And for srcset what we want to generate is a full list of allowed widths? Something like:

<img class="image" src="/sites/default/files/2025-07/2025-07-02_17-30.png" srcset="/sites/default/files/styles/xb_parameterized_width--640/public/2025-07/3534165-6_2.png 640w, /sites/default/files/styles/xb_parameterized_width--750/public/2025-07/3534165-6_2.png 750w, /sites/default/files/styles/xb_parameterized_width--828/public/2025-07/3534165-6_2.png 828w, /sites/default/files/styles/xb_parameterized_width--1080/public/2025-07/3534165-6_2.png 1080w, /sites/default/files/styles/xb_parameterized_width--1200/public/2025-07/3534165-6_2.png 1200w, /sites/default/files/styles/xb_parameterized_width--1920/public/2025-07/3534165-6_2.png 1920w, /sites/default/files/styles/xb_parameterized_width--2048/public/2025-07/3534165-6_2.png 2048w, /sites/default/files/styles/xb_parameterized_width--3840/public/2025-07/3534165-6_2.png 3840w" alt="Phandalin" width="1487" height="1003" sizes="auto 100vw" loading="lazy">

I still don't get what is the HTML we want to return for this, sorry :(

🇪🇸Spain isholgueras

We talked about this issue in our last team meeting. While we agree it's a real problem, we decided not to move forward with it right now. Instead of fixing it specifically for Pathauto, we’d like to step back and think about a more general approach that could work for other similar cases too.

Marking this as postponed until we revisit it with a broader solution in mind.

🇪🇸Spain isholgueras

AFAIK, This is the default behavior and it comes from Core:
https://git.drupalcode.org/project/drupal/-/blob/11.x/core/lib/Drupal/Co...

Here I have 11 Terms in this vocab and the 11th only appears if I autocomplete for "Term1"

IMO, this works as designed (in drupal core).

🇪🇸Spain isholgueras

Is this still an issue? The dropdown is shown correctly. Or there is another issue that I'm missing?


This is tested in 0.x on July 7th.

🇪🇸Spain isholgueras

Rebase done and tests moved fixed, but a $this->drupalLogout() from a previous tests is failing because Mink can't find "op" button.

https://git.drupalcode.org/project/experience_builder/-/jobs/5801240#L976

After inspecting the HTML right there, the user is logged in and there is a link for logout.

        <ul>
          <li>
            <a href="/user" data-drupal-link-system-path="user">My account</a>
          </li>
          <li>
            <a href="/user/logout?token=INyOF8qdzpnT1xzUapL9vdyU4YKpjP1WM6X-JZOG-VM"
               data-drupal-link-system-path="user/logout">Log out</a>
          </li>
        </ul>

I also added a $this->drupalLogin($this->httpApiUser) and it also fails.

🇪🇸Spain isholgueras

After testing it, it now works well. In `HEAD`

In this branch, it works

The code looks good too.

🇪🇸Spain isholgueras

After chatting with @balintbrews, the resolution for this issue is not correct.

drupalSettings.xbData should be in global scope and appear only once. This branch does too much and it has some errors:
- The preview (top right) doesn't work. When users create code, the client is not yet talking to the backend.
- The non-compiled code wont work.

The front-end API will consume this settings with methods (not decided yet) like getPageData() (for title and breadcrumb) and getSiteData() (for base URL and branding).

🇪🇸Spain isholgueras

You can reproduce it in a fresh Drupal 11.2 (not in 11.1). I've discovered it while working on 🐛 PHPUnit Next Major tests failing Active .

Here are the failures: https://git.drupalcode.org/project/experience_builder/-/jobs/5696383#L721.

In the XbContentEntityHttpApiTest::testPost test, the components sdc.experience_builder.my-section and sdc.experience_builder.druplicon have this issue.

I'll reopen it and investigate this on tests because it's affecting 🐛 PHPUnit Next Major tests failing Active .

🇪🇸Spain isholgueras

isholgueras made their first commit to this issue’s fork.

🇪🇸Spain isholgueras

There is only one thing left to do. How are the code component going to require that it depends on... breadcrumbs for example?

I still missing how, if I'm creating a code component, I'm going to depend on breadcrumb or pageTitle.

I mean, in the whole process as a user or XB user, I click in Add new, I get the editor and... where I define what features I will require?

I was playing around with a new schema Choice property, drupalSettings, similar to a blockOverride and list there all the options allowing developers to add it to the yaml files, maybe in the future adding them to the UI, but that's very much out of scope here.

Any thoughts here?

🇪🇸Spain isholgueras

Maybe we're deleting errors that we don't want to remove from the messenger and wont be shown in following visits to the backend.

We also want to suppress all messages to XB interface.

After chatting with @f.mazeikis and @tedbow, @wim proposed to replace the messenger service right before we're starting to do things, and revert right after the rendering, so all the messages in the bag in Drupal are there but the messages produced by XB are automatically removed and do not interfere between both.

🇪🇸Spain isholgueras

Also the current MR in #3487533: Cannot delete a field which uses JSON type is a one-liner - it looks like it either needs test coverage or possibly just a comment, so why go to all the trouble here instead of trying to get that one committed?

I think that would be ideal.

I'll check what it needs to be done or which tests needs to be updated/created in core.

🇪🇸Spain isholgueras

Remove duplicated code and tree in description for clarity because it doesn't exist, only inputs.

🇪🇸Spain isholgueras

Is this still an issue? I can't reproduce it.

What I've found is another issue (recorded in the gif). Here are the steps to reproduce:
1. Create and publish a page with alias /new-page
2. Create a second page, without alias, and publish
3. Now, add the same alias /new-page, expect to have the error message
4. Now try to publish and expect to have the error message in the publish modal.
5. And here the ... issue??... modify the alias to 123 and now you have 2 different errors.

The original issue, allowing to publish different nodes with the same alias, is not reproducible, but here is another (imho minor) issue.

What do you think, @laurii?

🇪🇸Spain isholgueras

I've pushed a proof of concept of removing the hash by the original id.

I've adapted the tests and expected more things to break but they don't.

  1. I've created a code component named nachojs and added to components. XB created default/files/astro-island/nachojs.js well.
  2. I've modified the nachojs.js file title "nachojs changed title" and it changes well.

I've run all the tests that I've modified and all pass (except for those hash/filename that I expect), but I'll let the CI to run the whole battery.

🇪🇸Spain isholgueras

Yep, I've updated again 0.x, reinstalled, rebuilt ui (I thought I did it because I have it in the same command to reinstall the site) and now looks good. I can add any block component, change any setting and the UI is automatically refreshed.

It can be closed.

🇪🇸Spain isholgueras

After investigating a bit, that's what I've found:

The new code added in ComponentInputsForm.php

    $tree = $request->get('form_xb_tree');
    [$component_id, $version] = \explode('@', \json_decode($tree, TRUE)['type']);
    if (empty($version)) {
      throw new \UnexpectedValueException('No component version specified.');
    }

Extracts the version, and if it's empty throw an exception.

In my fresh installed Drupal with XB and xb_test_block, If I add the who_s_new I get the error.

What is in tree is:

{"slots":[],"nodeType":"component","type":"block.views_block.who_s_new-block_1","uuid":"4adc7962-290e-4bcf-81f7-798f275518ba"}

and there is no such version

🇪🇸Spain isholgueras

Maybe it's because of my installation/branch, but in the branch 📌 [SPIKE] Prove that it's possible to apply block settings update paths to stored XB component trees Active , right after rebasing 0.x, I get the following error every time I place a block:

	UnexpectedValueException: No component version specified. in Drupal\experience_builder\Form\ComponentInputsForm->buildForm() (line 72 of /var/www/html/web/modules/contrib/experience_builder/src/Form/ComponentInputsForm.php).

I'll investigate a bit

🇪🇸Spain isholgueras

This is the error that is thrown:

{
    "message": "Body does not match schema for content-type \u0022application\/json\u0022 for Request [patch \/xb\/api\/v0\/layout\/{entityTypeId}\/{entityId}]. [Keyword validation failed: Data has additional properties (name) which are not allowed in model]",
    "file": "\/var\/www\/html\/vendor\/league\/openapi-psr7-validator\/src\/PSR7\/Exception\/Validation\/AddressValidationFailed.php",
    "line": 31,
    "trace": [
        {
            "file": "\/var\/www\/html\/vendor\/league\/openapi-psr7-validator\/src\/PSR7\/Exception\/Validation\/InvalidBody.php",
            "line": 19,
            "function": "fromAddrAndPrev",
            "class": "League\\OpenAPIValidation\\PSR7\\Exception\\Validation\\AddressValidationFailed",
            "type": "::"
        },
        {
            "file": "\/var\/www\/html\/vendor\/league\/openapi-psr7-validator\/src\/PSR7\/Validators\/BodyValidator\/UnipartValidator.php",
            "line": 60,
            "function": "becauseBodyDoesNotMatchSchema",
            "class": "League\\OpenAPIValidation\\PSR7\\Exception\\Validation\\InvalidBody",
            "type": "::"
        },
        {
            "file": "\/var\/www\/html\/vendor\/league\/openapi-psr7-validator\/src\/PSR7\/Validators\/BodyValidator\/BodyValidator.php",
            "line": 73,
            "function": "validate",
            "class": "League\\OpenAPIValidation\\PSR7\\Validators\\BodyValidator\\UnipartValidator",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/vendor\/league\/openapi-psr7-validator\/src\/PSR7\/Validators\/ValidatorChain.php",
            "line": 25,
            "function": "validate",
            "class": "League\\OpenAPIValidation\\PSR7\\Validators\\BodyValidator\\BodyValidator",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/vendor\/league\/openapi-psr7-validator\/src\/PSR7\/RequestValidator.php",
            "line": 79,
            "function": "validate",
            "class": "League\\OpenAPIValidation\\PSR7\\Validators\\ValidatorChain",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/modules\/contrib\/experience_builder\/src\/EventSubscriber\/ApiRequestValidator.php",
            "line": 44,
            "function": "validate",
            "class": "League\\OpenAPIValidation\\PSR7\\RequestValidator",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/modules\/contrib\/experience_builder\/src\/EventSubscriber\/ApiMessageValidatorBase.php",
            "line": 75,
            "function": "validate",
            "class": "Drupal\\experience_builder\\EventSubscriber\\ApiRequestValidator",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/vendor\/symfony\/event-dispatcher\/EventDispatcher.php",
            "line": 246,
            "function": "onMessage",
            "class": "Drupal\\experience_builder\\EventSubscriber\\ApiMessageValidatorBase",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/vendor\/symfony\/event-dispatcher\/EventDispatcher.php",
            "line": 206,
            "function": "Symfony\\Component\\EventDispatcher\\{closure}",
            "class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
            "type": "::"
        },
        {
            "file": "\/var\/www\/html\/vendor\/symfony\/event-dispatcher\/EventDispatcher.php",
            "line": 56,
            "function": "callListeners",
            "class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/vendor\/symfony\/http-kernel\/HttpKernel.php",
            "line": 159,
            "function": "dispatch",
            "class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/vendor\/symfony\/http-kernel\/HttpKernel.php",
            "line": 76,
            "function": "handleRaw",
            "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/Session.php",
            "line": 53,
            "function": "handle",
            "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/KernelPreHandle.php",
            "line": 48,
            "function": "handle",
            "class": "Drupal\\Core\\StackMiddleware\\Session",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/ContentLength.php",
            "line": 28,
            "function": "handle",
            "class": "Drupal\\Core\\StackMiddleware\\KernelPreHandle",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/core\/modules\/big_pipe\/src\/StackMiddleware\/ContentLength.php",
            "line": 32,
            "function": "handle",
            "class": "Drupal\\Core\\StackMiddleware\\ContentLength",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/core\/modules\/page_cache\/src\/StackMiddleware\/PageCache.php",
            "line": 116,
            "function": "handle",
            "class": "Drupal\\big_pipe\\StackMiddleware\\ContentLength",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/core\/modules\/page_cache\/src\/StackMiddleware\/PageCache.php",
            "line": 90,
            "function": "pass",
            "class": "Drupal\\page_cache\\StackMiddleware\\PageCache",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/ReverseProxyMiddleware.php",
            "line": 48,
            "function": "handle",
            "class": "Drupal\\page_cache\\StackMiddleware\\PageCache",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/NegotiationMiddleware.php",
            "line": 51,
            "function": "handle",
            "class": "Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/AjaxPageState.php",
            "line": 36,
            "function": "handle",
            "class": "Drupal\\Core\\StackMiddleware\\NegotiationMiddleware",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/StackedHttpKernel.php",
            "line": 51,
            "function": "handle",
            "class": "Drupal\\Core\\StackMiddleware\\AjaxPageState",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/DrupalKernel.php",
            "line": 709,
            "function": "handle",
            "class": "Drupal\\Core\\StackMiddleware\\StackedHttpKernel",
            "type": "-\u003E"
        },
        {
            "file": "\/var\/www\/html\/web\/index.php",
            "line": 19,
            "function": "handle",
            "class": "Drupal\\Core\\DrupalKernel",
            "type": "-\u003E"
        }
    ]
}
Production build 0.71.5 2024