FEATURE: generate invite token

This commit is contained in:
Arpit Jalan 2015-08-26 07:11:52 +05:30
parent 727827dc25
commit 4ad07b8c09
8 changed files with 132 additions and 6 deletions

View File

@ -27,7 +27,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
}.property('isAdmin', 'emailOrUsername', 'invitingToTopic', 'isPrivateTopic', 'model.groupNames', 'model.saving'),
buttonTitle: function() {
return this.get('model.saving') ? I18n.t('topic.inviting') : I18n.t('topic.invite_reply.action');
return this.get('model.saving') ? 'topic.inviting' : 'topic.invite_reply.action';
}.property('model.saving'),
// We are inviting to a topic if the model isn't the current user.
@ -36,6 +36,10 @@ export default Ember.Controller.extend(ModalFunctionality, {
return this.get('model') !== this.currentUser;
}.property('model'),
invitingToForum: function() {
return (!Discourse.SiteSettings.enable_sso && !this.get('invitingToTopic') && !this.get('isMessage'));
}.property('invitingToTopic', 'isMessage'),
topicId: Ember.computed.alias('model.id'),
// Is Private Topic? (i.e. visible only to specific group members)
@ -95,14 +99,16 @@ export default Ember.Controller.extend(ModalFunctionality, {
},
successMessage: function() {
if (this.get('isMessage')) {
if (this.get('model.inviteLink')) {
return I18n.t('user.invited.generated_link_message', {inviteLink: this.get('model.inviteLink'), invitedEmail: this.get('emailOrUsername')});
} else if (this.get('isMessage')) {
return I18n.t('topic.invite_private.success');
} else if ( Discourse.Utilities.emailValid(this.get('emailOrUsername')) ) {
return I18n.t('topic.invite_reply.success_email', { emailOrUsername: this.get('emailOrUsername') });
} else {
return I18n.t('topic.invite_reply.success_username');
}
}.property('isMessage', 'emailOrUsername'),
}.property('model.inviteLink', 'isMessage', 'emailOrUsername'),
errorMessage: function() {
return this.get('isMessage') ? I18n.t('topic.invite_private.error') : I18n.t('topic.invite_reply.error');
@ -121,7 +127,8 @@ export default Ember.Controller.extend(ModalFunctionality, {
groupNames: null,
error: false,
saving: false,
finished: false
finished: false,
inviteLink: null
});
},
@ -147,6 +154,24 @@ export default Ember.Controller.extend(ModalFunctionality, {
this.get('model.details.allowed_users').pushObject(result.user);
}
}).catch(() => model.setProperties({ saving: false, error: true }));
},
generateInvitelink() {
if (this.get('disabled')) { return; }
const groupNames = this.get('model.groupNames'),
userInvitedController = this.get('controllers.user-invited-show'),
model = this.get('model');
model.setProperties({ saving: true, error: false });
return this.get('model').generateInviteLink(this.get('emailOrUsername').trim(), groupNames).then(result => {
model.setProperties({ saving: false, finished: true, inviteLink: result });
Invite.findInvitedBy(this.currentUser, userInvitedController.get('filter')).then(invite_model => {
userInvitedController.set('model', invite_model);
userInvitedController.set('totalInvites', invite_model.invites.length);
});
}).catch(() => model.setProperties({ saving: false, error: true }));
}
}

View File

@ -372,6 +372,13 @@ const User = RestModel.extend({
});
},
generateInviteLink: function(email, groupNames) {
return Discourse.ajax('/invites/link', {
type: 'POST',
data: {email: email, group_names: groupNames}
});
},
updateMutedCategories: function() {
this.set("mutedCategories", Discourse.Category.findByIds(this.muted_category_ids));
}.observes("muted_category_ids"),

View File

