diff --git a/app/assets/javascripts/discourse/app/controllers/invites-show.js b/app/assets/javascripts/discourse/app/controllers/invites-show.js index c9ad1157079..ea121fac4bb 100644 --- a/app/assets/javascripts/discourse/app/controllers/invites-show.js +++ b/app/assets/javascripts/discourse/app/controllers/invites-show.js @@ -31,6 +31,9 @@ export default Controller.extend( accountEmail: alias("email"), existingUserId: readOnly("model.existing_user_id"), existingUserCanRedeem: readOnly("model.existing_user_can_redeem"), + existingUserCanRedeemError: readOnly( + "model.existing_user_can_redeem_error" + ), existingUserRedeeming: bool("existingUserId"), hiddenEmail: alias("model.hidden_email"), emailVerifiedByLink: alias("model.email_verified_by_link"), diff --git a/app/assets/javascripts/discourse/app/templates/invites/show.hbs b/app/assets/javascripts/discourse/app/templates/invites/show.hbs index c7d9122c4e8..ac357949956 100644 --- a/app/assets/javascripts/discourse/app/templates/invites/show.hbs +++ b/app/assets/javascripts/discourse/app/templates/invites/show.hbs @@ -135,7 +135,7 @@ {{#if this.existingUserCanRedeem}} {{else}} -
{{i18n "invites.existing_user_cannot_redeem"}}
+
{{this.existingUserCanRedeemError}}
{{/if}} {{/if}} {{/if}} diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index 5c5275c12e7..9f9eb041901 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -408,6 +408,7 @@ class InvitesController < ApplicationController if current_user info[:existing_user_id] = current_user.id info[:existing_user_can_redeem] = invite.can_be_redeemed_by?(current_user) + info[:existing_user_can_redeem_error] = existing_user_can_redeem_error(invite) info[:email] = current_user.email info[:username] = current_user.username end @@ -493,4 +494,13 @@ class InvitesController < ApplicationController end end end + + def existing_user_can_redeem_error(invite) + return if invite.can_be_redeemed_by?(current_user) + if invite.invited_users.exists?(user: current_user) + I18n.t("invite.existing_user_already_redemeed") + else + I18n.t("invite.existing_user_cannot_redeem") + end + end end diff --git a/app/models/invite.rb b/app/models/invite.rb index 19107001857..48b0efe072f 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -90,6 +90,10 @@ class Invite < ActiveRecord::Base !redeemed? && !expired? && !deleted_at? && !destroyed? && link_valid? end + def redeemed_by_user?(redeeming_user) + self.invited_users.exists?(user: redeeming_user) + end + def redeemed? if is_invite_link? redemption_count >= max_redemptions_allowed @@ -109,7 +113,7 @@ class Invite < ActiveRecord::Base def can_be_redeemed_by?(user) return false if !self.redeemable? - return true if self.email.blank? && self.domain.blank? + return false if redeemed_by_user?(user) return true if self.email.present? && email_matches?(user.email) self.domain.present? && domain_matches?(user.email) end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index c016759f072..ebd5a1519d3 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2097,7 +2097,6 @@ en: name_label: "Name" password_label: "Password" existing_user_can_redeem: "Redeem your invitation to a topic or group." - existing_user_cannot_redeem: "This invitation cannot be redeemed. Please ask the person who invited you to send you a new invitation." password_reset: continue: "Continue to %{site_name}" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 669a4edcedb..71d42ab3d8b 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -250,6 +250,8 @@ en:

Otherwise please Reset Password.

not_found_template_link: |

This invitation to %{site_name} can no longer be redeemed. Please ask the person who invited you to send you a new invitation.

+ existing_user_cannot_redeem: "This invitation cannot be redeemed. Please ask the person who invited you to send you a new invitation." + existing_user_already_redemeed: "You have already redeemed this invite link." user_exists: "There's no need to invite %{email}, they already have an account!" invite_exists: "You already invited %{email}." invalid_email: "%{email} isn't a valid email address." diff --git a/spec/requests/invites_controller_spec.rb b/spec/requests/invites_controller_spec.rb index 8410bd66ef5..cebeeab6477 100644 --- a/spec/requests/invites_controller_spec.rb +++ b/spec/requests/invites_controller_spec.rb @@ -126,6 +126,7 @@ RSpec.describe InvitesController do json = JSON.parse(element.current_scope.attribute('data-preloaded').value) invite_info = JSON.parse(json['invite_info']) expect(invite_info['existing_user_can_redeem']).to eq(false) + expect(invite_info['existing_user_can_redeem_error']).to eq(I18n.t("invite.existing_user_cannot_redeem")) end end @@ -141,6 +142,22 @@ RSpec.describe InvitesController do expect(invite_info['existing_user_can_redeem']).to eq(false) end end + + it "does not allow the user to accept the invite when a multi-use invite link has already been redeemed by the user" do + invite.update!(email: nil, max_redemptions_allowed: 10) + expect(invite.redeem(redeeming_user: user)).not_to eq(nil) + + get "/invites/#{invite.invite_key}" + expect(response.status).to eq(200) + + expect(response.body).to have_tag('div#data-preloaded') do |element| + json = JSON.parse(element.current_scope.attribute('data-preloaded').value) + invite_info = JSON.parse(json['invite_info']) + expect(invite_info['existing_user_id']).to eq(user.id) + expect(invite_info['existing_user_can_redeem']).to eq(false) + expect(invite_info['existing_user_can_redeem_error']).to eq(I18n.t("invite.existing_user_already_redemeed")) + end + end end it 'fails if invite does not exist' do