modifiedcontent Posted September 17, 2016 Share Posted September 17, 2016 Great! The basic process now works without errors. Since this is the first Processwire module I am working with, I am still very confused how to finish this. I need to figure out the following: 1. What happens after the user has registered, with the email validation code added to the form? There is no second 'Welcome' email with instructions for the user. Or instead of a confirmation/welcome email you could put a message on the screen after registering: 'Thank you for registering. You can now log in with the username and the password you just picked.' Or log them in automatically after registering. But most users will probably expect a Welcome/confirmation email. 2. How can I assign a role to users upon registration? I have added a custom role "member". 3. How can I add fields to the form, starting with Firstname Lastname? Can Processwire's users db table(s) be extended and used to store entire user accounts - address, phone number, rss feed, favorite color, hobbies, spirit animal, ...? 4. How can I style the form, including the html, not just using the CSS classes/ids? I want to get rid of the listing tags li etc. Would there be a way to just hardcode a form in the template instead of generating it with fu->register()? 5. How can I change the text/style of the validation email? Probably add a template file somewhere? The answers to this are probably to be found in PW documentation and elsewhere on the forum. Any pointers in the right direction would help. Link to comment Share on other sites More sharing options...
pwFoo Posted September 17, 2016 Author Share Posted September 17, 2016 Take a look at the frontenduser repo wiki (nickname, default role) and the PW hook documentation for examples how to add / modify features. FrontendUser just use PW features (API, InputfieldForm and hooks)! Add own features / plugins by hook into PW / FrontendUser methods (add notification or welcome email after a new user registered successful). Link to comment Share on other sites More sharing options...
modifiedcontent Posted September 17, 2016 Share Posted September 17, 2016 Thanks pwFoo. I guess I could use this and this as guides then? They are from 2011... I don't see anything about nickname (?) or Firstname Lastname in the frontenduser repo wiki. What is the hook for new user registered successful? I can't find it here searching on 'register' or 'registration' or 'user'. Has anyone implemented a welcome/confirmation email with FrontendUser? Link to comment Share on other sites More sharing options...
pwFoo Posted September 17, 2016 Author Share Posted September 17, 2016 You can use examples from the forum or just modify to fit your needs. There is a mail example inside FrontendUserRegisterEmailValidation. PW hooks: https://processwire.com/api/hooks/ FrontendUser Wiki:https://bitbucket.org/pwFoo/frontenduser/wiki/Register extensions and pluginshttps://bitbucket.org/pwFoo/frontenduser/wiki/Login extensions and pluginshttps://bitbucket.org/pwFoo/frontenduser/wiki/Code snippets / Exampleshttps://bitbucket.org/pwFoo/frontenduser/wiki/Documentation#markdown-header-styles-scripts-templates You can hook the FrontendUser methods auth (=login) and save (=register / save user). Just hook after FrontendUser save() and check the event return value if user was saved sucessful... Captain Hook -> search login -> /wire/core/session __login($name, $pass) I love the PW API and hooks 2 Link to comment Share on other sites More sharing options...
flydev Posted September 18, 2016 Share Posted September 18, 2016 (edited) @modifiedcontent Imagine we have 3 pages : Home, Register, Member Area and 3 templates : home, register, member-area. In the template home, if the user is not logged in, the login form appear on the homepage - there is also a link to our register page, another for the member area page and the logout link. Code of home.php : Spoiler <?php // load the module $fu = $modules->get('FrontendUser'); // if the user is logged in and the get variable "logout" is set, logout the user if($input->get->logout && $user->isLoggedin()) $fu->logout($pages->get('/')->url); ?> <html> <head> </head> <body> <h1>Page: <?php echo $page->title; ?></h1> <ul> <li><a href="<?php echo $pages->get('/register/')->url; ?>">Register</a> <?php if($user->isLoggedin()): ?> | <a href="?logout=logout">Logout</a> <?php endif; ?> </li> </ul> <?php $out = ''; // check if user is logged if($user->isLoggedin()) { $out = "Welcome {$user->name} - You are already logged in..."; // if the user have the role "member" if ($user->hasRole("member")) { //redirect the user to the member area $session->redirect($pages->get('/member-area/')->url); // or do something } else { // do other stuff for other roles } } else { // user is not logged in, show the login form $fu->login(['username', 'password']); $fu->process($pages->get('/member-area/')->url); $out .= $fu->render(); } echo $out; ?> </body> </html> In the register template, we show the registration form. First step, once the form is filled, a validation email is sent to the user. Then if the user go to his mailbox and click to the validation link, he return to our register page and submit the form with the validation token. If everything went smooth, we use a hook before the form is saved to add a role "member" to the user. Then we use a hook after the form is saved to send a confirmation email with the details (username, email, passowrd, welcome message, etc.). Code of register.php : Spoiler <html> <head> </head> <body> <h1>Page: <?php echo $page->title; ?></h1> <ul> <li><a href="<?php echo $pages->get('/')->url; ?>">Homepage / login</a></li> </ul> <?php $out = ''; // check if user is logged if($user->isLoggedin()) { // the user is already logged in, no registration possible, redirect the user to the hmepage $session->redirect($pages->get('/')->url); } else { // user is not logged in then show the registration form $fu = $modules->get('FrontendUser'); // load the module $fu->register(['username', 'email', 'password', 'emailValidation']); // prepare the form // hook to add role $fu->addHookBefore('FrontendUser::save', function() use($fu) { $fu->userObj->addRole('member'); // add role "member" }); // hook to send an email back to the user with connection/login details $fu->addHookAfter('FrontendUser::save', function($event) use($fu, $input) { if($event->return === true) { if(!empty($input->post->password) && !count($fu->form->getErrors())) { $subject = "Your new account at mydomain.com"; $emailContentPlain = "Your connection details:\nUsername: {$fu->userObj->name}\nEmail: {$fu->userObj->email}\nPassword: {$input->post->password}"; $mail = wireMail(); $mail->to($fu->userObj->email); $mail->subject($subject); // plain body $mail->body($emailContentPlain); $mail->send(); } } }); $fu->process($pages->get('/')->url); $out .= $fu->render(); } echo $out; ?> </body> </html> And then in the member-area template, just show welcome message and a logout link. Nothing special here. Code of member-area.php : Spoiler <?php // load the module $fu = $modules->get('FrontendUser'); // if the user is logged in and the get variable "logout" is set, logout the user and redirect to the homepage if($input->get->logout && $user->isLoggedin()) $fu->logout($pages->get('/')->url); // if the user has not the role "member", redirect him to the homepage if(!$user->hasRole("member") || !$user->isLoggedin()) $session->redirect($pages->get('/')->url); ?> <html> <head> </head> <body> <h1>Page: <?php echo $page->title; ?></h1> <ul> <li><?php if($user->isLoggedin()): ?> <a href="?logout=logout">Logout</a> <?php endif; ?> </li> </ul> <p>Hello <?php echo $user->name; ?> - Welcome to the member Area / profile page / whatever.</p> </body> </html> To test it, just create and copy paste the code of each template. FAQ: Quote 1. [...] But most users will probably expect a Welcome/confirmation email. Check the hook in the register.php template above. Quote 2. How can I assign a role to users upon registration? I have added a custom role "member". Check the hook in the register.php template above. Quote 3. How can I add fields to the form, starting with Firstname Lastname? Can Processwire's users db table(s) be extended and used to store entire user accounts - address, phone number, rss feed, favorite color, hobbies, spirit animal, ...? Read this post and ask for example/help if needed. Quote 4. How can I style the form, including the html, not just using the CSS classes/ids? I want to get rid of the listing tags li etc. Would there be a way to just hardcode a form in the template instead of generating it with fu->register()? You could do something like that : $markup = $fu->render(); $markup = str_replace('<ul', '<div', $markup); $markup = str_replace('<li', '<div', $markup); $markup = str_replace('</li>', '</div>', $markup); $markup = str_replace('</ul>', '</div>', $markup); $out .= $markup; (I am sure there is a better way to do it) Quote 5. How can I change the text/style of the validation email? Probably add a template file somewhere? You can modify the file emailValidation.php or use your own in /site/modules/FrontendUser/templates. Hope it help Edited September 18, 2016 by flydev FAQ 5 Link to comment Share on other sites More sharing options...
modifiedcontent Posted September 18, 2016 Share Posted September 18, 2016 Yes, that helps a lot, especially the addHookAfter bit for the confirmation email. Thanks flydev & pwFoo! Below is my one page working version, as a minumum starting point: Spoiler $fu = $modules->get('FrontendUser'); $redirectDestination = htmlentities($_SERVER['REQUEST_URI']); $fu->addHookBefore('save', function($event) { $user = wire('fu')->userObj; $user->addRole('member'); }); $fu->addHookAfter('FrontendUser::save', function($event) use($fu, $input) { if($event->return === true) { if(!empty($input->post->password) && !count($fu->form->getErrors())) { $subject = "Welcome - your new account at mydomain.com"; $emailContentPlain = "Your connection details:\nUsername: {$fu->userObj->name}\nEmail: {$fu->userObj->email}\nPassword: {$input->post->password}"; $mail = wireMail(); $mail->to($fu->userObj->email); $mail->subject($subject); $mail->body($emailContentPlain); $mail->send(); } } }); // Login with the email address instead of the username $fu->addHookBefore('FrontendUser::auth', function($event) { $email = wire('fu')->form->fhValue('username', 'email'); $loginUser = wire('users')->get("email={$email}"); if ($loginUser instanceof User && !$loginUser->isGuest()) { $userObj = $event->arguments[0]; $userObj->name = $loginUser->name; } }); if ($user->isGuest()) { $fu->register(array('username', 'email', 'emailValidation', 'password')); $fu->process($redirectDestination); echo $fu->render(); echo 'or log in here if you already have an account'; echo $fu->login(null, $redirectDestination); } if($user->isLoggedin()) { if (isset($_GET['logout'])) $fu->logout($redirectDestination); echo "<a href='?logout'>log out</a>"; } Any suggestions for improvements? Is that log out link the cleanest way to do it? Link to comment Share on other sites More sharing options...
szabesz Posted September 18, 2016 Share Posted September 18, 2016 14 minutes ago, modifiedcontent said: .flydev, how do you put a big block of code in a 'Reveal hidden contents' block? The button with the eye on the right, labelled Spoiler Spoiler 3 Link to comment Share on other sites More sharing options...
netcarver Posted September 20, 2016 Share Posted September 20, 2016 Hello pwFoo, Thank you for this suite of modules - I've just been trying them out for the first time and they look very useful. I particularly like the email validation portion of this. One small observation though, when registering a new user and using a name or email that already exists, all the entered values are lost on unsuccessful form submission. I think it would be helpful to keep the value of any field that is not in error. So, if a user typed an in-use name, but the email was OK, then we should keep the email field and only clear the name field. Thank you! Link to comment Share on other sites More sharing options...
pwFoo Posted September 21, 2016 Author Share Posted September 21, 2016 I'll take a look... don't know when, but I think I have to change the clear session values part... // Clear session values wire('session')->remove('registerToken'); wire('session')->remove('registerUsername'); wire('session')->remove('registerEmail'); https://bitbucket.org/pwFoo/frontenduser/src/0070dc3106945198ac438bab3ab742b1784080e9/FrontendUser/FrontendUserRegisterEmailValidation.module?at=master&fileviewer=file-view-default#FrontendUserRegisterEmailValidation.module-165 https://bitbucket.org/pwFoo/frontenduser/src/0070dc3106945198ac438bab3ab742b1784080e9/FrontendUser/FrontendUserRegisterEmailValidation.module?at=master&fileviewer=file-view-default#FrontendUserRegisterEmailValidation.module-143 It should check and take care about field errors first. Link to comment Share on other sites More sharing options...
modifiedcontent Posted September 24, 2016 Share Posted September 24, 2016 Trying to add firstname, lastname fields now. Here is what I have so far, following an earlier post suggested by flydev: Spoiler <?php $fu = $modules->get('FrontendUser'); $redirectDestination = htmlentities($_SERVER['REQUEST_URI']); $firstname = $modules->get('InputfieldText'); $firstname->label = 'First name'; $firstname->attr('id+name','firstname'); $firstname->required = 1; $firstname->addHookAfter('processInput', function($event) { $field = $event->object; $mySanitizedCustomInput = wire('fu')->form->fhValue($field->name); wire('fu')->userObj->firstname = $mySanitizedCustomInput; }); $fu->addHookBefore('save', function($event) { $user = wire('fu')->userObj; $user->addRole('member'); }); $fu->addHookAfter('FrontendUser::save', function($event) use($fu, $input) { if($event->return === true) { if(!empty($input->post->password) && !count($fu->form->getErrors())) { $subject = "Welcome - your new account at mydomain.com"; $emailContentPlain = "Your connection details:\nUsername: {$fu->userObj->name}\nEmail: {$fu->userObj->email}\nPassword: {$input->post->password}"; $mail = wireMail(); $mail->to($fu->userObj->email); $mail->subject($subject); $mail->body($emailContentPlain); $mail->send(); } } }); // Login with the email address instead of the username $fu->addHookBefore('FrontendUser::auth', function($event) { $email = wire('fu')->form->fhValue('username', 'email'); $loginUser = wire('users')->get("email={$email}"); if ($loginUser instanceof User && !$loginUser->isGuest()) { $userObj = $event->arguments[0]; $userObj->name = $loginUser->name; } }); if ($user->isGuest()) { $fu->register(array($firstname,'username', 'email', 'emailValidation', 'password')); $fu->process($redirectDestination); echo $fu->render(); echo 'or log in here if you already have an account'; echo $fu->login(null, $redirectDestination); } if($user->isLoggedin()) { if (isset($_GET['logout'])) $fu->logout($redirectDestination); echo "<a href='?logout'>log out</a>"; echo '<h3>Hello '. $user->firstname .'</h3>'; } ?> This sort of works, but the firstname is not filled in when you click on the email validation link; the user has to manually add it again. It should be stored along with the username, right? Is 'addHookAfter ...' the wrong hook? Is it essentially the same problem netcarver posted about here? Will I have to add the same lines like the $firstname stuff for each custom field or is there a way to consolidate that, process all custom fields with one function? Link to comment Share on other sites More sharing options...
pwFoo Posted September 25, 2016 Author Share Posted September 25, 2016 At the moment I think about a rewrite and code cleanup, but have no time to do it. The return value of process() method could cause a strange behavior because it returns "true" also if the form wasn't submitted because of the "return $this" for chaining public function process($redirect) { if ($this->form->fhProcessForm()) { switch ($this->action) { case 'Login': $result = $this->auth($this->userObj); break; case 'Register': $result = $this->save($this->userObj); break; } if ($result === true) { $this->session->redirect($redirect, false); // User sucessfully registered } else { $this->form->fhSubmitBtn->error($result); // Save user failed? } } return $this; } 1 Link to comment Share on other sites More sharing options...
modifiedcontent Posted November 6, 2016 Share Posted November 6, 2016 I want to generate the username from a real/full name; firstname + lastname. How can I get onkeyup into the fullname input field? This didn't work: $fullname = $modules->get('InputfieldText'); $fullname->label = 'Full name'; $fullname->attr('id+name','fullname'); $fullname->required = 1; $fullname->onkeyup='generateusername()'; Question now asked here with the username autogenerating javascript included. Link to comment Share on other sites More sharing options...
pwFoo Posted November 6, 2016 Author Share Posted November 6, 2016 Hi @modifiedcontent, your question isn't module related. It's form api related. Link to comment Share on other sites More sharing options...
modifiedcontent Posted November 6, 2016 Share Posted November 6, 2016 Does the module provide a way to autogenerate the username from a real name - firstname + lastname? Link to comment Share on other sites More sharing options...
pwFoo Posted November 6, 2016 Author Share Posted November 6, 2016 Not the module, but PW. Just hook into form processing / user registration and generate the needed value before the user object will be saved. The module methods are hookable too. 1 Link to comment Share on other sites More sharing options...
modifiedcontent Posted November 6, 2016 Share Posted November 6, 2016 I can't figure out the forms API and hookable etc. I only have very rudimentary PHP knowledge. I had copied and pasted this together to get a custom realname/fullname field: Spoiler $fullname = $modules->get('InputfieldText'); $fullname->label = 'Full name'; $fullname->attr('id+name','fullname'); $fullname->required = 1; $fullname->addHookAfter('processInput', function($event) { $field = $event->object; $mySanitizedCustomInput = wire('fu')->form->fhValue($field->name); wire('fu')->userObj->fullname = $mySanitizedCustomInput; }); $fu->addHookBefore('save', function($event) { $user = wire('fu')->userObj; $user->addRole('member'); }); > But the fullname field is not stored. How do you store custom fields with userObj? I can't find any clear code examples anywhere that don't go off into a million different directions. Should I add something under $fu->addHookBefore, like $user->fullname or something like that? What is fhValue($field->name)? What "name" is that? Is that the username? I tried it changed to $field->fullname, but that didn't do anything. Adding a new user apparently looks something like this: Spoiler $u = new User(); $u->name = $form->get('username')->value; $u->pass = $form->get('password')->value; $u->email = $form->get('email')->value; $u->addRole('guest'); So I guess I have to define fullname "somewhere" using $form->get('fullname')->value? I have no idea what the module does and what I am supposed to code together myself and how it is all supposed to hang together. Completely lost. Getting this ready for "production" is starting to take way too long. Link to comment Share on other sites More sharing options...
pwFoo Posted November 6, 2016 Author Share Posted November 6, 2016 The module just uses PW features. So it uses the default user template. You need to add custom fields to save additional attributes! Link to comment Share on other sites More sharing options...
modifiedcontent Posted November 6, 2016 Share Posted November 6, 2016 Yes, I get that. I have a custom fullname field, but can't find anywhere how to store that value and how that works in combination with your module. PW features and hookable etc. may be completely obvious to you, but this is the first PW module I am working with. I had added the fullname field to the template. Should I also add a username field to the template? Or does the module take care of that? There is this error log in admin: Error: Exception: Method FrontendUser::fullnameRegister does not exist or is not callable in this context (in /home/mysitepath/wire/core/Wire.php line 410) Link to comment Share on other sites More sharing options...
Webrocker Posted November 7, 2016 Share Posted November 7, 2016 12 hours ago, modifiedcontent said: Yes, I get that. I have a custom fullname field, but can't find anywhere how to store that value and how that works in combination with your module. Hi, you first need a backend template for your users that has the fields you want to work with. think of the fields in a template as the columns of a row of data for the 'table' (here: "user", most times "page") (actually this is not how it works under the hood, but it helps to think of it this way in the beginning); the standard user template just has the minimum (data)fields required, aka name, password, and email and a bit of meta things. so to work with your new field 'fullname', you need to create a field 'fullname' in the admin, then apply this field to a new template (for example 'user_extended'; this template should have all the fields of the regular user template, plus your newly created one(s)) or. You can extend the user-template with your new field(s), by editing the user template (and setting the list to include system fields). once you have the template applied to the 'user'Once your field is in the user template, your $userObject->fullname = blablainputfield->fullname will work (assuming that the field for the full name in the template is named 'fullname'). hope this helps to get you started, Tom Link to comment Share on other sites More sharing options...
dotnetic Posted November 11, 2016 Share Posted November 11, 2016 Hi, I would like to do a register form with email pre-registration validation, but without the username field. Instead the username should be the email sanitzed as a pageName. I have the following code right now: <?php namespace ProcessWire; // prepare register form // Additional email pre-register validation plugin (built-in) $fu->register(array('email', 'emailValidation', 'password')); $fu->form->setMarkup($form_markup); $fu->form->setClasses = ($form_classes); $fu->addHookBefore('FrontendUser::save', function ($event) { $user = wire('fu')->userObj; $form = wire('fu')->form; if (!count($form->getErrors())) { // set the username to sanitized email value // $user->name = $form->fhValue('email', 'pageName'); $user->addRole('editor'); } }); // process register / form submit $fu->process($profile_url); $register_form = $fu->render(); $view->set('registerForm', $register_form); // this is for Twig But when I submit that form I get an error: Call to a member function getErrors() on null in line 94 of FrontendUserRegisterEmailValidation.module I think it is because in the function "hookRegisterFormAfterProcess" there is line 84: $user = $form->get('username'); As I send no username, this can not work. So how can I make it work without touching the FrontendUserRegisterEmailValidation.module? Thanks in advance. 1 Link to comment Share on other sites More sharing options...
modifiedcontent Posted November 11, 2016 Share Posted November 11, 2016 @jmartsch, actually go without a username entirely would probably require a rewrite of this module and maybe PW core itself. I am trying to get rid of the username field by autogenerating a username based on the full name input and then make the username field hidden; there is still a username, but only for internal system purposes. I use this javascript: Spoiler <script> $("#fullname").bind("keyup change", function(e) { var input=$(this).val(); input = input.replace(/^\s+|\s+$/g, ''); // trim input = input.toLowerCase(); // replace diacritics, weird accent letters, swap ñ for n, è for e, etc var diacritics = [ {char: 'A', base: /[\300-\306]/g}, {char: 'a', base: /[\340-\346]/g}, {char: 'E', base: /[\310-\313]/g}, {char: 'e', base: /[\350-\353]/g}, {char: 'I', base: /[\314-\317]/g}, {char: 'i', base: /[\354-\357]/g}, {char: 'O', base: /[\322-\330]/g}, {char: 'o', base: /[\362-\370]/g}, {char: 'U', base: /[\331-\334]/g}, {char: 'u', base: /[\371-\374]/g}, {char: 'N', base: /[\321]/g}, {char: 'n', base: /[\361]/g}, {char: 'C', base: /[\307]/g}, {char: 'c', base: /[\347]/g} ] diacritics.forEach(function(letter){ input = input.replace(letter.base, letter.char); }); input = input.replace(/[^a-z0-9 -]/g, '') // remove invalid chars .replace(/\s+/g, '') // collapse whitespace and replace by - .replace(/-+/g, ''); // collapse dashes $("#username").val(input); }); </script> Link to comment Share on other sites More sharing options...
pwFoo Posted November 11, 2016 Author Share Posted November 11, 2016 It could also be done with a PW hook Make username hidden and not required. Hook into form process and set the required user object value server side. 1 Link to comment Share on other sites More sharing options...
dotnetic Posted November 11, 2016 Share Posted November 11, 2016 @modifiedcontent Thank you for your suggestion. But relying on JavaScript is not good. I also think that a rewrite is not needed. I don´t want no username. I just don´t want the username in the form because it could be manipulated. The username should be the same as the email address, but sanitized as pageName. 1 Link to comment Share on other sites More sharing options...
modifiedcontent Posted November 11, 2016 Share Posted November 11, 2016 @pwFoo, could you give a few more pointers how to do that? This 'do it with a PW hook' is still not obvious to me at all. Is there a code example somewhere? If it is possible to have a registration process with just email, that would be great. I'd like to avoid javascript - good point @jmartsch. Link to comment Share on other sites More sharing options...
dotnetic Posted November 11, 2016 Share Posted November 11, 2016 @pwFoo Could you give me an example how to do this? I think this could be of help for others too. EDIT: pls note that I do not want to send the username with the form, because it can be manipulated. See my previous post. 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