🇧🇪Belgium @Robin.Houtevelts

Account created on 7 October 2016, over 7 years ago
  • Backend developer at Wieni 
#

Merge Requests

Recent comments

🇧🇪Belgium Robin.Houtevelts

I don't immediately see any incompatible changes when I check v6.4.8...7.1 so it makes sense to mark this compatible with ^7.0 too.

It's a bit late here right now, I'll take a closer look after a good night of sleep.

🇧🇪Belgium Robin.Houtevelts

Thanks for reviewing and merging this 🙏
Are there plans of creating a new release anytime soon?

🇧🇪Belgium Robin.Houtevelts

After applying the patch above I was still seeing this error.
In my case I was using drupal/office_hours which seems to call the ModuleHandler very very early in their .module file.

I fixed it by replacing those two lines with:

require_once __DIR__ . '/office_hours.theme.inc';
require_once __DIR__ . '/office_hours.theme.exceptions.inc';

Leaving it here in case it might help someone else.

🇧🇪Belgium Robin.Houtevelts

Cool!
I'm actually planned to update one of our projects to 10.2 this week.
That project currently has a module whose HOOK_entity_type_alter appears to run after node_singles too -just like Trash- but *doesn't* extend/wrap this AccessHandler.

So I think I can test this exact use case.
I'll test this once I get to 10.2 somewhere this or next week, and let you know here.

🇧🇪Belgium Robin.Houtevelts

Isn't that fixed by the node_singles_module_implements_alter added in this MR?
Then we decorate whatever was final.

🇧🇪Belgium Robin.Houtevelts

I see. Thanks for clarifying!

let me know if you do.

No, not without being fonky.
Ideally the AccessHandler would be an actual service we could decorate.
But something like this might make sure we can decorate the previously defined handler too.

But it might be a bit too fonky..

function node_singles_entity_type_alter(array &$entityTypes): void {
  \Drupal::getContainer()->get('state')
    ->set(
        SingleNodeAccessControlHandler::STATE_DECORATED_CLASS_KEY,
        $entityTypes['node']->getHandlerClass('access')
     )

  $entityTypes['node']->setHandlerClass('access', SingleNodeAccessControlHandler::class);
  $entityTypes['node_type']->setFormClass('delete', SingleNodeTypeDeleteConfirm::class);
}

use Drupal\Core\Entity\EntityAccessControlHandlerInterface;
use Drupal\Core\Entity\EntityHandlerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\State\StateInterface;
use Drupal\node\NodeAccessControlHandler;
use Drupal\node\NodeAccessControlHandlerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class SingleNodeAccessControlHandler implements NodeAccessControlHandlerInterface, EntityAccessControlHandlerInterface, EntityHandlerInterface
{

    protected EntityTypeInterface $entityType;
    protected EntityTypeManagerInterface $entityTypeManager;
    protected StateInterface $state;
    protected NodeAccessControlHandlerInterface&EntityAccessControlHandlerInterface $decorated;

    public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type)
    {
        $self = new self();
        $self->entityType = $entity_type;
        $self->entityTypeManager = $container->get('entity_type.manager');
        $self->state = $container->get('state');

        return $self;
    }

    /**
     * {@inheritdoc}
     */
    public function access(EntityInterface $entity, $operation, AccountInterface $account = NULL, $return_as_object = FALSE)
    {
        return $this->decorated()->access($entity, $operation, $account, $return_as_object);
    }

    /**
     * {@inheritdoc}
     */
    public function createAccess($entity_bundle = NULL, AccountInterface $account = NULL, array $context = [], $return_as_object = FALSE)
    {
        return $this->decorated()->createAccess($entity_bundle, $account, $context, $return_as_object);
    }

    /**
     * {@inheritdoc}
     */
    public function resetCache()
    {
        $this->decorated()->resetCache();
    }

    /**
     * {@inheritdoc}
     */
    public function setModuleHandler(ModuleHandlerInterface $module_handler)
    {
        $this->decorated()->setModuleHandler($module_handler);
        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function fieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account = NULL, FieldItemListInterface $items = NULL, $return_as_object = FALSE)
    {
        return $this->decorated()->fieldAccess($operation, $field_definition, $account, $items, $return_as_object);
    }

    /**
     * {@inheritdoc}
     */
    public function acquireGrants(NodeInterface $node)
    {
        return $this->decorated()->acquireGrants($node);
    }

    /**
     * {@inheritdoc}
     */
    public function writeDefaultGrant()
    {
        $this->decorated()->writeDefaultGrant();
    }

    /**
     * {@inheritdoc}
     */
    public function deleteGrants()
    {
        $this->decorated()->deleteGrants();
    }

    /**
     * {@inheritdoc}
     */
    public function countGrants()
    {
        return $this->decorated()->countGrants();
    }

    /**
     * {@inheritdoc}
     */
    public function checkAllGrants(AccountInterface $account)
    {
        return $this->decorated()->checkAllGrants($account);
    }

    private function decorated(): NodeAccessControlHandlerInterface&EntityAccessControlHandlerInterface
    {
        if (isset($this->decorated)) {
            return $this->decorated;
        }
        $originalAccessHandlerClass = $this->state->get(self::STATE_DECORATED_CLASS_KEY, NodeAccessControlHandler::class);
        $handler = $this->entityTypeManager->createHandlerInstance($originalAccessHandlerClass, $this->entityType);

        if (!$handler instanceof NodeAccessControlHandlerInterface) {
            throw new \InvalidArgumentException('The decorated access control handler must implement NodeAccessControlHandlerInterface.');
        }
        if (!$handler instanceof EntityAccessControlHandlerInterface) {
            throw new \InvalidArgumentException('The decorated access control handler must implement EntityAccessControlHandlerInterface.');
        }

        return $handler;
    }
}
🇧🇪Belgium Robin.Houtevelts

