mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
Support for sending PMs to email addresses (#4988)
* Added support for sending PMs to email addresses. * Made changes after review. * Added settings validator. * Fixed tests.
This commit is contained in:
parent
4623b46b0b
commit
bb3a5910d7
@ -42,8 +42,11 @@ export default TextField.extend({
|
|||||||
updateData: (opts && opts.updateData) ? opts.updateData : false,
|
updateData: (opts && opts.updateData) ? opts.updateData : false,
|
||||||
|
|
||||||
dataSource: function(term) {
|
dataSource: function(term) {
|
||||||
|
const termRegex = Discourse.User.currentProp('can_send_private_email_messages') ?
|
||||||
|
/[^a-zA-Z0-9_\-\.@\+]/ : /[^a-zA-Z0-9_\-\.]/;
|
||||||
|
|
||||||
var results = userSearch({
|
var results = userSearch({
|
||||||
term: term.replace(/[^a-zA-Z0-9_\-\.]/, ''),
|
term: term.replace(termRegex, ''),
|
||||||
topicId: self.get('topicId'),
|
topicId: self.get('topicId'),
|
||||||
exclude: excludedUsernames(),
|
exclude: excludedUsernames(),
|
||||||
includeGroups,
|
includeGroups,
|
||||||
|
@ -46,6 +46,7 @@ function organizeResults(r, options) {
|
|||||||
var exclude = options.exclude || [],
|
var exclude = options.exclude || [],
|
||||||
limit = options.limit || 5,
|
limit = options.limit || 5,
|
||||||
users = [],
|
users = [],
|
||||||
|
emails = [],
|
||||||
groups = [],
|
groups = [],
|
||||||
results = [];
|
results = [];
|
||||||
|
|
||||||
@ -59,6 +60,12 @@ function organizeResults(r, options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.term.match(/@/)) {
|
||||||
|
let e = { username: options.term };
|
||||||
|
emails = [ e ];
|
||||||
|
results.push(e);
|
||||||
|
}
|
||||||
|
|
||||||
if (r.groups) {
|
if (r.groups) {
|
||||||
r.groups.every(function(g) {
|
r.groups.every(function(g) {
|
||||||
if (results.length > limit && options.term.toLowerCase() !== g.name.toLowerCase()) return false;
|
if (results.length > limit && options.term.toLowerCase() !== g.name.toLowerCase()) return false;
|
||||||
@ -71,6 +78,7 @@ function organizeResults(r, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
results.users = users;
|
results.users = users;
|
||||||
|
results.emails = emails;
|
||||||
results.groups = groups;
|
results.groups = groups;
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
@ -94,7 +102,7 @@ export default function userSearch(options) {
|
|||||||
|
|
||||||
return new Ember.RSVP.Promise(function(resolve) {
|
return new Ember.RSVP.Promise(function(resolve) {
|
||||||
// TODO site setting for allowed regex in username
|
// TODO site setting for allowed regex in username
|
||||||
if (term.match(/[^\w\.\-]/)) {
|
if (term.match(/[^\w_\-\.@\+]/)) {
|
||||||
resolve([]);
|
resolve([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,18 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|
||||||
|
{{#if options.emails}}
|
||||||
|
{{#each options.emails as |email|}}
|
||||||
|
<li>
|
||||||
|
<a href title="{{email.username}}">
|
||||||
|
<i class='fa fa-envelope'></i>
|
||||||
|
<span class='username'>{{email.username}}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if options.groups}}
|
{{#if options.groups}}
|
||||||
{{#each options.groups as |group|}}
|
{{#each options.groups as |group|}}
|
||||||
<li>
|
<li>
|
||||||
|
@ -617,7 +617,10 @@ class PostsController < ApplicationController
|
|||||||
usernames = usernames.split(",")
|
usernames = usernames.split(",")
|
||||||
groups = Group.mentionable(current_user).where('name in (?)', usernames).pluck('name')
|
groups = Group.mentionable(current_user).where('name in (?)', usernames).pluck('name')
|
||||||
usernames -= groups
|
usernames -= groups
|
||||||
|
emails = usernames.select { |user| user.match(/@/) }
|
||||||
|
usernames -= emails
|
||||||
result[:target_usernames] = usernames.join(",")
|
result[:target_usernames] = usernames.join(",")
|
||||||
|
result[:target_emails] = emails.join(",")
|
||||||
result[:target_group_names] = groups.join(",")
|
result[:target_group_names] = groups.join(",")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ class CurrentUserSerializer < BasicUserSerializer
|
|||||||
:external_links_in_new_tab,
|
:external_links_in_new_tab,
|
||||||
:dynamic_favicon,
|
:dynamic_favicon,
|
||||||
:trust_level,
|
:trust_level,
|
||||||
|
:can_send_private_email_messages,
|
||||||
:can_edit,
|
:can_edit,
|
||||||
:can_invite_to_forum,
|
:can_invite_to_forum,
|
||||||
:no_password,
|
:no_password,
|
||||||
@ -87,6 +88,10 @@ class CurrentUserSerializer < BasicUserSerializer
|
|||||||
PostAction.flagged_posts_count
|
PostAction.flagged_posts_count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_send_private_email_messages
|
||||||
|
scope.cand_send_private_messages_to_email?
|
||||||
|
end
|
||||||
|
|
||||||
def can_edit
|
def can_edit
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
@ -382,6 +382,7 @@ en:
|
|||||||
too_many_users: "You can only send warnings to one user at a time."
|
too_many_users: "You can only send warnings to one user at a time."
|
||||||
cant_send_pm: "Sorry, you cannot send a private message to that user."
|
cant_send_pm: "Sorry, you cannot send a private message to that user."
|
||||||
no_user_selected: "You must select a valid user."
|
no_user_selected: "You must select a valid user."
|
||||||
|
reply_by_email_disabled: "Reply by email has been disabled."
|
||||||
featured_link:
|
featured_link:
|
||||||
invalid: "is invalid. URL should include http:// or https://."
|
invalid: "is invalid. URL should include http:// or https://."
|
||||||
invalid_category: "can't be edited in this category."
|
invalid_category: "can't be edited in this category."
|
||||||
@ -1013,6 +1014,7 @@ en:
|
|||||||
summary_max_results: "Maximum posts returned by 'Summary This Topic'"
|
summary_max_results: "Maximum posts returned by 'Summary This Topic'"
|
||||||
|
|
||||||
enable_private_messages: "Allow trust level 1 (configurable via min trust level to send messages) users to create messages and reply to messages. Note that staff can always send messages no matter what."
|
enable_private_messages: "Allow trust level 1 (configurable via min trust level to send messages) users to create messages and reply to messages. Note that staff can always send messages no matter what."
|
||||||
|
enable_private_email_messages: "Allow trust level 4 (configurable via min trust level to send messages) users to send private email messages. Note that staff can always send messages no matter what."
|
||||||
|
|
||||||
enable_long_polling: "Message bus used for notification can use long polling"
|
enable_long_polling: "Message bus used for notification can use long polling"
|
||||||
long_polling_base_url: "Base URL used for long polling (when a CDN is serving dynamic content, be sure to set this to origin pull) eg: http://origin.site.com"
|
long_polling_base_url: "Base URL used for long polling (when a CDN is serving dynamic content, be sure to set this to origin pull) eg: http://origin.site.com"
|
||||||
@ -1259,6 +1261,8 @@ en:
|
|||||||
|
|
||||||
min_trust_to_send_messages: "The minimum trust level required to create new private messages."
|
min_trust_to_send_messages: "The minimum trust level required to create new private messages."
|
||||||
|
|
||||||
|
min_trust_to_send_email_messages: "The minimum trust level required to send new private messages via email (to staged users)."
|
||||||
|
|
||||||
newuser_max_links: "How many links a new user can add to a post."
|
newuser_max_links: "How many links a new user can add to a post."
|
||||||
newuser_max_images: "How many images a new user can add to a post."
|
newuser_max_images: "How many images a new user can add to a post."
|
||||||
newuser_max_attachments: "How many attachments a new user can add to a post."
|
newuser_max_attachments: "How many attachments a new user can add to a post."
|
||||||
@ -1586,6 +1590,8 @@ en:
|
|||||||
invalid_regex: "Regex is invalid or not allowed."
|
invalid_regex: "Regex is invalid or not allowed."
|
||||||
email_editable_enabled: "You must disable 'email editable' before enabling this setting."
|
email_editable_enabled: "You must disable 'email editable' before enabling this setting."
|
||||||
enable_sso_disabled: "You must first enable 'enable sso' before enabling this setting."
|
enable_sso_disabled: "You must first enable 'enable sso' before enabling this setting."
|
||||||
|
staged_users_disabled: "You must first enable 'staged users' before enabling this setting."
|
||||||
|
reply_by_email_disabled: "You must first enable 'reply by email' before enabling this setting."
|
||||||
|
|
||||||
search:
|
search:
|
||||||
within_post: "#%{post_number} by %{username}"
|
within_post: "#%{post_number} by %{username}"
|
||||||
|
@ -508,6 +508,10 @@ posting:
|
|||||||
enable_private_messages:
|
enable_private_messages:
|
||||||
default: true
|
default: true
|
||||||
client: true
|
client: true
|
||||||
|
enable_private_email_messages:
|
||||||
|
default: false
|
||||||
|
client: true
|
||||||
|
validator: "EnablePrivateEmailMessagesValidator"
|
||||||
editing_grace_period: 300
|
editing_grace_period: 300
|
||||||
post_edit_time_limit:
|
post_edit_time_limit:
|
||||||
default: 86400
|
default: 86400
|
||||||
@ -857,6 +861,9 @@ trust:
|
|||||||
min_trust_to_send_messages:
|
min_trust_to_send_messages:
|
||||||
default: 1
|
default: 1
|
||||||
enum: 'TrustLevelSetting'
|
enum: 'TrustLevelSetting'
|
||||||
|
min_trust_to_send_email_messages:
|
||||||
|
default: 4
|
||||||
|
enum: 'TrustLevelSetting'
|
||||||
tl1_requires_topics_entered: 5
|
tl1_requires_topics_entered: 5
|
||||||
tl1_requires_read_posts:
|
tl1_requires_read_posts:
|
||||||
default: 30
|
default: 30
|
||||||
|
@ -299,6 +299,17 @@ class Guardian
|
|||||||
(!is_blocked? || target.staff?)
|
(!is_blocked? || target.staff?)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cand_send_private_messages_to_email?
|
||||||
|
# Staged users must be enabled to create a temporary user.
|
||||||
|
SiteSetting.enable_staged_users &&
|
||||||
|
# User is authenticated
|
||||||
|
authenticated? &&
|
||||||
|
# User is trusted enough
|
||||||
|
@user.has_trust_level?(SiteSetting.min_trust_to_send_email_messages) &&
|
||||||
|
# PMs to email addresses are enabled
|
||||||
|
(is_staff? || SiteSetting.enable_private_email_messages)
|
||||||
|
end
|
||||||
|
|
||||||
def can_see_emails?
|
def can_see_emails?
|
||||||
@can_see_emails
|
@can_see_emails
|
||||||
end
|
end
|
||||||
|
@ -162,11 +162,16 @@ class TopicCreator
|
|||||||
return unless @opts[:archetype] == Archetype.private_message
|
return unless @opts[:archetype] == Archetype.private_message
|
||||||
topic.subtype = TopicSubtype.user_to_user unless topic.subtype
|
topic.subtype = TopicSubtype.user_to_user unless topic.subtype
|
||||||
|
|
||||||
unless @opts[:target_usernames].present? || @opts[:target_group_names].present?
|
unless @opts[:target_usernames].present? || @opts[:target_emails].present? || @opts[:target_group_names].present?
|
||||||
rollback_with!(topic, :no_user_selected)
|
rollback_with!(topic, :no_user_selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if @opts[:target_emails].present? && !@guardian.cand_send_private_messages_to_email? then
|
||||||
|
rollback_with!(topic, :reply_by_email_disabled)
|
||||||
|
end
|
||||||
|
|
||||||
add_users(topic, @opts[:target_usernames])
|
add_users(topic, @opts[:target_usernames])
|
||||||
|
add_emails(topic, @opts[:target_emails])
|
||||||
add_groups(topic, @opts[:target_group_names])
|
add_groups(topic, @opts[:target_group_names])
|
||||||
topic.topic_allowed_users.build(user_id: @user.id)
|
topic.topic_allowed_users.build(user_id: @user.id)
|
||||||
end
|
end
|
||||||
@ -195,6 +200,23 @@ class TopicCreator
|
|||||||
rollback_with!(topic, :target_user_not_found) unless len == names.length
|
rollback_with!(topic, :target_user_not_found) unless len == names.length
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_emails(topic, emails)
|
||||||
|
return unless emails
|
||||||
|
|
||||||
|
emails = emails.split(',').flatten
|
||||||
|
len = 0
|
||||||
|
|
||||||
|
emails.each do |email|
|
||||||
|
display_name = email.split("@").first
|
||||||
|
user = find_or_create_user(email, display_name)
|
||||||
|
@added_users << user
|
||||||
|
topic.topic_allowed_users.build(user_id: user.id)
|
||||||
|
len += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
rollback_with!(topic, :target_user_not_found) unless len == emails.length
|
||||||
|
end
|
||||||
|
|
||||||
def add_groups(topic, groups)
|
def add_groups(topic, groups)
|
||||||
return unless groups
|
return unless groups
|
||||||
names = groups.split(',').flatten
|
names = groups.split(',').flatten
|
||||||
@ -213,4 +235,23 @@ class TopicCreator
|
|||||||
def check_can_send_permission!(topic, obj)
|
def check_can_send_permission!(topic, obj)
|
||||||
rollback_with!(topic, :cant_send_pm) unless @opts[:skip_validations] || @guardian.can_send_private_message?(obj)
|
rollback_with!(topic, :cant_send_pm) unless @opts[:skip_validations] || @guardian.can_send_private_message?(obj)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def find_or_create_user(email, display_name)
|
||||||
|
user = User.find_by_email(email)
|
||||||
|
|
||||||
|
if user.nil? && SiteSetting.enable_staged_users
|
||||||
|
username = UserNameSuggester.sanitize_username(display_name) if display_name.present?
|
||||||
|
user = User.create!(
|
||||||
|
email: email,
|
||||||
|
username: UserNameSuggester.suggest(username.presence || email),
|
||||||
|
name: display_name.presence || User.suggest_name(email),
|
||||||
|
staged: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
user
|
||||||
|
rescue
|
||||||
|
rollback_with!(topic, :target_user_not_found)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
20
lib/validators/enable_private_email_messages_validator.rb
Normal file
20
lib/validators/enable_private_email_messages_validator.rb
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
class EnablePrivateEmailMessagesValidator
|
||||||
|
|
||||||
|
def initialize(opts = {})
|
||||||
|
@opts = opts
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid_value?(val)
|
||||||
|
return true if val == "f"
|
||||||
|
SiteSetting.enable_staged_users &&
|
||||||
|
SiteSetting.reply_by_email_enabled
|
||||||
|
end
|
||||||
|
|
||||||
|
def error_message
|
||||||
|
if !SiteSetting.enable_staged_users
|
||||||
|
I18n.t("site_settings.errors.staged_users_disabled")
|
||||||
|
elsif !SiteSetting.reply_by_email_enabled
|
||||||
|
I18n.t("site_settings.errors.reply_by_email_disabled")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -8,6 +8,7 @@ describe TopicCreator do
|
|||||||
|
|
||||||
let(:valid_attrs) { Fabricate.attributes_for(:topic) }
|
let(:valid_attrs) { Fabricate.attributes_for(:topic) }
|
||||||
let(:pm_valid_attrs) { { raw: 'this is a new post', title: 'this is a new title', archetype: Archetype.private_message, target_usernames: moderator.username } }
|
let(:pm_valid_attrs) { { raw: 'this is a new post', title: 'this is a new title', archetype: Archetype.private_message, target_usernames: moderator.username } }
|
||||||
|
let(:pm_to_email_valid_attrs) { { raw: 'this is a new email', title: 'this is a new subject', archetype: Archetype.private_message, target_emails: 'moderator@example.com' } }
|
||||||
|
|
||||||
describe '#create' do
|
describe '#create' do
|
||||||
context 'topic success cases' do
|
context 'topic success cases' do
|
||||||
@ -58,6 +59,7 @@ describe TopicCreator do
|
|||||||
TopicCreator.any_instance.expects(:save_topic).returns(true)
|
TopicCreator.any_instance.expects(:save_topic).returns(true)
|
||||||
TopicCreator.any_instance.expects(:watch_topic).returns(true)
|
TopicCreator.any_instance.expects(:watch_topic).returns(true)
|
||||||
SiteSetting.allow_duplicate_topic_titles = true
|
SiteSetting.allow_duplicate_topic_titles = true
|
||||||
|
SiteSetting.enable_staged_users = true
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should be possible for a regular user to send private message" do
|
it "should be possible for a regular user to send private message" do
|
||||||
@ -68,6 +70,14 @@ describe TopicCreator do
|
|||||||
SiteSetting.min_trust_to_create_topic = TrustLevel[4]
|
SiteSetting.min_trust_to_create_topic = TrustLevel[4]
|
||||||
expect(TopicCreator.create(user, Guardian.new(user), pm_valid_attrs)).to be_valid
|
expect(TopicCreator.create(user, Guardian.new(user), pm_valid_attrs)).to be_valid
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should be possible for a trusted user to send private messages via email" do
|
||||||
|
SiteSetting.expects(:enable_staged_users).returns(true)
|
||||||
|
SiteSetting.expects(:enable_staged_users).returns(true)
|
||||||
|
SiteSetting.expects(:enable_private_email_messages).returns(true)
|
||||||
|
SiteSetting.min_trust_to_send_email_messages = TrustLevel[1]
|
||||||
|
expect(TopicCreator.create(user, Guardian.new(user), pm_to_email_valid_attrs)).to be_valid
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'failure cases' do
|
context 'failure cases' do
|
||||||
@ -75,6 +85,11 @@ describe TopicCreator do
|
|||||||
SiteSetting.min_trust_to_send_messages = TrustLevel[4]
|
SiteSetting.min_trust_to_send_messages = TrustLevel[4]
|
||||||
expect(-> { TopicCreator.create(user, Guardian.new(user), pm_valid_attrs) }).to raise_error(ActiveRecord::Rollback)
|
expect(-> { TopicCreator.create(user, Guardian.new(user), pm_valid_attrs) }).to raise_error(ActiveRecord::Rollback)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "min_trust_to_send_email_messages should be checked when sending private messages via email" do
|
||||||
|
SiteSetting.min_trust_to_send_email_messages = TrustLevel[4]
|
||||||
|
expect(-> { TopicCreator.create(user, Guardian.new(user), pm_to_email_valid_attrs) }).to raise_error(ActiveRecord::Rollback)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user