Blog

Some of my recent posts and ramblings.

Send account activation emails to users on sign up in Mura

This is a very simple way to send account activation emails and activate the user account once they click the link in their emails in mura. It uses encrypted usernames as the passphrase in the activation emails. You can set this up in 5 min :) by following the instructions below

Look for this snippet in your {theme folder}/display_objects/dsp_edit_profile.cfm. You can copy this file from {siteroot}/{siteid}/includes/ if it’s not there already.

 <input name="inactive" type="hidden" value="1" />

Make sure the value is 1

To send e-mails, edit the {siteroot}/{siteid}/includes/eventHandler.cfc and add this snippet of code.

set your own encryption key.


<cffunction name="onApplicationLoad" output="false">
<cfargument name="$">
<cfargument name="event">

<cfset application.encKey="as7gas6fgas6fga76sfg6gfw6gqfg6fgfwe6fgwe8fg6">

</cffunction>

Here, we send the activation email, I’m using cfusion_encrypt() because the encrypted output only contains alphanumeric characters. This avoids any issues with emails clients or browsers.


<cffunction name="onBeforeUserCreate" output="true">
<cfargument name="$">

<cfset uu.userEmail = $.event("userBean").getemail()>
<cfset uu.username = $.event("userBean").getusername()>

<cfmail to="#uu.userEmail#"
from='#$.siteConfig().getmailserverusername()#'
subject="Activate Your Account on #$.siteConfig().getsite()#"
server="#$.siteConfig().getmailserverip()#"
username="#$.siteConfig().getmailserverusername()#"
password="#$.siteConfig().getmailserverpassword()#"
port="#$.siteConfig().getmailserversmtpport()#"
replyto="#$.siteConfig().getmailserverusernameemail()#"
failto="#$.siteConfig().getmailserverusernameemail()#"
type="html">

<p>Thank you for creating an account on #$.siteConfig().getsite()#. You need to confirm your email to activate your account</p>

<p><a href="http://#$.siteConfig().getdomain()##$.createHREF(filename='accounts/verify')#?passphrase=#cfusion_encrypt(uu.username, application.encKey)#">Click here</a> to activate your account</p>

<p>If you are not able to click directly, copy and paste the URL below:</p>

<p>http://#$.siteConfig().getdomain()##$.createHREF(filename='accounts/verify')#?passphrase=#cfusion_encrypt(uu.username, application.encKey)#</p>

<p>Thanks, <br />
The ArrowConcept Team</p>

</cfmail>

</cffunction>

OK. So, the email is sent, now we need to activate the account when they click on the link. In my activation email, I’m pointing them to accounts/verify. You can change it per your need and create that page in the mura admin. Assign it a separate template and in that template, add this code

<cfparam name="url.passphrase" default="">
<cfoutput>
<cfinclude template="inc/html_head.cfm" />
<body id="#$.getTopID()#" class="oneCol depth#arrayLen($.event('crumbdata'))#">
<div class="container #$.createCSSid($.content('menuTitle'))#">
<cfinclude template="inc/header.cfm" />
</div>
<div id="container" class="#$.createCSSid($.content('menuTitle'))#">
  <div id="content" class="clearfix">
    <div id="primary" class="article">
      #$.dspCrumbListLinks("crumbList","&nbsp;&raquo;&nbsp;")#
            <cfif len(url.passphrase)>
                <cftry>
                  <cfset uu.username = cfusion_decrypt(url.passphrase, application.encKey)>
                <cfcatch type="any">
                </cfcatch>
                </cftry>

                <cfif len(uu.username) and $.getBean("user").checkUsername(uu.username)>
                    <cfset uu.getUserBean=$.getBean("user").loadby(username=uu.username)>
                    <cfset uu.getUserBean.setInactive(0)>
                    <cfset uu.getUserBean.save()>
                    #$.dspBody(body=$.content('body'),crumbList=0,showMetaImage=1)#
<pre>               <cfelse>
               <cflocation url="/" addtoken="no"></pre>
</cfif>
 <cfelse>
 <cflocation url="/" addtoken="no">
 </cfif>
 </div>
 </div>
</div>
<cfinclude template="inc/footer.cfm" />
</body>
</html>
</cfoutput>​

That’s it, if the user is activated, it shows them the message you type in the body of the page. Any other scenario, it redirects to the home page.

Now there are a couple more things you can do to improve the usability of the accounts creation process. Change the create account confirmation message to say that they need to click the activation link in the email to be able to login. In our usability testing, most did not bother reading the message and kept trying to login but the application just gives a username and password do not match notification message. So, need to do one more change to show a message saying, your account is inactive, you need to click the activation link in your email to be able to login.

Back to the eventHandler.cfc file in your includes folder