@ -28,6 +28,9 @@
{{#if model.finished}}
{{d-button class="btn-primary" action="closeModal" label="close"}}
{{else}}
<button class='btn btn-primary' {{bind-attr disabled="disabled"}} {{action "createInvite"}}>{{fa-icon "user-plus"}}{{buttonTitle}}</button>
{{d-button icon="envelope" action="createInvite" class="btn-primary" disabled=disabled label=buttonTitle}}
{{#if invitingToForum}}
{{d-button icon="link" action="generateInvitelink" class="btn-primary" disabled=disabled label='user.invited.generate_link'}}
{{/if}}
{{/if}}
</div>

View File

@ -4,7 +4,7 @@ class InvitesController < ApplicationController
skip_before_filter :check_xhr, :preload_json
skip_before_filter :redirect_to_login_if_required
before_filter :ensure_logged_in, only: [:destroy, :create, :resend_invite, :check_csv_chunk, :upload_csv_chunk]
before_filter :ensure_logged_in, only: [:destroy, :create, :create_invite_link, :resend_invite, :check_csv_chunk, :upload_csv_chunk]
before_filter :ensure_new_registrations_allowed, only: [:show, :redeem_disposable_invite]
def show
@ -48,6 +48,21 @@ class InvitesController < ApplicationController
end
end
def create_invite_link
params.require(:email)
group_ids = Group.lookup_group_ids(params)
guardian.ensure_can_invite_to_forum!(group_ids)
invite_exists = Invite.where(email: params[:email], invited_by_id: current_user.id).first
if invite_exists
guardian.ensure_can_send_multiple_invites!(current_user)
end
# generate invite link
invite_link = Invite.generate_invite_link(params[:email], current_user, group_ids)
render_json_dump(invite_link)
end
def create_disposable_invite
guardian.ensure_can_create_disposable_invite!(current_user)
params.permit(:username, :email, :quantity, :group_names)

View File

@ -71,6 +71,7 @@ class Invite < ActiveRecord::Base
end
end
end
# Create an invite for a user, supplying an optional topic
#
# Return the previously existing invite if already exists. Returns nil if the invite can't be created.
@ -121,6 +122,34 @@ class Invite < ActiveRecord::Base
invite
end
# generate invite link
def self.generate_invite_link(email, invited_by, group_ids=nil)
lower_email = Email.downcase(email)
invite = Invite.with_deleted
.where(email: lower_email, invited_by_id: invited_by.id)
.order('created_at DESC')
.first
if invite && (invite.expired? || invite.deleted_at)
invite.destroy
invite = nil
end
if !invite
invite = Invite.create!(invited_by: invited_by, email: lower_email)
end
if group_ids.present?
group_ids = group_ids - invite.invited_groups.pluck(:group_id)
group_ids.each do |group_id|
invite.invited_groups.create!(group_id: group_id)
end
end
return "#{Discourse.base_url}/invites/#{invite.invite_key}"
end
# generate invite tokens without email
def self.generate_disposable_tokens(invited_by, quantity=nil, group_names=nil)
invite_tokens = []
@ -153,6 +182,7 @@ class Invite < ActiveRecord::Base
def self.find_all_invites_from(inviter, offset=0, limit=SiteSetting.invites_per_page)
Invite.where(invited_by_id: inviter.id)
.where('invites.email IS NOT NULL')
.includes(:user => :user_stat)
.order('CASE WHEN invites.user_id IS NOT NULL THEN 0 ELSE 1 END',
'user_stats.time_read DESC',

View File

@ -625,6 +625,8 @@ en:
days_visited: "Days Visited"
account_age_days: "Account age in days"
create: "Send an Invite"
generate_link: "Copy Invite Link"
generated_link_message: '<p>Invite link generated successfully!</p><p><code><b>%{inviteLink}</b></code></p><p>Invite link is only valid for this email address: <b>%{invitedEmail}</b></p>'
bulk_invite:
none: "You haven't invited anyone here yet. You can send individual invites, or invite a bunch of people at once by <a href='https://meta.discourse.org/t/send-bulk-invites/16468'>uploading a bulk invite file</a>."
text: "Bulk Invite from File"

View File

@ -498,6 +498,7 @@ Discourse::Application.routes.draw do
end
end
post "invites/reinvite" => "invites#resend_invite"
post "invites/link" => "invites#create_invite_link"
post "invites/disposable" => "invites#create_disposable_invite"
get "invites/redeem/:token" => "invites#redeem_disposable_invite"
delete "invites" => "invites#destroy"

View File

@ -80,6 +80,49 @@ describe InvitesController do
end
context '.create_invite_link' do
it 'requires you to be logged in' do
expect {
post :create_invite_link, email: 'jake@adventuretime.ooo'
}.to raise_error(Discourse::NotLoggedIn)
end
context 'while logged in' do
let(:email) { 'jake@adventuretime.ooo' }
it "fails if you can't invite to the forum" do
log_in
post :create_invite_link, email: email
expect(response).not_to be_success
end
it "fails for normal user if invite email already exists" do
user = log_in(:trust_level_4)
invite = Invite.invite_by_email("invite@example.com", user)
invite.reload
post :create_invite_link, email: invite.email
expect(response).not_to be_success
end
it "allows admins to invite to groups" do
group = Fabricate(:group)
log_in(:admin)
post :create_invite_link, email: email, group_names: group.name
expect(response).to be_success
expect(Invite.find_by(email: email).invited_groups.count).to eq(1)
end
it "allows multiple group invite" do
group_1 = Fabricate(:group, name: "security")
group_2 = Fabricate(:group, name: "support")
log_in(:admin)
post :create_invite_link, email: email, group_names: "security,support"
expect(response).to be_success
expect(Invite.find_by(email: email).invited_groups.count).to eq(2)
end
end
end
context '.show' do
context 'with an invalid invite id' do