mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-04-01 06:41:41 +00:00
Merge pull request #2138 from Matt-Yorkley/email/manager_invite
Email/manager invite
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
angular.module("admin.enterprises")
|
||||
.controller "enterpriseCtrl", ($scope, $window, NavigationCheck, enterprise, EnterprisePaymentMethods, EnterpriseShippingMethods, SideMenu, StatusMessage) ->
|
||||
.controller "enterpriseCtrl", ($scope, $http, $window, NavigationCheck, enterprise, EnterprisePaymentMethods, EnterpriseShippingMethods, SideMenu, StatusMessage) ->
|
||||
$scope.Enterprise = enterprise
|
||||
$scope.PaymentMethods = EnterprisePaymentMethods.paymentMethods
|
||||
$scope.ShippingMethods = EnterpriseShippingMethods.shippingMethods
|
||||
@@ -26,7 +26,7 @@ angular.module("admin.enterprises")
|
||||
# from a directive "nav-check" in the page - if we pass it here it will be called in the test suite,
|
||||
# and on all new uses of this contoller, and we might not want that.
|
||||
enterpriseNavCallback = ->
|
||||
if $scope.enterprise_form.$dirty
|
||||
if $scope.enterprise_form != undefined && $scope.enterprise_form.$dirty
|
||||
t('admin.unsaved_confirm_leave')
|
||||
|
||||
# Register the NavigationCheck callback
|
||||
@@ -51,3 +51,16 @@ angular.module("admin.enterprises")
|
||||
$scope.enterprise_form?.$setDirty()
|
||||
else
|
||||
alert ("#{manager.email}" + " " + t("is_already_manager"))
|
||||
|
||||
$scope.inviteManager = ->
|
||||
$scope.invite_errors = $scope.invite_success = null
|
||||
email = $scope.newUser
|
||||
|
||||
$http.post("/admin/manager_invitations", {email: email, enterprise_id: $scope.Enterprise.id}).success (data)->
|
||||
$scope.addManager({id: data.user, email: email})
|
||||
$scope.invite_success = t('user_invited', email: email)
|
||||
.error (data) ->
|
||||
$scope.invite_errors = data.errors
|
||||
|
||||
$scope.resetModal = ->
|
||||
$scope.newUser = $scope.invite_errors = $scope.invite_success = null
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#invite-manager-modal{ng: {app: 'admin.enterprises', controller: 'enterpriseCtrl'}}
|
||||
|
||||
.margin-bottom-30.text-center
|
||||
.text-big
|
||||
= t('js.admin.modals.invite_title')
|
||||
|
||||
%p.alert-box.ok{ng: {show: 'invite_success'}}
|
||||
{{invite_success}}
|
||||
|
||||
%p.alert-box.error{ng: {show: 'invite_errors'}}
|
||||
{{invite_errors}}
|
||||
|
||||
%input#invite_email.fullwidth.margin-bottom-20{ng: {model: 'newUser'}}
|
||||
|
||||
.margin-bottom-20.text-center
|
||||
%button.text-center.margin-top-10{ng: {show: '!invite_success', click: 'inviteManager()'}}
|
||||
= t('js.admin.modals.invite')
|
||||
%button.text-center.margin-top-10{ng: {show: 'invite_success', click: 'resetModal(); close()'}}
|
||||
= t('js.admin.modals.close')
|
||||
@@ -1,3 +1,7 @@
|
||||
input[type="submit"], input[type="button"], button, .button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
39
app/controllers/admin/manager_invitations_controller.rb
Normal file
39
app/controllers/admin/manager_invitations_controller.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
module Admin
|
||||
class ManagerInvitationsController < Spree::Admin::BaseController
|
||||
def create
|
||||
@email = params[:email]
|
||||
@enterprise = Enterprise.find(params[:enterprise_id])
|
||||
|
||||
authorize! :edit, @enterprise
|
||||
|
||||
existing_user = Spree::User.find_by_email(@email)
|
||||
|
||||
if existing_user
|
||||
render json: { errors: t('admin.enterprises.invite_manager.user_already_exists') }, status: :unprocessable_entity
|
||||
return
|
||||
end
|
||||
|
||||
new_user = create_new_manager
|
||||
|
||||
if new_user
|
||||
render json: { user: new_user.id }, status: :ok
|
||||
else
|
||||
render json: { errors: t('admin.enterprises.invite_manager.error') }, status: 500
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_new_manager
|
||||
password = Devise.friendly_token
|
||||
new_user = Spree::User.create(email: @email, unconfirmed_email: @email, password: password)
|
||||
new_user.reset_password_token = Devise.friendly_token
|
||||
new_user.save!
|
||||
|
||||
@enterprise.users << new_user
|
||||
Delayed::Job.enqueue ManagerInvitationJob.new(@enterprise.id, new_user.id)
|
||||
|
||||
new_user
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -38,6 +38,10 @@ class UserConfirmationsController < DeviseController
|
||||
'not_confirmed'
|
||||
end
|
||||
|
||||
if resource.reset_password_token.present?
|
||||
return spree.edit_spree_user_password_path(reset_password_token: resource.reset_password_token)
|
||||
end
|
||||
|
||||
path = (session[:confirmation_return_url] || login_path).to_s
|
||||
path += path.include?('?') ? '&' : '?'
|
||||
path + "validation=#{result}"
|
||||
|
||||
7
app/jobs/manager_invitation_job.rb
Normal file
7
app/jobs/manager_invitation_job.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
ManagerInvitationJob = Struct.new(:enterprise_id, :user_id) do
|
||||
def perform
|
||||
enterprise = Enterprise.find enterprise_id
|
||||
user = Spree::User.find user_id
|
||||
EnterpriseMailer.manager_invitation(enterprise, user).deliver
|
||||
end
|
||||
end
|
||||
@@ -12,6 +12,18 @@ class EnterpriseMailer < Spree::BaseMailer
|
||||
:subject => subject)
|
||||
end
|
||||
|
||||
def manager_invitation(enterprise, user)
|
||||
@enterprise = enterprise
|
||||
@instance = Spree::Config[:site_name]
|
||||
@instance_email = from_address
|
||||
|
||||
subject = t('enterprise_mailer.invite_manager.subject', enterprise: @enterprise.name)
|
||||
|
||||
mail(to: user.email,
|
||||
from: from_address,
|
||||
subject: subject)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_enterprise(enterprise)
|
||||
|
||||
@@ -60,3 +60,16 @@
|
||||
- @enterprise.users.each do |manager|
|
||||
= manager.email
|
||||
%br
|
||||
|
||||
- if full_permissions
|
||||
%form
|
||||
.row
|
||||
.three.columns.alpha
|
||||
%label
|
||||
= t('.invite_manager')
|
||||
%div{'ofn-with-tip' => t('.invite_manager_tip')}
|
||||
%a= t('admin.whats_this')
|
||||
.eight.columns.omega
|
||||
.row
|
||||
%a.button.help-modal{template: 'admin/modals/invite_manager.html'}
|
||||
= t('.add_unregistered_user')
|
||||
|
||||
16
app/views/enterprise_mailer/manager_invitation.html.haml
Normal file
16
app/views/enterprise_mailer/manager_invitation.html.haml
Normal file
@@ -0,0 +1,16 @@
|
||||
%h3
|
||||
= t('invite_email.greeting')
|
||||
%p.lead
|
||||
= t('invite_email.invited_to_manage', enterprise: @enterprise.name, instance: @instance)
|
||||
|
||||
%p
|
||||
= t('invite_email.confirm_your_email')
|
||||
%p
|
||||
= t('invite_email.mistakenly_sent', owner_email: @enterprise.owner.email, instance: @instance, instance_email: @instance_email)
|
||||
|
||||
%p
|
||||
= t :email_help
|
||||
|
||||
= render 'shared/mailers/signoff'
|
||||
|
||||
= render 'shared/mailers/social_and_contact'
|
||||
@@ -114,6 +114,8 @@ en:
|
||||
subject: "Please confirm the email address for %{enterprise}"
|
||||
welcome:
|
||||
subject: "%{enterprise} is now on %{sitename}"
|
||||
invite_manager:
|
||||
subject: "%{enterprise} has invited you to be a manager"
|
||||
producer_mailer:
|
||||
order_cycle:
|
||||
subject: "Order cycle report for %{producer}"
|
||||
@@ -665,6 +667,9 @@ en:
|
||||
notifications_note: 'Note: A new email address may need to be confirmed prior to use'
|
||||
managers: Managers
|
||||
managers_tip: The other users with permission to manage this enterprise.
|
||||
invite_manager: "Invite Manager"
|
||||
invite_manager_tip: "Invite an unregistered user to sign up and become a manager of this enterprise."
|
||||
add_unregistered_user: "Add an unregistered user"
|
||||
email_confirmed: "Email confirmed"
|
||||
email_not_confirmed: "Email not confirmed"
|
||||
actions:
|
||||
@@ -724,6 +729,9 @@ en:
|
||||
welcome_text: You have successfully created a
|
||||
next_step: Next step
|
||||
choose_starting_point: 'Choose your starting point:'
|
||||
invite_manager:
|
||||
user_already_exists: "User already exists"
|
||||
error: "Something went wrong"
|
||||
order_cycles:
|
||||
edit:
|
||||
advanced_settings: Advanced Settings
|
||||
@@ -1319,6 +1327,13 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
If you are a producer or food enterprise, we are excited to have you as a part of the network."
|
||||
email_signup_help_html: "We welcome all your questions and feedback; you can use the <em>Send Feedback</em> button on the site or email us at %{email}"
|
||||
|
||||
invite_email:
|
||||
greeting: "Hello!"
|
||||
invited_to_manage: "You have been invited to manage %{enterprise} on %{instance}."
|
||||
confirm_your_email: "You will receive an email shortly to confirm your registration."
|
||||
set_a_password: "You will then be prompted to set a password before you are able to administer the enterprise."
|
||||
mistakenly_sent: "Not sure why you have received this email? Please contact %{owner_email} for more information, or you can contact %{instance} at %{instance_email}."
|
||||
|
||||
producer_mail_greeting: "Dear"
|
||||
producer_mail_text_before: "We now have all the consumer orders for the next food drop."
|
||||
producer_mail_order_text: "Here is a summary of the orders for your products:"
|
||||
@@ -2133,10 +2148,10 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
unsaved_changes_confirmation: "Unsaved changes will be lost. Continue anyway?"
|
||||
one_product_unsaved: "Changes to one product remain unsaved."
|
||||
products_unsaved: "Changes to %{n} products remain unsaved."
|
||||
add_manager: "Add a manager"
|
||||
is_already_manager: "is already a manager!"
|
||||
no_change_to_save: " No change to save"
|
||||
add_manager: "Add a manager"
|
||||
user_invited: "%{email} has been invited to manage this enterprise"
|
||||
add_manager: "Add an existing user"
|
||||
users: "Users"
|
||||
about: "About"
|
||||
images: "Images"
|
||||
@@ -2219,6 +2234,9 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
enterprise_limit_reached: "You have reached the standard limit of enterprises per account. Write to %{contact_email} if you need to increase it."
|
||||
modals:
|
||||
got_it: Got it
|
||||
close: "Close"
|
||||
invite: "Invite"
|
||||
invite_title: "Invite an unregistered user"
|
||||
tag_rule_help:
|
||||
title: Tag Rules
|
||||
overview: Overview
|
||||
|
||||
@@ -113,6 +113,8 @@ Openfoodnetwork::Application.routes.draw do
|
||||
resources :tag_rules, only: [:destroy]
|
||||
end
|
||||
|
||||
resources :manager_invitations, only: [:create]
|
||||
|
||||
resources :enterprise_relationships
|
||||
resources :enterprise_roles
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
require 'spec_helper'
|
||||
|
||||
module Admin
|
||||
describe ManagerInvitationsController, type: :controller do
|
||||
let!(:existing_user) { create(:user) }
|
||||
let!(:enterprise) { create(:enterprise) }
|
||||
let(:admin) { create(:admin_user) }
|
||||
|
||||
describe "#create" do
|
||||
context "when given email matches an existing user" do
|
||||
before do
|
||||
controller.stub spree_current_user: admin
|
||||
end
|
||||
|
||||
it "returns an error" do
|
||||
spree_post :create, {email: existing_user.email, enterprise_id: enterprise.id}
|
||||
|
||||
expect(response.status).to eq 422
|
||||
expect(json_response['errors']).to eq I18n.t('admin.enterprises.invite_manager.user_already_exists')
|
||||
end
|
||||
end
|
||||
|
||||
context "signing up a new user" do
|
||||
before do
|
||||
controller.stub spree_current_user: admin
|
||||
end
|
||||
|
||||
it "creates a new user, sends an invitation email, and returns the user id" do
|
||||
expect do
|
||||
spree_post :create, {email: 'un.registered@email.com', enterprise_id: enterprise.id}
|
||||
end.to enqueue_job Delayed::PerformableMethod
|
||||
|
||||
new_user = Spree::User.find_by_email('un.registered@email.com')
|
||||
|
||||
expect(new_user.reset_password_token).to_not be_nil
|
||||
expect(response.status).to eq 200
|
||||
expect(json_response['user']).to eq new_user.id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -25,17 +25,17 @@ describe UserConfirmationsController, type: :controller do
|
||||
end
|
||||
|
||||
context "that has not been confirmed" do
|
||||
it "redirects the user to login" do
|
||||
spree_get :show, confirmation_token: unconfirmed_user.confirmation_token
|
||||
expect(response).to redirect_to login_path(validation: 'confirmed')
|
||||
end
|
||||
|
||||
it "confirms the user" do
|
||||
spree_get :show, confirmation_token: unconfirmed_user.confirmation_token
|
||||
expect(unconfirmed_user.reload.confirmed_at).not_to eq(nil)
|
||||
end
|
||||
|
||||
it "redirects to previous url" do
|
||||
it "redirects the user to #/login by default" do
|
||||
spree_get :show, confirmation_token: unconfirmed_user.confirmation_token
|
||||
expect(response).to redirect_to login_path(validation: 'confirmed')
|
||||
end
|
||||
|
||||
it "redirects to previous url, if present" do
|
||||
session[:confirmation_return_url] = producers_path + '#/login'
|
||||
spree_get :show, confirmation_token: unconfirmed_user.confirmation_token
|
||||
expect(response).to redirect_to producers_path + '#/login?validation=confirmed'
|
||||
@@ -46,6 +46,13 @@ describe UserConfirmationsController, type: :controller do
|
||||
spree_get :show, confirmation_token: unconfirmed_user.confirmation_token
|
||||
expect(response).to redirect_to registration_path + '#/signup?after_login=%2Fregister&validation=confirmed'
|
||||
end
|
||||
|
||||
it "redirects to set password page, if user needs to reset their password" do
|
||||
unconfirmed_user.reset_password_token = Devise.friendly_token
|
||||
unconfirmed_user.save!
|
||||
spree_get :show, confirmation_token: unconfirmed_user.confirmation_token
|
||||
expect(response).to redirect_to spree.edit_spree_user_password_path(reset_password_token: unconfirmed_user.reset_password_token)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ feature %q{
|
||||
let!(:user1) { create(:user, email: 'user1@example.com') }
|
||||
let!(:user2) { create(:user, email: 'user2@example.com') }
|
||||
let!(:user3) { create(:user, email: 'user3@example.com', confirmed_at: nil) }
|
||||
let(:new_email) { 'new@manager.com' }
|
||||
|
||||
let!(:enterprise) { create(:enterprise, name: 'Test Enterprise', owner: user1) }
|
||||
let!(:enterprise_role) { create(:enterprise_role, user_id: user2.id, enterprise_id: enterprise.id) }
|
||||
@@ -114,7 +115,9 @@ feature %q{
|
||||
|
||||
# user3 has been added and has an unconfirmed email address
|
||||
expect(page).to have_css "tr#manager-#{user3.id}"
|
||||
expect(page).to have_css 'i.unconfirmed'
|
||||
within "tr#manager-#{user3.id}" do
|
||||
expect(page).to have_css 'i.unconfirmed'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -133,6 +136,29 @@ feature %q{
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "can invite unregistered users to be managers" do
|
||||
find('a.button.help-modal').click
|
||||
expect(page).to have_css '#invite-manager-modal'
|
||||
|
||||
within '#invite-manager-modal' do
|
||||
fill_in 'invite_email', with: new_email
|
||||
click_button I18n.t('js.admin.modals.invite')
|
||||
expect(page).to have_content I18n.t('user_invited', email: new_email)
|
||||
click_button I18n.t('js.admin.modals.close')
|
||||
end
|
||||
|
||||
new_user = Spree::User.find_by_email_and_confirmed_at(new_email, nil)
|
||||
expect(Enterprise.managed_by(new_user)).to include enterprise
|
||||
|
||||
within 'table.managers' do
|
||||
expect(page).to have_content new_email
|
||||
|
||||
within "tr#manager-#{new_user.id}" do
|
||||
expect(page).to have_css 'i.unconfirmed'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -2,17 +2,29 @@ require 'spec_helper'
|
||||
|
||||
describe EnterpriseMailer do
|
||||
let!(:enterprise) { create(:enterprise) }
|
||||
let!(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
ActionMailer::Base.deliveries = []
|
||||
Spree::MailMethod.create!(environment: 'test')
|
||||
end
|
||||
|
||||
it "sends a welcome email when given an enterprise" do
|
||||
describe "#welcome" do
|
||||
it "sends a welcome email when given an enterprise" do
|
||||
EnterpriseMailer.welcome(enterprise).deliver
|
||||
|
||||
mail = ActionMailer::Base.deliveries.first
|
||||
expect(mail.subject)
|
||||
.to eq "#{enterprise.name} is now on #{Spree::Config[:site_name]}"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#manager_invitation" do
|
||||
it "should send a manager invitation email when given an enterprise and user" do
|
||||
EnterpriseMailer.manager_invitation(enterprise, user).deliver
|
||||
expect(ActionMailer::Base.deliveries.count).to eq 1
|
||||
mail = ActionMailer::Base.deliveries.first
|
||||
expect(mail.subject).to eq "#{enterprise.name} has invited you to be a manager"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user