Commit 84ec00fa authored by Dennis Schubert's avatar Dennis Schubert

Merge pull request #6873 from SuperTux88/federation-gem-salmon

Federation rewrite with diaspora_federation gem
parents 1bbe4923 3b1d113a
......@@ -111,6 +111,7 @@ before.
* Removed deprecated `REDISTOGO_URL` environment variable [#6863](https://github.com/diaspora/diaspora/pull/6863)
* Use Poltergeist instead of Selenium [#6768](https://github.com/diaspora/diaspora/pull/6768)
* Redesigned the landing page and added dedicated notes for podmins [#6268](https://github.com/diaspora/diaspora/pull/6268)
* Moved the entire federation implementation into its own gem. 🎉 [#6873](https://github.com/diaspora/diaspora/pull/6873)
## Bug fixes
* Destroy Participation when removing interactions with a post [#5852](https://github.com/diaspora/diaspora/pull/5852)
......
......@@ -13,7 +13,7 @@ gem "unicorn-worker-killer", "0.4.4"
# Federation
gem "diaspora_federation-rails", "0.0.13"
gem "diaspora_federation-rails", "0.1.0"
# API and JSON
......@@ -136,7 +136,6 @@ gem "leaflet-rails", "0.7.7"
gem "nokogiri", "1.6.8"
gem "redcarpet", "3.3.4"
gem "twitter-text", "1.13.4"
gem "roxml", "3.1.6"
gem "ruby-oembed", "0.10.1"
gem "open_graph_reader", "0.6.1"
......@@ -289,7 +288,7 @@ group :test do
gem "webmock", "2.1.0", require: false
gem "shoulda-matchers", "3.1.1"
gem "diaspora_federation-test", "0.0.13"
gem "diaspora_federation-test", "0.1.0"
end
group :development, :test do
......
......@@ -172,18 +172,18 @@ GEM
devise
rails (>= 3.0.4)
diaspora-prosody-config (0.0.5)
diaspora_federation (0.0.13)
diaspora_federation (0.1.0)
faraday (~> 0.9.0)
faraday_middleware (~> 0.10.0)
nokogiri (~> 1.6, >= 1.6.7.2)
nokogiri (~> 1.6, >= 1.6.8)
typhoeus (~> 1.0)
valid (~> 1.0)
diaspora_federation-rails (0.0.13)
diaspora_federation (= 0.0.13)
diaspora_federation-rails (0.1.0)
diaspora_federation (= 0.1.0)
rails (~> 4.2)
diaspora_federation-test (0.0.13)
diaspora_federation (= 0.0.13)
factory_girl (~> 4.5, >= 4.5.0)
diaspora_federation-test (0.1.0)
diaspora_federation (= 0.1.0)
factory_girl (~> 4.7)
diff-lcs (1.2.5)
docile (1.1.5)
domain_name (0.5.20160310)
......@@ -725,9 +725,6 @@ GEM
request_store (1.3.1)
responders (2.2.0)
railties (>= 4.2.0, < 5.1)
roxml (3.1.6)
activesupport (>= 2.3.0)
nokogiri (>= 1.3.3)
rspec (3.4.0)
rspec-core (~> 3.4.0)
rspec-expectations (~> 3.4.0)
......@@ -927,8 +924,8 @@ DEPENDENCIES
devise-token_authenticatable (= 0.4.6)
devise_lastseenable (= 0.0.6)
diaspora-prosody-config (= 0.0.5)
diaspora_federation-rails (= 0.0.13)
diaspora_federation-test (= 0.0.13)
diaspora_federation-rails (= 0.1.0)
diaspora_federation-test (= 0.1.0)
entypo-rails (= 3.0.0.pre.rc2)
eye (= 0.8.1)
factory_girl_rails (= 4.7.0)
......@@ -1015,7 +1012,6 @@ DEPENDENCIES
redcarpet (= 3.3.4)
remotipart (= 1.2.1)
responders (= 2.2.0)
roxml (= 3.1.6)
rspec-rails (= 3.4.2)
rubocop (= 0.40.0)
ruby-oembed (= 0.10.1)
......
......@@ -35,9 +35,7 @@ class BlocksController < ApplicationController
private
def disconnect_if_contact(person)
if contact = current_user.contact_for(person)
current_user.disconnect(contact, :force => true)
end
current_user.contact_for(person).try {|contact| current_user.disconnect(contact) }
end
def block_params
......
......@@ -45,7 +45,7 @@ class ConversationsController < ApplicationController
@response = {}
if person_ids.present? && @conversation.save
Postzord::Dispatcher.build(current_user, @conversation).post
Diaspora::Federation::Dispatcher.defer_dispatch(current_user, @conversation)
@response[:success] = true
@response[:message] = I18n.t('conversations.create.sent')
@response[:conversation_id] = @conversation.id
......
......@@ -17,7 +17,7 @@ class MessagesController < ApplicationController
if message.save
logger.info "event=create type=comment user=#{current_user.diaspora_handle} status=success " \
"message=#{message.id} chars=#{params[:message][:text].length}"
Postzord::Dispatcher.build(current_user, message).post
Diaspora::Federation::Dispatcher.defer_dispatch(current_user, message)
else
flash[:error] = I18n.t('conversations.new_conversation.fail')
end
......
......@@ -25,7 +25,7 @@ class PostsController < ApplicationController
render locals: {post: post}
}
format.mobile { render locals: {post: post} }
format.xml { render xml: post.to_diaspora_xml }
format.xml { render xml: DiasporaFederation::Salmon::XmlPayload.pack(Diaspora::Federation::Entities.post(post)) }
format.json { render json: PostPresenter.new(post, current_user) }
end
end
......
......@@ -11,7 +11,7 @@ class ResharesController < ApplicationController
end
if @reshare.save
current_user.dispatch_post(@reshare, :url => post_url(@reshare), :additional_subscribers => @reshare.root_author)
current_user.dispatch_post(@reshare)
render :json => ExtremePostPresenter.new(@reshare, current_user), :status => 201
else
render :nothing => true, :status => 422
......
......@@ -10,10 +10,6 @@ class AccountDeletion < ActiveRecord::Base
belongs_to :person
after_commit :queue_delete_account, :on => :create
xml_name :account_deletion
xml_attr :diaspora_handle
def person=(person)
self[:diaspora_handle] = person.diaspora_handle
self[:person_id] = person.id
......@@ -29,18 +25,14 @@ class AccountDeletion < ActiveRecord::Base
end
def perform!
self.dispatch if person.local?
AccountDeleter.new(self.diaspora_handle).perform!
Diaspora::Federation::Dispatcher.build(person.owner, self).dispatch if person.local?
AccountDeleter.new(diaspora_handle).perform!
end
def subscribers(user)
def subscribers
person.owner.contact_people.remote | Person.who_have_reshared_a_users_posts(person.owner).remote
end
def dispatch
Postzord::Dispatcher.build(person.owner, self).post
end
def public?
true
end
......
......@@ -5,8 +5,8 @@
class Comment < ActiveRecord::Base
include Diaspora::Federated::Base
include Diaspora::Guid
include Diaspora::Fields::Guid
include Diaspora::Fields::Author
include Diaspora::Relayable
include Diaspora::Taggable
......@@ -16,12 +16,9 @@ class Comment < ActiveRecord::Base
extract_tags_from :text
before_create :build_tags
xml_attr :text
xml_attr :diaspora_handle
belongs_to :commentable, :touch => true, :polymorphic => true
alias_attribute :post, :commentable
belongs_to :author, :class_name => 'Person'
alias_attribute :parent, :commentable
delegate :name, to: :author, prefix: true
delegate :comment_email_subject, to: :parent
......@@ -39,10 +36,6 @@ class Comment < ActiveRecord::Base
self.text.strip! unless self.text.nil?
end
after_save do
self.post.touch
end
after_commit :on => :create do
self.parent.update_comments_counter
end
......@@ -53,36 +46,6 @@ class Comment < ActiveRecord::Base
participation.unparticipate! if participation.present?
end
def diaspora_handle
self.author.diaspora_handle
end
def diaspora_handle= nh
self.author = Person.find_or_fetch_by_identifier(nh)
end
def notification_type(user, person)
if self.post.author == user.person
return Notifications::CommentOnPost
elsif user.participations.where(:target_id => self.post).exists? && self.author_id != user.person.id
return Notifications::AlsoCommented
else
return false
end
end
def parent_class
Post
end
def parent
self.post
end
def parent= parent
self.post = parent
end
def message
@message ||= Diaspora::MessageRenderer.new text
end
......@@ -91,14 +54,13 @@ class Comment < ActiveRecord::Base
self[:text] = text.to_s.strip #to_s if for nil, for whatever reason
end
class Generator < Federated::Generator
class Generator < Diaspora::Federated::Generator
def self.federated_class
Comment
end
def initialize(person, target, text)
@text = text
@dispatcher_opts = {additional_subscribers: target.comments_authors.where.not(id: person.id)}
super(person, target)
end
......
......@@ -3,58 +3,47 @@
# the COPYRIGHT file.
class Contact < ActiveRecord::Base
include Diaspora::Federated::Base
belongs_to :user
validates :user, presence: true
belongs_to :person
validates :person, :presence => true
validates :person, presence: true
validates :person_id, uniqueness: {scope: :user_id}
delegate :name, :diaspora_handle, :guid, :first_name,
to: :person, prefix: true
has_many :aspect_memberships, :dependent => :destroy
has_many :aspects, :through => :aspect_memberships
has_many :aspect_memberships, dependent: :destroy
has_many :aspects, through: :aspect_memberships
validate :not_contact_for_self,
:not_blocked_user,
:not_contact_with_closed_account
validates_presence_of :user
validates_uniqueness_of :person_id, :scope => :user_id
before_destroy :destroy_notifications
scope :all_contacts_of_person, ->(x) { where(:person_id => x.id) }
scope :all_contacts_of_person, ->(x) { where(person_id: x.id) }
# contact.sharing is true when contact.person is sharing with contact.user
scope :sharing, -> { where(:sharing => true) }
# contact.sharing is true when contact.person is sharing with contact.user
scope :sharing, -> { where(sharing: true) }
# contact.receiving is true when contact.user is sharing with contact.person
scope :receiving, -> { where(:receiving => true) }
scope :receiving, -> { where(receiving: true) }
scope :for_a_stream, -> {
includes(:aspects, :person => :profile).
order('profiles.last_name ASC')
}
scope :for_a_stream, -> { includes(:aspects, person: :profile).order("profiles.last_name ASC") }
scope :only_sharing, -> { sharing.where(:receiving => false) }
scope :only_sharing, -> { sharing.where(receiving: false) }
def destroy_notifications
Notification.where(:target_type => "Person",
:target_id => person_id,
:recipient_id => user_id,
:type => "Notifications::StartedSharing").destroy_all
end
def dispatch_request
request = self.generate_request
Postzord::Dispatcher.build(self.user, request).post
request
end
def generate_request
Request.diaspora_initialize(:from => self.user.person,
:to => self.person,
:into => aspects.first)
Notification.where(
target_type: "Person",
target_id: person_id,
recipient_id: user_id,
type: "Notifications::StartedSharing"
).destroy_all
end
def contacts
......@@ -69,10 +58,10 @@ class Contact < ActiveRecord::Base
end
def mutual?
self.sharing && self.receiving
sharing && receiving
end
def in_aspect? aspect
def in_aspect?(aspect)
if aspect_memberships.loaded?
aspect_memberships.detect{ |am| am.aspect_id == aspect.id }
elsif aspects.loaded?
......@@ -93,26 +82,43 @@ class Contact < ActiveRecord::Base
end
end
# Follows back if user setting is set so
def receive(_recipient_user_ids)
user.share_with(person, user.auto_follow_back_aspect) if user.auto_follow_back && !receiving
end
# object for local recipients
def object_to_receive
Contact.create_or_update_sharing_contact(person.owner, user.person)
end
# @return [Array<Person>] The recipient of the contact
def subscribers
[person]
end
# creates or updates a contact with active sharing flag. Returns nil if already sharing.
def self.create_or_update_sharing_contact(recipient, sender)
contact = recipient.contacts.find_or_initialize_by(person_id: sender.id)
return if contact.sharing
contact.update(sharing: true)
contact
end
private
def not_contact_with_closed_account
if person_id && person.closed_account?
errors[:base] << 'Cannot be in contact with a closed account'
end
errors.add(:base, "Cannot be in contact with a closed account") if person_id && person.closed_account?
end
def not_contact_for_self
if person_id && person.owner == user
errors[:base] << 'Cannot create self-contact'
end
errors.add(:base, "Cannot create self-contact") if person_id && person.owner == user
end
def not_blocked_user
if user && user.blocks.where(:person_id => person_id).exists?
errors[:base] << 'Cannot connect to an ignored user'
false
else
true
end
errors.add(:base, "Cannot connect to an ignored user") if user && user.blocks.where(person_id: person_id).exists?
end
end
class Conversation < ActiveRecord::Base
include Diaspora::Federated::Base
include Diaspora::Guid
include Diaspora::Fields::Guid
include Diaspora::Fields::Author
xml_attr :subject
xml_attr :created_at
xml_attr :messages, :as => [Message]
xml_reader :diaspora_handle
xml_reader :participant_handles
has_many :conversation_visibilities, :dependent => :destroy
has_many :participants, :class_name => 'Person', :through => :conversation_visibilities, :source => :person
has_many :messages, -> { order('created_at ASC') }
belongs_to :author, :class_name => 'Person'
has_many :conversation_visibilities, dependent: :destroy
has_many :participants, class_name: "Person", through: :conversation_visibilities, source: :person
has_many :messages, -> { order("created_at ASC") }, inverse_of: :conversation
validate :max_participants
validate :local_recipients
......@@ -38,14 +31,6 @@ class Conversation < ActiveRecord::Base
self.participants - [self.author]
end
def diaspora_handle
self.author.diaspora_handle
end
def diaspora_handle= nh
self.author = Person.find_or_fetch_by_identifier(nh)
end
def first_unread_message(user)
if visibility = self.conversation_visibilities.where(:person_id => user.person.id).where('unread > 0').first
self.messages.to_a[-visibility.unread]
......@@ -59,15 +44,12 @@ class Conversation < ActiveRecord::Base
end
end
def public?
false
end
def participant_handles
self.participants.map{|p| p.diaspora_handle}.join(";")
participants.map(&:diaspora_handle).join(";")
end
def participant_handles= handles
handles.split(';').each do |handle|
def participant_handles=(handles)
handles.split(";").each do |handle|
participants << Person.find_or_fetch_by_identifier(handle)
end
end
......@@ -86,21 +68,7 @@ class Conversation < ActiveRecord::Base
self[:subject].blank? ? I18n.t("conversations.new.subject_default") : self[:subject]
end
def subscribers(user)
self.recipients
end
def receive(user, person)
cnv = Conversation.create_with(self.attributes).find_or_create_by!(guid: guid)
self.participants.each do |participant|
ConversationVisibility.find_or_create_by(conversation_id: cnv.id, person_id: participant.id)
end
self.messages.each do |msg|
msg.conversation_id = cnv.id
received_msg = msg.receive(user, person)
Notification.notify(user, received_msg, person) if msg.respond_to?(:notification_type)
end
def subscribers
recipients
end
end
......@@ -2,8 +2,17 @@
# licensed under the Affero General Public License version 3 or later. See
# the COPYRIGHT file.
class Like < Federated::Relayable
class Generator < Federated::Generator
class Like < ActiveRecord::Base
include Diaspora::Federated::Base
include Diaspora::Fields::Guid
include Diaspora::Fields::Author
include Diaspora::Fields::Target
include Diaspora::Relayable
alias_attribute :parent, :target
class Generator < Diaspora::Federated::Generator
def self.federated_class
Like
end
......@@ -23,8 +32,6 @@ class Like < Federated::Relayable
participation.unparticipate! if participation.present?
end
xml_attr :positive
# NOTE API V1 to be extracted
acts_as_api
api_accessible :backbone do |t|
......@@ -33,10 +40,4 @@ class Like < Federated::Relayable
t.add :author
t.add :created_at
end
def notification_type(user, person)
#TODO(dan) need to have a notification for likes on comments, until then, return nil
return nil if self.target_type == "Comment"
Notifications::Liked if self.target.author == user.person && user.person != person
end
end
......@@ -5,9 +5,6 @@ class Location < ActiveRecord::Base
attr_accessor :coordinates
include Diaspora::Federated::Base
xml_attr :address
xml_attr :lat
xml_attr :lng
belongs_to :status_message
......
......@@ -5,21 +5,12 @@
class Mention < ActiveRecord::Base
belongs_to :post
belongs_to :person
validates :post, :presence => true
validates :person, :presence => true
validates :post, presence: true
validates :person, presence: true
after_destroy :delete_notification
def notify_recipient
logger.info "event=mention_sent id=#{id} to=#{person.diaspora_handle} from=#{post.author.diaspora_handle}"
Notification.notify(person.owner, self, post.author) unless person.remote?
end
def notification_type(*args)
Notifications::Mentioned
end
def delete_notification
Notification.where(:target_type => self.class.name, :target_id => self.id).destroy_all
Notification.where(target_type: self.class.name, target_id: id).destroy_all
end
end
class NotVisibleError < RuntimeError; end
class Message < ActiveRecord::Base
include Diaspora::Federated::Base
include Diaspora::Guid
include Diaspora::Relayable
include Diaspora::Fields::Guid
include Diaspora::Fields::Author
xml_attr :text
xml_attr :created_at
xml_reader :diaspora_handle
xml_reader :conversation_guid
belongs_to :author, :class_name => 'Person'
belongs_to :conversation, :touch => true
belongs_to :conversation, touch: true
delegate :name, to: :author, prefix: true
validates :text, :presence => true
validate :participant_of_parent_conversation
after_create do # don't use 'after_commit' here since there is a call to 'save!'
# inside, which would cause an infinite recursion
#sign comment as commenter
self.author_signature = self.sign_with_key(self.author.owner.encryption_key) if self.author.owner
self.save!
self
end
def diaspora_handle
self.author.diaspora_handle
end
def diaspora_handle= nh
self.author = Person.find_or_fetch_by_identifier(nh)
end
def conversation_guid
self.conversation.guid
end
# TODO: can be removed when messages are not relayed anymore
alias_attribute :parent, :conversation
def conversation_guid= guid
if cnv = Conversation.find_by_guid(guid)
self.conversation_id = cnv.id
end
end
def parent_class
Conversation
end
def parent
self.conversation
end
validates :conversation, presence: true
validates :text, presence: true
validate :participant_of_parent_conversation
def parent= parent
self.conversation = parent
def conversation_guid=(guid)
self.conversation_id = Conversation.where(guid: guid).ids.first
end
def increase_unread(user)
......@@ -62,17 +25,23 @@ class Message < ActiveRecord::Base
end
end
def notification_type(user, person)
Notifications::PrivateMessage unless user.person == person
end
def message
@message ||= Diaspora::MessageRenderer.new text
end
# @return [Array<Person>]
def subscribers
if author.local?
conversation.participants
else # for relaying, TODO: can be removed when messages are not relayed anymore
conversation.participants.remote
end
end