From eb96016043fa646b486f04790ff31eed60234a06 Mon Sep 17 00:00:00 2001
From: Arpit Jalan <arpit@techapj.com>
Date: Mon, 31 Aug 2015 19:36:13 +0530
Subject: [PATCH] FEATURE: copy invite link for topic invites

---
 .../discourse/controllers/invite.js.es6       | 29 ++++++++++---
 .../javascripts/discourse/models/topic.js.es6 |  7 ++++
 .../javascripts/discourse/models/user.js.es6  |  4 +-
 .../discourse/templates/modal/invite.hbs      |  4 +-
 app/controllers/invites_controller.rb         |  3 +-
 app/models/invite.rb                          | 42 ++++++-------------
 6 files changed, 49 insertions(+), 40 deletions(-)

diff --git a/app/assets/javascripts/discourse/controllers/invite.js.es6 b/app/assets/javascripts/discourse/controllers/invite.js.es6
index fa9516773..54edc7706 100644
--- a/app/assets/javascripts/discourse/controllers/invite.js.es6
+++ b/app/assets/javascripts/discourse/controllers/invite.js.es6
@@ -20,12 +20,25 @@ export default Ember.Controller.extend(ModalFunctionality, {
     if (!this.get('invitingToTopic') && !Discourse.Utilities.emailValid(emailOrUsername)) return true;
     // normal users (not admin) can't invite users to private topic via email
     if (!this.get('isAdmin') && this.get('isPrivateTopic') && Discourse.Utilities.emailValid(emailOrUsername)) return true;
-    // when invting to private topic via email, group name must be specified
+    // when inviting to private topic via email, group name must be specified
     if (this.get('isPrivateTopic') && Ember.isEmpty(this.get('model.groupNames')) && Discourse.Utilities.emailValid(emailOrUsername)) return true;
     if (this.get('model.details.can_invite_to')) return false;
     return false;
   }.property('isAdmin', 'emailOrUsername', 'invitingToTopic', 'isPrivateTopic', 'model.groupNames', 'model.saving'),
 
+  disabledCopyLink: function() {
+    if (this.get('model.saving')) return true;
+    if (Ember.isEmpty(this.get('emailOrUsername'))) return true;
+    const emailOrUsername = this.get('emailOrUsername').trim();
+    // email must be valid
+    if (!Discourse.Utilities.emailValid(emailOrUsername)) return true;
+    // normal users (not admin) can't invite users to private topic via email
+    if (!this.get('isAdmin') && this.get('isPrivateTopic') && Discourse.Utilities.emailValid(emailOrUsername)) return true;
+    // when inviting to private topic via email, group name must be specified
+    if (this.get('isPrivateTopic') && Ember.isEmpty(this.get('model.groupNames')) && Discourse.Utilities.emailValid(emailOrUsername)) return true;
+    return false;
+  }.property('emailOrUsername', 'model.saving', 'isPrivateTopic', 'model.groupNames'),
+
   buttonTitle: function() {
     return this.get('model.saving') ? 'topic.inviting' : 'topic.invite_reply.action';
   }.property('model.saving'),
@@ -36,9 +49,9 @@ 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'),
+  showCopyInviteButton: function() {
+    return (!Discourse.SiteSettings.enable_sso && !this.get('isMessage'));
+  }.property('isMessage'),
 
   topicId: Ember.computed.alias('model.id'),
 
@@ -163,9 +176,15 @@ export default Ember.Controller.extend(ModalFunctionality, {
             userInvitedController = this.get('controllers.user-invited-show'),
             model = this.get('model');
 
+      if (this.get('invitingToTopic')) {
+        var topicId = this.get('model.id');
+      } else {
+        var topicId = null;
+      }
+
       model.setProperties({ saving: true, error: false });
 
-      return this.get('model').generateInviteLink(this.get('emailOrUsername').trim(), groupNames).then(result => {
+      return this.get('model').generateInviteLink(this.get('emailOrUsername').trim(), groupNames, topicId).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);
diff --git a/app/assets/javascripts/discourse/models/topic.js.es6 b/app/assets/javascripts/discourse/models/topic.js.es6
index 157af104d..3b63069ed 100644
--- a/app/assets/javascripts/discourse/models/topic.js.es6
+++ b/app/assets/javascripts/discourse/models/topic.js.es6
@@ -263,6 +263,13 @@ const Topic = RestModel.extend({
     });
   },
 
+  generateInviteLink: function(email, groupNames, topicId) {
+    return Discourse.ajax('/invites/link', {
+      type: 'POST',
+      data: {email: email, group_names: groupNames, topic_id: topicId}
+    });
+  },
+
   // Delete this topic
   destroy(deleted_by) {
     this.setProperties({
diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6
index 8e3d15e29..19cf8eace 100644
--- a/app/assets/javascripts/discourse/models/user.js.es6
+++ b/app/assets/javascripts/discourse/models/user.js.es6
@@ -372,10 +372,10 @@ const User = RestModel.extend({
     });
   },
 
-  generateInviteLink: function(email, groupNames) {
+  generateInviteLink: function(email, groupNames, topicId) {
     return Discourse.ajax('/invites/link', {
       type: 'POST',
-      data: {email: email, group_names: groupNames}
+      data: {email: email, group_names: groupNames, topic_id: topicId}
     });
   },
 
diff --git a/app/assets/javascripts/discourse/templates/modal/invite.hbs b/app/assets/javascripts/discourse/templates/modal/invite.hbs
index 0f41fb397..a5ccb897f 100644
--- a/app/assets/javascripts/discourse/templates/modal/invite.hbs
+++ b/app/assets/javascripts/discourse/templates/modal/invite.hbs
@@ -29,8 +29,8 @@
     {{d-button class="btn-primary" action="closeModal" label="close"}}
   {{else}}
     {{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 showCopyInviteButton}}
+      {{d-button icon="link" action="generateInvitelink" class="btn-primary" disabled=disabledCopyLink label='user.invited.generate_link'}}
     {{/if}}
   {{/if}}
 </div>
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index c8d5679e7..eeb55b67f 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -51,6 +51,7 @@ class InvitesController < ApplicationController
   def create_invite_link
     params.require(:email)
     group_ids = Group.lookup_group_ids(params)
+    topic = Topic.find_by(id: params[:topic_id])
     guardian.ensure_can_invite_to_forum!(group_ids)
 
     invite_exists = Invite.where(email: params[:email], invited_by_id: current_user.id).first
@@ -59,7 +60,7 @@ class InvitesController < ApplicationController
     end
 
     # generate invite link
-    invite_link = Invite.generate_invite_link(params[:email], current_user, group_ids)
+    invite_link = Invite.generate_invite_link(params[:email], current_user, topic, group_ids)
     render_json_dump(invite_link)
   end
 
diff --git a/app/models/invite.rb b/app/models/invite.rb
index 6bc76bfae..8cf304d45 100644
--- a/app/models/invite.rb
+++ b/app/models/invite.rb
@@ -72,10 +72,20 @@ class Invite < ActiveRecord::Base
     end
   end
 
+  def self.invite_by_email(email, invited_by, topic=nil, group_ids=nil)
+    create_invite_by_email(email, invited_by, topic, group_ids, true)
+  end
+
+  # generate invite link
+  def self.generate_invite_link(email, invited_by, topic=nil, group_ids=nil)
+    invite = create_invite_by_email(email, invited_by, topic, group_ids, false)
+    return "#{Discourse.base_url}/invites/#{invite.invite_key}"
+  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.
-  def self.invite_by_email(email, invited_by, topic=nil, group_ids=nil)
+  def self.create_invite_by_email(email, invited_by, topic=nil, group_ids=nil, send_email=true)
     lower_email = Email.downcase(email)
     user = User.find_by(email: lower_email)
 
@@ -116,40 +126,12 @@ class Invite < ActiveRecord::Base
       end
     end
 
-    Jobs.enqueue(:invite_email, invite_id: invite.id)
+    Jobs.enqueue(:invite_email, invite_id: invite.id) if send_email
 
     invite.reload
     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 = []