Jump to content

FrontendUser: login, logout and register users / members


pwFoo

Recommended Posts

You should hook into the PW form field.

Take a look at "For example inside a hook fired after form field processed" here

https://bitbucket.org/pwFoo/frontenduser/wiki/Code%20snippets%20/%20Examples

@pwFoo, can't get it to work. Could you please post a code sample?

Edit: got it by creating a "test" field, but not sure how to apply the same code to the "username" field:

$test = $modules->get('InputfieldText');
$test->label = $this->_('Test');
$test->attr('id+name', 'test');
$test->required = 1;
$test->fhSanitizer = 'name';


$test->addHookAfter('processInput', function($event) {
    $currentField = $event->object;
    $value = $currentField->value;
    $sanitizedValue = wire('sanitizer')->pageName($value);
    if ($value != $sanitizedValue) {
     $currentField->error('Your username cannot contain uppercase letters, spaces, accents or special characters');
   }
});
Link to comment
Share on other sites

Thanks, but when I tried 

$uname = $fu->form->get('username');

to retrieve the "username" field, I got a 500 Internal error on 3.0.18:

Error: 	Uncaught Error: Call to a member function get() on null in /home/xxxx/public_html/site/assets/cache/FileCompiler/site/templates/register.php:34

Link to comment
Share on other sites

Yep, same error.

I managed to do what I wanted by adding after line 266 in "FrontendUser.module" the following code:

$value = $field->value;
$sanitizedValue = wire('sanitizer')->pageName($value);
if ($value != $sanitizedValue) {
 $field->error('Your username cannot contain uppercase letters, spaces, accents or special characters.');
}

But of course hacking the module is not the recommended way to proceed.

  • Like 1
Link to comment
Share on other sites

Ok, works for now, but you shouldn't (need to) hack the module...

The $fu should be global available.

 wire()->set('fu', $this);

I also used the mentioned way with my plugins.

    $fieldUser = $fu->form->get('username');
    $fieldUser->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;
        }
    });

So get form field should work...

$fieldUser = $fu->form->get('username');

Is it PW 3.0 related only?

Link to comment
Share on other sites

Here is a little trick how a user the user template to manage things like a /profile/ page to frontend edit a users profile and this could be easy used for a own register logic and let the new user fill in all needed fields....the logic could be implemented via URL segments:

//load module
$fh = $modules->get('FormHelper');
// Template based form, skip fields 'title' and other adminfields, unstyled
$form = $fh->create($templates->get('user'), array(
	'notifications',
	'admin_theme',
	'language',
	'roles',
	'user_email',  //this is where we fill a created token/hash to register the user and verifie via emaillink...
	'confirm'  //confirm link for an admin to set user created stuff puplic
));

and in your template you could use URL segements for very simple or complex logic on registration - in my case i need to register a user but hide his content until an admin activated the user...email from the user itself should have to be validated, too so i've to build my own code here...since i wanna stick to just email + password and let the username created automatical in the background...this is just the basic for such a template nothing fancy here - maybe i wrote a little topic if it is running stable.

Pseudocode for a template file register.php and URL's like:

http://mysite.com/app/regsiter/URLsegment1/UserID/Hash

http://mysite.com/app/register/activate/1235/fb5a44cfc2ecd8b5667a667319b66688

http://mysite.com/app/register/confirm/1235/d41d8cd98f00b204e9800998ecf8427e

// we are using 3 URL segment, so send a 404 if there's more than 1
if($input->urlSegment4) throw new Wire404Exception();

