Browse Source

Added email verify feature for self signup accounts.

Martin Edenhofer 8 years ago
parent
commit
5adba8c9d0

+ 8 - 4
app/assets/javascripts/app/controllers/_application_controller.coffee

@@ -223,7 +223,7 @@ class App.Controller extends Spine.Controller
     # remember requested url
     if !checkOnly
       location = window.location.hash
-      if location isnt '#login' && location isnt '#logout' && location isnt '#keyboard_shortcuts'
+      if location && location isnt '#login' && location isnt '#logout' && location isnt '#keyboard_shortcuts'
         @Config.set('requested_url', location)
 
     return false if checkOnly
@@ -559,16 +559,20 @@ class App.Controller extends Spine.Controller
     return if !@initLoadingDoneDelay
     @clearDelay(@initLoadingDoneDelay)
 
+  renderScreenSuccess: (data) ->
+    App.TaskManager.touch(@task_key) if @task_key
+    @html App.view('generic/error/success')(data)
+
   renderScreenError: (data) ->
-    App.TaskManager.touch(@task_key)
+    App.TaskManager.touch(@task_key) if @task_key
     @html App.view('generic/error/generic')(data)
 
   renderScreenNotFound: (data) ->
-    App.TaskManager.touch(@task_key)
+    App.TaskManager.touch(@task_key) if @task_key
     @html App.view('generic/error/not_found')(data)
 
   renderScreenUnauthorized: (data) ->
-    App.TaskManager.touch(@task_key)
+    App.TaskManager.touch(@task_key) if @task_key
     @html App.view('generic/error/unauthorized')(data)
 
   locationVerify: (e) =>

+ 2 - 2
app/assets/javascripts/app/controllers/default_route.coffee

@@ -28,5 +28,5 @@ class DefaultRouter extends App.Controller
 
     @navigate '#dashboard', true
 
-App.Config.set( '', DefaultRouter, 'Routes' )
-App.Config.set( '/', DefaultRouter, 'Routes' )
+App.Config.set('', DefaultRouter, 'Routes')
+App.Config.set('/', DefaultRouter, 'Routes')

+ 55 - 0
app/assets/javascripts/app/controllers/email_verify.coffee

@@ -0,0 +1,55 @@
+class Index extends App.Controller
+  constructor: ->
+    super
+    return if !@authenticate()
+    @verifyCall()
+
+  verifyCall: =>
+    @ajax(
+      id:          'email_verify'
+      type:        'POST'
+      url:         @apiPath + '/users/email_verify'
+      data:        JSON.stringify(token: @token)
+      processData: true
+      success:     @success
+      error:       @error
+    )
+
+  success: =>
+    new Success(el: @el)
+
+  error: =>
+    new Fail(el: @el)
+
+class Success extends App.ControllerContent
+  constructor: ->
+    super
+    @render()
+
+    # rerender view, e. g. on language change
+    @bind 'ui:rerender', =>
+      @render()
+
+  render: =>
+    @renderScreenSuccess(
+      detail: 'Woo hoo! Your email is verified!'
+    )
+    delay = =>
+      @navigate '#'
+    @delay(delay, 20500)
+
+class Fail extends App.ControllerContent
+  constructor: ->
+    super
+    @render()
+
+    # rerender view, e. g. on language change
+    @bind 'ui:rerender', =>
+      @render()
+
+  render: =>
+    @renderScreenError(
+      detail: 'Unable to verify email. Please contact your administrator.'
+    )
+
+App.Config.set('email_verify/:token', Index, 'Routes')

+ 1 - 1
app/assets/javascripts/app/controllers/login.coffee

@@ -136,4 +136,4 @@ class Index extends App.ControllerContent
       600
     )
 
-App.Config.set( 'login', Index, 'Routes' )
+App.Config.set('login', Index, 'Routes')

+ 7 - 7
app/assets/javascripts/app/controllers/password_reset.coffee

@@ -64,8 +64,8 @@ class Index extends App.ControllerContent
       if data.token && @Config.get('developer_mode') is true
         redirect = =>
           @navigate "#password_reset_verify/#{data.token}"
-        @delay( redirect, 2000 )
-      @render( sent: true )
+        @delay(redirect, 2000)
+      @render(sent: true)
 
     else
       @$('[name=username]').val('')
@@ -75,7 +75,7 @@ class Index extends App.ControllerContent
       )
       @formEnable( @el.find('.form-password') )
 