This might be a good moment to ask this:
Do we really need to define our own AccessHandler?

Should we use access hooks instead so we don't have to do this for each module that also wants to define an AccessHandler?

🇧🇪Belgium Robin.Houtevelts

Seems to work! Thanks!
Not sure this is worthy of a changelog entry, so LGTM 🎉

diff --git a/config/sync/node.type.article.yml b/config/sync/node.type.article.yml
index 0a94094b7..d259d783e 100644
--- a/config/sync/node.type.article.yml
+++ b/config/sync/node.type.article.yml
@@ -7,7 +7,7 @@ dependencies:
     - node_singles
 third_party_settings:
   node_singles:
-    is_single: 0
+    is_single: false
   menu_ui:
     available_menus: {  }
     parent: ''
diff --git a/config/sync/node.type.homepage.yml b/config/sync/node.type.homepage.yml
index dcf01fd47..6aae14164 100644
--- a/config/sync/node.type.homepage.yml
+++ b/config/sync/node.type.homepage.yml
@@ -10,7 +10,7 @@ third_party_settings:
     available_menus: {  }
     parent: ''
   node_singles:
-    is_single: 1
+    is_single: true
 name: Homepage
 type: homepage
 description: 'Homepage (homepage)'
--- a/config/sync/node.type.news_overview.yml
+++ b/config/sync/node.type.news_overview.yml
@@ -10,7 +10,7 @@ third_party_settings:
     available_menus: {  }
     parent: ''
   node_singles:
-    is_single: 1
+    is_single: true
 name: 'News overview'
 type: news_overview
 description: 'News overview (news_overview)'

...
🇧🇪Belgium Robin.Houtevelts

Thanks for the suggestion!
I've implemented it and will create a new release shortly.

🇧🇪Belgium Robin.Houtevelts

Changing the title to reflect that we want to be able to filter by the different mapped statuses (mapped/unmapped)

🇧🇪Belgium Robin.Houtevelts

This module doesn't aim to do much more than it currently does: registering service definitions the way it is done by the Symfony bundle.
Other modules can provide the higher-level niceties you mention (Views, an overview, ...).
I'm happy to see that drupal/inotify already takes a stab at this.
(In the future we might be open to adding such things as a submodule.)

For documentation on how to use/configure this module I think the Symfony Mercure Bundle documentation is the best resource.
For setting up a Mercure Hub, refer to the official installation guide.
And of course the getting started guide.

It's a relatively young module and Mercure is being used more in Symfony/Api-Platform projects.
At the moment I'm not aware of other Drupal modules using Mercure so I can't provide a starting point unfortunately.
I'll keep this open for others to promote their work 🤞

🇧🇪Belgium Robin.Houtevelts

Thanks for the MR and sorry I haven't reviewed this in time 🙏
Unfortunately we dropped support for Drupal core <9.5 in 📌 Remove MercureServiceProvider::registr workaround Fixed .

Please consider upgrading your project.
Happy holidays!

🇧🇪Belgium Robin.Houtevelts

I have updated the Readme in 📌 Add Drupal 10 compatibility Fixed .
Thanks for the suggestion! And sorry it took so long for me to respond 🙏

Happy holidays!

🇧🇪Belgium Robin.Houtevelts

This has been taken into account in 📌 Add Drupal 10 compatibility Fixed and fixed there.
The minimum core version has been increased to 9.5

🇧🇪Belgium Robin.Houtevelts

I can also confirm I'm still having this problem on 10.1.
Applying #177 resolved it for me.

🇧🇪Belgium Robin.Houtevelts

This is broken in upcoming Drupal 10.2 https://www.drupal.org/project/drupal/issues/3301573 📌 Remove the aggregate stale file threshold and state entry Fixed
They have removed the state property https://git.drupalcode.org/project/drupal/-/commit/405f8b33a7d0643ba39bf...

I'm getting

PHP Fatal error: Uncaught Error: Call to a member function delete() on null in

🇧🇪Belgium Robin.Houtevelts

I'll try to get it reviewed this week. Thanks for the reminder!

🇧🇪Belgium Robin.Houtevelts

I stumbled upon this issue through my RSS feed on the change records.
However it doesn't read like a CR.

I agree that it needs a different text and an example usage.

Production build 0.69.0 2024