<cffunction name="LoginWithEmailToo" output="false">
  <cfargument name="username" type="string" required="true" default="">
  <cfargument name="password" type="string" required="true" default="">
  <cfargument name="siteid" type="string" required="false" default="">
  <cfset var rolelist = "" />
  <cfset var rsUser = "" />
  <cfset var user = "" />
  <cfset var group = "" />
  <cfset var lastLogin = now() />
  <cfset var pluginEvent = createObject("component","mura.event").init(arguments) />
  <cfset var strikes = createObject("component","mura.user.userstrikes").init(arguments.username,variables.configBean) />

  <cflogout>
  <cfparam name="session.blockLoginUntil" type="string" default="#strikes.blockedUntil()#" />

  <cfif len(arguments.siteID)>
    <cfset variables.pluginManager.announceEvent('onSiteLogin',pluginEvent)/>
  <cfelse>
    <cfset variables.pluginManager.announceEvent('onGlobalLogin',pluginEvent)/>
  </cfif>
  <cfquery datasource="#application.configBean.getDatasource()#" name="rsUser" username="#variables.configBean.getDBUsername()#" password="#variables.configBean.getDBPassword()#">
  SELECT * FROM tusers WHERE
  (username=<cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(arguments.username)#">
  OR email=<cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(arguments.username)#">)
  AND
  (
    password=<cfqueryparam cfsqltype="cf_sql_varchar" value="#hash(trim(arguments.password))#">
    <cfif not variables.configBean.getEncryptPasswords() and len(trim(arguments.password)) neq 32>
    OR
    password=<cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(arguments.password)#">
    </cfif>
  )
  AND Type = 2
  and inactive=0
  </cfquery>

  <cfif rsUser.RecordCount GREATER THAN 0
    and not strikes.isBlocked()>

      <cfif rsUser.isPublic and (arguments.siteid eq '' or variables.settingsManager.getSite(arguments.siteid).getPublicUserPoolID() neq rsUser.siteid)>

        <cfset strikes.addStrike()>

        <cfreturn false  >
      </cfif>

      <cfset session.blockLoginUntil=""/>

      <cfset loginByQuery(rsUser)/>
      <cfset strikes.clear()>
      <cfif len(arguments.siteID)>
        <cfset variables.pluginManager.announceEvent('onSiteLoginSuccess',pluginEvent)/>
      <cfelse>
        <cfset variables.pluginManager.announceEvent('onGlobalLoginSuccess',pluginEvent)/>
      </cfif>

      <cfreturn true />

  <cfelse>
    <cfif not strikes.isBlocked()>
      <cfset strikes.addStrike()>
    <cfelse>

      <cfif len(arguments.siteID)>
        <cfset variables.pluginManager.announceEvent('onSiteLoginBlocked',pluginEvent)/>
      <cfelse>
        <cfset variables.pluginManager.announceEvent('onGlobalLoginBlocked',pluginEvent)/>
      </cfif>

      <cfset session.blockLoginUntil=strikes.blockedUntil()/>

    </cfif>

        <cfquery datasource="#application.configBean.getDatasource()#" name="rsUserInactive" username="#variables.configBean.getDBUsername()#" password="#variables.configBean.getDBPassword()#">
        SELECT * FROM tusers WHERE
        (username=<cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(arguments.username)#">
        OR email=<cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(arguments.username)#">)
        AND
        (
            password=<cfqueryparam cfsqltype="cf_sql_varchar" value="#hash(trim(arguments.password))#">
            <cfif not variables.configBean.getEncryptPasswords() and len(trim(arguments.password)) neq 32>
            OR
            password=<cfqueryparam cfsqltype="cf_sql_varchar" value="#trim(arguments.password)#">
            </cfif>
        )
        AND Type = 2
        and inactive=1
        </cfquery>

        <cfif rsUserInactive.recordcount>
            <cfset session.logininactive = true>
        </cfif>

  </cfif>

  <cfreturn false />
</cffunction>

<cffunction name="onApplicationLoad" output="false">
    <cfargument name="$">
    <cfargument name="event">

    <cfset $.getBean("userUtility").injectMethod("Login",LoginWithEmailToo)/>

    <cfset application.encKey="as7gas6fgas6fga76sfg6gfw6gqfg6fgfwe6fgwe8fg6">

</cffunction>

This includes code to use email address along with username to login. The first highlighted block does that. You can copy it if you want that feature. For what we are doing now, you need the bottom 2 highlighted code blocks.

And in the {themefolder}/display_objects/dsp_login.cfm, replace this block of text at the top

<cfif request.status eq 'failed'>
 <cfif isDate(session.blockLoginUntil) and session.blockLoginUntil gt now()>
 <cfset request.isBlocked=true />
 <p id="loginMsg" class="error">#variables.rbFactory.getKey('user.loginblocked')#</p>
 <cfelse>
 <cfif isDefined("session.logininactive")>
 <p id="loginMsg" class="error">#variables.rbFactory.getKey('user.logininactive')#</p>
 <cfset structDelete(session, "logininactive")>
 <cfelse>
 <p id="loginMsg" class="error">#variables.rbFactory.getKey('user.loginfailed')#</p>
 </cfif>

 </cfif>
 </cfif>

Phew! Add the ‘user.logininactive’ key in the properties file in resourcebundles folder in your theme. And you’re done.

Obviously there are better ways to do this, plugin would be ideal, with the passphrase generated randomly and stored in a column in the database which is more secure, but this is quite secure in itself and you can make it harder to crack by using email or join date along with the username during encryption and encrypting multiple times.

So, there you go, a simple way to add account activation emails in mura


2 comments.

  1. Travis Kelleher

    Do you know of anyway to prevent new users from registering with an email address thats already being used by another user?

    This could prevent users creating multiple accounts and or prevent new users from accidentally registering with someone else’s email.



Leave a Reply

Your email is never published nor shared. Required fields are marked *

You may use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>