Mentions in comments backend changes
This commit is contained in:
parent
ef5751b808
commit
33ad411bbd
61 changed files with 1192 additions and 451 deletions
|
|
@ -23,8 +23,8 @@ class NotificationsController < ApplicationController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
conditions = {:recipient_id => current_user.id}
|
conditions = {:recipient_id => current_user.id}
|
||||||
if params[:type] && Notification.types.has_key?(params[:type])
|
if params[:type] && types.has_key?(params[:type])
|
||||||
conditions[:type] = Notification.types[params[:type]]
|
conditions[:type] = types[params[:type]]
|
||||||
end
|
end
|
||||||
if params[:show] == "unread" then conditions[:unread] = true end
|
if params[:show] == "unread" then conditions[:unread] = true end
|
||||||
page = params[:page] || 1
|
page = params[:page] || 1
|
||||||
|
|
@ -44,7 +44,7 @@ class NotificationsController < ApplicationController
|
||||||
|
|
||||||
@grouped_unread_notification_counts = {}
|
@grouped_unread_notification_counts = {}
|
||||||
|
|
||||||
Notification.types.each_with_object(current_user.unread_notifications.group_by(&:type)) {|(name, type), notifications|
|
types.each_with_object(current_user.unread_notifications.group_by(&:type)) {|(name, type), notifications|
|
||||||
@grouped_unread_notification_counts[name] = notifications.has_key?(type) ? notifications[type].count : 0
|
@grouped_unread_notification_counts[name] = notifications.has_key?(type) ? notifications[type].count : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,7 +65,7 @@ class NotificationsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_all
|
def read_all
|
||||||
current_type = Notification.types[params[:type]]
|
current_type = types[params[:type]]
|
||||||
notifications = Notification.where(recipient_id: current_user.id, unread: true)
|
notifications = Notification.where(recipient_id: current_user.id, unread: true)
|
||||||
notifications = notifications.where(type: current_type) if params[:type]
|
notifications = notifications.where(type: current_type) if params[:type]
|
||||||
notifications.update_all(unread: false)
|
notifications.update_all(unread: false)
|
||||||
|
|
@ -93,4 +93,17 @@ class NotificationsController < ApplicationController
|
||||||
}
|
}
|
||||||
}.as_json
|
}.as_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def types
|
||||||
|
{
|
||||||
|
"also_commented" => "Notifications::AlsoCommented",
|
||||||
|
"comment_on_post" => "Notifications::CommentOnPost",
|
||||||
|
"liked" => "Notifications::Liked",
|
||||||
|
"mentioned" => "Notifications::MentionedInPost",
|
||||||
|
"mentioned_in_comment" => "Notifications::MentionedInComment",
|
||||||
|
"reshared" => "Notifications::Reshared",
|
||||||
|
"started_sharing" => "Notifications::StartedSharing"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
helper_method :types
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,10 @@ class StatusMessagesController < ApplicationController
|
||||||
|
|
||||||
def handle_mention_feedback(status_message)
|
def handle_mention_feedback(status_message)
|
||||||
return unless comes_from_others_profile_page?
|
return unless comes_from_others_profile_page?
|
||||||
flash[:notice] = t("status_messages.create.success", names: status_message.mentioned_people_names)
|
flash[:notice] = t(
|
||||||
|
"status_messages.create.success",
|
||||||
|
names: PersonPresenter.people_names(status_message.mentioned_people)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def comes_from_others_profile_page?
|
def comes_from_others_profile_page?
|
||||||
|
|
|
||||||
|
|
@ -150,16 +150,7 @@ class UsersController < ApplicationController
|
||||||
:auto_follow_back_aspect_id,
|
:auto_follow_back_aspect_id,
|
||||||
:getting_started,
|
:getting_started,
|
||||||
:post_default_public,
|
:post_default_public,
|
||||||
email_preferences: %i(
|
email_preferences: UserPreference::VALID_EMAIL_TYPES.map(&:to_sym)
|
||||||
someone_reported
|
|
||||||
also_commented
|
|
||||||
mentioned
|
|
||||||
comment_on_post
|
|
||||||
private_message
|
|
||||||
started_sharing
|
|
||||||
liked
|
|
||||||
reshared
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
# rubocop:enable Metrics/MethodLength
|
# rubocop:enable Metrics/MethodLength
|
||||||
|
|
|
||||||
|
|
@ -2,42 +2,46 @@ module NotificationsHelper
|
||||||
include PeopleHelper
|
include PeopleHelper
|
||||||
include PostsHelper
|
include PostsHelper
|
||||||
|
|
||||||
def object_link(note, actors)
|
def object_link(note, actors_html)
|
||||||
target_type = note.popup_translation_key
|
target_type = note.popup_translation_key
|
||||||
actors_count = note.actors.size
|
opts = {actors: actors_html, count: note.actors.size}
|
||||||
|
|
||||||
if note.instance_of?(Notifications::Mentioned)
|
if note.respond_to?(:linked_object)
|
||||||
if post = note.linked_object
|
if note.linked_object.nil? && note.respond_to?(:deleted_translation_key)
|
||||||
translation(target_type,
|
target_type = note.deleted_translation_key
|
||||||
actors: actors,
|
elsif note.is_a?(Notifications::Mentioned)
|
||||||
count: actors_count,
|
opts.merge!(opts_for_mentioned(note.linked_object))
|
||||||
post_link: link_to(post_page_title(post), post_path(post)).html_safe)
|
elsif %w(Notifications::CommentOnPost Notifications::AlsoCommented Notifications::Reshared Notifications::Liked)
|
||||||
else
|
.include?(note.type)
|
||||||
t(note.deleted_translation_key, :actors => actors, :count => actors_count).html_safe
|
opts.merge!(opts_for_post(note.linked_object))
|
||||||
end
|
end
|
||||||
elsif note.instance_of?(Notifications::CommentOnPost) || note.instance_of?(Notifications::AlsoCommented) || note.instance_of?(Notifications::Reshared) || note.instance_of?(Notifications::Liked)
|
|
||||||
if post = note.linked_object
|
|
||||||
translation(target_type,
|
|
||||||
actors: actors,
|
|
||||||
count: actors_count,
|
|
||||||
post_author: h(post.author_name),
|
|
||||||
post_link: link_to(post_page_title(post),
|
|
||||||
post_path(post),
|
|
||||||
data: {ref: post.id},
|
|
||||||
class: "hard_object_link").html_safe)
|
|
||||||
else
|
|
||||||
t(note.deleted_translation_key, :actors => actors, :count => actors_count).html_safe
|
|
||||||
end
|
|
||||||
else #Notifications:StartedSharing, etc.
|
|
||||||
translation(target_type, :actors => actors, :count => actors_count)
|
|
||||||
end
|
end
|
||||||
|
translation(target_type, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def translation(target_type, opts = {})
|
def translation(target_type, opts = {})
|
||||||
{:post_author => nil}.merge!(opts)
|
|
||||||
t("#{target_type}", opts).html_safe
|
t("#{target_type}", opts).html_safe
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def opts_for_post(post)
|
||||||
|
{
|
||||||
|
post_author: html_escape(post.author_name),
|
||||||
|
post_link: link_to(post_page_title(post),
|
||||||
|
post_path(post),
|
||||||
|
data: {ref: post.id},
|
||||||
|
class: "hard_object_link").html_safe
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def opts_for_mentioned(mentioned)
|
||||||
|
post = mentioned.instance_of?(Comment) ? mentioned.parent : mentioned
|
||||||
|
{
|
||||||
|
post_link: link_to(post_page_title(post), post_path(post)).html_safe
|
||||||
|
}.tap {|opts|
|
||||||
|
opts[:comment_path] = post_path(post, anchor: mentioned.guid).html_safe if mentioned.instance_of?(Comment)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def notification_people_link(note, people=nil)
|
def notification_people_link(note, people=nil)
|
||||||
actors =people || note.actors
|
actors =people || note.actors
|
||||||
number_of_actors = actors.size
|
number_of_actors = actors.size
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ module NotificationMailers
|
||||||
delegate :author_name, to: :post, prefix: true
|
delegate :author_name, to: :post, prefix: true
|
||||||
|
|
||||||
def set_headers(target_id)
|
def set_headers(target_id)
|
||||||
@post = Mention.find_by_id(target_id).post
|
@post = Mention.find_by_id(target_id).mentions_container
|
||||||
|
|
||||||
@headers[:subject] = I18n.t('notifier.mentioned.subject', :name => @sender.name)
|
@headers[:subject] = I18n.t('notifier.mentioned.subject', :name => @sender.name)
|
||||||
@headers[:in_reply_to] = @headers[:references] = "<#{@post.guid}@#{AppConfig.pod_uri.host}>"
|
@headers[:in_reply_to] = @headers[:references] = "<#{@post.guid}@#{AppConfig.pod_uri.host}>"
|
||||||
|
|
|
||||||
11
app/mailers/notification_mailers/mentioned_in_comment.rb
Normal file
11
app/mailers/notification_mailers/mentioned_in_comment.rb
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
module NotificationMailers
|
||||||
|
class MentionedInComment < NotificationMailers::Base
|
||||||
|
attr_reader :comment
|
||||||
|
|
||||||
|
def set_headers(target_id) # rubocop:disable Style/AccessorMethodName
|
||||||
|
@comment = Mention.find_by_id(target_id).mentions_container
|
||||||
|
|
||||||
|
@headers[:subject] = I18n.t("notifier.mentioned.subject", name: @sender.name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -11,6 +11,7 @@ class Comment < ActiveRecord::Base
|
||||||
|
|
||||||
include Diaspora::Taggable
|
include Diaspora::Taggable
|
||||||
include Diaspora::Likeable
|
include Diaspora::Likeable
|
||||||
|
include Diaspora::MentionsContainer
|
||||||
|
|
||||||
acts_as_taggable_on :tags
|
acts_as_taggable_on :tags
|
||||||
extract_tags_from :text
|
extract_tags_from :text
|
||||||
|
|
@ -49,14 +50,22 @@ class Comment < ActiveRecord::Base
|
||||||
participation.unparticipate! if participation.present?
|
participation.unparticipate! if participation.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def message
|
|
||||||
@message ||= Diaspora::MessageRenderer.new text
|
|
||||||
end
|
|
||||||
|
|
||||||
def text= text
|
def text= text
|
||||||
self[:text] = text.to_s.strip #to_s if for nil, for whatever reason
|
self[:text] = text.to_s.strip #to_s if for nil, for whatever reason
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def people_allowed_to_be_mentioned
|
||||||
|
if parent.public?
|
||||||
|
:all
|
||||||
|
else
|
||||||
|
[*parent.comments.pluck(:author_id), *parent.likes.pluck(:author_id), parent.author_id].uniq
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_mention_subscribers?
|
||||||
|
super && parent.author.local?
|
||||||
|
end
|
||||||
|
|
||||||
class Generator < Diaspora::Federated::Generator
|
class Generator < Diaspora::Federated::Generator
|
||||||
def self.federated_class
|
def self.federated_class
|
||||||
Comment
|
Comment
|
||||||
|
|
@ -68,7 +77,7 @@ class Comment < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def relayable_options
|
def relayable_options
|
||||||
{:post => @target, :text => @text}
|
{post: @target, text: @text}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,15 @@
|
||||||
# the COPYRIGHT file.
|
# the COPYRIGHT file.
|
||||||
|
|
||||||
class Mention < ActiveRecord::Base
|
class Mention < ActiveRecord::Base
|
||||||
belongs_to :post
|
belongs_to :mentions_container, polymorphic: true
|
||||||
belongs_to :person
|
belongs_to :person
|
||||||
validates :post, presence: true
|
validates :mentions_container, presence: true
|
||||||
validates :person, presence: true
|
validates :person, presence: true
|
||||||
|
|
||||||
|
scope :local, -> {
|
||||||
|
joins(:person).where.not(people: {owner_id: nil})
|
||||||
|
}
|
||||||
|
|
||||||
after_destroy :delete_notification
|
after_destroy :delete_notification
|
||||||
|
|
||||||
def delete_notification
|
def delete_notification
|
||||||
|
|
|
||||||
|
|
@ -51,15 +51,4 @@ class Notification < ActiveRecord::Base
|
||||||
private_class_method def self.suppress_notification?(recipient, actor)
|
private_class_method def self.suppress_notification?(recipient, actor)
|
||||||
recipient.blocks.where(person: actor).exists?
|
recipient.blocks.where(person: actor).exists?
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.types
|
|
||||||
{
|
|
||||||
"also_commented" => "Notifications::AlsoCommented",
|
|
||||||
"comment_on_post" => "Notifications::CommentOnPost",
|
|
||||||
"liked" => "Notifications::Liked",
|
|
||||||
"mentioned" => "Notifications::Mentioned",
|
|
||||||
"reshared" => "Notifications::Reshared",
|
|
||||||
"started_sharing" => "Notifications::StartedSharing"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
module Notifications
|
module Notifications
|
||||||
class AlsoCommented < Notification
|
class AlsoCommented < Notification
|
||||||
|
include Notifications::Commented
|
||||||
|
|
||||||
def mail_job
|
def mail_job
|
||||||
Workers::Mail::AlsoCommented
|
Workers::Mail::AlsoCommented
|
||||||
end
|
end
|
||||||
|
|
@ -8,17 +10,13 @@ module Notifications
|
||||||
"notifications.also_commented"
|
"notifications.also_commented"
|
||||||
end
|
end
|
||||||
|
|
||||||
def deleted_translation_key
|
|
||||||
"notifications.also_commented_deleted"
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.notify(comment, _recipient_user_ids)
|
def self.notify(comment, _recipient_user_ids)
|
||||||
actor = comment.author
|
actor = comment.author
|
||||||
commentable = comment.commentable
|
commentable = comment.commentable
|
||||||
recipient_ids = commentable.participants.local.where.not(id: [commentable.author_id, actor.id]).pluck(:owner_id)
|
recipient_ids = commentable.participants.local.where.not(id: [commentable.author_id, actor.id]).pluck(:owner_id)
|
||||||
|
|
||||||
User.where(id: recipient_ids).find_each do |recipient|
|
User.where(id: recipient_ids).find_each do |recipient|
|
||||||
next if recipient.is_shareable_hidden?(commentable)
|
next if recipient.is_shareable_hidden?(commentable) || mention_notification_exists?(comment, recipient.person)
|
||||||
|
|
||||||
concatenate_or_create(recipient, commentable, actor).try(:email_the_user, comment, actor)
|
concatenate_or_create(recipient, commentable, actor).try(:email_the_user, comment, actor)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
module Notifications
|
module Notifications
|
||||||
class CommentOnPost < Notification
|
class CommentOnPost < Notification
|
||||||
|
include Notifications::Commented
|
||||||
|
|
||||||
def mail_job
|
def mail_job
|
||||||
Workers::Mail::CommentOnPost
|
Workers::Mail::CommentOnPost
|
||||||
end
|
end
|
||||||
|
|
@ -8,15 +10,12 @@ module Notifications
|
||||||
"notifications.comment_on_post"
|
"notifications.comment_on_post"
|
||||||
end
|
end
|
||||||
|
|
||||||
def deleted_translation_key
|
|
||||||
"notifications.also_commented_deleted"
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.notify(comment, _recipient_user_ids)
|
def self.notify(comment, _recipient_user_ids)
|
||||||
actor = comment.author
|
actor = comment.author
|
||||||
commentable_author = comment.commentable.author
|
commentable_author = comment.commentable.author
|
||||||
|
|
||||||
return unless commentable_author.local? && actor != commentable_author
|
return unless commentable_author.local? && actor != commentable_author
|
||||||
|
return if mention_notification_exists?(comment, commentable_author)
|
||||||
|
|
||||||
concatenate_or_create(commentable_author.owner, comment.commentable, actor).email_the_user(comment, actor)
|
concatenate_or_create(commentable_author.owner, comment.commentable, actor).email_the_user(comment, actor)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
15
app/models/notifications/commented.rb
Normal file
15
app/models/notifications/commented.rb
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
module Notifications
|
||||||
|
module Commented
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
def deleted_translation_key
|
||||||
|
"notifications.also_commented_deleted"
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def mention_notification_exists?(comment, recipient_person)
|
||||||
|
Notifications::MentionedInComment.exists?(target: comment.mentions.where(person: recipient_person))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,30 +1,23 @@
|
||||||
module Notifications
|
module Notifications
|
||||||
class Mentioned < Notification
|
module Mentioned
|
||||||
def mail_job
|
extend ActiveSupport::Concern
|
||||||
Workers::Mail::Mentioned
|
|
||||||
end
|
|
||||||
|
|
||||||
def popup_translation_key
|
|
||||||
"notifications.mentioned"
|
|
||||||
end
|
|
||||||
|
|
||||||
def deleted_translation_key
|
|
||||||
"notifications.mentioned_deleted"
|
|
||||||
end
|
|
||||||
|
|
||||||
def linked_object
|
def linked_object
|
||||||
target.post
|
target.mentions_container
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.notify(mentionable, recipient_user_ids)
|
module ClassMethods
|
||||||
actor = mentionable.author
|
def notify(mentionable, recipient_user_ids)
|
||||||
|
actor = mentionable.author
|
||||||
|
relevant_mentions = filter_mentions(
|
||||||
|
mentionable.mentions.local.where.not(person: actor),
|
||||||
|
mentionable,
|
||||||
|
recipient_user_ids
|
||||||
|
)
|
||||||
|
|
||||||
mentionable.mentions.select {|mention| mention.person.local? }.each do |mention|
|
relevant_mentions.each do |mention|
|
||||||
recipient = mention.person
|
create_notification(mention.person.owner, mention, actor).try(:email_the_user, mention, actor)
|
||||||
|
end
|
||||||
next if recipient == actor || !(mentionable.public || recipient_user_ids.include?(recipient.owner_id))
|
|
||||||
|
|
||||||
create_notification(recipient.owner, mention, actor).try(:email_the_user, mention, actor)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
38
app/models/notifications/mentioned_in_comment.rb
Normal file
38
app/models/notifications/mentioned_in_comment.rb
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
module Notifications
|
||||||
|
class MentionedInComment < Notification
|
||||||
|
include Notifications::Mentioned
|
||||||
|
|
||||||
|
def popup_translation_key
|
||||||
|
"notifications.mentioned_in_comment"
|
||||||
|
end
|
||||||
|
|
||||||
|
def deleted_translation_key
|
||||||
|
"notifications.mentioned_in_comment_deleted"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.filter_mentions(mentions, mentionable, _recipient_user_ids)
|
||||||
|
people = mentionable.people_allowed_to_be_mentioned
|
||||||
|
if people == :all
|
||||||
|
mentions
|
||||||
|
else
|
||||||
|
mentions.where(person_id: people)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def mail_job
|
||||||
|
if !recipient.user_preferences.exists?(email_type: "mentioned_in_comment")
|
||||||
|
Workers::Mail::MentionedInComment
|
||||||
|
elsif shareable.author.owner_id == recipient_id
|
||||||
|
Workers::Mail::CommentOnPost
|
||||||
|
elsif shareable.participants.local.where(owner_id: recipient_id)
|
||||||
|
Workers::Mail::AlsoCommented
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def shareable
|
||||||
|
linked_object.parent
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
22
app/models/notifications/mentioned_in_post.rb
Normal file
22
app/models/notifications/mentioned_in_post.rb
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
module Notifications
|
||||||
|
class MentionedInPost < Notification
|
||||||
|
include Notifications::Mentioned
|
||||||
|
|
||||||
|
def mail_job
|
||||||
|
Workers::Mail::Mentioned
|
||||||
|
end
|
||||||
|
|
||||||
|
def popup_translation_key
|
||||||
|
"notifications.mentioned"
|
||||||
|
end
|
||||||
|
|
||||||
|
def deleted_translation_key
|
||||||
|
"notifications.mentioned_deleted"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.filter_mentions(mentions, mentionable, recipient_user_ids)
|
||||||
|
return mentions if mentionable.public
|
||||||
|
mentions.where(person: Person.where(owner_id: recipient_user_ids).ids)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -12,16 +12,15 @@ class Post < ActiveRecord::Base
|
||||||
include Diaspora::Likeable
|
include Diaspora::Likeable
|
||||||
include Diaspora::Commentable
|
include Diaspora::Commentable
|
||||||
include Diaspora::Shareable
|
include Diaspora::Shareable
|
||||||
|
include Diaspora::MentionsContainer
|
||||||
|
|
||||||
has_many :participations, dependent: :delete_all, as: :target, inverse_of: :target
|
has_many :participations, dependent: :delete_all, as: :target, inverse_of: :target
|
||||||
has_many :participants, class_name: "Person", through: :participations, source: :author
|
has_many :participants, through: :participations, source: :author
|
||||||
|
|
||||||
attr_accessor :user_like
|
attr_accessor :user_like
|
||||||
|
|
||||||
has_many :reports, as: :item
|
has_many :reports, as: :item
|
||||||
|
|
||||||
has_many :mentions, dependent: :destroy
|
|
||||||
|
|
||||||
has_many :reshares, class_name: "Reshare", foreign_key: :root_guid, primary_key: :guid
|
has_many :reshares, class_name: "Reshare", foreign_key: :root_guid, primary_key: :guid
|
||||||
has_many :resharers, class_name: "Person", through: :reshares, source: :author
|
has_many :resharers, class_name: "Person", through: :reshares, source: :author
|
||||||
|
|
||||||
|
|
@ -60,7 +59,6 @@ class Post < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def root; end
|
def root; end
|
||||||
def mentioned_people; []; end
|
|
||||||
def photos; []; end
|
def photos; []; end
|
||||||
|
|
||||||
#prevents error when trying to access @post.address in a post different than Reshare and StatusMessage types;
|
#prevents error when trying to access @post.address in a post different than Reshare and StatusMessage types;
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,12 @@ class StatusMessage < Post
|
||||||
attr_accessor :oembed_url
|
attr_accessor :oembed_url
|
||||||
attr_accessor :open_graph_url
|
attr_accessor :open_graph_url
|
||||||
|
|
||||||
after_create :create_mentions
|
|
||||||
after_commit :queue_gather_oembed_data, :on => :create, :if => :contains_oembed_url_in_text?
|
after_commit :queue_gather_oembed_data, :on => :create, :if => :contains_oembed_url_in_text?
|
||||||
after_commit :queue_gather_open_graph_data, :on => :create, :if => :contains_open_graph_url_in_text?
|
after_commit :queue_gather_open_graph_data, :on => :create, :if => :contains_open_graph_url_in_text?
|
||||||
|
|
||||||
#scopes
|
#scopes
|
||||||
scope :where_person_is_mentioned, ->(person) {
|
scope :where_person_is_mentioned, ->(person) {
|
||||||
joins(:mentions).where(:mentions => {:person_id => person.id})
|
owned_or_visible_by_user(person.owner).joins(:mentions).where(mentions: {person_id: person.id})
|
||||||
}
|
}
|
||||||
|
|
||||||
def self.guids_for_author(person)
|
def self.guids_for_author(person)
|
||||||
|
|
@ -52,36 +51,6 @@ class StatusMessage < Post
|
||||||
text.try(:match, /#nsfw/i) || super
|
text.try(:match, /#nsfw/i) || super
|
||||||
end
|
end
|
||||||
|
|
||||||
def message
|
|
||||||
@message ||= Diaspora::MessageRenderer.new(text, mentioned_people: mentioned_people)
|
|
||||||
end
|
|
||||||
|
|
||||||
def mentioned_people
|
|
||||||
if self.persisted?
|
|
||||||
self.mentions.includes(:person => :profile).map{ |mention| mention.person }
|
|
||||||
else
|
|
||||||
Diaspora::Mentionable.people_from_string(text)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
## TODO ----
|
|
||||||
# don't put presentation logic in the model!
|
|
||||||
def mentioned_people_names
|
|
||||||
self.mentioned_people.map(&:name).join(', ')
|
|
||||||
end
|
|
||||||
## ---- ----
|
|
||||||
|
|
||||||
def create_mentions
|
|
||||||
ppl = Diaspora::Mentionable.people_from_string(text)
|
|
||||||
ppl.each do |person|
|
|
||||||
self.mentions.find_or_create_by(person_id: person.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def mentions?(person)
|
|
||||||
mentioned_people.include? person
|
|
||||||
end
|
|
||||||
|
|
||||||
def comment_email_subject
|
def comment_email_subject
|
||||||
message.title
|
message.title
|
||||||
end
|
end
|
||||||
|
|
@ -126,6 +95,22 @@ class StatusMessage < Post
|
||||||
photos.each {|photo| photo.receive(recipient_user_ids) }
|
photos.each {|photo| photo.receive(recipient_user_ids) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Only includes those people, to whom we're going to send a federation entity
|
||||||
|
# (and doesn't define exhaustive list of people who can receive it)
|
||||||
|
def people_allowed_to_be_mentioned
|
||||||
|
@aspects_ppl ||=
|
||||||
|
if public?
|
||||||
|
:all
|
||||||
|
else
|
||||||
|
Contact.joins(:aspect_memberships).where(aspect_memberships: {aspect: aspects}).distinct.pluck(:person_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_mentions
|
||||||
|
return if people_allowed_to_be_mentioned == :all
|
||||||
|
update(text: Diaspora::Mentionable.filter_people(text, people_allowed_to_be_mentioned))
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def presence_of_content
|
def presence_of_content
|
||||||
|
|
|
||||||
|
|
@ -333,6 +333,7 @@ class User < ActiveRecord::Base
|
||||||
|
|
||||||
######### Mailer #######################
|
######### Mailer #######################
|
||||||
def mail(job, *args)
|
def mail(job, *args)
|
||||||
|
return unless job.present?
|
||||||
pref = job.to_s.gsub('Workers::Mail::', '').underscore
|
pref = job.to_s.gsub('Workers::Mail::', '').underscore
|
||||||
if(self.disable_mail == false && !self.user_preferences.exists?(:email_type => pref))
|
if(self.disable_mail == false && !self.user_preferences.exists?(:email_type => pref))
|
||||||
job.perform_async(*args)
|
job.perform_async(*args)
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@ class UserPreference < ActiveRecord::Base
|
||||||
|
|
||||||
VALID_EMAIL_TYPES =
|
VALID_EMAIL_TYPES =
|
||||||
["someone_reported",
|
["someone_reported",
|
||||||
"mentioned",
|
"mentioned",
|
||||||
"comment_on_post",
|
"mentioned_in_comment",
|
||||||
"private_message",
|
"comment_on_post",
|
||||||
"started_sharing",
|
"private_message",
|
||||||
"also_commented",
|
"started_sharing",
|
||||||
"liked",
|
"also_commented",
|
||||||
"reshared"]
|
"liked",
|
||||||
|
"reshared"]
|
||||||
|
|
||||||
def must_be_valid_email_type
|
def must_be_valid_email_type
|
||||||
unless VALID_EMAIL_TYPES.include?(self.email_type)
|
unless VALID_EMAIL_TYPES.include?(self.email_type)
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,10 @@ class PersonPresenter < BasePresenter
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.people_names(people)
|
||||||
|
people.map(&:name).join(", ")
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def own_profile?
|
def own_profile?
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
class NotificationService
|
class NotificationService
|
||||||
NOTIFICATION_TYPES = {
|
NOTIFICATION_TYPES = {
|
||||||
Comment => [Notifications::CommentOnPost, Notifications::AlsoCommented],
|
Comment => [Notifications::MentionedInComment, Notifications::CommentOnPost, Notifications::AlsoCommented],
|
||||||
Like => [Notifications::Liked],
|
Like => [Notifications::Liked],
|
||||||
StatusMessage => [Notifications::Mentioned],
|
StatusMessage => [Notifications::MentionedInPost],
|
||||||
Conversation => [Notifications::PrivateMessage],
|
Conversation => [Notifications::PrivateMessage],
|
||||||
Message => [Notifications::PrivateMessage],
|
Message => [Notifications::PrivateMessage],
|
||||||
Reshare => [Notifications::Reshared],
|
Reshare => [Notifications::Reshared],
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,20 @@ class PostService
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_mention_notifications_read(post_id)
|
def mark_mention_notifications_read(post_id)
|
||||||
mention_id = Mention.where(post_id: post_id, person_id: user.person_id).pluck(:id)
|
mention_ids = Mention.where(
|
||||||
Notification.where(recipient_id: user.id, target_type: "Mention", target_id: mention_id, unread: true)
|
mentions_container_id: post_id,
|
||||||
.update_all(unread: false) if mention_id
|
mentions_container_type: "Post",
|
||||||
|
person_id: user.person_id
|
||||||
|
).ids
|
||||||
|
mention_ids.concat(mentions_in_comments_for_post(post_id).pluck(:id))
|
||||||
|
|
||||||
|
Notification.where(recipient_id: user.id, target_type: "Mention", target_id: mention_ids, unread: true)
|
||||||
|
.update_all(unread: false) if mention_ids.any?
|
||||||
|
end
|
||||||
|
|
||||||
|
def mentions_in_comments_for_post(post_id)
|
||||||
|
Mention
|
||||||
|
.joins("INNER JOIN comments ON mentions_container_id = comments.id AND mentions_container_type = 'Comment'")
|
||||||
|
.where(comments: {commentable_id: post_id, commentable_type: "Post"})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -19,20 +19,9 @@ class StatusMessageCreationService
|
||||||
|
|
||||||
def build_status_message(params)
|
def build_status_message(params)
|
||||||
public = params[:public] || false
|
public = params[:public] || false
|
||||||
filter_mentions params
|
|
||||||
user.build_post(:status_message, params[:status_message].merge(public: public))
|
user.build_post(:status_message, params[:status_message].merge(public: public))
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_mentions(params)
|
|
||||||
unless params[:public]
|
|
||||||
params[:status_message][:text] = Diaspora::Mentionable.filter_for_aspects(
|
|
||||||
params[:status_message][:text],
|
|
||||||
user,
|
|
||||||
*params[:aspect_ids]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_attachments(status_message, params)
|
def add_attachments(status_message, params)
|
||||||
add_location(status_message, params[:location_address], params[:location_coords])
|
add_location(status_message, params[:location_address], params[:location_coords])
|
||||||
add_poll(status_message, params)
|
add_poll(status_message, params)
|
||||||
|
|
@ -75,6 +64,7 @@ class StatusMessageCreationService
|
||||||
|
|
||||||
def dispatch(status_message, services)
|
def dispatch(status_message, services)
|
||||||
receiving_services = services ? Service.titles(services) : []
|
receiving_services = services ? Service.titles(services) : []
|
||||||
|
status_message.filter_mentions # this is only required until changes from #6818 are deployed on every pod
|
||||||
user.dispatch_post(status_message,
|
user.dispatch_post(status_message,
|
||||||
url: short_post_url(status_message.guid, host: AppConfig.environment.url),
|
url: short_post_url(status_message.guid, host: AppConfig.environment.url),
|
||||||
service_types: receiving_services)
|
service_types: receiving_services)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
.media.stream-element{data: {guid: note.id, type: (Notification.types.key(note.type) || "")},
|
.media.stream-element{data: {guid: note.id, type: (types.key(note.type) || "")},
|
||||||
class: (note.unread ? "unread" : "read")}
|
class: (note.unread ? "unread" : "read")}
|
||||||
.unread-toggle.pull-right
|
.unread-toggle.pull-right
|
||||||
%i.entypo-eye{title: (note.unread ? t("notifications.index.mark_read") : t("notifications.index.mark_unread"))}
|
%i.entypo-eye{title: (note.unread ? t("notifications.index.mark_read") : t("notifications.index.mark_unread"))}
|
||||||
- if note.type == "Notifications::StartedSharing" && (!defined?(no_aspect_dropdown) || !no_aspect_dropdown)
|
- if note.type == "Notifications::StartedSharing" && (!defined?(no_aspect_dropdown) || !no_aspect_dropdown)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
.notification_element{:data=>{:guid => note.id, :type => (Notification.types.key(note.type) || '')}, :class => (note.unread ? "unread" : "read")}
|
.notification_element{data: {guid: note.id, type: (types.key(note.type) || "")},
|
||||||
|
class: (note.unread ? "unread" : "read")}
|
||||||
.pull-right.unread-toggle
|
.pull-right.unread-toggle
|
||||||
%i.entypo-eye{title: (note.unread ? t("notifications.index.mark_read") : t("notifications.index.mark_unread"))}
|
%i.entypo-eye{title: (note.unread ? t("notifications.index.mark_read") : t("notifications.index.mark_unread"))}
|
||||||
= person_image_tag note.actors.first, :thumb_small
|
= person_image_tag note.actors.first, :thumb_small
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
%i.entypo-comment
|
%i.entypo-comment
|
||||||
- when "liked"
|
- when "liked"
|
||||||
%i.entypo-heart
|
%i.entypo-heart
|
||||||
- when "mentioned"
|
- when "mentioned", "mentioned_in_comment"
|
||||||
%span.mentionIcon
|
%span.mentionIcon
|
||||||
@
|
@
|
||||||
- when "reshared"
|
- when "reshared"
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,6 @@
|
||||||
<%= t('notifier.mentioned.limited_post') %>
|
<%= t('notifier.mentioned.limited_post') %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
[<%= t('notifier.comment_on_post.reply', :name => @notification.post_author_name) %>][1]
|
[<%= t("notifier.comment_on_post.reply", name: @notification.post_author_name) %>][1]
|
||||||
|
|
||||||
[1]: <%= post_url(@notification.post) %>
|
[1]: <%= post_url(@notification.post) %>
|
||||||
|
|
|
||||||
9
app/views/notifier/mentioned_in_comment.markerb
Normal file
9
app/views/notifier/mentioned_in_comment.markerb
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<% if @notification.comment.public? %>
|
||||||
|
<%= @notification.comment.message.plain_text_without_markdown %>
|
||||||
|
<% else %>
|
||||||
|
<%= t("notifier.mentioned_in_comment.limited_post") %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
[<%= t("notifier.mentioned_in_comment.reply") %>][1]
|
||||||
|
|
||||||
|
[1]: <%= post_url(@notification.comment.parent, anchor: @notification.comment.guid) %>
|
||||||
|
|
@ -142,6 +142,11 @@
|
||||||
= t(".mentioned")
|
= t(".mentioned")
|
||||||
.small-horizontal-spacer
|
.small-horizontal-spacer
|
||||||
|
|
||||||
|
= type.label :mentioned_in_comment, class: "checkbox-inline" do
|
||||||
|
= type.check_box :mentioned_in_comment, {checked: @email_prefs["mentioned_in_comment"]}, false, true
|
||||||
|
= t(".mentioned_in_comment")
|
||||||
|
.small-horizontal-spacer
|
||||||
|
|
||||||
= type.label :liked, class: "checkbox-inline" do
|
= type.label :liked, class: "checkbox-inline" do
|
||||||
= type.check_box :liked, {checked: @email_prefs["liked"]}, false, true
|
= type.check_box :liked, {checked: @email_prefs["liked"]}, false, true
|
||||||
= t(".liked")
|
= t(".liked")
|
||||||
|
|
|
||||||
6
app/workers/mail/mentioned_in_comment.rb
Normal file
6
app/workers/mail/mentioned_in_comment.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
module Workers
|
||||||
|
module Mail
|
||||||
|
class MentionedInComment < NotifierBase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -615,9 +615,11 @@ en:
|
||||||
one: "%{actors} also commented on %{post_author}’s post %{post_link}."
|
one: "%{actors} also commented on %{post_author}’s post %{post_link}."
|
||||||
other: "%{actors} also commented on %{post_author}’s post %{post_link}."
|
other: "%{actors} also commented on %{post_author}’s post %{post_link}."
|
||||||
mentioned:
|
mentioned:
|
||||||
zero: "%{actors} have mentioned you in the post %{post_link}."
|
|
||||||
one: "%{actors} has mentioned you in the post %{post_link}."
|
one: "%{actors} has mentioned you in the post %{post_link}."
|
||||||
other: "%{actors} have mentioned you in the post %{post_link}."
|
other: "%{actors} have mentioned you in the post %{post_link}."
|
||||||
|
mentioned_in_comment:
|
||||||
|
one: "%{actors} has mentioned you in a <a href='%{comment_path}'>comment</a> to the post %{post_link}."
|
||||||
|
other: "%{actors} have mentioned you in a <a href='%{comment_path}'>comment</a> to the post %{post_link}."
|
||||||
liked:
|
liked:
|
||||||
zero: "%{actors} have liked your post %{post_link}."
|
zero: "%{actors} have liked your post %{post_link}."
|
||||||
one: "%{actors} has liked your post %{post_link}."
|
one: "%{actors} has liked your post %{post_link}."
|
||||||
|
|
@ -640,9 +642,11 @@ en:
|
||||||
one: "%{actors} reshared your deleted post."
|
one: "%{actors} reshared your deleted post."
|
||||||
other: "%{actors} reshared your deleted post."
|
other: "%{actors} reshared your deleted post."
|
||||||
mentioned_deleted:
|
mentioned_deleted:
|
||||||
zero: "%{actors} mentioned you in a deleted post."
|
|
||||||
one: "%{actors} mentioned you in a deleted post."
|
one: "%{actors} mentioned you in a deleted post."
|
||||||
other: "%{actors} mentioned you in a deleted post."
|
other: "%{actors} mentioned you in a deleted post."
|
||||||
|
mentioned_in_comment_deleted:
|
||||||
|
one: "%{actors} mentioned you in a deleted comment."
|
||||||
|
other: "%{actors} mentioned you in a deleted comment."
|
||||||
index:
|
index:
|
||||||
notifications: "Notifications"
|
notifications: "Notifications"
|
||||||
mark_all_as_read: "Mark all as read"
|
mark_all_as_read: "Mark all as read"
|
||||||
|
|
@ -655,7 +659,8 @@ en:
|
||||||
also_commented: "Also commented"
|
also_commented: "Also commented"
|
||||||
comment_on_post: "Comment on post"
|
comment_on_post: "Comment on post"
|
||||||
liked: "Liked"
|
liked: "Liked"
|
||||||
mentioned: "Mentioned"
|
mentioned: "Mentioned in post"
|
||||||
|
mentioned_in_comment: "Mentioned in comment"
|
||||||
reshared: "Reshared"
|
reshared: "Reshared"
|
||||||
started_sharing: "Started sharing"
|
started_sharing: "Started sharing"
|
||||||
no_notifications: "You don't have any notifications yet."
|
no_notifications: "You don't have any notifications yet."
|
||||||
|
|
@ -689,6 +694,9 @@ en:
|
||||||
mentioned:
|
mentioned:
|
||||||
subject: "%{name} has mentioned you on diaspora*"
|
subject: "%{name} has mentioned you on diaspora*"
|
||||||
limited_post: "You were mentioned in a limited post."
|
limited_post: "You were mentioned in a limited post."
|
||||||
|
mentioned_in_comment:
|
||||||
|
limited_post: "You were mentioned in a comment to a limited post."
|
||||||
|
reply: "Reply to or view this conversation >"
|
||||||
private_message:
|
private_message:
|
||||||
subject: "There’s a new private message for you"
|
subject: "There’s a new private message for you"
|
||||||
reply_to_or_view: "Reply to or view this conversation >"
|
reply_to_or_view: "Reply to or view this conversation >"
|
||||||
|
|
@ -1167,6 +1175,7 @@ en:
|
||||||
started_sharing: "someone starts sharing with you"
|
started_sharing: "someone starts sharing with you"
|
||||||
someone_reported: "someone sends a report"
|
someone_reported: "someone sends a report"
|
||||||
mentioned: "you are mentioned in a post"
|
mentioned: "you are mentioned in a post"
|
||||||
|
mentioned_in_comment: "you are mentioned in a comment"
|
||||||
liked: "someone likes your post"
|
liked: "someone likes your post"
|
||||||
reshared: "someone reshares your post"
|
reshared: "someone reshares your post"
|
||||||
comment_on_post: "someone comments on your post"
|
comment_on_post: "someone comments on your post"
|
||||||
|
|
|
||||||
38
db/migrate/20161107100840_polymorphic_mentions.rb
Normal file
38
db/migrate/20161107100840_polymorphic_mentions.rb
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
class PolymorphicMentions < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
remove_index :mentions, column: %i(post_id)
|
||||||
|
remove_index :mentions, column: %i(person_id post_id), unique: true
|
||||||
|
rename_column :mentions, :post_id, :mentions_container_id
|
||||||
|
add_column :mentions, :mentions_container_type, :string, null: false
|
||||||
|
add_index :mentions,
|
||||||
|
%i(mentions_container_id mentions_container_type),
|
||||||
|
name: "index_mentions_on_mc_id_and_mc_type",
|
||||||
|
length: {mentions_container_type: 191}
|
||||||
|
add_index :mentions,
|
||||||
|
%i(person_id mentions_container_id mentions_container_type),
|
||||||
|
name: "index_mentions_on_person_and_mc_id_and_mc_type",
|
||||||
|
length: {mentions_container_type: 191},
|
||||||
|
unique: true
|
||||||
|
|
||||||
|
reversible(&method(:up_down))
|
||||||
|
end
|
||||||
|
|
||||||
|
class Mention < ActiveRecord::Base
|
||||||
|
end
|
||||||
|
|
||||||
|
class Notification < ActiveRecord::Base
|
||||||
|
end
|
||||||
|
|
||||||
|
def up_down(change)
|
||||||
|
change.up do
|
||||||
|
Mention.update_all(mentions_container_type: "Post")
|
||||||
|
Notification.where(type: "Notifications::Mentioned").update_all(type: "Notifications::MentionedInPost")
|
||||||
|
end
|
||||||
|
|
||||||
|
change.down do
|
||||||
|
Notification.where(type: "Notifications::MentionedInPost").update_all(type: "Notifications::Mentioned")
|
||||||
|
Mention.where(mentions_container_type: "Comment").destroy_all
|
||||||
|
Notification.where(type: "Notifications::MentionedInComment").destroy_all
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
11
db/schema.rb
11
db/schema.rb
|
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20161024231443) do
|
ActiveRecord::Schema.define(version: 20161107100840) do
|
||||||
|
|
||||||
create_table "account_deletions", force: :cascade do |t|
|
create_table "account_deletions", force: :cascade do |t|
|
||||||
t.string "diaspora_handle", limit: 255
|
t.string "diaspora_handle", limit: 255
|
||||||
|
|
@ -204,13 +204,14 @@ ActiveRecord::Schema.define(version: 20161024231443) do
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "mentions", force: :cascade do |t|
|
create_table "mentions", force: :cascade do |t|
|
||||||
t.integer "post_id", limit: 4, null: false
|
t.integer "mentions_container_id", limit: 4, null: false
|
||||||
t.integer "person_id", limit: 4, null: false
|
t.integer "person_id", limit: 4, null: false
|
||||||
|
t.string "mentions_container_type", limit: 255, null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "mentions", ["person_id", "post_id"], name: "index_mentions_on_person_id_and_post_id", unique: true, using: :btree
|
add_index "mentions", ["mentions_container_id", "mentions_container_type"], name: "index_mentions_on_mc_id_and_mc_type", length: {"mentions_container_id"=>nil, "mentions_container_type"=>191}, using: :btree
|
||||||
|
add_index "mentions", ["person_id", "mentions_container_id", "mentions_container_type"], name: "index_mentions_on_person_and_mc_id_and_mc_type", unique: true, length: {"person_id"=>nil, "mentions_container_id"=>nil, "mentions_container_type"=>191}, using: :btree
|
||||||
add_index "mentions", ["person_id"], name: "index_mentions_on_person_id", using: :btree
|
add_index "mentions", ["person_id"], name: "index_mentions_on_person_id", using: :btree
|
||||||
add_index "mentions", ["post_id"], name: "index_mentions_on_post_id", using: :btree
|
|
||||||
|
|
||||||
create_table "messages", force: :cascade do |t|
|
create_table "messages", force: :cascade do |t|
|
||||||
t.integer "conversation_id", limit: 4, null: false
|
t.integer "conversation_id", limit: 4, null: false
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,10 @@ Feature: Change settings
|
||||||
And I press "change_email_preferences"
|
And I press "change_email_preferences"
|
||||||
Then I should see "Email notifications changed"
|
Then I should see "Email notifications changed"
|
||||||
And the "user_email_preferences_mentioned" checkbox should not be checked
|
And the "user_email_preferences_mentioned" checkbox should not be checked
|
||||||
|
When I uncheck "user_email_preferences_mentioned_in_comment"
|
||||||
|
And I press "change_email_preferences"
|
||||||
|
Then I should see "Email notifications changed"
|
||||||
|
And the "user_email_preferences_mentioned_in_comment" checkbox should not be checked
|
||||||
|
|
||||||
Scenario: Change my preferred language
|
Scenario: Change my preferred language
|
||||||
When I select "polski" from "user_language"
|
When I select "polski" from "user_language"
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,26 @@ Feature: Notifications
|
||||||
Then I should see "mentioned you in the post"
|
Then I should see "mentioned you in the post"
|
||||||
And I should have 1 email delivery
|
And I should have 1 email delivery
|
||||||
|
|
||||||
|
Scenario: someone mentioned me in a comment
|
||||||
|
Given "alice@alice.alice" has a public post with text "check this out!"
|
||||||
|
And "bob@bob.bob" has commented mentioning "alice@alice.alice" on "check this out!"
|
||||||
|
When I sign in as "alice@alice.alice"
|
||||||
|
And I follow "Notifications" in the header
|
||||||
|
Then the notification dropdown should be visible
|
||||||
|
And I should see "mentioned you in a comment"
|
||||||
|
And I should have 1 email delivery
|
||||||
|
|
||||||
|
Scenario: I mark a notification as read
|
||||||
|
Given a user with email "bob@bob.bob" is connected with "alice@alice.alice"
|
||||||
|
And Alice has a post mentioning Bob
|
||||||
|
When I sign in as "bob@bob.bob"
|
||||||
|
And I follow "Notifications" in the header
|
||||||
|
Then the notification dropdown should be visible
|
||||||
|
And I wait for notifications to load
|
||||||
|
And I should see a ".unread .unread-toggle .entypo-eye"
|
||||||
|
When I click on selector ".unread .unread-toggle .entypo-eye"
|
||||||
|
Then I should see a ".read .unread-toggle"
|
||||||
|
|
||||||
Scenario: filter notifications
|
Scenario: filter notifications
|
||||||
Given a user with email "bob@bob.bob" is connected with "alice@alice.alice"
|
Given a user with email "bob@bob.bob" is connected with "alice@alice.alice"
|
||||||
And Alice has a post mentioning Bob
|
And Alice has a post mentioning Bob
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,12 @@ Given /^"([^"]*)" has commented "([^"]*)" on "([^"]*)"$/ do |email, comment_text
|
||||||
user.comment!(post, comment_text)
|
user.comment!(post, comment_text)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Given /^"([^"]*)" has commented mentioning "([^"]*)" on "([^"]*)"$/ do |email, mentionee_email, post_text|
|
||||||
|
user = User.find_by(email: email)
|
||||||
|
post = StatusMessage.find_by(text: post_text)
|
||||||
|
user.comment!(post, text_mentioning(User.find_by(email: mentionee_email)))
|
||||||
|
end
|
||||||
|
|
||||||
Given /^"([^"]*)" has commented a lot on "([^"]*)"$/ do |email, post_text|
|
Given /^"([^"]*)" has commented a lot on "([^"]*)"$/ do |email, post_text|
|
||||||
user = User.find_by(email: email)
|
user = User.find_by(email: email)
|
||||||
post = StatusMessage.find_by(text: post_text)
|
post = StatusMessage.find_by(text: post_text)
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,7 @@ end
|
||||||
|
|
||||||
Then /^(?:|I )should see a "([^\"]*)"(?: within "([^\"]*)")?$/ do |selector, scope_selector|
|
Then /^(?:|I )should see a "([^\"]*)"(?: within "([^\"]*)")?$/ do |selector, scope_selector|
|
||||||
with_scope(scope_selector) do
|
with_scope(scope_selector) do
|
||||||
current_scope.should have_css selector
|
expect(current_scope).to have_css(selector)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ When "I filter notifications by likes" do
|
||||||
end
|
end
|
||||||
|
|
||||||
When "I filter notifications by mentions" do
|
When "I filter notifications by mentions" do
|
||||||
step %(I follow "Mentioned" within "#notifications_container .list-group")
|
step %(I follow "Mentioned in post" within "#notifications_container .list-group")
|
||||||
end
|
end
|
||||||
|
|
||||||
Then /^I should( not)? have activated notifications for the post( in the single post view)?$/ do |negate, spv|
|
Then /^I should( not)? have activated notifications for the post( in the single post view)?$/ do |negate, spv|
|
||||||
|
|
|
||||||
|
|
@ -54,25 +54,19 @@ module Diaspora::Mentionable
|
||||||
end
|
end
|
||||||
|
|
||||||
# takes a message text and converts mentions for people that are not in the
|
# takes a message text and converts mentions for people that are not in the
|
||||||
# given aspects to simple markdown links, leaving only mentions for people who
|
# given array to simple markdown links, leaving only mentions for people who
|
||||||
# will actually be able to receive notifications for being mentioned.
|
# will actually be able to receive notifications for being mentioned.
|
||||||
#
|
#
|
||||||
# @param [String] message text
|
# @param [String] message text
|
||||||
# @param [User] aspect owner
|
# @param [Array] allowed_people ids of people that are allowed to stay
|
||||||
# @param [Mixed] array containing aspect ids or "all"
|
|
||||||
# @return [String] message text with filtered mentions
|
# @return [String] message text with filtered mentions
|
||||||
def self.filter_for_aspects(msg_text, user, *aspects)
|
def self.filter_people(msg_text, allowed_people)
|
||||||
aspect_ids = MentionsInternal.get_aspect_ids(user, *aspects)
|
|
||||||
|
|
||||||
mentioned_ppl = people_from_string(msg_text)
|
mentioned_ppl = people_from_string(msg_text)
|
||||||
aspects_ppl = AspectMembership.where(aspect_id: aspect_ids)
|
|
||||||
.includes(:contact => :person)
|
|
||||||
.map(&:person)
|
|
||||||
|
|
||||||
msg_text.to_s.gsub(REGEX) {|match_str|
|
msg_text.to_s.gsub(REGEX) {|match_str|
|
||||||
name, handle = mention_attrs(match_str)
|
name, handle = mention_attrs(match_str)
|
||||||
person = mentioned_ppl.find {|p| p.diaspora_handle == handle }
|
person = mentioned_ppl.find {|p| p.diaspora_handle == handle }
|
||||||
mention = MentionsInternal.profile_link(person, name) unless aspects_ppl.include?(person)
|
mention = MentionsInternal.profile_link(person, name) unless allowed_people.include?(person.id)
|
||||||
|
|
||||||
mention || match_str
|
mention || match_str
|
||||||
}
|
}
|
||||||
|
|
@ -118,26 +112,6 @@ module Diaspora::Mentionable
|
||||||
|
|
||||||
"[#{display_name.presence || person.name}](#{local_or_remote_person_path(person)})"
|
"[#{display_name.presence || person.name}](#{local_or_remote_person_path(person)})"
|
||||||
end
|
end
|
||||||
|
|
||||||
# takes a user and an array of aspect ids or an array containing "all" as
|
|
||||||
# the first element. will do some checking on ids and return them in an array
|
|
||||||
# in case of "all", returns an array with all the users aspect ids
|
|
||||||
#
|
|
||||||
# @param [User] owner of the aspects
|
|
||||||
# @param [Array] aspect ids or "all"
|
|
||||||
# @return [Array] aspect ids
|
|
||||||
def self.get_aspect_ids(user, *aspects)
|
|
||||||
return [] if aspects.empty?
|
|
||||||
|
|
||||||
if (!aspects.first.is_a?(Integer)) && aspects.first.to_s == 'all'
|
|
||||||
return user.aspects.pluck(:id)
|
|
||||||
end
|
|
||||||
|
|
||||||
ids = aspects.reject {|id| Integer(id) == nil } # only numeric
|
|
||||||
|
|
||||||
#make sure they really belong to the user
|
|
||||||
user.aspects.where(id: ids).pluck(:id)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
38
lib/diaspora/mentions_container.rb
Normal file
38
lib/diaspora/mentions_container.rb
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
module Diaspora
|
||||||
|
module MentionsContainer
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
after_create :create_mentions
|
||||||
|
has_many :mentions, as: :mentions_container, dependent: :destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
def mentioned_people
|
||||||
|
if persisted?
|
||||||
|
mentions.includes(person: :profile).map(&:person)
|
||||||
|
else
|
||||||
|
Diaspora::Mentionable.people_from_string(text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_mention_subscribers?
|
||||||
|
public?
|
||||||
|
end
|
||||||
|
|
||||||
|
def subscribers
|
||||||
|
super.tap {|subscribers|
|
||||||
|
subscribers.concat(mentions.map(&:person).select(&:remote?)) if add_mention_subscribers?
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_mentions
|
||||||
|
Diaspora::Mentionable.people_from_string(text).each do |person|
|
||||||
|
mentions.find_or_create_by(person_id: person.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def message
|
||||||
|
@message ||= Diaspora::MessageRenderer.new text, mentioned_people: mentioned_people
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -72,7 +72,7 @@ module Diaspora
|
||||||
end
|
end
|
||||||
|
|
||||||
if options[:disable_hovercards] || options[:link_all_mentions]
|
if options[:disable_hovercards] || options[:link_all_mentions]
|
||||||
@message = Diaspora::Mentionable.filter_for_aspects message, nil
|
@message = Diaspora::Mentionable.filter_people message, []
|
||||||
else
|
else
|
||||||
make_mentions_plain_text
|
make_mentions_plain_text
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -77,12 +77,13 @@ describe NotificationsController, :type => :controller do
|
||||||
timeago_content = note_html.css("time")[0]["data-time-ago"]
|
timeago_content = note_html.css("time")[0]["data-time-ago"]
|
||||||
expect(response_json["unread_count"]).to be(1)
|
expect(response_json["unread_count"]).to be(1)
|
||||||
expect(response_json["unread_count_by_type"]).to eq(
|
expect(response_json["unread_count_by_type"]).to eq(
|
||||||
"also_commented" => 1,
|
"also_commented" => 1,
|
||||||
"comment_on_post" => 0,
|
"comment_on_post" => 0,
|
||||||
"liked" => 0,
|
"liked" => 0,
|
||||||
"mentioned" => 0,
|
"mentioned" => 0,
|
||||||
"reshared" => 0,
|
"mentioned_in_comment" => 0,
|
||||||
"started_sharing" => 0
|
"reshared" => 0,
|
||||||
|
"started_sharing" => 0
|
||||||
)
|
)
|
||||||
expect(timeago_content).to include(@notification.updated_at.iso8601)
|
expect(timeago_content).to include(@notification.updated_at.iso8601)
|
||||||
expect(response.body).to match(/note_html/)
|
expect(response.body).to match(/note_html/)
|
||||||
|
|
|
||||||
|
|
@ -184,20 +184,24 @@ describe UsersController, :type => :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'email settings' do
|
describe "email settings" do
|
||||||
it 'lets the user turn off mail' do
|
UserPreference::VALID_EMAIL_TYPES.each do |email_type|
|
||||||
par = {:id => @user.id, :user => {:email_preferences => {'mentioned' => 'true'}}}
|
context "for #{email_type}" do
|
||||||
expect{
|
it "lets the user turn off mail" do
|
||||||
put :update, par
|
par = {id: @user.id, user: {email_preferences: {email_type => "true"}}}
|
||||||
}.to change(@user.user_preferences, :count).by(1)
|
expect {
|
||||||
end
|
put :update, par
|
||||||
|
}.to change(@user.user_preferences, :count).by(1)
|
||||||
|
end
|
||||||
|
|
||||||
it 'lets the user get mail again' do
|
it "lets the user get mail again" do
|
||||||
@user.user_preferences.create(:email_type => 'mentioned')
|
@user.user_preferences.create(email_type: email_type)
|
||||||
par = {:id => @user.id, :user => {:email_preferences => {'mentioned' => 'false'}}}
|
par = {id: @user.id, user: {email_preferences: {email_type => "false"}}}
|
||||||
expect{
|
expect {
|
||||||
put :update, par
|
put :update, par
|
||||||
}.to change(@user.user_preferences, :count).by(-1)
|
}.to change(@user.user_preferences, :count).by(-1)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,17 @@ FactoryGirl.define do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :user_with_aspect, :parent => :user do
|
factory :user_with_aspect, parent: :user do
|
||||||
after(:create) { |u| FactoryGirl.create(:aspect, :user => u) }
|
transient do
|
||||||
|
friends []
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:create) do |user, evaluator|
|
||||||
|
FactoryGirl.create(:aspect, user: user)
|
||||||
|
evaluator.friends.each do |friend|
|
||||||
|
connect_users_with_aspects(user, friend)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :aspect do
|
factory :aspect do
|
||||||
|
|
@ -116,8 +125,8 @@ FactoryGirl.define do
|
||||||
|
|
||||||
factory(:status_message_in_aspect) do
|
factory(:status_message_in_aspect) do
|
||||||
public false
|
public false
|
||||||
|
author { FactoryGirl.create(:user_with_aspect).person }
|
||||||
after(:build) do |sm|
|
after(:build) do |sm|
|
||||||
sm.author = FactoryGirl.create(:user_with_aspect).person
|
|
||||||
sm.aspects << sm.author.owner.aspects.first
|
sm.aspects << sm.author.owner.aspects.first
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -228,8 +237,8 @@ FactoryGirl.define do
|
||||||
|
|
||||||
factory(:comment) do
|
factory(:comment) do
|
||||||
sequence(:text) {|n| "#{n} cats"}
|
sequence(:text) {|n| "#{n} cats"}
|
||||||
association(:author, :factory => :person)
|
association(:author, factory: :person)
|
||||||
association(:post, :factory => :status_message)
|
association(:post, factory: :status_message)
|
||||||
end
|
end
|
||||||
|
|
||||||
factory(:notification) do
|
factory(:notification) do
|
||||||
|
|
@ -242,6 +251,16 @@ FactoryGirl.define do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
factory(:notification_mentioned_in_comment, class: Notification) do
|
||||||
|
association :recipient, factory: :user
|
||||||
|
type "Notifications::MentionedInComment"
|
||||||
|
|
||||||
|
after(:build) do |note|
|
||||||
|
note.actors << FactoryGirl.build(:person)
|
||||||
|
note.target = FactoryGirl.create :mention_in_comment, person: note.recipient.person
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
factory(:tag, :class => ActsAsTaggableOn::Tag) do
|
factory(:tag, :class => ActsAsTaggableOn::Tag) do
|
||||||
name "partytimeexcellent"
|
name "partytimeexcellent"
|
||||||
end
|
end
|
||||||
|
|
@ -271,8 +290,13 @@ FactoryGirl.define do
|
||||||
end
|
end
|
||||||
|
|
||||||
factory(:mention) do
|
factory(:mention) do
|
||||||
association(:person, :factory => :person)
|
association(:person, factory: :person)
|
||||||
association(:post, :factory => :status_message)
|
association(:mentions_container, factory: :status_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
factory(:mention_in_comment, class: Mention) do
|
||||||
|
association(:person, factory: :person)
|
||||||
|
association(:mentions_container, factory: :comment)
|
||||||
end
|
end
|
||||||
|
|
||||||
factory(:conversation) do
|
factory(:conversation) do
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,12 @@ require Rails.root.join("spec", "support", "inlined_jobs")
|
||||||
|
|
||||||
module HelperMethods
|
module HelperMethods
|
||||||
def connect_users_with_aspects(u1, u2)
|
def connect_users_with_aspects(u1, u2)
|
||||||
aspect1 = u1.aspects.length == 1 ? u1.aspects.first : u1.aspects.where(:name => "Besties").first
|
aspect1, aspect2 = [u1, u2].map do |user|
|
||||||
aspect2 = u2.aspects.length == 1 ? u2.aspects.first : u2.aspects.where(:name => "Besties").first
|
user.aspects.where(name: "Besties").first.presence || user.aspects.first
|
||||||
|
end
|
||||||
connect_users(u1, aspect1, u2, aspect2)
|
connect_users(u1, aspect1, u2, aspect2)
|
||||||
end
|
end
|
||||||
|
|
||||||
def connect_users(user1, aspect1, user2, aspect2)
|
def connect_users(user1, aspect1, user2, aspect2)
|
||||||
user1.contacts.create!(:person => user2.person,
|
user1.contacts.create!(:person => user2.person,
|
||||||
:aspects => [aspect1],
|
:aspects => [aspect1],
|
||||||
|
|
@ -42,4 +44,18 @@ module HelperMethods
|
||||||
body.close if body.respond_to?(:close) # avoids deadlock after 3 tests
|
body.close if body.respond_to?(:close) # avoids deadlock after 3 tests
|
||||||
ActionDispatch::TestResponse.new(status, headers, body)
|
ActionDispatch::TestResponse.new(status, headers, body)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def text_mentioning(*people)
|
||||||
|
people.map {|person|
|
||||||
|
"this is a text mentioning @{#{person.name}; #{person.diaspora_handle}} ... have fun testing!"
|
||||||
|
}.join(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_relayable_federation_entity(type, data={}, additional_xml_elements={})
|
||||||
|
attributes = FactoryGirl.attributes_for("#{type}_entity".to_sym, data)
|
||||||
|
entity_class = "DiasporaFederation::Entities::#{type.capitalize}".constantize
|
||||||
|
signable_fields = attributes.keys - [:author_signature]
|
||||||
|
|
||||||
|
entity_class.new(attributes, [*signable_fields, *additional_xml_elements.keys], additional_xml_elements)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,37 @@ describe NotificationsHelper, type: :helper do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
let(:status_message) {
|
||||||
|
FactoryGirl.create(:status_message_in_aspect, author: alice.person, text: text_mentioning(bob))
|
||||||
|
}
|
||||||
|
|
||||||
|
describe "when mentioned in status message" do
|
||||||
|
it "should include correct wording and post link" do
|
||||||
|
Notifications::MentionedInPost.notify(status_message, [bob.id])
|
||||||
|
notification = Notifications::MentionedInPost.last
|
||||||
|
expect(notification).not_to be_nil
|
||||||
|
|
||||||
|
link = object_link(notification, notification_people_link(notification))
|
||||||
|
expect(link).to include("mentioned you in the post")
|
||||||
|
expect(link).to include(post_path(status_message))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "when mentioned in comment" do
|
||||||
|
it "should include correct wording, post link and comment link" do
|
||||||
|
comment = FactoryGirl.create(:comment, author: bob.person, text: text_mentioning(alice), post: status_message)
|
||||||
|
Notifications::MentionedInComment.notify(comment, [alice.id])
|
||||||
|
notification = Notifications::MentionedInComment.last
|
||||||
|
expect(notification).not_to be_nil
|
||||||
|
|
||||||
|
link = object_link(notification, notification_people_link(notification))
|
||||||
|
expect(link).to include("mentioned you in a")
|
||||||
|
expect(link).to include(">comment</a>")
|
||||||
|
expect(link).to include("href=\"#{post_path(status_message)}\"")
|
||||||
|
expect(link).to include("#{post_path(status_message)}##{comment.guid}")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#display_year?' do
|
describe '#display_year?' do
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,39 @@
|
||||||
module MentioningSpecHelpers
|
module MentioningSpecHelpers
|
||||||
def default_aspect
|
def notifications_about_mentioning(user, object)
|
||||||
@user1.aspects.where(name: "generic").first
|
table = object.class.table_name
|
||||||
|
|
||||||
|
if object.is_a?(StatusMessage)
|
||||||
|
klass = Notifications::MentionedInPost
|
||||||
|
elsif object.is_a?(Comment)
|
||||||
|
klass = Notifications::MentionedInComment
|
||||||
|
end
|
||||||
|
|
||||||
|
klass
|
||||||
|
.where(recipient_id: user.id)
|
||||||
|
.joins("LEFT OUTER JOIN mentions ON notifications.target_id = mentions.id AND "\
|
||||||
|
"notifications.target_type = 'Mention'")
|
||||||
|
.joins("LEFT OUTER JOIN #{table} ON mentions_container_id = #{table}.id AND "\
|
||||||
|
"mentions_container_type = '#{object.class.base_class}'").where(table.to_sym => {id: object.id})
|
||||||
end
|
end
|
||||||
|
|
||||||
def text_mentioning(user)
|
def mention_container_path(object)
|
||||||
"this is a text mentioning @{#{user.name}; #{user.diaspora_handle}} ... have fun testing!"
|
object.is_a?(Post) ? post_path(object) : post_path(object.parent, anchor: object.guid)
|
||||||
|
end
|
||||||
|
|
||||||
|
def mentioning_mail_notification(user, object)
|
||||||
|
ActionMailer::Base.deliveries.select {|delivery|
|
||||||
|
delivery.to.include?(user.email) &&
|
||||||
|
delivery.subject.include?(I18n.t("notifier.mentioned.subject", name: "")) &&
|
||||||
|
delivery.body.parts[0].body.include?(mention_container_path(object))
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def also_commented_mail_notification(user, post)
|
||||||
|
ActionMailer::Base.deliveries.select {|delivery|
|
||||||
|
delivery.to.include?(user.email) &&
|
||||||
|
delivery.subject.include?(I18n.t("notifier.also_commented.limited_subject")) &&
|
||||||
|
delivery.body.parts[0].body.include?(post_path(post))
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def stream_for(user)
|
def stream_for(user)
|
||||||
|
|
@ -17,77 +46,336 @@ module MentioningSpecHelpers
|
||||||
stream.posts
|
stream.posts
|
||||||
end
|
end
|
||||||
|
|
||||||
def users_connected?(user1, user2)
|
def post_status_message(mentioned_user, aspects=nil)
|
||||||
user1.contacts.where(person_id: user2.person).count > 0
|
aspects = user1.aspects.first.id.to_s if aspects.nil?
|
||||||
|
sign_in user1
|
||||||
|
status_msg = nil
|
||||||
|
inlined_jobs do
|
||||||
|
post "/status_messages.json", status_message: {text: text_mentioning(mentioned_user)}, aspect_ids: aspects
|
||||||
|
status_msg = StatusMessage.find(JSON.parse(response.body)["id"])
|
||||||
|
end
|
||||||
|
status_msg
|
||||||
|
end
|
||||||
|
|
||||||
|
def receive_each(entity, recipients)
|
||||||
|
inlined_jobs do
|
||||||
|
recipients.each do |recipient|
|
||||||
|
DiasporaFederation.callbacks.trigger(:receive_entity, entity, entity.author, recipient.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_private_message(guid)
|
||||||
|
StatusMessage.find_by(guid: guid).tap do |status_msg|
|
||||||
|
expect(status_msg).not_to be_nil
|
||||||
|
expect(status_msg.public?).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def receive_status_message_via_federation(text, *recipients)
|
||||||
|
entity = FactoryGirl.build(
|
||||||
|
:status_message_entity,
|
||||||
|
author: remote_raphael.diaspora_handle,
|
||||||
|
text: text,
|
||||||
|
public: false
|
||||||
|
)
|
||||||
|
|
||||||
|
expect {
|
||||||
|
receive_each(entity, recipients)
|
||||||
|
}.to change(Post, :count).by(1).and change(ShareVisibility, :count).by(recipients.count)
|
||||||
|
|
||||||
|
find_private_message(entity.guid)
|
||||||
|
end
|
||||||
|
|
||||||
|
def receive_comment_via_federation(text, parent)
|
||||||
|
entity = build_relayable_federation_entity(
|
||||||
|
:comment,
|
||||||
|
parent_guid: parent.guid,
|
||||||
|
author: remote_raphael.diaspora_handle,
|
||||||
|
parent: Diaspora::Federation::Entities.related_entity(parent),
|
||||||
|
text: text
|
||||||
|
)
|
||||||
|
|
||||||
|
receive_each(entity, [parent.author.owner])
|
||||||
|
|
||||||
|
Comment.find_by(guid: entity.guid)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
describe "mentioning", type: :request do
|
describe "mentioning", type: :request do
|
||||||
include MentioningSpecHelpers
|
include MentioningSpecHelpers
|
||||||
|
|
||||||
before do
|
RSpec::Matchers.define :be_mentioned_in do |object|
|
||||||
@user1 = FactoryGirl.create :user_with_aspect
|
include Rails.application.routes.url_helpers
|
||||||
@user2 = FactoryGirl.create :user
|
|
||||||
@user3 = FactoryGirl.create :user
|
|
||||||
|
|
||||||
@user1.share_with(@user2.person, default_aspect)
|
def user_notified?(user, object)
|
||||||
sign_in @user1
|
notifications_about_mentioning(user, object).any? && mentioning_mail_notification(user, object).any?
|
||||||
|
end
|
||||||
|
|
||||||
|
match do |user|
|
||||||
|
object.message.markdownified.include?(person_path(id: user.person.guid)) && user_notified?(user, object)
|
||||||
|
end
|
||||||
|
|
||||||
|
match_when_negated do |user|
|
||||||
|
!user_notified?(user, object)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# see: https://github.com/diaspora/diaspora/issues/4160
|
RSpec::Matchers.define :be_in_streams_of do |user|
|
||||||
it "doesn't mention people that aren't in the target aspect" do
|
match do |status_message|
|
||||||
expect(users_connected?(@user1, @user3)).to be false
|
stream_for(user).map(&:id).include?(status_message.id) &&
|
||||||
|
mention_stream_for(user).map(&:id).include?(status_message.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
match_when_negated do |status_message|
|
||||||
|
!stream_for(user).map(&:id).include?(status_message.id) &&
|
||||||
|
!mention_stream_for(user).map(&:id).include?(status_message.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:user1) { FactoryGirl.create(:user_with_aspect) }
|
||||||
|
let(:user2) { FactoryGirl.create(:user_with_aspect, friends: [user1, user3]) }
|
||||||
|
let(:user3) { FactoryGirl.create(:user_with_aspect) }
|
||||||
|
|
||||||
|
# see: https://github.com/diaspora/diaspora/issues/4160
|
||||||
|
it "only mentions people that are in the target aspect" do
|
||||||
status_msg = nil
|
status_msg = nil
|
||||||
expect {
|
expect {
|
||||||
post "/status_messages.json", status_message: {text: text_mentioning(@user3)}, aspect_ids: default_aspect.id.to_s
|
status_msg = post_status_message(user3)
|
||||||
status_msg = StatusMessage.find(JSON.parse(response.body)["id"])
|
|
||||||
}.to change(Post, :count).by(1).and change(AspectVisibility, :count).by(1)
|
}.to change(Post, :count).by(1).and change(AspectVisibility, :count).by(1)
|
||||||
|
|
||||||
expect(status_msg).not_to be_nil
|
expect(status_msg).not_to be_nil
|
||||||
expect(status_msg.public?).to be false
|
expect(status_msg.public?).to be false
|
||||||
expect(status_msg.text).to include(@user3.name)
|
expect(status_msg.text).to include(user3.name)
|
||||||
expect(status_msg.text).not_to include(@user3.diaspora_handle)
|
|
||||||
expect(status_msg.text).to include(user_profile_path(username: @user3.username))
|
|
||||||
|
|
||||||
expect(stream_for(@user3).map(&:id)).not_to include(status_msg.id)
|
expect(user3).not_to be_mentioned_in(status_msg)
|
||||||
expect(mention_stream_for(@user3).map(&:id)).not_to include(status_msg.id)
|
expect(status_msg).not_to be_in_streams_of(user3)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "in private post via federation" do
|
||||||
|
let(:status_msg) {
|
||||||
|
receive_status_message_via_federation(text_mentioning(user2, user3), user3)
|
||||||
|
}
|
||||||
|
|
||||||
|
it "receiver is mentioned in status message" do
|
||||||
|
expect(user3).to be_mentioned_in(status_msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "receiver can see status message in streams" do
|
||||||
|
expect(status_msg).to be_in_streams_of(user3)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "non-receiver is not mentioned in status message" do
|
||||||
|
expect(user2).not_to be_mentioned_in(status_msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "non-receiver can't see status message in streams" do
|
||||||
|
expect(status_msg).not_to be_in_streams_of(user2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "in private post via federation with multiple recipients" do
|
||||||
|
let(:status_msg) {
|
||||||
|
receive_status_message_via_federation(text_mentioning(user3, user2), user3, user2)
|
||||||
|
}
|
||||||
|
|
||||||
|
it "mentions all recipients in the status message" do
|
||||||
|
[user2, user3].each do |user|
|
||||||
|
expect(user).to be_mentioned_in(status_msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "all recipients can see status message in streams" do
|
||||||
|
[user2, user3].each do |user|
|
||||||
|
expect(status_msg).to be_in_streams_of(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "mentions people in public posts" do
|
it "mentions people in public posts" do
|
||||||
expect(users_connected?(@user1, @user3)).to be false
|
|
||||||
|
|
||||||
status_msg = nil
|
status_msg = nil
|
||||||
expect {
|
expect {
|
||||||
post "/status_messages.json", status_message: {text: text_mentioning(@user3)}, aspect_ids: "public"
|
status_msg = post_status_message(user3, "public")
|
||||||
status_msg = StatusMessage.find(JSON.parse(response.body)["id"])
|
|
||||||
}.to change(Post, :count).by(1)
|
}.to change(Post, :count).by(1)
|
||||||
|
|
||||||
expect(status_msg).not_to be_nil
|
expect(status_msg).not_to be_nil
|
||||||
expect(status_msg.public?).to be true
|
expect(status_msg.public?).to be true
|
||||||
expect(status_msg.text).to include(@user3.name)
|
expect(status_msg.text).to include(user3.name)
|
||||||
expect(status_msg.text).to include(@user3.diaspora_handle)
|
expect(status_msg.text).to include(user3.diaspora_handle)
|
||||||
|
|
||||||
expect(stream_for(@user3).map(&:id)).to include(status_msg.id)
|
expect(user3).to be_mentioned_in(status_msg)
|
||||||
expect(mention_stream_for(@user3).map(&:id)).to include(status_msg.id)
|
expect(status_msg).to be_in_streams_of(user3)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "mentions people that are in the target aspect" do
|
it "mentions people that are in the target aspect" do
|
||||||
expect(users_connected?(@user1, @user2)).to be true
|
|
||||||
|
|
||||||
status_msg = nil
|
status_msg = nil
|
||||||
expect {
|
expect {
|
||||||
post "/status_messages.json", status_message: {text: text_mentioning(@user2)}, aspect_ids: default_aspect.id.to_s
|
status_msg = post_status_message(user2)
|
||||||
status_msg = StatusMessage.find(JSON.parse(response.body)["id"])
|
|
||||||
}.to change(Post, :count).by(1).and change(AspectVisibility, :count).by(1)
|
}.to change(Post, :count).by(1).and change(AspectVisibility, :count).by(1)
|
||||||
|
|
||||||
expect(status_msg).not_to be_nil
|
expect(status_msg).not_to be_nil
|
||||||
expect(status_msg.public?).to be false
|
expect(status_msg.public?).to be false
|
||||||
expect(status_msg.text).to include(@user2.name)
|
expect(status_msg.text).to include(user2.name)
|
||||||
expect(status_msg.text).to include(@user2.diaspora_handle)
|
expect(status_msg.text).to include(user2.diaspora_handle)
|
||||||
|
|
||||||
expect(stream_for(@user2).map(&:id)).to include(status_msg.id)
|
expect(user2).to be_mentioned_in(status_msg)
|
||||||
expect(mention_stream_for(@user2).map(&:id)).to include(status_msg.id)
|
expect(status_msg).to be_in_streams_of(user2)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "in comments" do
|
||||||
|
let(:author) { FactoryGirl.create(:user_with_aspect) }
|
||||||
|
|
||||||
|
shared_context "commenter is author" do
|
||||||
|
let(:commenter) { author }
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_context "commenter is author's friend" do
|
||||||
|
let(:commenter) { FactoryGirl.create(:user_with_aspect, friends: [author]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_context "commenter is not author's friend" do
|
||||||
|
let(:commenter) { FactoryGirl.create(:user) }
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_context "mentioned user is author" do
|
||||||
|
let(:mentioned_user) { author }
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_context "mentioned user is author's friend" do
|
||||||
|
let(:mentioned_user) { FactoryGirl.create(:user_with_aspect, friends: [author]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_context "mentioned user is not author's friend" do
|
||||||
|
let(:mentioned_user) { FactoryGirl.create(:user) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with public post" do
|
||||||
|
let(:status_msg) { FactoryGirl.create(:status_message, author: author.person, public: true) }
|
||||||
|
|
||||||
|
[
|
||||||
|
["commenter is author's friend", "mentioned user is not author's friend"],
|
||||||
|
["commenter is author's friend", "mentioned user is author"],
|
||||||
|
["commenter is not author's friend", "mentioned user is author's friend"],
|
||||||
|
["commenter is not author's friend", "mentioned user is not author's friend"],
|
||||||
|
["commenter is author", "mentioned user is author's friend"],
|
||||||
|
["commenter is author", "mentioned user is not author's friend"]
|
||||||
|
].each do |commenters_context, mentioned_context|
|
||||||
|
context "when #{commenters_context} and #{mentioned_context}" do
|
||||||
|
include_context commenters_context
|
||||||
|
include_context mentioned_context
|
||||||
|
|
||||||
|
let(:comment) {
|
||||||
|
inlined_jobs do
|
||||||
|
commenter.comment!(status_msg, text_mentioning(mentioned_user))
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
subject { mentioned_user }
|
||||||
|
it { is_expected.to be_mentioned_in(comment) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when comment is received via federation" do
|
||||||
|
context "when mentioned user is remote" do
|
||||||
|
it "relays the comment to the mentioned user" do
|
||||||
|
mentioned_person = FactoryGirl.create(:person)
|
||||||
|
expect_any_instance_of(Diaspora::Federation::Dispatcher::Public)
|
||||||
|
.to receive(:deliver_to_remote).with([mentioned_person])
|
||||||
|
|
||||||
|
receive_comment_via_federation(text_mentioning(mentioned_person), status_msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with private post" do
|
||||||
|
[
|
||||||
|
["commenter is author's friend", "mentioned user is author's friend"],
|
||||||
|
["commenter is author", "mentioned user is author's friend"],
|
||||||
|
["commenter is author's friend", "mentioned user is author"]
|
||||||
|
].each do |commenters_context, mentioned_context|
|
||||||
|
context "when #{commenters_context} and #{mentioned_context}" do
|
||||||
|
include_context commenters_context
|
||||||
|
include_context mentioned_context
|
||||||
|
|
||||||
|
let(:parent) { FactoryGirl.create(:status_message_in_aspect, author: author.person) }
|
||||||
|
let(:comment) {
|
||||||
|
inlined_jobs do
|
||||||
|
commenter.comment!(parent, text_mentioning(mentioned_user))
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
before do
|
||||||
|
mentioned_user.like!(parent)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { mentioned_user }
|
||||||
|
it { is_expected.to be_mentioned_in(comment) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when comment is received via federation" do
|
||||||
|
let(:parent) { FactoryGirl.create(:status_message_in_aspect, author: user2.person) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
user3.like!(parent)
|
||||||
|
user1.like!(parent)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:comment_text) { text_mentioning(user2, user3, user1) }
|
||||||
|
let(:comment) { receive_comment_via_federation(comment_text, parent) }
|
||||||
|
|
||||||
|
it "mentions all the recepients" do
|
||||||
|
[user1, user2, user3].each do |user|
|
||||||
|
expect(user).to be_mentioned_in(comment)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with only post author mentioned" do
|
||||||
|
let(:post_author) { parent.author.owner }
|
||||||
|
let(:comment_text) { text_mentioning(post_author) }
|
||||||
|
|
||||||
|
it "makes only one notification for each recipient" do
|
||||||
|
expect {
|
||||||
|
comment
|
||||||
|
}.to change { Notifications::MentionedInComment.for(post_author).count }.by(1)
|
||||||
|
.and change { Notifications::AlsoCommented.for(user1).count }.by(1)
|
||||||
|
.and change { Notifications::AlsoCommented.for(user3).count }.by(1)
|
||||||
|
|
||||||
|
expect(mentioning_mail_notification(post_author, comment).count).to eq(1)
|
||||||
|
|
||||||
|
[user1, user3].each do |user|
|
||||||
|
expect(also_commented_mail_notification(user, parent).count).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "commenter can't mention a non-participant" do
|
||||||
|
let(:status_msg) { FactoryGirl.create(:status_message_in_aspect, author: author.person) }
|
||||||
|
|
||||||
|
[
|
||||||
|
["commenter is author's friend", "mentioned user is not author's friend"],
|
||||||
|
["commenter is not author's friend", "mentioned user is author's friend"],
|
||||||
|
["commenter is not author's friend", "mentioned user is not author's friend"],
|
||||||
|
["commenter is author", "mentioned user is author's friend"],
|
||||||
|
["commenter is author", "mentioned user is not author's friend"]
|
||||||
|
].each do |commenters_context, mentioned_context|
|
||||||
|
context "when #{commenters_context} and #{mentioned_context}" do
|
||||||
|
include_context commenters_context
|
||||||
|
include_context mentioned_context
|
||||||
|
|
||||||
|
let(:comment) {
|
||||||
|
inlined_jobs do
|
||||||
|
commenter.comment!(status_msg, text_mentioning(mentioned_user))
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
subject { mentioned_user }
|
||||||
|
it { is_expected.not_to be_mentioned_in(comment) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,15 @@ describe Diaspora::Federation::Receive do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".comment" do
|
describe ".comment" do
|
||||||
let(:comment_data) {
|
|
||||||
FactoryGirl.attributes_for(
|
|
||||||
:comment_entity,
|
|
||||||
author: sender.diaspora_handle,
|
|
||||||
parent_guid: post.guid,
|
|
||||||
author_signature: "aa"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
let(:comment_entity) {
|
let(:comment_entity) {
|
||||||
DiasporaFederation::Entities::Comment.new(
|
build_relayable_federation_entity(
|
||||||
comment_data, [:author, :guid, :parent_guid, :text, "new_property"], "new_property" => "data"
|
:comment,
|
||||||
|
{
|
||||||
|
author: sender.diaspora_handle,
|
||||||
|
parent_guid: post.guid,
|
||||||
|
author_signature: "aa"
|
||||||
|
},
|
||||||
|
"new_property" => "data"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,7 +55,7 @@ describe Diaspora::Federation::Receive do
|
||||||
expect(comment.signature).not_to be_nil
|
expect(comment.signature).not_to be_nil
|
||||||
expect(comment.signature.author_signature).to eq("aa")
|
expect(comment.signature.author_signature).to eq("aa")
|
||||||
expect(comment.signature.additional_data).to eq("new_property" => "data")
|
expect(comment.signature.additional_data).to eq("new_property" => "data")
|
||||||
expect(comment.signature.order).to eq(%w(author guid parent_guid text new_property))
|
expect(comment.signature.order).to eq(comment_entity.xml_order.map(&:to_s))
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:entity) { comment_entity }
|
let(:entity) { comment_entity }
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ STR
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#filter_for_aspects" do
|
describe "#filter_people" do
|
||||||
before do
|
before do
|
||||||
@user_a = FactoryGirl.create(:user_with_aspect, username: "user_a")
|
@user_a = FactoryGirl.create(:user_with_aspect, username: "user_a")
|
||||||
@user_b = FactoryGirl.create(:user, username: "user_b")
|
@user_b = FactoryGirl.create(:user, username: "user_b")
|
||||||
|
|
@ -122,8 +122,10 @@ STR
|
||||||
end
|
end
|
||||||
|
|
||||||
it "filters mention, if contact is not in a given aspect" do
|
it "filters mention, if contact is not in a given aspect" do
|
||||||
aspect_id = @user_a.aspects.where(name: "generic").first.id
|
txt = Diaspora::Mentionable.filter_people(
|
||||||
txt = Diaspora::Mentionable.filter_for_aspects(@test_txt_c, @user_a, aspect_id)
|
@test_txt_c,
|
||||||
|
@user_a.aspects.where(name: "generic").first.contacts.map(&:person_id)
|
||||||
|
)
|
||||||
|
|
||||||
expect(txt).to include("user C")
|
expect(txt).to include("user C")
|
||||||
expect(txt).to include(local_or_remote_person_path(@user_c.person))
|
expect(txt).to include(local_or_remote_person_path(@user_c.person))
|
||||||
|
|
@ -132,18 +134,13 @@ STR
|
||||||
end
|
end
|
||||||
|
|
||||||
it "leaves mention, if contact is in a given aspect" do
|
it "leaves mention, if contact is in a given aspect" do
|
||||||
aspect_id = @user_a.aspects.where(name: "generic").first.id
|
txt = Diaspora::Mentionable.filter_people(
|
||||||
txt = Diaspora::Mentionable.filter_for_aspects(@test_txt_b, @user_a, aspect_id)
|
@test_txt_b,
|
||||||
|
@user_a.aspects.where(name: "generic").first.contacts.map(&:person_id)
|
||||||
|
)
|
||||||
|
|
||||||
expect(txt).to include("user B")
|
expect(txt).to include("user B")
|
||||||
expect(txt).to include(@mention_b)
|
expect(txt).to include(@mention_b)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "recognizes 'all' as keyword for aspects" do
|
|
||||||
txt = Diaspora::Mentionable.filter_for_aspects(@test_txt_bc, @user_a, "all")
|
|
||||||
|
|
||||||
expect(txt).to include(@mention_b)
|
|
||||||
expect(txt).to include(@mention_c)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,9 @@ describe Notifier, type: :mailer do
|
||||||
before do
|
before do
|
||||||
@user = alice
|
@user = alice
|
||||||
@post = FactoryGirl.create(:status_message, public: true)
|
@post = FactoryGirl.create(:status_message, public: true)
|
||||||
@mention = Mention.create(person: @user.person, post: @post)
|
@mention = Mention.create(person: @user.person, mentions_container: @post)
|
||||||
|
|
||||||
@mail = Notifier.mentioned(@user.id, @post.author.id, @mention.id)
|
@mail = Notifier.send_notification("mentioned", @user.id, @post.author.id, @mention.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "TO: goes to the right person" do
|
it "TO: goes to the right person" do
|
||||||
|
|
@ -106,13 +106,41 @@ describe Notifier, type: :mailer do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe ".mentioned_in_comment" do
|
||||||
|
let(:user) { alice }
|
||||||
|
let(:comment) { FactoryGirl.create(:comment) }
|
||||||
|
let(:mention) { Mention.create(person: user.person, mentions_container: comment) }
|
||||||
|
let(:mail) { Notifier.send_notification("mentioned_in_comment", user.id, comment.author.id, mention.id) }
|
||||||
|
|
||||||
|
it "TO: goes to the right person" do
|
||||||
|
expect(mail.to).to eq([user.email])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "SUBJECT: has the name of person mentioning in the subject" do
|
||||||
|
expect(mail.subject).to include(comment.author.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "has the comment link in the body" do
|
||||||
|
expect(mail.body.encoded).to include(post_url(comment.parent, anchor: comment.guid))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "renders proper wording when limited" do
|
||||||
|
expect(mail.body.encoded).to include(I18n.translate("notifier.mentioned_in_comment.limited_post"))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "renders comment text when public" do
|
||||||
|
comment.parent.update(public: true)
|
||||||
|
expect(mail.body.encoded).to include(comment.message.plain_text_without_markdown)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe ".mentioned limited" do
|
describe ".mentioned limited" do
|
||||||
before do
|
before do
|
||||||
@user = alice
|
@user = alice
|
||||||
@post = FactoryGirl.create(:status_message, public: false)
|
@post = FactoryGirl.create(:status_message, public: false)
|
||||||
@mention = Mention.create(person: @user.person, post: @post)
|
@mention = Mention.create(person: @user.person, mentions_container: @post)
|
||||||
|
|
||||||
@mail = Notifier.mentioned(@user.id, @post.author.id, @mention.id)
|
@mail = Notifier.send_notification("mentioned", @user.id, @post.author.id, @mention.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "TO: goes to the right person" do
|
it "TO: goes to the right person" do
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ describe Comment, type: :model do
|
||||||
let(:status_bob) { bob.post(:status_message, text: "hello", to: bob.aspects.first.id) }
|
let(:status_bob) { bob.post(:status_message, text: "hello", to: bob.aspects.first.id) }
|
||||||
let(:comment_alice) { alice.comment!(status_bob, "why so formal?") }
|
let(:comment_alice) { alice.comment!(status_bob, "why so formal?") }
|
||||||
|
|
||||||
|
it_behaves_like "it is mentions container"
|
||||||
|
|
||||||
describe "#destroy" do
|
describe "#destroy" do
|
||||||
it "should delete a participation" do
|
it "should delete a participation" do
|
||||||
comment_alice
|
comment_alice
|
||||||
|
|
@ -21,6 +23,55 @@ describe Comment, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#people_allowed_to_be_mentioned" do
|
||||||
|
let(:kate) { FactoryGirl.create(:user_with_aspect, friends: [bob]) }
|
||||||
|
let(:olga) { FactoryGirl.create(:user_with_aspect, friends: [bob]) }
|
||||||
|
|
||||||
|
it "returns the author and people who have commented or liked the private post" do
|
||||||
|
eve.comment!(status_bob, "comment text")
|
||||||
|
kate.like!(status_bob)
|
||||||
|
olga.participate!(status_bob)
|
||||||
|
status_bob.reload
|
||||||
|
expect(comment_alice.people_allowed_to_be_mentioned).to match_array([alice, bob, eve, kate].map(&:person_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns :all for public posts" do
|
||||||
|
# set parent public
|
||||||
|
status_bob.update(public: true)
|
||||||
|
expect(comment_alice.people_allowed_to_be_mentioned).to eq(:all)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#subscribers" do
|
||||||
|
let(:status_bob) { FactoryGirl.create(:status_message, public: true, author: bob.person) }
|
||||||
|
let(:comment_alice) {
|
||||||
|
FactoryGirl.create(
|
||||||
|
:comment,
|
||||||
|
text: text_mentioning(remote_raphael, local_luke),
|
||||||
|
post: status_bob,
|
||||||
|
author: alice.person
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
context "on the parent post pod" do
|
||||||
|
it "includes mentioned people to subscribers list" do
|
||||||
|
expect(comment_alice.subscribers).to include(remote_raphael)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't include local mentioned people if they aren't participant or contact" do
|
||||||
|
expect(comment_alice.subscribers).not_to include(local_luke)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "on a non parent post pod" do
|
||||||
|
let(:status_bob) { FactoryGirl.create(:status_message) } # make the message remote
|
||||||
|
|
||||||
|
it "doesn't include mentioned people to subscribers list" do
|
||||||
|
expect(comment_alice.subscribers).not_to include(remote_raphael)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "User#comment" do
|
describe "User#comment" do
|
||||||
it "should be able to comment on one's own status" do
|
it "should be able to comment on one's own status" do
|
||||||
bob.comment!(status_bob, "sup dog")
|
bob.comment!(status_bob, "sup dog")
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@ describe Mention, type: :model do
|
||||||
describe "after destroy" do
|
describe "after destroy" do
|
||||||
it "destroys a notification" do
|
it "destroys a notification" do
|
||||||
sm = alice.post(:status_message, text: "hi", to: alice.aspects.first)
|
sm = alice.post(:status_message, text: "hi", to: alice.aspects.first)
|
||||||
mention = Mention.create!(person: bob.person, post: sm)
|
mention = Mention.create!(person: bob.person, mentions_container: sm)
|
||||||
|
|
||||||
Notifications::Mentioned.notify(sm, [bob.id])
|
Notifications::MentionedInPost.notify(sm, [bob.id])
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
mention.destroy
|
mention.destroy
|
||||||
|
|
|
||||||
72
spec/models/notifications/mentioned_in_post_spec.rb
Normal file
72
spec/models/notifications/mentioned_in_post_spec.rb
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
describe Notifications::MentionedInPost, type: :model do
|
||||||
|
let(:sm) {
|
||||||
|
FactoryGirl.create(:status_message, author: alice.person, text: "hi @{bob; #{bob.diaspora_handle}}", public: true)
|
||||||
|
}
|
||||||
|
let(:mentioned_notification) { Notifications::MentionedInPost.new(recipient: bob) }
|
||||||
|
|
||||||
|
describe ".notify" do
|
||||||
|
it "calls create_notification with mention" do
|
||||||
|
expect(Notifications::MentionedInPost).to receive(:create_notification).with(
|
||||||
|
bob, sm.mentions.first, sm.author
|
||||||
|
).and_return(mentioned_notification)
|
||||||
|
|
||||||
|
Notifications::MentionedInPost.notify(sm, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sends an email to the mentioned person" do
|
||||||
|
allow(Notifications::MentionedInPost).to receive(:create_notification).and_return(mentioned_notification)
|
||||||
|
expect(bob).to receive(:mail).with(Workers::Mail::Mentioned, bob.id, sm.author.id, sm.mentions.first.id)
|
||||||
|
|
||||||
|
Notifications::MentionedInPost.notify(sm, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does nothing if the mentioned person is not local" do
|
||||||
|
sm = FactoryGirl.create(
|
||||||
|
:status_message,
|
||||||
|
author: alice.person,
|
||||||
|
text: "hi @{raphael; #{remote_raphael.diaspora_handle}}",
|
||||||
|
public: true
|
||||||
|
)
|
||||||
|
expect(Notifications::MentionedInPost).not_to receive(:create_notification)
|
||||||
|
|
||||||
|
Notifications::MentionedInPost.notify(sm, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not notify if the author of the post is ignored" do
|
||||||
|
bob.blocks.create(person: sm.author)
|
||||||
|
|
||||||
|
expect_any_instance_of(Notifications::MentionedInPost).not_to receive(:email_the_user)
|
||||||
|
|
||||||
|
Notifications::MentionedInPost.notify(sm, [])
|
||||||
|
|
||||||
|
expect(Notifications::MentionedInPost.where(target: sm.mentions.first)).not_to exist
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with private post" do
|
||||||
|
let(:private_sm) {
|
||||||
|
FactoryGirl.create(
|
||||||
|
:status_message,
|
||||||
|
author: remote_raphael,
|
||||||
|
text: "hi @{bob; #{bob.diaspora_handle}}",
|
||||||
|
public: false
|
||||||
|
).tap {|private_sm|
|
||||||
|
private_sm.receive([bob.id, alice.id])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it "calls create_notification if the mentioned person is a recipient of the post" do
|
||||||
|
expect(Notifications::MentionedInPost).to receive(:create_notification).with(
|
||||||
|
bob, private_sm.mentions.first, private_sm.author
|
||||||
|
).and_return(mentioned_notification)
|
||||||
|
|
||||||
|
Notifications::MentionedInPost.notify(private_sm, [bob.id])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not call create_notification if the mentioned person is not a recipient of the post" do
|
||||||
|
expect(Notifications::MentionedInPost).not_to receive(:create_notification)
|
||||||
|
|
||||||
|
Notifications::MentionedInPost.notify(private_sm, [alice.id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,70 +1,53 @@
|
||||||
describe Notifications::Mentioned, type: :model do
|
describe Notifications::Mentioned do
|
||||||
let(:sm) {
|
class TestNotification < Notification
|
||||||
FactoryGirl.create(:status_message, author: alice.person, text: "hi @{bob; #{bob.diaspora_handle}}", public: true)
|
include Notifications::Mentioned
|
||||||
}
|
|
||||||
let(:mentioned_notification) { Notifications::Mentioned.new(recipient: bob) }
|
def self.filter_mentions(mentions, *)
|
||||||
|
mentions
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe ".notify" do
|
describe ".notify" do
|
||||||
it "calls create_notification with mention" do
|
let(:status_message) {
|
||||||
expect(Notifications::Mentioned).to receive(:create_notification).with(
|
FactoryGirl.create(:status_message, text: text_mentioning(remote_raphael, alice, bob, eve), author: eve.person)
|
||||||
bob, sm.mentions.first, sm.author
|
}
|
||||||
).and_return(mentioned_notification)
|
|
||||||
|
|
||||||
Notifications::Mentioned.notify(sm, [])
|
it "calls filter_mentions on self" do
|
||||||
|
expect(TestNotification).to receive(:filter_mentions).with(
|
||||||
|
Mention.where(mentions_container: status_message, person: [alice, bob].map(&:person)),
|
||||||
|
status_message,
|
||||||
|
[alice.id, bob.id]
|
||||||
|
).and_return([])
|
||||||
|
|
||||||
|
TestNotification.notify(status_message, [alice.id, bob.id])
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sends an email to the mentioned person" do
|
it "creates notification for each mention" do
|
||||||
allow(Notifications::Mentioned).to receive(:create_notification).and_return(mentioned_notification)
|
[alice, bob].each do |recipient|
|
||||||
expect(bob).to receive(:mail).with(Workers::Mail::Mentioned, bob.id, sm.author.id, sm.mentions.first.id)
|
expect(TestNotification).to receive(:create_notification).with(
|
||||||
|
recipient,
|
||||||
Notifications::Mentioned.notify(sm, [])
|
Mention.where(mentions_container: status_message, person: recipient.person_id).first,
|
||||||
end
|
status_message.author
|
||||||
|
|
||||||
it "does nothing if the mentioned person is not local" do
|
|
||||||
sm = FactoryGirl.create(
|
|
||||||
:status_message,
|
|
||||||
author: alice.person,
|
|
||||||
text: "hi @{raphael; #{remote_raphael.diaspora_handle}}",
|
|
||||||
public: true
|
|
||||||
)
|
|
||||||
expect(Notifications::Mentioned).not_to receive(:create_notification)
|
|
||||||
|
|
||||||
Notifications::Mentioned.notify(sm, [])
|
|
||||||
end
|
|
||||||
|
|
||||||
it "does not notify if the author of the post is ignored" do
|
|
||||||
bob.blocks.create(person: sm.author)
|
|
||||||
|
|
||||||
expect_any_instance_of(Notifications::Mentioned).not_to receive(:email_the_user)
|
|
||||||
|
|
||||||
Notifications::Mentioned.notify(sm, [])
|
|
||||||
|
|
||||||
expect(Notifications::Mentioned.where(target: sm.mentions.first)).not_to exist
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with private post" do
|
|
||||||
let(:private_sm) {
|
|
||||||
FactoryGirl.create(
|
|
||||||
:status_message,
|
|
||||||
author: remote_raphael,
|
|
||||||
text: "hi @{bob; #{bob.diaspora_handle}}",
|
|
||||||
public: false
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
it "calls create_notification if the mentioned person is a recipient of the post" do
|
|
||||||
expect(Notifications::Mentioned).to receive(:create_notification).with(
|
|
||||||
bob, private_sm.mentions.first, private_sm.author
|
|
||||||
).and_return(mentioned_notification)
|
|
||||||
|
|
||||||
Notifications::Mentioned.notify(private_sm, [bob.id])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not call create_notification if the mentioned person is not a recipient of the post" do
|
TestNotification.notify(status_message, nil)
|
||||||
expect(Notifications::Mentioned).not_to receive(:create_notification)
|
end
|
||||||
|
|
||||||
Notifications::Mentioned.notify(private_sm, [alice.id])
|
it "creates email notification for mention" do
|
||||||
end
|
status_message = FactoryGirl.create(:status_message, text: text_mentioning(alice), author: eve.person)
|
||||||
|
expect_any_instance_of(TestNotification).to receive(:email_the_user).with(
|
||||||
|
Mention.where(mentions_container: status_message, person: alice.person_id).first,
|
||||||
|
status_message.author
|
||||||
|
)
|
||||||
|
|
||||||
|
TestNotification.notify(status_message, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't create notification if it was filtered out by filter_mentions" do
|
||||||
|
expect(TestNotification).to receive(:filter_mentions).and_return([])
|
||||||
|
expect(TestNotification).not_to receive(:create_notification)
|
||||||
|
TestNotification.notify(status_message, nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,12 @@ describe StatusMessage, type: :model do
|
||||||
it "returns status messages where the given person is mentioned" do
|
it "returns status messages where the given person is mentioned" do
|
||||||
@bob = bob.person
|
@bob = bob.person
|
||||||
@test_string = "@{Daniel; #{@bob.diaspora_handle}} can mention people like Raph"
|
@test_string = "@{Daniel; #{@bob.diaspora_handle}} can mention people like Raph"
|
||||||
|
post1 = FactoryGirl.create(:status_message, text: @test_string, public: true)
|
||||||
|
post2 = FactoryGirl.create(:status_message, text: @test_string, public: true)
|
||||||
FactoryGirl.create(:status_message, text: @test_string)
|
FactoryGirl.create(:status_message, text: @test_string)
|
||||||
FactoryGirl.create(:status_message, text: @test_string)
|
FactoryGirl.create(:status_message, public: true)
|
||||||
FactoryGirl.create(:status_message)
|
|
||||||
|
|
||||||
expect(StatusMessage.where_person_is_mentioned(bob).count).to eq(2)
|
expect(StatusMessage.where_person_is_mentioned(@bob).ids).to match_array([post1.id, post2.id])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -81,14 +82,6 @@ describe StatusMessage, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".after_create" do
|
|
||||||
it "calls create_mentions" do
|
|
||||||
status = FactoryGirl.build(:status_message, text: "text @{Test; #{alice.diaspora_handle}}")
|
|
||||||
expect(status).to receive(:create_mentions).and_call_original
|
|
||||||
status.save
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "emptiness" do
|
context "emptiness" do
|
||||||
it "needs either a message or at least one photo" do
|
it "needs either a message or at least one photo" do
|
||||||
post = user.build_post(:status_message, text: nil)
|
post = user.build_post(:status_message, text: nil)
|
||||||
|
|
@ -131,57 +124,20 @@ describe StatusMessage, type: :model do
|
||||||
expect(status_message).not_to be_valid
|
expect(status_message).not_to be_valid
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "mentions" do
|
it_behaves_like "it is mentions container"
|
||||||
let(:people) { [alice, bob, eve].map(&:person) }
|
|
||||||
let(:test_string) {
|
|
||||||
"@{Raphael; #{people[0].diaspora_handle}} can mention people like Raphael @{Ilya; #{people[1].diaspora_handle}}
|
|
||||||
can mention people like Raphaellike Raphael @{Daniel; #{people[2].diaspora_handle}} can mention people like Raph"
|
|
||||||
}
|
|
||||||
let(:status_message) { create(:status_message, text: test_string) }
|
|
||||||
|
|
||||||
describe "#create_mentions" do
|
describe "#people_allowed_to_be_mentioned" do
|
||||||
it "creates a mention for everyone mentioned in the message" do
|
it "returns only aspects members for private posts" do
|
||||||
status_message
|
sm = FactoryGirl.build(:status_message_in_aspect)
|
||||||
expect(Diaspora::Mentionable).to receive(:people_from_string).and_return(people)
|
sm.author.owner.share_with(alice.person, sm.author.owner.aspects.first)
|
||||||
status_message.mentions.delete_all
|
sm.author.owner.share_with(eve.person, sm.author.owner.aspects.first)
|
||||||
status_message.create_mentions
|
sm.save!
|
||||||
expect(status_message.mentions(true).map(&:person).to_set).to eq(people.to_set)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "does not barf if it gets called twice" do
|
expect(sm.people_allowed_to_be_mentioned).to match_array([alice.person_id, eve.person_id])
|
||||||
status_message.create_mentions
|
|
||||||
|
|
||||||
expect {
|
|
||||||
status_message.create_mentions
|
|
||||||
}.to_not raise_error
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#mentioned_people" do
|
it "returns :all for public posts" do
|
||||||
it "does not call create_mentions if there are no mentions in the db" do
|
expect(FactoryGirl.create(:status_message, public: true).people_allowed_to_be_mentioned).to eq(:all)
|
||||||
status_message.mentions.delete_all
|
|
||||||
expect(status_message).not_to receive(:create_mentions)
|
|
||||||
status_message.mentioned_people
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns the mentioned people" do
|
|
||||||
expect(status_message.mentioned_people.to_set).to eq(people.to_set)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "does not call create_mentions if there are mentions in the db" do
|
|
||||||
expect(status_message).not_to receive(:create_mentions)
|
|
||||||
status_message.mentioned_people
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "#mentions?" do
|
|
||||||
it "returns true if the person was mentioned" do
|
|
||||||
expect(status_message.mentions?(people[0])).to be true
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns false if the person was not mentioned" do
|
|
||||||
expect(status_message.mentions?(FactoryGirl.build(:person))).to be false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,9 +126,9 @@ describe PostPresenter do
|
||||||
|
|
||||||
it "does not raise if the root of a reshare does not exist anymore" do
|
it "does not raise if the root of a reshare does not exist anymore" do
|
||||||
reshare = FactoryGirl.create(:reshare)
|
reshare = FactoryGirl.create(:reshare)
|
||||||
reshare.root = nil
|
reshare.update(root: nil)
|
||||||
|
|
||||||
expect(PostPresenter.new(reshare).send(:description)).to eq(nil)
|
expect(PostPresenter.new(Post.find(reshare.id)).send(:description)).to eq(nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
74
spec/services/notification_service_spec.rb
Normal file
74
spec/services/notification_service_spec.rb
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
describe NotificationService do
|
||||||
|
describe "notification interrelation" do
|
||||||
|
context "with mention in comment" do
|
||||||
|
let(:status_message) {
|
||||||
|
FactoryGirl.create(:status_message, public: true, author: alice.person).tap {|status_message|
|
||||||
|
eve.comment!(status_message, "whatever")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let(:comment) {
|
||||||
|
FactoryGirl.create(
|
||||||
|
:comment,
|
||||||
|
author: bob.person,
|
||||||
|
text: text_mentioning(alice, eve),
|
||||||
|
post: status_message
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
it "sends only mention notification" do
|
||||||
|
[alice, eve].each do |user|
|
||||||
|
expect(Workers::Mail::MentionedInComment).to receive(:perform_async).with(
|
||||||
|
user.id,
|
||||||
|
bob.person.id,
|
||||||
|
*comment.mentions.where(person: user.person).ids
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
expect {
|
||||||
|
NotificationService.new.notify(comment, [])
|
||||||
|
}.to change { Notification.where(recipient_id: alice).count }.by(1)
|
||||||
|
.and change { Notification.where(recipient_id: eve).count }.by(1)
|
||||||
|
|
||||||
|
[alice, eve].each do |user|
|
||||||
|
expect(
|
||||||
|
Notifications::MentionedInComment.where(target: comment.mentions, recipient_id: user.id)
|
||||||
|
).to exist
|
||||||
|
|
||||||
|
expect(
|
||||||
|
Notifications::CommentOnPost.where(target: comment.parent, recipient_id: user.id)
|
||||||
|
).not_to exist
|
||||||
|
|
||||||
|
expect(
|
||||||
|
Notifications::AlsoCommented.where(target: comment.parent, recipient_id: user.id)
|
||||||
|
).not_to exist
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with \"mentioned in comment\" email turned off" do
|
||||||
|
before do
|
||||||
|
alice.user_preferences.create(email_type: "mentioned_in_comment")
|
||||||
|
eve.user_preferences.create(email_type: "mentioned_in_comment")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "calls appropriate mail worker instead" do
|
||||||
|
expect(Workers::Mail::MentionedInComment).not_to receive(:perform_async)
|
||||||
|
|
||||||
|
expect(Workers::Mail::CommentOnPost).to receive(:perform_async).with(
|
||||||
|
alice.id,
|
||||||
|
bob.person.id,
|
||||||
|
*comment.mentions.where(person: alice.person).ids
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(Workers::Mail::AlsoCommented).to receive(:perform_async).with(
|
||||||
|
eve.id,
|
||||||
|
bob.person.id,
|
||||||
|
*comment.mentions.where(person: eve.person).ids
|
||||||
|
)
|
||||||
|
|
||||||
|
NotificationService.new.notify(comment, [])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -111,6 +111,8 @@ describe PostService do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#mark_user_notifications" do
|
describe "#mark_user_notifications" do
|
||||||
|
let(:status_text) { text_mentioning(alice) }
|
||||||
|
|
||||||
it "marks a corresponding notifications as read" do
|
it "marks a corresponding notifications as read" do
|
||||||
FactoryGirl.create(:notification, recipient: alice, target: post, unread: true)
|
FactoryGirl.create(:notification, recipient: alice, target: post, unread: true)
|
||||||
FactoryGirl.create(:notification, recipient: alice, target: post, unread: true)
|
FactoryGirl.create(:notification, recipient: alice, target: post, unread: true)
|
||||||
|
|
@ -121,7 +123,6 @@ describe PostService do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "marks a corresponding mention notification as read" do
|
it "marks a corresponding mention notification as read" do
|
||||||
status_text = "this is a text mentioning @{Mention User ; #{alice.diaspora_handle}} ... have fun testing!"
|
|
||||||
mention_post = bob.post(:status_message, text: status_text, public: true)
|
mention_post = bob.post(:status_message, text: status_text, public: true)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
|
|
@ -129,6 +130,16 @@ describe PostService do
|
||||||
}.to change(Notification.where(unread: true), :count).by(-1)
|
}.to change(Notification.where(unread: true), :count).by(-1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "marks a corresponding mention in comment notification as read" do
|
||||||
|
notification = FactoryGirl.create(:notification_mentioned_in_comment)
|
||||||
|
status_message = notification.target.mentions_container.parent
|
||||||
|
user = notification.recipient
|
||||||
|
|
||||||
|
expect {
|
||||||
|
PostService.new(user).mark_user_notifications(status_message.id)
|
||||||
|
}.to change(Notification.where(unread: true), :count).by(-1)
|
||||||
|
end
|
||||||
|
|
||||||
it "does not change the update_at date/time for post notifications" do
|
it "does not change the update_at date/time for post notifications" do
|
||||||
notification = Timecop.travel(1.minute.ago) do
|
notification = Timecop.travel(1.minute.ago) do
|
||||||
FactoryGirl.create(:notification, recipient: alice, target: post, unread: true)
|
FactoryGirl.create(:notification, recipient: alice, target: post, unread: true)
|
||||||
|
|
@ -140,7 +151,6 @@ describe PostService do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not change the update_at date/time for mention notifications" do
|
it "does not change the update_at date/time for mention notifications" do
|
||||||
status_text = "this is a text mentioning @{Mention User ; #{alice.diaspora_handle}} ... have fun testing!"
|
|
||||||
mention_post = Timecop.travel(1.minute.ago) do
|
mention_post = Timecop.travel(1.minute.ago) do
|
||||||
bob.post(:status_message, text: status_text, public: true)
|
bob.post(:status_message, text: status_text, public: true)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -151,44 +151,6 @@ describe StatusMessageCreationService do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with mentions" do
|
|
||||||
let(:mentioned_user) { FactoryGirl.create(:user) }
|
|
||||||
let(:text) {
|
|
||||||
"Hey @{#{bob.name}; #{bob.diaspora_handle}} and @{#{mentioned_user.name}; #{mentioned_user.diaspora_handle}}!"
|
|
||||||
}
|
|
||||||
|
|
||||||
it "calls Diaspora::Mentionable#filter_for_aspects for private posts" do
|
|
||||||
expect(Diaspora::Mentionable).to receive(:filter_for_aspects).with(text, alice, aspect.id.to_s)
|
|
||||||
.and_call_original
|
|
||||||
StatusMessageCreationService.new(alice).create(params)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "keeps mentions for users in one of the aspects" do
|
|
||||||
status_message = StatusMessageCreationService.new(alice).create(params)
|
|
||||||
expect(status_message.text).to include bob.name
|
|
||||||
expect(status_message.text).to include bob.diaspora_handle
|
|
||||||
end
|
|
||||||
|
|
||||||
it "removes mentions for users in other aspects" do
|
|
||||||
status_message = StatusMessageCreationService.new(alice).create(params)
|
|
||||||
expect(status_message.text).to include mentioned_user.name
|
|
||||||
expect(status_message.text).not_to include mentioned_user.diaspora_handle
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't call Diaspora::Mentionable#filter_for_aspects for public posts" do
|
|
||||||
expect(Diaspora::Mentionable).not_to receive(:filter_for_aspects)
|
|
||||||
StatusMessageCreationService.new(alice).create(params.merge(public: true))
|
|
||||||
end
|
|
||||||
|
|
||||||
it "keeps all mentions in public posts" do
|
|
||||||
status_message = StatusMessageCreationService.new(alice).create(params.merge(public: true))
|
|
||||||
expect(status_message.text).to include bob.name
|
|
||||||
expect(status_message.text).to include bob.diaspora_handle
|
|
||||||
expect(status_message.text).to include mentioned_user.name
|
|
||||||
expect(status_message.text).to include mentioned_user.diaspora_handle
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "dispatch" do
|
context "dispatch" do
|
||||||
it "dispatches the StatusMessage" do
|
it "dispatches the StatusMessage" do
|
||||||
expect(alice).to receive(:dispatch_post).with(instance_of(StatusMessage), hash_including(service_types: []))
|
expect(alice).to receive(:dispatch_post).with(instance_of(StatusMessage), hash_including(service_types: []))
|
||||||
|
|
@ -201,6 +163,16 @@ describe StatusMessageCreationService do
|
||||||
hash_including(service_types: array_including(%w(Services::Facebook Services::Twitter))))
|
hash_including(service_types: array_including(%w(Services::Facebook Services::Twitter))))
|
||||||
StatusMessageCreationService.new(alice).create(params.merge(services: %w(twitter facebook)))
|
StatusMessageCreationService.new(alice).create(params.merge(services: %w(twitter facebook)))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "with mention" do
|
||||||
|
let(:text) { text_mentioning(eve) }
|
||||||
|
|
||||||
|
# this is only required until changes from #6818 are deployed on every pod
|
||||||
|
it "filters out mentions from text attribute" do
|
||||||
|
status_message = StatusMessageCreationService.new(alice).create(params)
|
||||||
|
expect(status_message.text).not_to include(eve.diaspora_handle)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
44
spec/shared_behaviors/mentions_container.rb
Normal file
44
spec/shared_behaviors/mentions_container.rb
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
shared_examples_for "it is mentions container" do
|
||||||
|
let(:people) { [alice, bob, eve].map(&:person) }
|
||||||
|
let(:test_string) {
|
||||||
|
"@{Raphael; #{people[0].diaspora_handle}} can mention people like @{Ilya; #{people[1].diaspora_handle}}"\
|
||||||
|
"can mention people like @{Daniel; #{people[2].diaspora_handle}}"
|
||||||
|
}
|
||||||
|
let(:target) { FactoryGirl.build(described_class.to_s.underscore.to_sym, text: test_string, author: alice.person) }
|
||||||
|
let(:target_persisted) {
|
||||||
|
target.save!
|
||||||
|
target
|
||||||
|
}
|
||||||
|
|
||||||
|
describe ".after_create" do
|
||||||
|
it "calls create_mentions" do
|
||||||
|
expect(target).to receive(:create_mentions).and_call_original
|
||||||
|
target.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#create_mentions" do
|
||||||
|
it "creates a mention for everyone mentioned in the message" do
|
||||||
|
people.each do |person|
|
||||||
|
expect(target.mentions).to receive(:find_or_create_by).with(person_id: person.id)
|
||||||
|
end
|
||||||
|
target.create_mentions
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not barf if it gets called twice" do
|
||||||
|
expect {
|
||||||
|
target_persisted.create_mentions
|
||||||
|
}.to_not raise_error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#mentioned_people" do
|
||||||
|
it "returns the mentioned people if non-persisted" do
|
||||||
|
expect(target.mentioned_people).to match_array(people)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the mentioned people if persisted" do
|
||||||
|
expect(target_persisted.mentioned_people).to match_array(people)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -7,11 +7,12 @@ describe Workers::Mail::Mentioned do
|
||||||
it "should call .deliver on the notifier object" do
|
it "should call .deliver on the notifier object" do
|
||||||
user = alice
|
user = alice
|
||||||
sm = FactoryGirl.build(:status_message)
|
sm = FactoryGirl.build(:status_message)
|
||||||
m = Mention.new(:person => user.person, :post=> sm)
|
m = Mention.new(person: user.person, mentions_container: sm)
|
||||||
|
|
||||||
mail_double = double()
|
mail_double = double()
|
||||||
expect(mail_double).to receive(:deliver_now)
|
expect(mail_double).to receive(:deliver_now)
|
||||||
expect(Notifier).to receive(:send_notification).with("mentioned", user.id, sm.author.id, m.id).and_return(mail_double)
|
expect(Notifier).to receive(:send_notification)
|
||||||
|
.with("mentioned", user.id, sm.author.id, m.id).and_return(mail_double)
|
||||||
|
|
||||||
Workers::Mail::Mentioned.new.perform(user.id, sm.author.id, m.id)
|
Workers::Mail::Mentioned.new.perform(user.id, sm.author.id, m.id)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue