Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
replace mentions of out-of-aspect people in limited posts with just a
markdown link to their profile (fixes #2516) add failing spec for #4160 / #2516 extend the spec a bit more refactor mention handling in a status message add method for filtering mentions by aspects wire mention filtering into the status message model, adapt a few tests to work properly cosmetic changes shorten helper methods add changelog entry
- Loading branch information
Showing
10 changed files
with
402 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
|
||
module Diaspora::Mentionable | ||
|
||
# regex for finding mention markup in plain text | ||
# ex. | ||
# "message @{User Name; user@pod.net} text" | ||
# will yield "User Name" and "user@pod.net" | ||
REGEX = /@\{([^;]+); ([^\}]+)\}/ | ||
|
||
# class attribute that will be added to all mention html links | ||
PERSON_HREF_CLASS = "mention hovercardable" | ||
|
||
# takes a message text and returns the text with mentions in (html escaped) | ||
# plain text or formatted with html markup linking to user profiles. | ||
# default is html output. | ||
# | ||
# @param [String] text containing mentions | ||
# @param [Array<Person>] list of mentioned people | ||
# @param [Hash] formatting options | ||
# @return [String] formatted message | ||
def self.format(msg_text, people, *opts) | ||
people = [*people] | ||
fmt_msg = msg_text.to_s.gsub(REGEX) do |match_str| | ||
# for some reason gsub doesn't always produce MatchData... | ||
m = REGEX.match(match_str) | ||
person = people.detect{ |p| p.diaspora_handle == m[2] } | ||
|
||
ERB::Util.h(MentionsInternal.mention_link(person, m[1], *opts)) | ||
end | ||
|
||
fmt_msg | ||
end | ||
|
||
# takes a message text and returns an array of people constructed from the | ||
# contained mentions | ||
# | ||
# @param [String] text containing mentions | ||
# @return [Array<Person>] array of people | ||
def self.people_from_string(msg_text) | ||
identifiers = msg_text.to_s.scan(REGEX).map do |match| | ||
match.last | ||
end | ||
|
||
return [] if identifiers.empty? | ||
Person.where(diaspora_handle: identifiers) | ||
end | ||
|
||
# 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 | ||
# will actually be able to receive notifications for being mentioned. | ||
# | ||
# @param [String] message text | ||
# @param [User] aspect owner | ||
# @param [Mixed] array containing aspect ids or "all" | ||
# @return [String] message text with filtered mentions | ||
def self.filter_for_aspects(msg_text, user, *aspects) | ||
aspect_ids = MentionsInternal.get_aspect_ids(user, *aspects) | ||
|
||
mentioned_ppl = people_from_string(msg_text) | ||
aspects_ppl = AspectMembership.where(aspect_id: aspect_ids) | ||
.includes(:contact => :person) | ||
.map(&:person) | ||
|
||
filtered_msg = msg_text.to_s.gsub(REGEX) do |match_str| | ||
# for some reason gsub doesn't always produce MatchData... | ||
m = REGEX.match(match_str) | ||
person = mentioned_ppl.detect{ |p| p.diaspora_handle == m[2] } | ||
|
||
mention = match_str | ||
mention = MentionsInternal.profile_link(person, m[1]) unless aspects_ppl.include?(person) | ||
|
||
mention | ||
end | ||
|
||
filtered_msg | ||
end | ||
|
||
private | ||
|
||
# inline module for namespacing | ||
module MentionsInternal | ||
extend ::PeopleHelper | ||
|
||
# output a formatted mention link as defined by the given options, | ||
# use the fallback name if the person is unavailable | ||
# @see Diaspora::Mentions#format | ||
# | ||
# @param [Person] AR Person | ||
# @param [String] fallback name | ||
# @param [Hash] formatting options | ||
def self.mention_link(person, fallback_name, *opts) | ||
return fallback_name unless person.present? | ||
|
||
if opts.include?(:plain_text) | ||
person.name | ||
else | ||
person_link(person, class: PERSON_HREF_CLASS) | ||
end | ||
end | ||
|
||
# output a markdown formatted link to the given person or the given fallback | ||
# string, in case the person is not present | ||
# | ||
# @param [Person] AR Person | ||
# @param [String] fallback name | ||
# @return [String] markdown person link | ||
def self.profile_link(person, fallback_name) | ||
return fallback_name unless person.present? | ||
|
||
"[#{person.name}](#{local_or_remote_person_path(person)})" | ||
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.kind_of?(Integer)) && aspects.first.to_sym == :all | ||
return user.aspects.pluck(:id) | ||
end | ||
|
||
ids = aspects.select { |id| Integer(id) != nil } # only numeric | ||
|
||
#make sure they really belong to the user | ||
user.aspects.where(id: ids).pluck(:id) | ||
end | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
|
||
require 'spec_helper' | ||
|
||
module MentioningSpecHelpers | ||
def default_aspect | ||
@user1.aspects.where(name: 'generic') | ||
end | ||
|
||
def text_mentioning(user) | ||
handle = user.diaspora_handle | ||
"this is a text mentioning @{Mention User ; #{handle}} ... have fun testing!" | ||
end | ||
|
||
def notifications_about_mentioning(user) | ||
Notifications::Mentioned.where(recipient_id: user.id) | ||
end | ||
|
||
def stream_for(user) | ||
stream = Stream::Multi.new(user) | ||
stream.posts | ||
end | ||
|
||
def users_connected?(user1, user2) | ||
user1.contacts.where(person_id: user2.person).count > 0 | ||
end | ||
end | ||
|
||
|
||
describe 'mentioning' do | ||
include MentioningSpecHelpers | ||
|
||
before do | ||
@user1 = FactoryGirl.create :user_with_aspect | ||
@user2 = FactoryGirl.create :user | ||
@user3 = FactoryGirl.create :user | ||
|
||
@user1.share_with(@user2.person, default_aspect) | ||
end | ||
|
||
# see: https://github.com/diaspora/diaspora/issues/4160 | ||
it 'only mentions people that are in the target aspect' do | ||
users_connected?(@user1, @user2).should be_true | ||
users_connected?(@user1, @user3).should be_false | ||
|
||
status_msg = nil | ||
lambda do | ||
status_msg = @user1.post(:status_message, {text: text_mentioning(@user3), to: default_aspect}) | ||
end.should change(Post, :count).by(1) | ||
|
||
status_msg.should_not be_nil | ||
status_msg.public?.should be_false | ||
status_msg.text.should include(@user3.name) | ||
|
||
notifications_about_mentioning(@user3).should be_empty | ||
stream_for(@user3).map { |item| item.id }.should_not include(status_msg.id) | ||
end | ||
|
||
end |
Oops, something went wrong.