diff --git a/.gitignore b/.gitignore index 80b096a..866b771 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ config/settings.local.yml config/settings/*.local.yml config/environments/*.local.yml + +# Ignore application configuration +/config/application.yml diff --git a/Gemfile b/Gemfile index 06f6764..3d72e7c 100644 --- a/Gemfile +++ b/Gemfile @@ -10,6 +10,7 @@ gem "bootstrap-will_paginate", "1.0.0" gem "coffee-rails", "~> 4.2" gem "config" gem "faker", "1.9.1" +gem "figaro" gem "jbuilder", "~> 2.5" gem "jquery-rails", "~> 4.3", ">= 4.3.3" gem "puma", "~> 3.11" diff --git a/Gemfile.lock b/Gemfile.lock index 03bb1f1..f39b4b1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -103,6 +103,8 @@ GEM faker (1.9.1) i18n (>= 0.7) ffi (1.9.25) + figaro (1.1.1) + thor (~> 0.14) globalid (0.4.1) activesupport (>= 4.2.0) i18n (1.0.1) @@ -238,6 +240,7 @@ DEPENDENCIES coffee-rails (~> 4.2) config faker (= 1.9.1) + figaro jbuilder (~> 2.5) jquery-rails (~> 4.3, >= 4.3.3) listen (>= 3.0.5, < 3.2) diff --git a/app/controllers/account_activations_controller.rb b/app/controllers/account_activations_controller.rb new file mode 100644 index 0000000..ae675ac --- /dev/null +++ b/app/controllers/account_activations_controller.rb @@ -0,0 +1,14 @@ +class AccountActivationsController < ApplicationController + def edit + user = User.find_by email: params[:email] + if user && !user.activated? && user.authenticated?(:activation, params[:id]) + user.activated + log_in user + flash[:success] = t "account_activated" + redirect_to user + else + flash[:danger] = t "account_activate_invalid" + redirect_to root_url + end + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 3d8a34e..03428d7 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -4,9 +4,16 @@ def new; end def create @user = User.find_by email: params[:session][:email].downcase if @user&.authenticate params[:session][:password] - log_in @user - remember_me - redirect_back_or @user + if @user.activated? + log_in @user + remember_me + redirect_back_or @user + else + message = t "activation_message1" + message += t "activation_message2" + flash[:warning] = message + redirect_to root_url + end else flash.now[:danger] = t ".error_login" render :new diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index df99185..af682e6 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -4,7 +4,7 @@ class UsersController < ApplicationController before_action :correct_user, only: [:edit, :update] before_action :admin_user, only: :destroy def index - @users = User.paginate page: params[:page], + @users = User.where(activated: true).paginate page: params[:page], per_page: Settings.total_user_per_page end @@ -15,15 +15,17 @@ def new def create @user = User.new user_params if @user.save - log_in @user - flash[:success] = t ".title_sample" - redirect_to @user + @user.send_activation_email + flash[:info] = t "activation_mesage" + redirect_to root_url else render :new end end - def show; end + def show + redirect_to(root_url) && return unless @user.activated == true + end def edit; end diff --git a/app/helpers/account_activations_helper.rb b/app/helpers/account_activations_helper.rb new file mode 100644 index 0000000..c4d5ac7 --- /dev/null +++ b/app/helpers/account_activations_helper.rb @@ -0,0 +1,2 @@ +module AccountActivationsHelper +end diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb index b093be3..87cedc8 100644 --- a/app/helpers/sessions_helper.rb +++ b/app/helpers/sessions_helper.rb @@ -18,7 +18,7 @@ def current_user @current_user ||= User.find_by id: user_id elsif user_id = cookies.signed[:user_id] user = User.find_by id: user_id - if user&.authenticated? cookies[:remember_token] + if user&.authenticated?(:remember, cookies[:remember_token]) log_in user @current_user = user end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 3c34c81..115c49c 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,4 +1,4 @@ class ApplicationMailer < ActionMailer::Base - default from: "from@example.com" + default from: "duong147nguyen@gmail.com" layout "mailer" end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb new file mode 100644 index 0000000..7ea11e1 --- /dev/null +++ b/app/mailers/user_mailer.rb @@ -0,0 +1,12 @@ +class UserMailer < ApplicationMailer + def account_activation user + @user = user + mail to: user.email, subject: t("account_activation") + end + + def password_reset + @greeting = "Hi" + + mail to: "to@example.org" + end +end diff --git a/app/models/user.rb b/app/models/user.rb index c2df066..220ed88 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,7 +1,9 @@ class User < ApplicationRecord VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i attr_reader :remember_token + attr_accessor :activation_token before_save :email_downcase + before_create :create_activation_digest has_secure_password validates :name, presence: true, @@ -18,9 +20,10 @@ def remember update_attributes remember_digest: User.digest(remember_token) end - def authenticated? remember_token - return false if remember_digest.nil? - BCrypt::Password.new(remember_digest).is_password?(remember_token) + def authenticated? attribute, token + digest = send "#{attribute}_digest" + return false if digest.nil? + BCrypt::Password.new(digest).is_password? token end def forget @@ -31,6 +34,14 @@ def current_user? user user == self end + def activated + update_columns activated: true, activated_at: Time.zone.now + end + + def send_activation_email + UserMailer.account_activation(self).deliver_now + end + class << self def digest string cost = if ActiveModel::SecurePassword.min_cost @@ -51,4 +62,9 @@ def new_token def email_downcase self.email = email.downcase end + + def create_activation_digest + self.activation_token = User.new_token + self.activation_digest = User.digest activation_token + end end diff --git a/app/views/user_mailer/account_activation.html.erb b/app/views/user_mailer/account_activation.html.erb new file mode 100644 index 0000000..32f2027 --- /dev/null +++ b/app/views/user_mailer/account_activation.html.erb @@ -0,0 +1,8 @@ +

<%= t ".title" %>

+ +

<%= t ".hi" %><%= @user.name %>,

+ +

<%= t ".welcome" %>

+ +<%= link_to t(".active"), edit_account_activation_url(id: @user.activation_token, + email: @user.email) %> diff --git a/app/views/user_mailer/account_activation.text.erb b/app/views/user_mailer/account_activation.text.erb new file mode 100644 index 0000000..7584fec --- /dev/null +++ b/app/views/user_mailer/account_activation.text.erb @@ -0,0 +1,5 @@ +<%= t ".hi" %><%= @user.name %>, + +<%= t ".welcome" %> + +<%= edit_account_activation_url(id: @user.activation_token, email: @user.email) %> diff --git a/app/views/user_mailer/password_reset.html.erb b/app/views/user_mailer/password_reset.html.erb new file mode 100644 index 0000000..6dec984 --- /dev/null +++ b/app/views/user_mailer/password_reset.html.erb @@ -0,0 +1,5 @@ +

User#password_reset

+ +

+ <%= @greeting %>, find me in app/views/user_mailer/password_reset.html.erb +

diff --git a/app/views/user_mailer/password_reset.text.erb b/app/views/user_mailer/password_reset.text.erb new file mode 100644 index 0000000..5ba80cc --- /dev/null +++ b/app/views/user_mailer/password_reset.text.erb @@ -0,0 +1,3 @@ +User#password_reset + +<%= @greeting %>, find me in app/views/user_mailer/password_reset.text.erb diff --git a/config/application.rb b/config/application.rb index f6c370b..4f560e5 100644 --- a/config/application.rb +++ b/config/application.rb @@ -23,5 +23,6 @@ class Application < Rails::Application config.load_defaults 5.2 config.i18n.default_locale = :vi config.generators.system_tests = nil + end end diff --git a/config/environments/development.rb b/config/environments/development.rb index 1311e3e..9e456de 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,25 +1,16 @@ Rails.application.configure do - # Settings specified here will take precedence over those in config/application.rb. - - # In the development environment your application's code is reloaded on - # every request. This slows down response time but is perfect for development - # since you don't have to restart the web server when you make code changes. config.cache_classes = false - # Do not eager load code on boot. config.eager_load = false - # Show full error reports. config.consider_all_requests_local = true - # Enable/disable caching. By default caching is disabled. - # Run rails dev:cache to toggle caching. - if Rails.root.join('tmp', 'caching-dev.txt').exist? + if Rails.root.join("tmp", "caching-dev.txt").exist? config.action_controller.perform_caching = true config.cache_store = :memory_store config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{2.days.to_i}" + "Cache-Control" => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false @@ -27,35 +18,34 @@ config.cache_store = :null_store end - # Store uploaded files on the local file system (see config/storage.yml for options) config.active_storage.service = :local - # Don't care if the mailer can't send. - config.action_mailer.raise_delivery_errors = false - - config.action_mailer.perform_caching = false + # Don"t care if the mailer can"t send. + config.action_mailer.raise_delivery_errors = true + host = "localhost:3000" # Local server + config.action_mailer.delivery_method = :smtp + config.action_mailer.default_url_options = { host: host, protocol: "http" } + config.action_mailer.smtp_settings = { + address: "smtp.gmail.com", + port: "587", + authentication: :plain, + user_name: ENV["mail_username"], + password: ENV["mail_password"], + enable_starttls_auto: true, + domain: "gmail.com" + } + + config.action_mailer.perform_caching = true - # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log - # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load - # Highlight code that triggered database queries in logs. config.active_record.verbose_query_logs = true - # Debug mode disables concatenation and preprocessing of assets. - # This option may cause significant delays in view rendering with a large - # number of complex assets. config.assets.debug = true - # Suppress logger output for asset requests. config.assets.quiet = true - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true - - # Use an evented file watcher to asynchronously detect changes in source code, - # routes, locales, etc. This feature depends on the listen gem. config.file_watcher = ActiveSupport::EventedFileUpdateChecker end diff --git a/config/locales/en.yml b/config/locales/en.yml index d90d5c8..0a5a535 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -6,6 +6,12 @@ en: delete_mesage_success: "User deleted" delete_mesage_failed: "User delete failed" login_message: "Please log in." + account_activation: "Account activation" + account_activated: "Account activated!" + account_activate_invalid: "Invalid activation link" + activation_message1: "Account not activated. " + activation_message2: "Check your email for the activation link." + activation_mesage: "Please check your email to activate your account." static_pages: home: title: "Home" @@ -95,3 +101,10 @@ en: new_user: "New user ?" create: error_login: "Invalid email/password combination" + user_mailer: + account_activation: + title: "Sample App" + hi: "Hi " + welcome: "Welcome to the Sample App! Click on the link below to activate your account:" + active: "Activate" + diff --git a/config/locales/vi.yml b/config/locales/vi.yml index 67590b4..af57174 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -6,6 +6,12 @@ vi: delete_mesage_success: "Người dùng đã xóa" delete_mesage_failed: "Người dùng chưa được xóa" login_message: "Làm ơn hay đăng nhập." + account_activation: "Kích hoạt tài khoản" + account_activated: "Tài khoản đã được kích hoạt!" + account_activate_invalid: "Đường dẫn kích hoạt không đúng" + activation_message1: "Tài khoản chưa được kích hoạt. " + activation_message2: "Kiểm trai thư của bạn để có thể kích hoạt." + activation_mesage: "Vui lòng kiểm tra email của bạn để kích hoạt tài khoản của bạn." will_paginate: next_label: "Sau" previous_label: "Trước" @@ -114,3 +120,9 @@ vi: new_user: "Người dùng mới ?" create: error_login: "Email không tồn tại/mật khẩu sai" + user_mailer: + account_activation: + title: "Ứng dụng mẫu" + hi: "Chào " + welcome: "Chào mừng bạn đến với Ứng dụng mẫu! Nhấp vào liên kết bên dưới để kích hoạt tài khoản của bạn:" + active: "Kích hoạt" diff --git a/config/routes.rb b/config/routes.rb index 889b8fe..0e3e6a4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -10,5 +10,6 @@ post "/login", to: "sessions#create" delete "/logout", to: "sessions#destroy" resources :users + resources :account_activations, only: [:edit] end end diff --git a/db/migrate/20180722125049_add_activation_to_users.rb b/db/migrate/20180722125049_add_activation_to_users.rb new file mode 100644 index 0000000..9bf56f4 --- /dev/null +++ b/db/migrate/20180722125049_add_activation_to_users.rb @@ -0,0 +1,7 @@ +class AddActivationToUsers < ActiveRecord::Migration[5.2] + def change + add_column :users, :activation_digest, :string + add_column :users, :activated, :boolean, default: false + add_column :users, :activated_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index 26687bc..d489b1e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2018_07_20_021324) do +ActiveRecord::Schema.define(version: 2018_07_22_125049) do create_table "users", force: :cascade do |t| t.string "name" @@ -20,6 +20,9 @@ t.string "password_digest" t.string "remember_digest" t.boolean "admin", default: false + t.string "activation_digest" + t.boolean "activated", default: false + t.datetime "activated_at" t.index ["email"], name: "index_users_on_email", unique: true end diff --git a/db/seeds.rb b/db/seeds.rb index 6c43df1..fe79f81 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -2,7 +2,9 @@ email: "example@railstutorial.org", password: "foobar", password_confirmation: "foobar", - admin: true) + admin: true, + activated: true, + activated_at: Time.zone.now) 99.times do |n| name = Faker::Name.name @@ -11,5 +13,7 @@ User.create!(name: name, email: email, password: password, - password_confirmation: password) + password_confirmation: password, + activated: true, + activated_at: Time.zone.now) end