B4J Tutorial [ABMaterial]: Creating a Sign In Modal Dialog with Options: Part 3.1

Discussion in 'B4J Tutorials' started by Mashiane, Feb 23, 2018.

  1. Mashiane

    Mashiane Expert Licensed User

    Hi again

    This is the final installment of this important tutorial and is a continuation of part 3, which dealt with the Sign Up process.

    So far, we have demonstrated how one can Sign Up, get Forgot Password, Change Password with the respective notifications and the ABM app sending emails during the process. What was left out, which is extremely crucial was how one can during the registration process ensure that only Signed Up uses can use the app.

    This then brings us to the final step, activating a user account after the user has done a Sign Up.



    Goal

    Any user who is Signed Up but who has not activated their account by confirming their email, will not be able to use the app.


    1. A user signs up to use the app
    2. The app sends a user a HTML email to click an activation link to activate their account
    3. Once this activation link is selected by the user, the account is activated.

    Reproduction

    1. Ensure each user has a unique GUID on Sign Up

    To be able to achieve this, each user being signed up should have a unique identifier. For this an extra field was created in the backend user database to store the user GUID.

    Code:
    Sub GUID As String
        
    Dim jo As JavaObject
        
    Return jo.InitializeStatic("java.util.UUID").RunMethod("randomUUID"Null)
    End Sub
    When the user record is saved, a "guid" field is updated using this sub. Any in-active user will not be able to sign into the app.

    2. Sending the activation email to the user

    As the first name, email address are specified by the user during Sign Up, these details, including the guid for the user are emailed in html format to the user so that they can click on a link to activate the account.

    activateemail.png

    To achieve this email being sent, an inline html template was used to send the email. Inside this email was a link to the app, inside "Activate My Account", that has the email address and the GUID for this particular user. When a user clicks the link, the app is opened and the email and GUID are used to activate the account. A GUID should match to its email address.

    Code:
    Sub EmailActivation(semail As String,subject As StringAs ResumableSub
        
    'define the email to search for
        Dim jSQL As SQL = SQLGet
        
    Dim w As Map = CreateMap("email=":semail)
        
    Dim uMap As Map = SQLSelectRecordWhereMap(jSQL,"users", w)
        
    If uMap.IsInitialized = False Then Return False
        
    Dim firstname As String = uMap.GetDefault("firstname","")
        
    Dim sGUID As String = uMap.GetDefault("guid","")
        
    'get the activate.html
        Dim html As String = File.ReadString(File.dirapp,"activate.html")
        
    'update the details
        html = html.Replace("{{user}}",firstname)
        html = html.Replace(
    "{{appname}}",AppName)
        
    Dim sLink As String = $"http://localhost:51049/askteencoach/frmLogin/frmLogin.html?email=${semail}&guid=${sGUID}"$
        html = html.Replace(
    "{{link}}",sLink)
        html = html.Replace(
    "=","=3D")
        
    Try
            
    Dim es As Map = GetEmailSettings
            
    Dim sserver As String = es.GetDefault("server","")
            
    Dim sport As String = es.GetDefault("port","25")
            
    Dim susername As String = es.GetDefault("username","")
            
    Dim spassword As String = es.GetDefault("password","")
            
    Dim sStartTLSMode As String = es.GetDefault("starttlsmode","0")
            
    Dim sUseSSL As String = es.GetDefault("usessl","0")
            
    'start sending the email
            esmtp.Initialize(sserver, sport, susername, spassword, "smtp")
            esmtp.To.Add(semail)
            esmtp.HtmlBody = 
    True
            esmtp.Body = html
            esmtp.Subject = subject
            
    If sStartTLSMode = "1" Then
                esmtp.StartTLSMode = 
    True
            
    Else
                esmtp.StartTLSMode = 
    False
            
    End If
            
    If sUseSSL = "1" Then
                esmtp.UseSSL = 
    True
            
    Else
                esmtp.UseSSL = 
    False
            
    End If
            
    Wait For (esmtp.Send) smtp_MessageSent(Success As Boolean)
            
    Return Success
        
    Catch
            
    Return False
        
    End Try
    End Sub
    In production, the localhost will be changed to my domain off course. From the above link, the frmLogin screen is being opened, meaning that all the activation code should be in the frmLogin page, though the call to send the email to the user should be in the Sign Up screen.

    This means, the Next button code for the Sign Up button needed to be changed to meet this requirement.

    frmCreateAccount.btnNext

    Code:
    Sub btnNEXT_Clicked(Target As String)
        
    If contLoginSignUp = False Then Return
        
    Dim m As Map = contLoginGetContents
        
    Dim semail As String = m.GetDefault("email","")
        
    Dim rs As ResumableSub = ABMShared.EmailActivation(semail,"askteencoach: Activate Account")
        
    Wait For (rs) Complete (Result As Boolean)
        
    If Result = False Then Return
        ABMShared.NavigateToPage(ws, ABMPageId, 
    "../frmTellCreateAccount/frmTellCreateAccount.html")
    End Sub
    This first validates and verifies the entered data and saves it. Then read the user details, sends the activation email and once the email has been sent, tell the user with another modal sheet that the account was registered.

    3. The user has clicked the Activate My Account link

    This opens up the Sign In screen, if the email and guid exists, the account is activated so that the user can continue using the app.

    frmLogin.ConnectPage

    Code:
    Public Sub ConnectPage()
        
    'connect navigation bar
        ConnectNavigationBar
        
    'add components for the page
        AdminAccess
        
    page.SetFontStack("arial,sans-serif")
        
    ' this page uses uploads, so needs some settings
        page.ws.session.SetAttribute("abmcallback", Me)
        
    page.ws.session.SetAttribute("abmdownloadfolder", DownloadFolder)
        
    page.ws.session.SetAttribute("abmmaxsize", DownloadMaxSize)
        
    page.AddModalSheetTemplate(msLoginBuild)
        
    page.Refresh ' IMPORTANT
        ' NEW, because we use ShowLoaderType=ABM.LOADER_TYPE_MANUAL
        page.FinishedLoading 'IMPORTANT
        page.RestoreNavigationBarPosition
        
    page.ShowModalSheet("msLogin")
        ActivateAccount
    End Sub
    From above, the modal sheet to login is built and opened and then the ActivateAccount sub is called.

    Code:
    Private Sub ActivateAccount()
        
    'get the details that need to be activated
        Dim email As String = ws.UpgradeRequest.GetParameter("email")
        
    Dim guid As String = ws.UpgradeRequest.GetParameter("guid")
        
    If email = "" Or guid = "" Then Return
        
    'define the search criteria, fields must equal
        Dim w As Map
        w.Initialize
        w.put(
    "email="email)
        w.put(
    "guid=", guid)
        
    'Get connection from current pool if MySQL/MSSQL
        Dim jSQL As SQL = ABMShared.SQLGet
        
    'find the user meeting this email and guid
        Dim UserMap As Map = ABMShared.SQLSelectRecordWhereMap(jSQL,"users", w)
        
    If UserMap.IsInitialized = False Then
            ABMShared.Warn(
    page,"The User specified could not be found in our records, this account cannot be activated.")
            
    Return
        
    End If
        
    'The email has been found, update the passwords
        UserMap.put("useractive","1")
        
    Dim bUpdate As Boolean = ABMShared.SQLRecordUpdate(jSQL, "users", UserMap, "email"email)
        
    If bUpdate = True Then
            myToastId = myToastId + 
    1
            
    page.ShowToast("toast" & myToastId, "toastgreen""Account activated successfully."3000,False)
            
    'Close the connection to the database
            ABMShared.SQLClose(jSQL)
            
    Return
        
    Else
            myToastId = myToastId + 
    1
            
    page.ShowToast("toast" & myToastId, "toastred""Account could NOT be activated successfully."3000,False)
            
    'Close the connection to the database
            ABMShared.SQLClose(jSQL)
            
    Return
        
    End If
    End Sub
    This method reads the email and the GUID, verifies those against the underlying database and then updates the useractive field to 1, meaning that the user can sign in. Remember, from part 1 of this article, clicking the Next button on the Sign In screen validates and verifies the user. If the account is in-active, the user is kept out.

    Code:
    Private Sub msLoginSignIn() As Boolean
        
    'define a map to hold the form contents
        Dim m As Map
        
    'read the file contents to a map
        m = msLoginGetContents
        
    'validate the form contents where required
        If msLoginValidate(m) = False Then
            
    Return False
        
    End If
        
    'the form contents are ok, continue with the sign in process
        'define the search criteria, fields must equal
        page.pause
        
    Dim w As Map
        
    Dim nk As String
        
    Dim nv As String
        w.Initialize
        
    For Each strKey As String In m.Keys
            nv = m.Get(strKey)
            nk = strKey & 
    "="
            w.put(nk, nv)
        
    Next
        
    'Get connection from current pool if MySQL/MSSQL
        Dim jSQL As SQL = ABMShared.SQLGet
        
    Dim UserMap As Map = ABMShared.SQLSelectRecordWhereMap(jSQL,"users", w)
        
    If UserMap.IsInitialized = False Then
            
    page.resume
            ABMShared.Warn(
    page,"The Sign In credentials you have provided are incorrect. Please check them and try to sign in again.")
            ws.Session.SetAttribute(
    "IsAuthorized""")
            
    Return False
        
    End If
        
    Dim uactive As String = UserMap.get("useractive")
        ws.Session.SetAttribute(
    "UserActive", uactive)
        
    If uactive = 0 Then
            
    page.resume
            ABMShared.Warn(
    page,"Your Profile is currently in-active, please contact the App Administrator.")
            
    Return False
        
    End If
        
    Dim uemail As String = UserMap.get("email")
        ws.Session.SetAttribute(
    "UserEmail", uemail)
        
    Dim uid As String = UserMap.get("id")
        ws.Session.SetAttribute(
    "UserID", uid)
        
    Dim utype As String = UserMap.get("groupid")
        ws.Session.SetAttribute(
    "UserType", utype)
        ws.Session.SetAttribute(
    "IsAuthorized""true")
        ws.Session.SetAttribute(
    "authType""local")
        
    'Build the permissions
        ABMShared.PermissionsL.Initialize
        ABMShared.Permissions.Initialize
        ABMShared.PermissionsL = ABMShared.SQLExecuteMaps(jSQL,
    "select * from GroupPermissions where groupid = ?"Array As String(utype))
        
    For Each permission As Map In ABMShared.PermissionsL
            
    Dim spageName As String = permission.get("pagename")
            spageName = spageName.tolowercase
            ABMShared.Permissions.put(spageName,permission)
        
    Next
        
    'Close the connection to the database
        ABMShared.SQLClose(jSQL)
        
    page.resume
        
    Return True
    End Sub
    frmLogin.btnNext

    Code:
    'An ABMButton has been clicked
    Sub btnNext_Clicked(Target As String)
        
    If msLoginSignIn = False Then Return
        ABMShared.NavigateToPage(ws, ABMPageId, 
    "../frmMainMenu/frmMainMenu.html")
    End Sub
    When everything is fine, account activated, the frmMainMenu is shown.

    That's all folks. This has been an eye opening chapter for me. :);)

    Ta!
     

    Attached Files:

  2. XbNnX_507

    XbNnX_507 Active Member Licensed User

    Hi, Minute 1:02 to 1:07 when you click the activate button at the mail...
    How did you managed to hide the url parameters email and Guid ?
    I've read the modules you uploaded but i just can't get my head around it.
     
  3. Mashiane

    Mashiane Expert Licensed User

    Ohh that is inside the HTML email that gets sent. There should be a template of that inside the project files.
     
    XbNnX_507 likes this.
  4. XbNnX_507

    XbNnX_507 Active Member Licensed User

  5. Mashiane

    Mashiane Expert Licensed User

    I see, I was watching this on my cell so it was too fast. I think its the browser that does that, I dont remember doing anything specific. I'm using Opera.

    Remember, the GUID on activation is compared to an existing GUID saved in the db for that particular email and thus both the email and GUID are verified to that db record and not any two should be the same. So if the email and the GUID dont match to the activation wont happen. One thing one could do is to hash the GUID before saving and sending it and then decode this and compare results.

    Sorry cant help much with that.
     
    XbNnX_507 likes this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice