Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add frontend controllers and views for user account management #101

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions app/controllers/alchemy/accounts_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module Alchemy
class AccountsController < ::Devise::RegistrationsController
helper "Alchemy::Pages"

def show
authorize! :show, current_alchemy_user
@user = current_alchemy_user
end

private

def permission_denied(*)
store_location_for(:user, account_path)
flash[:warning] = t(:unauthenticated, scope: "devise.failure")
redirect_to alchemy.login_path
end
end
end
10 changes: 1 addition & 9 deletions app/controllers/alchemy/admin/passwords_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,8 @@ def new_session_path(resource_name)
alchemy.admin_login_path
end

def admin_edit_password_url(_resource, options = {})
alchemy.admin_edit_password_url(options)
end

def after_resetting_password_path_for(resource)
if can? :index, :alchemy_admin_dashboard
alchemy.admin_dashboard_path
else
alchemy.root_path
end
alchemy.admin_dashboard_path
end
end
end
Expand Down
13 changes: 13 additions & 0 deletions app/controllers/alchemy/confirmations_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module Alchemy
class ConfirmationsController < ::Devise::ConfirmationsController
helper "Alchemy::Pages"

private

def new_session_path(*)
alchemy.login_path
end
end
end
13 changes: 13 additions & 0 deletions app/controllers/alchemy/passwords_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module Alchemy
class PasswordsController < ::Devise::PasswordsController
helper "Alchemy::Pages"

private

def new_session_path(*)
alchemy.login_path
end
end
end
13 changes: 13 additions & 0 deletions app/controllers/alchemy/sessions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module Alchemy
class SessionsController < ::Devise::SessionsController
helper "Alchemy::Pages"

private

def after_sign_in_path_for(user)
stored_location_for(user) || alchemy.account_path
end
end
end
33 changes: 26 additions & 7 deletions app/mailers/alchemy/notifications.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
module Alchemy
class Notifications < ActionMailer::Base
# frozen_string_literal: true

default(from: Config.get(:mailer)['mail_from'])
module Alchemy
class Notifications < ::Devise::Mailer
default(from: Config.get(:mailer)["mail_from"])

def member_created(user)
@user = user

mail(
to: user.email,
subject: Alchemy.t("Your user credentials")
subject: Alchemy.t("Your user credentials"),
)
end

Expand All @@ -17,16 +18,34 @@ def alchemy_user_created(user)
@url = admin_url
mail(
to: user.email,
subject: Alchemy.t("Your Alchemy Login")
subject: Alchemy.t("Your Alchemy Login"),
)
end

def member_reset_password_instructions(user, token, _opts = {})
@user = user
@token = token
mail(
to: user.email,
subject: Alchemy.t("Reset password instructions"),
)
end

def reset_password_instructions(user, token, _opts = {})
@user = user
@token = token
mail(
to: user.email,
subject: Alchemy.t("Reset password instructions"),
)
end

def reset_password_instructions(user, token, opts={})
def confirmation_instructions(user, token, _opts = {})
@user = user
@token = token
mail(
to: user.email,
subject: Alchemy.t("Reset password instructions")
subject: Alchemy.t("Account confirmation instructions"),
)
end
end
Expand Down
41 changes: 26 additions & 15 deletions app/models/alchemy/user.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require 'devise/orm/active_record'
require 'userstamp'
# frozen_string_literal: true

require "devise/orm/active_record"
require "userstamp"

module Alchemy
class User < ActiveRecord::Base
Expand All @@ -12,7 +14,7 @@ class User < ActiveRecord::Base
:password,
:password_confirmation,
:send_credentials,
:tag_list
:tag_list,
]

devise *Alchemy.devise_modules
Expand All @@ -29,9 +31,9 @@ class User < ActiveRecord::Base
# Unlock all locked pages before destroy.
before_destroy :unlock_pages!

scope :admins, -> { where(arel_table[:alchemy_roles].matches('%admin%')) }
scope :logged_in, -> { where('last_request_at > ?', logged_in_timeout.seconds.ago) }
scope :logged_out, -> { where('last_request_at is NULL or last_request_at <= ?', logged_in_timeout.seconds.ago) }
scope :admins, -> { where(arel_table[:alchemy_roles].matches("%admin%")) }
scope :logged_in, -> { where("last_request_at > ?", logged_in_timeout.seconds.ago) }
scope :logged_out, -> { where("last_request_at is NULL or last_request_at <= ?", logged_in_timeout.seconds.ago) }

ROLES = Config.get(:user_roles)

Expand All @@ -51,10 +53,7 @@ def logged_in_timeout
def search(query)
query = "%#{query.downcase}%"

where arel_table[:login].lower.matches(query)
.or arel_table[:email].lower.matches(query)
.or arel_table[:firstname].lower.matches(query)
.or arel_table[:lastname].lower.matches(query)
where arel_table[:login].lower.matches(query).or arel_table[:email].lower.matches(query).or arel_table[:firstname].lower.matches(query).or arel_table[:lastname].lower.matches(query)
end
end

Expand All @@ -67,12 +66,12 @@ def role
end

def alchemy_roles
read_attribute(:alchemy_roles).split(' ')
read_attribute(:alchemy_roles).split(" ")
end

def alchemy_roles=(roles_string)
if roles_string.is_a? Array
write_attribute(:alchemy_roles, roles_string.join(' '))
write_attribute(:alchemy_roles, roles_string.join(" "))
elsif roles_string.is_a? String
write_attribute(:alchemy_roles, roles_string)
end
Expand All @@ -84,8 +83,9 @@ def add_role(role)

# Returns true if the user ahs admin role
def is_admin?
has_role? 'admin'
has_role? "admin"
end

alias_method :admin?, :is_admin?

# Returns true if the user has the given role.
Expand All @@ -105,6 +105,7 @@ def unlock_pages!
def pages_locked_by_me
Page.locked_by(self).order(:updated_at)
end

alias_method :locked_pages, :pages_locked_by_me

# Returns the firstname and lastname as a string
Expand All @@ -118,11 +119,12 @@ def fullname(options = {})
if lastname.blank? && firstname.blank?
login
else
options = {:flipped => false}.merge(options)
options = { flipped: false }.merge(options)
fullname = options[:flipped] ? "#{lastname}, #{firstname}" : "#{firstname} #{lastname}"
fullname.squeeze(" ").strip
end
end

alias_method :name, :fullname
alias_method :alchemy_display_name, :fullname

Expand Down Expand Up @@ -150,13 +152,22 @@ def store_request_time!
# Delivers a welcome mail depending from user's role.
#
def deliver_welcome_mail
if has_role?('author') || has_role?('editor') || has_role?('admin')
if has_role?("author") || has_role?("editor") || has_role?("admin")
Notifications.alchemy_user_created(self).deliver_later
else
Notifications.member_created(self).deliver_later
end
end

# Overwritten to send a different email to members
def send_reset_password_instructions_notification(token)
if has_role?("member")
send_devise_notification(:member_reset_password_instructions, token, {})
else
send_devise_notification(:reset_password_instructions, token, {})
end
end

private

def logged_in_timeout
Expand Down
3 changes: 3 additions & 0 deletions app/views/alchemy/accounts/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<h2>Hallo <%= @user.fullname %></h2>

<%= link_to 'Edit account', alchemy.edit_account_path %>
15 changes: 15 additions & 0 deletions app/views/alchemy/devise/shared/_links.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<%- if controller_name != 'sessions' %>
<%= link_to t('.login', default: 'Log in'), alchemy.login_path %><br />
<% end %>

<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
<%= link_to t('.sign_up', default: 'Sign up'), alchemy.new_account_path %><br />
<% end %>

<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
<%= link_to t('.forgot_password', default: 'Forgot your password?'), alchemy.new_password_path %><br />
<% end %>

<%- if devise_mapping.confirmable? && controller_name != 'confirmations' %>
<%= link_to t('.confirmation_instructions', default: "Didn't receive confirmation instructions?"), alchemy.new_confirmation_path %><br />
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Willkommen <%= @user.fullname %>!

Bitte bestätigen Sie Ihre E-Mail-Adresse durch klicken auf folgenden Link:

<%= alchemy.confirmation_url(@user, confirmation_token: @token) %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Welcome <%= @user.fullname %>!

You can confirm your account email through the link below:

<%= alchemy.confirmation_url(@user, confirmation_token: @token) %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Hallo <%= @user.fullname %>.

Sie haben angefordert Ihr Passwort zurückzusetzen. Dies kann durch anklicken des nachfolgenden Links bestätigt werden.

<%= alchemy.edit_password_url(@user, reset_password_token: @token) %>

Wenn Sie diese Zurücksetzung nicht angefragt haben, dann können Sie diese E-Mail einfach ignorieren.
Ihr Passwort wird erst dann zurückgesetzt, wenn Sie den Link anklicken.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Hello <%= @user.name %>.

You have requested to change your password. Please confirm this by clicking the link below.

<%= alchemy.edit_password_url(@user, reset_password_token: @token) %>

If you didn't request this, please ignore this email.
Your password won't change until you access the link above and create a new one.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Hola <%= @user.name %>.

Has solicitado modificar tu contraseña. Por favor, confírmalo pulsando en el siguiente enlace.

<%= alchemy.edit_password_url(@user, reset_password_token: @token) %>

Si no has sido tu el que ha hecho la solicitud, ignora este correo.
Tu contraseña no cambiará hasta que no accedas al enlace de arriba y generes una nueva.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Здравствуйте, <%= @user.name %>.

Вы сделали запрос на смену пароля. Пожалуйста подтвердите это, нажав на ссылку ниже.

<%= alchemy.edit_password_url(@user, reset_password_token: @token) %>

Если вы не делали запрос, просто проигнорируйте это письмо.
Ваш пароль не изменится до тех пор, пока вы не перейдете по ссылке и сами не измените его.
27 changes: 27 additions & 0 deletions app/views/alchemy/passwords/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<h2><%= t('.title', default: 'Change your password') %></h2>

<%= simple_form_for(@user, as: :user, url: alchemy.password_path, html: { method: :put }) do |f| %>
<%= f.error_notification %>

<%= f.input :reset_password_token, as: :hidden %>
<%= f.full_error :reset_password_token %>

<div class="form-inputs">
<%= f.input :password,
label: t('.password.label', default: 'New password'),
required: true,
autofocus: true,
hint: t('.hint', default: '%{minimum} characters minimum', minimum: @minimum_password_length) if @minimum_password_length,
input_html: { autocomplete: "new-password" } %>
<%= f.input :password_confirmation,
label: t('.password_confirmation.label', default: 'Confirm your new password'),
required: true,
input_html: { autocomplete: "new-password" } %>
</div>

<div class="form-actions">
<%= f.button :submit, t('.button.label', default: 'Change my password') %>
</div>
<% end %>

<%= render "alchemy/devise/shared/links" %>
18 changes: 18 additions & 0 deletions app/views/alchemy/passwords/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<h2><%= t('.title', default: 'Forgot your password?') %></h2>

<%= simple_form_for(@user, as: :user, url: alchemy.password_path, html: { method: :post }) do |f| %>
<%= f.error_notification %>

<div class="form-inputs">
<%= f.input :email,
required: true,
autofocus: true,
input_html: { autocomplete: "email" } %>
</div>

<div class="form-actions">
<%= f.button :submit, t('.button.label', default: 'Send me reset password instructions') %>
</div>
<% end %>

<%= render "alchemy/devise/shared/links" %>
Loading