Torsten Baldes Posted July 12, 2019 Share Posted July 12, 2019 Hi, I'm building a site with user profiles, where the users can change and update their profile. For different reasons it's important that the person who oversees these users get's an email, when a user updates his profile. At best with all the values that changed and their value before. I tried to hook into User::changed and this kind of works, but it get's triggered for every field, that's changed. With each trigger the list of fields also get's longer, so that only the last run contains all the changed fields. This would mean, i would send multiple emails for one profile change. Is there a way to prevent this and get all changes at once in only one hook run? This is my code so far (in ready.php): wire('user')->setTrackChanges(Wire::trackChangesOn | Wire::trackChangesValues); wire()->addHookAfter("User::changed", null, function($event){ $user = $event->object; $changes = $user->getChanges(true); // output changes as log entry wire('log')->save('testchangelog', print_r($changes, true) ); // TODO: send mail }); Thanks! Link to comment Share on other sites More sharing options...
androbey Posted July 12, 2019 Share Posted July 12, 2019 Hi @Torsten Baldes, I am also not really a pro, but maybe it can help nevertheless. Did you already try to hook after Pages::saved and filter to only run for user template? This way the hook runs only if the user page is saved and you can get all changed fields. 1 Link to comment Share on other sites More sharing options...
Edison Posted July 12, 2019 Share Posted July 12, 2019 Hi @Torsten Baldes, you could try the following: wire('user')->setTrackChanges(Wire::trackChangesOn | Wire::trackChangesValues); wire()->addHookAfter('Pages::save', function($event) { // >>> ON PAGE SAVE $page = $event->arguments(0); if($page->template->name === 'user') { // >>> APPLY ONLY TO USER TEMPLATE $changes = user()->getChanges(true); // output changes as log entry wire('log')->save('testchangelog', print_r($changes, true) ); $m = new WireMail(); $m->from('Your email From'); $m->to('Your email To'); $m->subject('Your email subject'); $m->body('Your email text'); $m->send(); } }); Note: made a couple of corrections and changed to WireMail as per @horst suggestion. Still testing if changes are recorded... 3 Link to comment Share on other sites More sharing options...
horst Posted July 12, 2019 Share Posted July 12, 2019 1 hour ago, Edison said: @mail($receipt, $subject, $body, "From:{$sender}"); Hi @Edison, if this shows the old and the new values inlog and mail, this is really good! (Will test this later when back in buero). Only thing that should be changed is the use of (PHP) @mail function. Instead you should use wireMail(). :) 1 Link to comment Share on other sites More sharing options...
Torsten Baldes Posted July 12, 2019 Author Share Posted July 12, 2019 Thanks for your help! For now I ended up with something like this: wire('user')->setTrackChanges(Wire::trackChangesOn | Wire::trackChangesValues); wire()->addHookAfter("Pages::saved", null, function($event){ $user = $event->arguments(0); if($user->template->name == 'user'){ $userfields = $user->template->fieldgroup; $excludeFields = ['pass', 'roles', 'language', 'admin_theme', 'schedulerSkip', 'cpf_added']; $changes = $event->arguments(1); // check if there are any changes, if it's not an InputfieldFileAjax (also triggers the hook), the user changed his own profile, if the changed user is not a superuser (could be any other role to exclude) if(count($changes) && !wire('input')->get->InputfieldFileAjax && wire('user')->id == $user->id && !$user->hasRole('superuser')){ $userfieldsArray = array(); foreach($userfields as $userfield){ $userfieldsArray[] = $userfield->name; } //remove unwanted fields $changes = array_diff($changes, $excludeFields); // order changed fields like template $changesSorted = array(); foreach ($userfieldsArray as $item) { if(in_array($item, $changes)){ $changesSorted[] = $changes[$item]; } } $changes = $changesSorted; unset($changesSorted); // probably not necessary // get the changes in a string $changedFields = ''; foreach($changes as $change){ $changedFields .= $userfields->get($change)->label . "\r\n"; } // compose the mail $subject = "Änderung im Nutzerprofil von »".$user->u_fullname."«"; $message = "Hallo,\r\n\r\n das Nutzerprofil von ".$user->u_fullname." wurde aktualisiert.\r\nFolgende Felder haben sich geändert:\r\n\r\n" ; $message .= $changedFields; $message .= "\r\n\r\n" ; $message .= "Klicken Sie hier, um das Profil zu bearbeiten: ".trim(wire('urls')->httpRoot, '/').$user->editUrl ; $message .= "\r\n\r\n\r\n"; $mailmessage = wire('mail')->new(); $mailmessage->to('mail@domain.tld')->from($user->email); $mailmessage->subject($subject)->body($message); $sent = $mailmessage->send(); } } }); I decided that it's not necessary to send the before and after values within the email. This could even be a privacy and/or security issue. Thanks again for your ideas and help! Link to comment Share on other sites More sharing options...
Ralf Posted March 17, 2022 Share Posted March 17, 2022 Sorry to reopen this old topic, but I have a question about this exact topic. How can I restrict the sending of emails even further and ONLY if there has been a change to the template user (see code above) AND ONLY for the roles, for example? In other words, please send an email if there is a change in a role (or a field xy) for a user and DO NOT send an email for all other changes for a user! Link to comment Share on other sites More sharing options...
androbey Posted March 17, 2022 Share Posted March 17, 2022 Hi Ralf, e.g. you could check if the field you are looking for (e.g. roles) has changed: https://processwire.com/api/ref/page/is-changed/ .. and then only send an email if the field has changed. 1 Link to comment Share on other sites More sharing options...
Ralf Posted March 18, 2022 Share Posted March 18, 2022 @androbey Thanks for the tip, I think that's exactly what I'm looking for. BUT unfortunately I can't get it to work like in the example... If I use the code from @Edison above, it basically works. But if I extend the if-line - like this if($page->template->name === 'user' && $page->isChanged('roles')) { // >>> APPLY ONLY TO USER TEMPLATE and if I can't address roles directly but have to iterate, I also tried it with a "normal" textfield e.g. like this if($page->template->name === 'user' && $page->isChanged('surname')) { // >>> APPLY ONLY TO USER TEMPLATE OR the if-statement also separately after the if-statement of the template "user"... if($page->template->name === 'user') { // >>> APPLY ONLY TO USER TEMPLATE $changes = user()->getChanges(true); if($page->isChanged('surname')){ // LOG & send email from @Edison } } But with all my attempts I don't get an email anymore ? Is this related to the user template or have I simply implemented the code incorrectly? Link to comment Share on other sites More sharing options...
androbey Posted March 18, 2022 Share Posted March 18, 2022 @Ralf You're right, my bad. It seems "isChanged()" method does not work on User template. But you still have two options. 1) Tweek the code from above and check if the field you want to monitor is in changes array. (e.g. in_array('roles', $changes)). 2) Use a slightly different approach with User::changed hook. You have to modify to fit your needs, but here's a very basic example. wire()->addHookAfter('User::changed', function (HookEvent $event) { $user = $event->object; $changedField = $event->arguments(0); //which field you want to monitor $fieldToCheck = "roles"; if ($fieldToCheck == $changedField) { //send mail.. or do more checks.. } $return = $event->return; $event->return = $return; }); Edit: the second approach is ONLY sensible if you want to check for a single changed field! Otherwise you would get a mail for each changed field. If you want to check for changes of more than one field use the first approach. 1 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now