2019-05-03 06:17:27 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
class InvitesController < ApplicationController
|
|
|
|
|
2021-03-06 19:29:35 +08:00
|
|
|
requires_login only: [:create, :destroy, :destroy_all_expired, :resend_invite, :resend_all_invites, :upload_csv]
|
2018-02-01 09:26:45 +08:00
|
|
|
|
2017-08-31 12:06:56 +08:00
|
|
|
skip_before_action :check_xhr, except: [:perform_accept_invitation]
|
|
|
|
skip_before_action :preload_json, except: [:show]
|
|
|
|
skip_before_action :redirect_to_login_if_required
|
2013-06-06 02:12:37 +08:00
|
|
|
|
2021-03-02 15:13:04 +08:00
|
|
|
before_action :ensure_invites_allowed, only: [:show, :perform_accept_invitation]
|
2017-08-31 12:06:56 +08:00
|
|
|
before_action :ensure_new_registrations_allowed, only: [:show, :perform_accept_invitation]
|
|
|
|
before_action :ensure_not_logged_in, only: [:show, :perform_accept_invitation]
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
|
|
def show
|
2017-01-25 04:15:29 +08:00
|
|
|
expires_now
|
2017-02-14 05:19:41 +08:00
|
|
|
|
|
|
|
invite = Invite.find_by(invite_key: params[:id])
|
2020-09-30 23:02:33 +08:00
|
|
|
if invite.present? && !invite.expired? && !invite.redeemed?
|
|
|
|
store_preloaded("invite_info", MultiJson.dump(
|
|
|
|
invited_by: UserNameSerializer.new(invite.invited_by, scope: guardian, root: false),
|
|
|
|
email: invite.email,
|
|
|
|
username: UserNameSuggester.suggest(invite.email),
|
2021-03-03 17:45:29 +08:00
|
|
|
is_invite_link: invite.is_invite_link?
|
|
|
|
))
|
2020-09-30 23:02:33 +08:00
|
|
|
|
|
|
|
render layout: 'application'
|
|
|
|
else
|
2021-03-03 17:45:29 +08:00
|
|
|
flash.now[:error] = if invite&.expired?
|
2020-09-30 23:02:33 +08:00
|
|
|
I18n.t('invite.expired', base_url: Discourse.base_url)
|
2021-03-03 17:45:29 +08:00
|
|
|
elsif invite&.redeemed?
|
2020-09-30 23:02:33 +08:00
|
|
|
I18n.t('invite.not_found_template', site_name: SiteSetting.title, base_url: Discourse.base_url)
|
2019-01-03 10:16:05 +08:00
|
|
|
else
|
2020-09-30 23:02:33 +08:00
|
|
|
I18n.t('invite.not_found', base_url: Discourse.base_url)
|
2019-01-03 10:16:05 +08:00
|
|
|
end
|
2021-03-03 17:45:29 +08:00
|
|
|
|
2017-02-14 05:19:41 +08:00
|
|
|
render layout: 'no_ember'
|
|
|
|
end
|
2017-01-25 04:15:29 +08:00
|
|
|
end
|
|
|
|
|
2021-03-03 17:45:29 +08:00
|
|
|
def create
|
|
|
|
if params[:email].present? && Invite.exists?(email: params[:email])
|
|
|
|
return render json: failed_json, status: 422
|
2017-01-25 04:15:29 +08:00
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2021-03-03 17:45:29 +08:00
|
|
|
if params[:topic_id].present?
|
|
|
|
topic = Topic.find_by(id: params[:topic_id])
|
|
|
|
raise Discourse::InvalidParameters.new(:topic_id) if topic.blank?
|
|
|
|
guardian.ensure_can_invite_to!(topic)
|
|
|
|
end
|
2013-11-07 01:56:26 +08:00
|
|
|
|
2021-03-03 17:45:29 +08:00
|
|
|
if params[:group_ids].present? || params[:group_names].present?
|
|
|
|
groups = Group.lookup_groups(group_ids: params[:group_ids], group_names: params[:group_names])
|
|
|
|
end
|
2013-11-07 01:56:26 +08:00
|
|
|
|
2017-07-21 14:12:24 +08:00
|
|
|
guardian.ensure_can_invite_to_forum!(groups)
|
2014-07-30 01:57:08 +08:00
|
|
|
|
2015-12-15 00:02:23 +08:00
|
|
|
begin
|
2021-03-03 17:45:29 +08:00
|
|
|
invite = Invite.generate(current_user,
|
|
|
|
invite_key: params[:invite_key],
|
|
|
|
email: params[:email],
|
|
|
|
skip_email: params[:skip_email],
|
|
|
|
invited_by: current_user,
|
|
|
|
custom_message: params[:custom_message],
|
|
|
|
max_redemptions_allowed: params[:max_redemptions_allowed],
|
|
|
|
topic_id: topic&.id,
|
|
|
|
group_ids: groups&.map(&:id),
|
|
|
|
expires_at: params[:expires_at],
|
|
|
|
)
|
|
|
|
|
|
|
|
if invite.present?
|
|
|
|
render_serialized(invite, InviteSerializer, scope: guardian, root: nil, show_emails: params.has_key?(:email))
|
2015-12-15 00:02:23 +08:00
|
|
|
else
|
|
|
|
render json: failed_json, status: 422
|
|
|
|
end
|
2017-06-14 00:59:02 +08:00
|
|
|
rescue Invite::UserExists, ActiveRecord::RecordInvalid => e
|
2021-03-06 19:29:35 +08:00
|
|
|
render_json_error(e.message)
|
2013-11-07 01:56:26 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-03-03 17:45:29 +08:00
|
|
|
def update
|
|
|
|
invite = Invite.find_by(invited_by: current_user, id: params[:id])
|
|
|
|
raise Discourse::InvalidParameters.new(:id) if invite.blank?
|
2020-06-09 23:19:32 +08:00
|
|
|
|
2021-03-03 17:45:29 +08:00
|
|
|
if params[:topic_id].present?
|
|
|
|
topic = Topic.find_by(id: params[:topic_id])
|
|
|
|
raise Discourse::InvalidParameters.new(:topic_id) if topic.blank?
|
|
|
|
guardian.ensure_can_invite_to!(topic)
|
2020-06-09 23:19:32 +08:00
|
|
|
end
|
2017-07-21 14:12:24 +08:00
|
|
|
|
2021-03-03 17:45:29 +08:00
|
|
|
if params[:group_ids].present? || params[:group_names].present?
|
|
|
|
groups = Group.lookup_groups(group_ids: params[:group_ids], group_names: params[:group_names])
|
2020-07-13 20:39:36 +08:00
|
|
|
end
|
2015-08-26 09:41:52 +08:00
|
|
|
|
2021-03-03 17:45:29 +08:00
|
|
|
guardian.ensure_can_invite_to_forum!(groups)
|
|
|
|
|
|
|
|
Invite.transaction do
|
|
|
|
if params.has_key?(:topic_id)
|
|
|
|
invite.topic_invites.destroy_all
|
|
|
|
invite.topic_invites.create!(topic_id: topic.id) if topic.present?
|
2020-06-09 23:19:32 +08:00
|
|
|
end
|
2020-06-10 09:29:28 +08:00
|
|
|
|
2021-03-03 17:45:29 +08:00
|
|
|
if params.has_key?(:group_ids) || params.has_key?(:group_names)
|
|
|
|
invite.invited_groups.destroy_all
|
|
|
|
groups.each { |group| invite.invited_groups.find_or_create_by!(group_id: group.id) } if groups.present?
|
|
|
|
end
|
2020-06-10 09:29:28 +08:00
|
|
|
|
2021-03-03 17:45:29 +08:00
|
|
|
if params.has_key?(:email)
|
|
|
|
old_email = invite.email.presence
|
|
|
|
new_email = params[:email].presence
|
|
|
|
|
|
|
|
if old_email != new_email
|
2021-03-06 19:29:35 +08:00
|
|
|
invite.emailed_status = if new_email && !params[:skip_email]
|
|
|
|
Invite.emailed_status_types[:pending]
|
|
|
|
else
|
|
|
|
Invite.emailed_status_types[:not_required]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if params[:send_email]
|
|
|
|
if invite.emailed_status != Invite.emailed_status_types[:pending]
|
|
|
|
begin
|
|
|
|
RateLimiter.new(current_user, "resend-invite-per-hour", 10, 1.hour).performed!
|
|
|
|
rescue RateLimiter::LimitExceeded
|
|
|
|
return render_json_error(I18n.t("rate_limiter.slow_down"))
|
|
|
|
end
|
2020-06-10 09:29:28 +08:00
|
|
|
end
|
2021-03-03 17:45:29 +08:00
|
|
|
|
2021-03-06 19:29:35 +08:00
|
|
|
invite.emailed_status = Invite.emailed_status_types[:pending]
|
2020-06-10 09:29:28 +08:00
|
|
|
end
|
2015-08-26 09:41:52 +08:00
|
|
|
|
2021-03-06 19:29:35 +08:00
|
|
|
begin
|
|
|
|
invite.update!(params.permit(:email, :custom_message, :max_redemptions_allowed, :expires_at))
|
|
|
|
rescue ActiveRecord::RecordInvalid => e
|
|
|
|
return render_json_error(e.message)
|
|
|
|
end
|
2020-07-13 20:39:36 +08:00
|
|
|
end
|
2021-03-03 17:45:29 +08:00
|
|
|
|
|
|
|
if invite.emailed_status == Invite.emailed_status_types[:pending]
|
|
|
|
invite.update_column(:emailed_status, Invite.emailed_status_types[:sending])
|
|
|
|
Jobs.enqueue(:invite_email, invite_id: invite.id)
|
2015-09-16 19:57:32 +08:00
|
|
|
end
|
2021-03-03 17:45:29 +08:00
|
|
|
|
|
|
|
render_serialized(invite, InviteSerializer, scope: guardian, root: nil, show_emails: params.has_key?(:email))
|
2015-08-26 09:41:52 +08:00
|
|
|
end
|
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
def destroy
|
2020-06-09 23:19:32 +08:00
|
|
|
params.require(:id)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2020-06-09 23:19:32 +08:00
|
|
|
invite = Invite.find_by(invited_by_id: current_user.id, id: params[:id])
|
|
|
|
raise Discourse::InvalidParameters.new(:id) if invite.blank?
|
2021-03-03 17:45:29 +08:00
|
|
|
|
2013-07-10 03:20:18 +08:00
|
|
|
invite.trash!(current_user)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
2020-06-10 09:25:58 +08:00
|
|
|
render json: success_json
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|
|
|
|
|
2021-03-03 17:45:29 +08:00
|
|
|
def perform_accept_invitation
|
|
|
|
params.require(:id)
|
|
|
|
params.permit(:email, :username, :name, :password, :timezone, user_custom_fields: {})
|
|
|
|
|
|
|
|
invite = Invite.find_by(invite_key: params[:id])
|
|
|
|
|
|
|
|
if invite.present?
|
|
|
|
begin
|
2021-03-03 16:12:30 +08:00
|
|
|
attrs = {
|
2021-03-03 17:45:29 +08:00
|
|
|
username: params[:username],
|
|
|
|
name: params[:name],
|
|
|
|
password: params[:password],
|
|
|
|
user_custom_fields: params[:user_custom_fields],
|
2021-03-02 15:13:04 +08:00
|
|
|
ip_address: request.remote_ip,
|
|
|
|
session: session
|
2021-03-03 16:12:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
attrs[:email] =
|
|
|
|
if invite.is_invite_link?
|
|
|
|
params.require([:email])
|
|
|
|
params[:email]
|
|
|
|
else
|
|
|
|
invite.email
|
|
|
|
end
|
|
|
|
|
|
|
|
user = invite.redeem(attrs)
|
2021-03-03 17:45:29 +08:00
|
|
|
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved => e
|
|
|
|
return render json: failed_json.merge(errors: e.record&.errors&.to_hash, message: I18n.t('invite.error_message')), status: 412
|
|
|
|
rescue Invite::UserExists => e
|
|
|
|
return render json: failed_json.merge(message: e.message), status: 412
|
|
|
|
end
|
|
|
|
|
|
|
|
if user.blank?
|
|
|
|
return render json: failed_json.merge(message: I18n.t('invite.not_found_json')), status: 404
|
|
|
|
end
|
|
|
|
|
|
|
|
log_on_user(user) if user.active?
|
|
|
|
user.update_timezone_if_missing(params[:timezone])
|
|
|
|
post_process_invite(user)
|
|
|
|
|
|
|
|
topic = invite.topics.first
|
|
|
|
response = {}
|
|
|
|
|
|
|
|
if user.present? && user.active?
|
|
|
|
response[:redirect_to] = topic.present? ? path(topic.relative_url) : path("/")
|
|
|
|
elsif user.present?
|
|
|
|
response[:message] = I18n.t('invite.confirm_email')
|
|
|
|
cookies[:destination_url] = path(topic.relative_url) if topic.present?
|
|
|
|
end
|
|
|
|
|
|
|
|
render json: success_json.merge(response)
|
|
|
|
else
|
|
|
|
render json: failed_json.merge(message: I18n.t('invite.not_found_json')), status: 404
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def destroy_all_expired
|
|
|
|
guardian.ensure_can_destroy_all_invites!(current_user)
|
|
|
|
|
|
|
|
Invite
|
|
|
|
.where(invited_by: current_user)
|
|
|
|
.where('expires_at < ?', Time.zone.now)
|
|
|
|
.find_each { |invite| invite.trash!(current_user) }
|
2017-06-29 22:32:07 +08:00
|
|
|
|
2020-06-10 09:25:58 +08:00
|
|
|
render json: success_json
|
2017-06-29 22:32:07 +08:00
|
|
|
end
|
|
|
|
|
2014-10-07 02:48:56 +08:00
|
|
|
def resend_invite
|
|
|
|
params.require(:email)
|
2016-06-07 03:36:59 +08:00
|
|
|
RateLimiter.new(current_user, "resend-invite-per-hour", 10, 1.hour).performed!
|
2014-10-07 02:48:56 +08:00
|
|
|
|
|
|
|
invite = Invite.find_by(invited_by_id: current_user.id, email: params[:email])
|
|
|
|
raise Discourse::InvalidParameters.new(:email) if invite.blank?
|
|
|
|
invite.resend_invite
|
2020-06-10 09:25:58 +08:00
|
|
|
render json: success_json
|
2016-06-07 03:36:59 +08:00
|
|
|
|
|
|
|
rescue RateLimiter::LimitExceeded
|
|
|
|
render_json_error(I18n.t("rate_limiter.slow_down"))
|
2014-10-07 02:48:56 +08:00
|
|
|
end
|
|
|
|
|
2016-06-03 03:09:02 +08:00
|
|
|
def resend_all_invites
|
2016-06-07 13:27:08 +08:00
|
|
|
guardian.ensure_can_resend_all_invites!(current_user)
|
2016-06-03 03:09:02 +08:00
|
|
|
|
2021-03-03 17:45:29 +08:00
|
|
|
Invite
|
|
|
|
.left_outer_joins(:invited_users)
|
|
|
|
.where(invited_by: current_user)
|
|
|
|
.where('invites.email IS NOT NULL')
|
|
|
|
.where('invited_users.user_id IS NULL')
|
|
|
|
.group('invites.id')
|
|
|
|
.find_each { |invite| invite.resend_invite }
|
|
|
|
|
2020-06-10 09:25:58 +08:00
|
|
|
render json: success_json
|
2016-06-03 03:09:02 +08:00
|
|
|
end
|
|
|
|
|
2016-12-05 00:06:35 +08:00
|
|
|
def upload_csv
|
2019-10-02 13:07:37 +08:00
|
|
|
require 'csv'
|
|
|
|
|
2014-05-28 04:14:37 +08:00
|
|
|
guardian.ensure_can_bulk_invite_to_forum!(current_user)
|
|
|
|
|
2019-06-04 22:49:46 +08:00
|
|
|
hijack do
|
|
|
|
begin
|
|
|
|
file = params[:file] || params[:files].first
|
2016-12-05 00:06:35 +08:00
|
|
|
|
2019-06-12 17:05:21 +08:00
|
|
|
count = 0
|
2019-06-04 22:49:46 +08:00
|
|
|
invites = []
|
2019-06-12 17:05:21 +08:00
|
|
|
max_bulk_invites = SiteSetting.max_bulk_invites
|
2019-06-04 22:49:46 +08:00
|
|
|
CSV.foreach(file.tempfile) do |row|
|
2019-06-12 17:05:21 +08:00
|
|
|
count += 1
|
2019-06-12 19:14:17 +08:00
|
|
|
invites.push(email: row[0], groups: row[1], topic_id: row[2]) if row[0].present?
|
2019-06-12 17:05:21 +08:00
|
|
|
break if count >= max_bulk_invites
|
2019-06-04 22:49:46 +08:00
|
|
|
end
|
2019-06-12 17:05:21 +08:00
|
|
|
|
2019-06-04 22:49:46 +08:00
|
|
|
if invites.present?
|
|
|
|
Jobs.enqueue(:bulk_invite, invites: invites, current_user_id: current_user.id)
|
2019-06-12 17:05:21 +08:00
|
|
|
if count >= max_bulk_invites
|
|
|
|
render json: failed_json.merge(errors: [I18n.t("bulk_invite.max_rows", max_bulk_invites: max_bulk_invites)]), status: 422
|
|
|
|
else
|
|
|
|
render json: success_json
|
|
|
|
end
|
2019-06-04 22:49:46 +08:00
|
|
|
else
|
|
|
|
render json: failed_json.merge(errors: [I18n.t("bulk_invite.error")]), status: 422
|
|
|
|
end
|
|
|
|
rescue
|
|
|
|
render json: failed_json.merge(errors: [I18n.t("bulk_invite.error")]), status: 422
|
2016-12-05 00:06:35 +08:00
|
|
|
end
|
2014-05-28 04:14:37 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-03-03 17:45:29 +08:00
|
|
|
private
|
2014-07-14 23:56:26 +08:00
|
|
|
|
2021-03-02 15:13:04 +08:00
|
|
|
def ensure_invites_allowed
|
|
|
|
if SiteSetting.enable_discourse_connect || (!SiteSetting.enable_local_logins && Discourse.enabled_auth_providers.count == 0)
|
|
|
|
raise Discourse::NotFound
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-07-15 03:42:14 +08:00
|
|
|
def ensure_new_registrations_allowed
|
|
|
|
unless SiteSetting.allow_new_registrations
|
|
|
|
flash[:error] = I18n.t('login.new_registrations_disabled')
|
2015-01-16 04:56:53 +08:00
|
|
|
render layout: 'no_ember'
|
2014-07-15 03:42:14 +08:00
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
2016-02-23 21:33:12 +08:00
|
|
|
|
|
|
|
def ensure_not_logged_in
|
|
|
|
if current_user
|
2020-10-24 23:51:01 +08:00
|
|
|
flash[:error] = I18n.t("login.already_logged_in")
|
2016-02-23 21:33:12 +08:00
|
|
|
render layout: 'no_ember'
|
|
|
|
false
|
|
|
|
end
|
|
|
|
end
|
2017-04-15 17:18:05 +08:00
|
|
|
|
|
|
|
def post_process_invite(user)
|
|
|
|
user.enqueue_welcome_message('welcome_invite') if user.send_welcome_message
|
2018-12-11 06:24:02 +08:00
|
|
|
|
2019-05-10 20:49:12 +08:00
|
|
|
Group.refresh_automatic_groups!(:admins, :moderators, :staff) if user.staff?
|
|
|
|
|
2017-04-15 17:18:05 +08:00
|
|
|
if user.has_password?
|
2018-12-11 06:24:02 +08:00
|
|
|
send_activation_email(user) unless user.active
|
2021-02-08 18:04:33 +08:00
|
|
|
elsif !SiteSetting.enable_discourse_connect && SiteSetting.enable_local_logins
|
2017-04-18 16:55:52 +08:00
|
|
|
Jobs.enqueue(:invite_password_instructions_email, username: user.username)
|
2017-04-15 17:18:05 +08:00
|
|
|
end
|
2018-06-07 13:28:18 +08:00
|
|
|
end
|
2017-04-15 17:18:05 +08:00
|
|
|
|
2018-12-11 06:24:02 +08:00
|
|
|
def send_activation_email(user)
|
2018-12-12 08:36:13 +08:00
|
|
|
email_token = user.email_tokens.create!(email: user.email)
|
2018-12-11 06:24:02 +08:00
|
|
|
|
|
|
|
Jobs.enqueue(:critical_user_email,
|
|
|
|
type: :signup,
|
|
|
|
user_id: user.id,
|
|
|
|
email_token: email_token.token
|
|
|
|
)
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
end
|