Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User element behaviors event not being triggered #4362

Closed
tim-pixeldeluxe opened this issue Jun 5, 2019 · 12 comments

Comments

Projects
None yet
2 participants
@tim-pixeldeluxe
Copy link
Contributor

commented Jun 5, 2019

Description

The EVENT_DEFINE_BEHAVIORS event isn't being triggered for the User element. I'm trying to add some extra functions to the currentUser variable (aka the User element) but the event is never triggered.

This works fine:

Event::on(
    \craft\elements\Entry::class,
    \craft\elements\Entry::EVENT_DEFINE_BEHAVIORS,
    function (Event $event) {
        exit('test');
    }
);

This even isn't being triggered:

Event::on(
    \craft\elements\User::class,
    \craft\elements\User::EVENT_DEFINE_BEHAVIORS,
    function (Event $event) {
        exit('test');
    }
);

Steps to reproduce

  1. Create an event listener to define User element behaviors
  2. Try using the new functions on the currentUser variable (frontend)
  3. "Calling unknown method: craft\elements\User::myFunction()"

Additional info

  • Craft version: 3.1.28
  • PHP version: 7.1.6
  • Database driver & version: MySQL 5.5.59
  • Plugins & versions:
"php": ">=7.0.0",
"craftcms/cms": "^3.1.24",
"vlucas/phpdotenv": "^2.4.0",
"craftcms/aws-s3": "1.2.2",
"ether/logs": "v3.0.0",
"verbb/cp-nav": "2.0.9",
"verbb/expanded-singles": "1.0.7",
"twbs/bootstrap": "4.0.0-beta.2",
"verbb/super-table": "2.1.20",
"topshelfcraft/wordsmith": "3.0.5",
"verbb/field-manager": "2.1.0",
"mmikkel/cp-field-inspect": "1.0.5",
"sebastianlenz/linkfield": "1.0.19",
"mikestecker/craft-videoembedder": "1.1.3",
"ether/seo": "3.5.4",
"lukeyouell/craft-hideadmin": "1.0.1",
"supercool/tablemaker": "2.0.1",
"doublesecretagency/craft-siteswitcher": "2.1.0",
"anubarak/craft-relabel": "1.3.1",
"mmikkel/child-me": "1.0.4",
"craftcms/element-api": "^2.5",
"carlcs/craft-redactorcustomstyles": "^3.0",
"craftcms/contact-form": "2.2.5",
"nystudio107/craft-cookies": "^1.1",
"craftcms/redactor": "2.3.3.2",
"craftcms/feed-me": "4.1.0",
"ttempleton/craft-nocache": "^2.0",
"matt-west/craft-recaptcha": "^1.3"
@brandonkelly

This comment has been minimized.

Copy link
Member

commented Jun 5, 2019

craft\elements\User isn’t overriding behaviors() so there’s nothing that could stop the event from firing.

Do you have any plugins or modules that could be listening for the same event, and perhaps marking the event as handled? (If an event handler marks an event as handled, Yii won’t call any additional handlers.)

@tim-pixeldeluxe

This comment has been minimized.

Copy link
Contributor Author

commented Jun 6, 2019

Just used ctrl + f to search in my entire project (including the vendor). The only places I found EVENT_DEFINE_BEHAVIORS were inside the Craft source code (vendor), my own plugin (the event listener that isn't working), inside a web.log (I caused an error when setting up a behavior for Request earlier) and a Craft changelog.
https://pastebin.com/RrPeWjVN

I have already tried deleting my vendor and re-installing it (which often fixes strange bugs) but that didn't work this time. I've also looked at the Craft source code before making this report to see if behaviors() was being overwritten. This wasn't the case and you've confirmed that (thanks).

Are you able to reproduce this?

EDIT: The event is being triggered when I access the CP. Maybe this event isn't being triggered for currentUser?

@brandonkelly

This comment has been minimized.

Copy link
Member

commented Jun 6, 2019

Just verified that it’s working for me.

Most likely the user is just getting loaded before your event handler is registered. Where are you putting your Event::on() code? It should be within a plugin or module, from the init() method. And if it’s in a module, make sure you are listing the module ID within the 'bootstrap' key in the array returned by config/app.php.

return [
    'modules' => [
        'my-module-id' => my\Module::class,
    ],
    'bootstrap' => ['my-module-id'],
];
@tim-pixeldeluxe

This comment has been minimized.

Copy link
Contributor Author

commented Jun 7, 2019

I'm using a plugin and my event handler is registered from within the init() function.

Not sure where the currentUser global is set? Are plugins loaded before that? I noticed that my event also doesn't work when accessing the CP login screen. When I try to login the ajax call does trigger my event as the response is "test". So I think the currentUser variable might be set before loading the plugins.

@brandonkelly

This comment has been minimized.

Copy link
Member

commented Jun 7, 2019

currentUser is just a reference to whatever yii\web\User::getIdentity() returns for the request (the logged-in user):

$globals['currentUser'] = Craft::$app->getUser()->getIdentity();

The logged-in user is queried, instantiated (along with its EVENT_DEFINE_BEHAVIORS getting triggered) the first time that getIdentity() method is called in the request, which is almost certainly going to be before the point when the currentUser global variable is getting set.

If you have Xdebug, set a breakpoint on line 189 of vendor/yiisoft/yii2/web/User.php, and another one in your init() method where you’re registering the event listener. That should show you why getIdentity() is getting called first (assuming its breakpoint is hit first).

If not, send over your composer.json and composer.lock files, plus any other custom plugins/modules you may be referencing in composer.json and a database backup, over to support@craftcms.com and we can try to look into it for you.

@tim-pixeldeluxe

This comment has been minimized.

Copy link
Contributor Author

commented Jun 11, 2019

Don't have Xdebug unfortunately. Will send some more info via mail.

I also used the Yii debug bar to see whether the event is being fired. Not sure if it helps, but it might.
Frontend page:
https://i.postimg.cc/MTdhc9Js/Schermafbeelding-2019-06-11-om-09-09-27.png

CP dashboard:
https://i.postimg.cc/9QwV6HHc/Schermafbeelding-2019-06-11-om-09-11-41.png

The event also wasn't fired when I'm on the myAccount page in the CP.

EDIT: On a clean 3.2.0-beta.1 install the event is fired. Making sure its not caused by our custom plugins.

@tim-pixeldeluxe

This comment has been minimized.

Copy link
Contributor Author

commented Jun 11, 2019

Update: I have been installing all of my plugins one by one on a clean install. Whenever I install one of these two plugins the event is no longer fired: "CP Field Inspect", "Child Me!".

@tim-pixeldeluxe

This comment has been minimized.

Copy link
Contributor Author

commented Jun 11, 2019

Update 2: Whenever I place the following code inside my plugin's init function the event is no longer fired:

$isAdmin = $user->getIsAdmin();

This works:

public function init() {
 $user = Craft::$app->getUser();
}

This doesn't:

public function init() {
 $user = Craft::$app->getUser();
 $isAdmin = $user->getIsAdmin();
}

EDIT: Calling the getIdentity() function on craft\web\User seems to be causing this issue.

@tim-pixeldeluxe

This comment has been minimized.

Copy link
Contributor Author

commented Jun 11, 2019

Screenshots of Yii debug

Without $user->getIdentity() in my plugin init function:
Schermafbeelding 2019-06-11 om 10 59 52

With $user->getIdentity() in my plugin init function:
Schermafbeelding 2019-06-11 om 11 00 20

Init code:

public function init() {
    $user = Craft::$app->getUser();
    $identity = $user->getIdentity();
    //$isAdmin = $user->getIsAdmin();
    //$isCpRequest = $request->getIsCpRequest();
    //$isConsoleRequest = $request->getIsConsoleRequest();

    Event::on(
        Plugins::class,
        Plugins::EVENT_AFTER_LOAD_PLUGINS,
        function () {
            $fields = Craft::$app->getFields()->getAllFields();
            $request2 = Craft::$app->getRequest();
            Craft::info('test123: ' . $request2->getUrl());

            Craft::$app->getView()->registerAssetBundle(TestBundle::class);
            Craft::$app->getView()->registerJs('console.log("test");');
        }
    );
}
@brandonkelly

This comment has been minimized.

Copy link
Member

commented Jun 11, 2019

Update 2: Whenever I place the following code inside my plugin's init function the event is no longer fired:

$isAdmin = $user->getIsAdmin();

Are you calling that before or after registering your event handler?

If you register the event handler after calling getIsAdmin() (or anything else that will ultimately call getIdentity(), then it’s too late, the user has already been registered and their behaviors have already been set.

@tim-pixeldeluxe

This comment has been minimized.

Copy link
Contributor Author

commented Jun 12, 2019

Update 2: Whenever I place the following code inside my plugin's init function the event is no longer fired:

$isAdmin = $user->getIsAdmin();

Are you calling that before or after registering your event handler?

If you register the event handler after calling getIsAdmin() (or anything else that will ultimately call getIdentity(), then it’s too late, the user has already been registered and their behaviors have already been set.

Its registered after that function is called so that explains a lot. Is there any way I can register my event before the other plugins are loaded? Thanks in advance!

@brandonkelly

This comment has been minimized.

Copy link
Member

commented Jun 12, 2019

Is there any way I can register my event before the other plugins are loaded?

Not without getting those plugins to stop grabbing the user identity from their init() methods, maybe holding off until EVENT_AFTER_LOAD_PLUGINS event.

That’s probably not very practical though. So instead maybe just check if there’s a logged-in user, and if so attach it right from your init() method, otherwise register the event:

$user = Craft::$app->user->getIdentity();
if ($user) {
    $user->attachBehavior('my-behavior', new MyBehavior());
} else {
    Event::on(User::class, User::EVENT_DEFINE_BEHAVIORS, function(DefineBehaviorsEvent $e) {
        $e->behaviors['my-behavior'] = new MyBehavior();
    });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.