switch ($input->urlSegment1){
  case '':
        // segment 1 is empty so display normal content/register form
        // users could submit the register form and a new user is created two tokens are generated
        // email to the user and to admin
        // user is logged in and can add content - but the content will only published if 
        // his two tokens are created on the first step to validate his email and confirm by an admin
    break;

  case 'activate':
        if ($input->urlSegment2){
           // check for the user id
           if ($input->urlSegment3 == user_email)
           // check for the email token

	   // user has get an email on submit the register form with a token link
           // and he hit this link so publish/activate the user and show him a message
           // delete user_email token
    break;

  case 'confirm':
        if ($input->urlSegment2){
        // check for the user id
           if ($input->urlSegment2 == confirm){
	   // the user is confirmed by an admin and his content is published, delete confirm token
    break;

  default:
    // Anything else? Throw a 404
    throw new Wire404Exception();
}

This is just a example to how to rebuild the functions of the EmailValidation module in a flexible way with URL segments and some additional fields on the user template....AND the awesome form module from pwFoo... ;)

URL segments could setup with an regex in the template settings like this:

regex:^[0-9]*$      //regex for user id
regex:^[a-f0-9]{32}$     //reges for a hash/token

If my project is finished i will provide more and detailed examples with templates and FormHelper!

Best regards mr-fan

  • Like 4
Link to comment
Share on other sites

Aboslutely loving the module!

And would recommend the following changes in FrontendUserRegisterEmailValidation.module

line 127 change to:             

if ($form->fhValue('EmailPreValidation') != wire('session')->get('registerToken') && !wire('session')->get('registerStep')) { //dont throw the initial error...

line 160 insert: 

$form->remove($form->get('EmailPreValidation')); //hide the token field before the email is sent = less distraction
  • Like 2
Link to comment
Share on other sites

Thanks, krisj!

- new version 0.9.0

- added your changes UNTESTED! 

- changed module from alpha to beta

- added PW 3.0 as supported

Please verify and report back!!!

You are brave :) (the suggested edits work for me...)

Thanks for the update!

Link to comment
Share on other sites

On another note, how about a slightly deeper refactoring and adding something like a stepped form option...

some examples:

http://thecodeplayer.com/walkthrough/jquery-multi-step-form-with-progress-bar

http://www.jquery-steps.com/Examples

It would be nice to keep it decoupled from the markup as much as possible of course...

I think it would not take too much work but would be nice..

e.g. simple example like this:

1 - enter name & email

2 - instruction to check email, click on the link/copy token

3 - greetings/congratulations/more instructions screen

It is already possible but you could add a bit more support if you have time

Link to comment
Share on other sites

Very chuffed that it only took a minute to figure out... so here you are a way to quickly change the appearance (by adding your own class programmatically) of the register submit button:

$fu->form->fhSubmitBtn->attr('class', 'first-class-name another-cool-class another-amazing-thing-the-button-does green-button massive-button');

do you have a quick overview of what form elements are available as built in by the way? Not that I don't like codegazeing...

  • Like 1
Link to comment
Share on other sites

In case someone is wondering about the PW 3.0.22 new password field functionality ( InputfieldPasswordComplexify ) compatibility, this is what you will need in the <head>...

<link type="text/css" href="/wire/templates-admin/styles/font-awesome/css/font-awesome.min.css?v=17j" rel="stylesheet">
<script type="text/javascript" src="/wire/modules/Inputfield/InputfieldPassword/complexify/jquery.complexify.min.js"></script>
<script type="text/javascript" src="/wire/modules/Inputfield/InputfieldPassword/complexify/jquery.complexify.banlist.js"></script>
<script type="text/javascript" src="/wire/modules/Jquery/JqueryCore/xregexp.js?v=1466417387"></script>
<script src="/wire/modules/Inputfield/InputfieldPassword/InputfieldPassword.min.js?v=101-1466417387"></script>

 

  • Like 6
Link to comment
Share on other sites

pwFoo, how would you handle a scenario where the registration process has to check for a scenario where:

  • only existing users can register
  • they still have to verify the email they are entering via email-token-submit process
  • they still have to pick a password

so essentially we would have all the "potential/invited" users already in the PW entered as generic usernames with specific emails and then using the frontenduser module to just "activate" these accounts. An invite only system basically..

look forward to any ideas.

many thanks

Link to comment
Share on other sites

I created the pre register validation module because I don't like unused / bot created accounts ;)

Email address based invite needs an database to save the token generated to the email address. So it's more complex. Sometime an invited user don't want to use the invited email address. So... just send an invite token which allows the user to register an account?

 

Link to comment
Share on other sites

56 minutes ago, loukote said:

Krisj,

Do you have their email addresses? If so, could you enter random passwords and tell them to click the "Forgot password" button?

(that's how I approached it recently, no issues reported)

 

 

I thought about hacking something based on forgot pass functionality. But I need to manage the messaging etc. Nice idea though.

Link to comment
Share on other sites

19 hours ago, pwFoo said:

I created the pre register validation module because I don't like unused / bot created accounts ;)

Email address based invite needs an database to save the token generated to the email address. So it's more complex. Sometime an invited user don't want to use the invited email address. So... just send an invite token which allows the user to register an account?

 

the pre-register works really nicely I have to say, keeps the user table clean.

Regards to the invite system  - it could be any other identification info really. There are many scenarios where an organisation could be send say 10 x different "Keys/tokens" to distribute to their own users, that would be a "single use" key/token to register an account for example.

I wonder if you would track this in a separate table or just use an extra field in PW User?

sounds like a fu module using hooks no?

Link to comment
Share on other sites

5 hours ago, mr-fan said:

Is this module an option?

 

yes this would work as a final welcome message indeed.

There are however scenarios (we have a project right now that keeps changing goalpost as they are realising the data structure is different than they originally thought) where there might be other tokens/IDs needed as a means to identify the invitee before they are allowed to register. Sometimes without email. Say manually distributed invite.

Link to comment
Share on other sites

Some posts earlier i wrote i will give an detailed example of how you could use the Formhelper Module from pwFoo to create a register form an build the logic yourself if you need more complex setups.

This is a stripped example from my register.php (please ignore the spelling while i've translated the most parts just quick and dirty for this post...)

Spoiler

<?php
//check for login and start with defaul $out
if($user->isLoggedin()) {
	$out = headline("Wellcome $user->full_name - Your are already logged in...");
	//check if entry is active by user role stays "pending" until user_activation and user_confirmation is done
	if($user->hasRole("pending") && $user->user_confirmation != '') {
		//success message
		$out .= message("Registration was successfull","success");
		//info message if user is not activated
		$out .= message("Please visit your Mailbox to confirm your emailadress!","info");
	}
	if ($user->hasRole("pending") && $user->user_activation != '') {
		//info message if user is not confirmed
		$out .= message("Your account was not activated - please wait.","info");
	}
	if (!$user->hasRole("pending")) {
		$session->redirect($pages->get('6737')->url); //redirect to frontend user overview page
	}

//we are not logged in so show form for registration
} else {
	//change headline for the entry
	$headline = "Register New";

	//load FormHelper to generate the form from the user template
	$fh = $modules->get('FormHelper');
	// Template based form, skip fields 'title' and other adminfields, unstyled
	//....we know how pwFoo's module work
	// take a look at: https://bitbucket.org/pwFoo/formhelper/wiki/Documentation
	
	//special we need to fill the two hash fields in the user we save

	//create confirmation code
	$p1 = new Password();
	$hash1 = $p1->randomBase64String(32);


	//create activation code
	$p2 = new Password();
	$hash2 = $p2->randomBase64String(32);

	//create and show the normal register form....

	//if all form fields are correct validated and checked save the new user with the two hashes...and the role pending

	//you could check now on every page like
	if($user->hasRole("pending")) {
		//do something	
	}

}

//now follows the URL Segment magic

/*
 * We are using URL segments to handle activation and confirmation of new users
 *
 * URL's like http://mysite.com/app/register/URLsegment1/UserID/Hash
 *
 * URL segment 1 = activate or confirm
 * URL segment 2 = User ID
 * URL segment 3 = generated Hash
 *
 */

// we are using 3 URL segment, so send a 404 if there's more than 1
if($input->urlSegment4) throw new Wire404Exception();

switch ($input->urlSegment1){
  case '':
        // segment 1 is empty so display normal content/register form
        // users could submit the register form and a new user is created two tokens are generated
        // email to the user and to admin
        // user is logged in and can add content - but the content will only published if
        // his two tokens are created on the first and second step to validate his email and activated by an admin
		$content .= $out;
    break;

  case 'confirm':
		//set headline
		$headline = "Confirm User";

		//ceck for the user id
		if ($input->urlSegment2) {
			if ($input->urlSegment2 == $users->get($input->urlSegment2)->id) {

				//user found so get the right user to var
				$u = $users->get($input->urlSegment2);

				//check for the hash
				if ($input->urlSegment3) {
					if ($input->urlSegment3 == $u->user_activation) {

						//proceed activation of the user account

						//send the admin an email for user activation
						//confirmation url
						$confirm_url = $page->httpUrl.'confirm/'.$u->id.'/'.$u->user_confirmation;

						//html email template
						$emailContentHtml = '<html>
												<body>
													<p>My great Message to the Admin with details about the new user!<br>
													<a href="'.$confirm_url.'">Activate a New User Klick the Link!</a></p>
												</body>
											</html>';

						//send mail to the admin from the users select_mr field
						$mail = wireMail();
						$mail->to($adminmail);
						$mail->from($u->email);
						$mail->fromName($u->full_name);
						$mail->subject('Activate a new User');
						$mail->bodyHTML($emailContentHtml);
						$mail->send();

						//success message showed up if the user klick on the confirmation link
						$content .= message("You have confirmed your emailadress - the Admin get a email!","success");

						//delete user_activation hash
						$u->of(false);
						$u->user_activation = '';
						$u->save();
						$u->of(true);

					} else {
						//hash not found debug only
						$content .= message("Hash $input->urlSegment3 not found!","danger");
					}
				} else {
					//hash not there debug only
					$content .= message("Hash not there!","danger");
				}
			} else {
				//user id not found debug only
				$content .= message("User ID $input->urlSegment2 not found!","danger");
			}
		} else {
			//user id not there debug only
			$content .= message("Die User ID wurde NICHT angegeben!","danger");
		}

    break;

  case 'activate':
		//set headline
		$headline = "Activate User";

		//ceck for the user id
		if ($input->urlSegment2) {
			if ($input->urlSegment2 == $users->get($input->urlSegment2)->id) {

				//user found so get the right user to var
				$u = $users->get($input->urlSegment2);

				//check for the hash
				if ($input->urlSegment3) {
					if ($input->urlSegment3 == $u->user_confirmation) {

						//proceed activation of the user account

						//send the user an email that his accout is activated and link to login page
						//html email template
						$emailContentHtml = '<html>
												<body>
													<p>Dear User your account was activated<br>
													<a href="'.$app_root->httpUrl.'">Go for it...</a></p>
												</body>
											</html>';

						//send mail to the admin from the users select_mr field
						$mail = wireMail();
						$mail->to($u->email);
						$mail->from($adminemail);
						$mail->fromName($adminname);
						$mail->subject('Activation completed');
						$mail->bodyHTML($emailContentHtml);
						$mail->send();

						//success message showed up if the admin klick on the activation link
						$mr = $u->select_mr->title;
						$content .= message("The Account is now activated!","success");

						//delete user_activation hash
						$u->of(false);
						$u->user_confirmation = '';
						$u->removeRole('pending');
						$u->save();
						$u->of(true);

					} else {
						//hash not found debug only
						$content .= message("Hash $input->urlSegment3 not found!","danger");
					}
				} else {
					//hash not there debug only
					$content .= message("Hash not there!","danger");
				}
			} else {
				//user id not found debug only
				$content .= message("User ID $input->urlSegment2 not found!","danger");
			}
		} else {
			//user id not there debug only
			$content .= message("User ID not there","danger");
		}
    break;

  default:
    // Anything else? Throw a 404
    throw new Wire404Exception();
}

 

Best Regards mr-fan

  • Like 2
Link to comment
Share on other sites

On 6/23/2016 at 10:09 AM, mr-fan said:

Is this module an option?

 

so theEmail New User does not want to work with FronendUser registration... even though the "send automatic email" is turned on... head scratch in progress....

Link to comment
Share on other sites

11 minutes ago, cstevensjr said:

@krisj, You may want to post this information in the support forum for that Module.

 

 

Thank you for the suggestion @cstevensjr :P 

1 hour ago, krisj said:

so theEmail New User does not want to work with FronendUser registration... even though the "send automatic email" is turned on... head scratch in progress....

so here is the solution from that thread (

)

except it needs to be:

$modules->get('EmailNewUser');

problem solved....

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...