-App.Config.set( 'password_reset', Index, 'Routes' )
+App.Config.set('password_reset', Index, 'Routes')
 
 class Verify extends App.ControllerContent
   events:
@@ -105,10 +105,10 @@ class Verify extends App.ControllerContent
       url:         @apiPath + '/users/password_reset_verify'
       data:        JSON.stringify(params)
       processData: true
-      success:     @render_change
+      success:     @renderChange
     )
 
-  render_change: (data) =>
+  renderChange: (data) =>
     if data.message is 'ok'
       configure_attributes = [
         { name: 'password', display: 'Password', tag: 'input', type: 'password', limit: 100, null: false, class: 'input',  },
@@ -161,10 +161,10 @@ class Verify extends App.ControllerContent
       url:         @apiPath + '/users/password_reset_verify'
       data:        JSON.stringify(params)
       processData: true
-      success:     @render_changed
+      success:     @renderChanged
     )
 
-  render_changed: (data, status, xhr) =>
+  renderChanged: (data, status, xhr) =>
     if data.message is 'ok'
       App.Auth.login(
         data:

+ 2 - 0
app/assets/javascripts/app/controllers/widget/translation_inline.coffee

@@ -49,6 +49,7 @@ class Widget extends App.Controller
       .on 'blur.translation', '.translation', (e) ->
         element = $(e.target)
         source = element.attr('title')
+        return if !source
 
         # get new translation
         translation_new = element.text()
@@ -62,6 +63,7 @@ class Widget extends App.Controller
         App.i18n.setMap(source, translation_new)
 
         # replace rest in page
+        source = source.replace('\'', '\\\'')
         $(".translation[title='#{source}']").text(translation_new)
 
         # update permanent translation mapString

+ 82 - 0
app/assets/javascripts/app/controllers/widget/user_signup_check.coffee

@@ -0,0 +1,82 @@
+class Widget extends App.Controller
+  constructor: ->
+
+    # for browser test
+    App.Event.bind('user_signup_verify', (user) ->
+      new Modal(user: user)
+      'user_signup_verify'
+    )
+
+    App.Event.bind('auth:login', (user) =>
+      return if !user
+      @verifyLater(user.id)
+      'user_signup_verify'
+    )
+    currentUserId = App.Session.get('id')
+    return if !currentUserId
+    @verifyLater(currentUserId)
+
+  verifyLater: (userId) =>
+    delay = =>
+      @verify(userId)
+    @delay(delay, 5000, 'user_signup_verify_dialog')
+
+  verify: (userId) ->
+    return if !userId
+    user = App.User.find(userId)
+    return if user.source isnt 'signup'
+    return if user.verified is true
+    currentTime = new Date().getTime()
+    createdAt = Date.parse(user.created_at)
+    diff = currentTime - createdAt
+    max = 1000 * 60 * 30 # show message if account is older then 30 minutes
+    return if diff < max
+    new Modal(user: user)
+
+class Modal extends App.ControllerModal
+  backdrop: false
+  keyboard: false
+  head: 'Account not verified'
+  small: true
+  buttonClose: false
+  buttonCancel: false
+  buttonSubmit: 'Resend verification email'
+
+  constructor: ->
+    super
+
+  content: =>
+    if !@sent
+      return App.i18n.translateContent('Your account is not verified. Please click the link in the verification email.')
+    content = App.i18n.translateContent('We\'ve sent an email to _%s_. Click the link in the email to verify your account.', @user.email)
+    content += '<br><br>'
+    content += App.i18n.translateContent('If you don\'t see the email, check other places it might be, like your junk, spam, social, or other folders.')
+    content
+
+  onSubmit: =>
+    @ajax(
+      id:          'email_verify_send'
+      type:        'POST'
+      url:         @apiPath + '/users/email_verify_send'
+      data:        JSON.stringify(email: @user.email)
+      processData: true
+      success:     @success
+      error:       @error
+    )
+
+  success: (data) =>
+    @sent = true
+    @update()
+
+    # if in developer mode, redirect to verify
+    if data.token && @Config.get('developer_mode') is true
+      redirect = =>
+        @close()
+        @navigate "#email_verify/#{data.token}"
+      App.Delay.set(redirect, 4000)
+
+  error: =>
+    @contentInline = App.i18n.translateContent('Unable to send verify email.')
+    @update()
+
+App.Config.set('user_signup', Widget, 'Widgets')

+ 1 - 1
app/assets/javascripts/app/views/generic/error/generic.jst.eco

@@ -1,4 +1,4 @@
 <div class="fullscreenMessage">
   <%- @Icon('diagonal-cross', 'icon-error') %>
-  <h2><% if @status isnt undefined: %><%- @T('Status Code') %>: <%= @status %>. <% end %><%= @detail %></h2>
+  <h2><% if @status isnt undefined: %><%- @T('Status Code') %>: <%= @status %>. <% end %><%- @T(@detail) %></h2>
 </div>

+ 4 - 0
app/assets/javascripts/app/views/generic/error/success.jst.eco

@@ -0,0 +1,4 @@
+<div class="fullscreenMessage">
+  <%- @Icon('checkmark') %>
+  <h2><%- @T(@detail) %></h2>
+</div>

+ 109 - 5
app/controllers/users_controller.rb

@@ -73,6 +73,10 @@ class UsersController < ApplicationController
 
       # if it's a signup, add user to customer role
       if !current_user
+        if !params[:signup]
+          render json: { error_human: 'Only signup is possible!' }, status: :unprocessable_entity
+          return
+        end
         user.updated_by_id = 1
         user.created_by_id = 1
 
@@ -100,6 +104,9 @@ class UsersController < ApplicationController
         user.role_ids  = role_ids
         user.group_ids = group_ids
 
+        # remember source (in case show email verify banner)
+        user.source = 'signup'
+
       # else do assignment as defined
       else
 
@@ -150,14 +157,11 @@ class UsersController < ApplicationController
 
       # send email verify
       if params[:signup] && !current_user
-        token = Token.create(action: 'EmailVerify', user_id: user.id)
+        result = User.signup_new_token(user)
         NotificationFactory::Mailer.notification(
           template: 'signup',
           user: user,
-          objects: {
-            token: token,
-            user: user,
-          }
+          objects: result
         )
       end
       user_new = User.find(user.id).attributes_with_associations
@@ -393,6 +397,106 @@ class UsersController < ApplicationController
 
 =begin
 
+Resource:
+POST /api/v1/users/email_verify
+
+Payload:
+{
+  "token": "SoMeToKeN",
+}
+
+Response:
+{
+  :message => 'ok'
+}
+
+Test:
+curl http://localhost/api/v1/users/email_verify.json -v -u #{login}:#{password} -H "Content-Type: application/json" -X POST -d '{"token": "SoMeToKeN"}'
+
+=end
+
+  def email_verify
+    if !params[:token]
+      render json: { message: 'No token!' }, status: :unprocessable_entity
+      return
+    end
+
+    user = User.signup_verify_via_token(params[:token], current_user)
+    if !user
+      render json: { message: 'Invalid token!' }, status: :unprocessable_entity
+      return
+    end
+
+    render json: { message: 'ok', user_email: user.email }, status: :ok
+  end
+
+=begin
+
+Resource:
+POST /api/v1/users/email_verify_send
+
+Payload:
+{
+  "email": "some_email@example.com"
+}
+
+Response:
+{
+  :message => 'ok'
+}
+
+Test:
+curl http://localhost/api/v1/users/email_verify_send.json -v -u #{login}:#{password} -H "Content-Type: application/json" -X POST -d '{"email": "some_email@example.com"}'
+
+=end
+
+  def email_verify_send
+
+    if !params[:email]
+      render json: { message: 'No email!' }, status: :unprocessable_entity
+      return
+    end
+
+    # check is verify is possible to send
+    user = User.find_by(email: params[:email].downcase)
+    if !user
+      render json: { error_human: 'No such user!' }, status: :unprocessable_entity
+      return
+    end
+
+    #if user.verified == true
+    #  render json: { error_human: 'Already verified!' }, status: :unprocessable_entity
+    #  return
+    #end
+
+    token = Token.create(action: 'Signup', user_id: user.id)
+
+    result = User.signup_new_token(user)
+    if result && result[:token]
+      user = result[:user]
+      NotificationFactory::Mailer.notification(
+        template: 'signup',
+        user: user,
+        objects: result
+      )
+
+      # only if system is in develop mode, send token back to browser for browser tests
+      if Setting.get('developer_mode') == true
+        render json: { message: 'ok', token: result[:token].name }, status: :ok
+        return
+      end
+
+      # token sent to user, send ok to browser
+      render json: { message: 'ok' }, status: :ok
+      return
+    end
+
+    # unable to generate token
+    render json: { message: 'failed' }, status: :ok
+  end
+
+=begin
+
 Resource:
 POST /api/v1/users/password_reset
 

Some files were not shown because too many files changed in this diff