craftcms / cms

Build bespoke content experiences with Craft.
https://craftcms.com
Other
3.29k stars 638 forks source link

[4.x]: Attach a User Element to another User via user registration frontend form with conditions #12283

Closed masiorama closed 2 years ago

masiorama commented 2 years ago

What happened?

Description

I attached to user fields a field which relate it to another group of user, let's call the first one "client" and the last one "agent". It seems to me that it is possibile to attach a User element via frontend form creating an hidden input like this:

{{ hiddenInput('fields[associatedAgent]', associatedAgent.id) }}

It works without restrictions, but it stops working if you add a condition on user field associatedAgent conditional field, like 'show this field only on user of group clients.

Clients is the default group on user registration.

Here you can find more details and screens: https://craftcms.stackexchange.com/questions/40075/attach-a-user-element-to-another-user-via-user-registration-frontend-form

Steps to reproduce

  1. create "agents" and "clients" user groups on the panel
  2. set "clients" user group as default on registration
  3. create an associatedAgent field which can be populated with "agents" users only
  4. associate the field to user fields section
  5. add condition to show that field only on "clients" user

Expected behavior

The related user agent forced by frontend registration form is associated.

Actual behavior

The related user agent forced by frontend registration form is not associated.

Craft CMS version

4.3.1

PHP version

8.2

Operating system and version

Win 10

Database type and version

Mysql 5.7

Image driver and version

No response

Installed plugins and versions

brandonkelly commented 2 years ago

This is expected behavior. User conditions are enforced for all form submissions, regardless of whether the submission happened on the front end or back end.

masiorama commented 2 years ago

Hi @brandonkelly thanks for your reply. I don't understand completely: I have different behavior in those 2 scenarios. On frontend, with the active rule, the saving fails, while on backend, with the active rule, it works saving the relation... maybe I explained myself badly...

update: I'm discussing the matter with @mmikkel on Craft CMS Stack exchange, and he suggested that it could be that on the frontend the saving of the agent does not work because the system doesn't know yet that the created user has to be in the "client" group, even if it is the default group... it rings the bell, but I'm reaching out to you just to be sure this is the problem.

Just in case, how do you suggest me to achieve my goal, is there a specific event I could leverage on before the associatedAgent data is purged before saving?

Thanks for any further help!

brandonkelly commented 1 year ago

Sorry for the delay. I think I may have been confused a bit – can you post a screenshot of the condition you added?

masiorama commented 1 year ago

Hello @brandonkelly , here the screenshot you requested.

ab7oO

More details on craft stackexchange: https://craftcms.stackexchange.com/questions/40075/attach-a-user-element-to-another-user-via-user-registration-frontend-form

i-just commented 1 year ago

Thanks for more info on this. This happens because of the order of actions when creating a new user. First, all data is validated and saved, and at the end, the user is assigned to a default group (in your case: clients). Only once the user is assigned to a default group is the conditional met. But at that point, the data was already processed, with the conditional deemed not to have been met. (The same thing happens when you create a new user via the control panel, only after you successfully save for the first time do conditional fields relying on the user group start showing.)

You can achieve what you’re after with custom code if you, e.g. hook up to the craft\services\Users::EVENT_AFTER_ASSIGN_USER_TO_DEFAULT_GROUP event.

We’ll talk about this internally and update this issue if we come up with anything meaningful.

masiorama commented 1 year ago

Thank you very much, I will investigate on the event you suggested. Thanks again for the support!

brandonkelly commented 1 year ago

This is fixed now for the next release, via #12544. Thanks for reporting!

brandonkelly commented 1 year ago

Craft 4.3.7 has been released with that fix.

ryssbowh commented 1 year ago

I've had the same problem assigning a user to a group depending on some conditions :

I have 2 groups with each their own conditional fields and each can use the public registration (I can't use the "Default User Group" setting), the user would not be populated/validated properly.

My solution was to pass the user group in the registration request and use the following events :

if (\Craft::$app->request->isSiteRequest) {
    Event::on(User::class, User::EVENT_INIT, function (Event $event) {
        if (!$event->sender->id) {
            $group = \Craft::$app->userGroups->getGroupByHandle(\Craft::$app->request->getBodyParam('group'));
            $user->setGroups([$group]);
        }
    });
    Event::on(Elements::class, Elements::EVENT_AFTER_SAVE_ELEMENT, function (Event $event) {
        if ($event->element instanceof User and $event->isNew) {
            $group = \Craft::$app->userGroups->getGroupByHandle(\Craft::$app->request->getBodyParam('group'));
            \Craft::$app->getUsers()->assignUserToGroups($event->element->id, [$group->id]);
        }
    });
}

Obviously it's not great since requests can be forged.

It would probably be useful to add an event before the user is populated with the post fields on the registration process ? Maybe only if the "Default User Group" is not set ? https://github.com/craftcms/cms/blob/develop/src/controllers/UsersController.php#L1437

brandonkelly commented 1 year ago

@ryssbowh Good suggestion. I just added a new craft\services\Users::getDefaultUserGroups() method for the next release, which centrally defines the default user groups for a given user.

It fires an EVENT_DEFINE_DEFAULT_USER_GROUPS event that gives plugins an opportunity to customize the groups:

use craft\base\Event;
use craft\events\DefineUserGroupsEvent;
use craft\services\Users;

Event::on(
    Users::class,
    Users::EVENT_DEFINE_DEFAULT_USER_GROUPS,
    function(DefineUserGroupsEvent $event) {
        $group = Craft::$app->userGroups->getGroupByHandle('myGroup');
        $event->userGroups[] = $group;
    }
);
brandonkelly commented 1 year ago

Craft 4.5.4 is out with that new event.