Merge branch 'next-minor' into develop
This commit is contained in:
commit
0029f2c1da
33 changed files with 490 additions and 76 deletions
|
|
@ -21,6 +21,7 @@
|
|||
* Enable frozen string literals [#7595](https://github.com/diaspora/diaspora/pull/7595)
|
||||
* Remove `rails_admin_histories` table [#7597](https://github.com/diaspora/diaspora/pull/7597)
|
||||
* Optimize memory usage on profile export [#7627](https://github.com/diaspora/diaspora/pull/7627)
|
||||
* Limit the number of parallel exports [#7629](https://github.com/diaspora/diaspora/pull/7629)
|
||||
|
||||
## Bug fixes
|
||||
* Fix displaying polls with long answers [#7579](https://github.com/diaspora/diaspora/pull/7579)
|
||||
|
|
@ -32,12 +33,16 @@
|
|||
* Fix invalid data in the database for user data export [#7614](https://github.com/diaspora/diaspora/pull/7614)
|
||||
* Fix local migration run without old private key [#7558](https://github.com/diaspora/diaspora/pull/7558)
|
||||
* Fix export not downloadable because the filename was resetted on access [#7622](https://github.com/diaspora/diaspora/pull/7622)
|
||||
* Delete invalid oEmbed caches with binary titles [#7620](https://github.com/diaspora/diaspora/pull/7620)
|
||||
|
||||
## Features
|
||||
* Ask for confirmation when leaving a submittable comment field [#7530](https://github.com/diaspora/diaspora/pull/7530)
|
||||
* Show users vote in polls [#7550](https://github.com/diaspora/diaspora/pull/7550)
|
||||
* Add explanation of ignore function to in-app help section [#7585](https://github.com/diaspora/diaspora/pull/7585)
|
||||
* Add camo information to NodeInfo [#7617](https://github.com/diaspora/diaspora/pull/7617)
|
||||
* Add support for `diaspora://` links [#7625](https://github.com/diaspora/diaspora/pull/7625)
|
||||
* Add support to relay likes for comments [#7625](https://github.com/diaspora/diaspora/pull/7625)
|
||||
* Implement RFC 7033 WebFinger [#7625](https://github.com/diaspora/diaspora/pull/7625)
|
||||
|
||||
# 0.7.0.1
|
||||
|
||||
|
|
|
|||
6
Gemfile
6
Gemfile
|
|
@ -15,8 +15,8 @@ gem "unicorn-worker-killer", "0.4.4"
|
|||
|
||||
# Federation
|
||||
|
||||
gem "diaspora_federation-json_schema", "0.2.1"
|
||||
gem "diaspora_federation-rails", "0.2.1"
|
||||
gem "diaspora_federation-json_schema", "0.2.2"
|
||||
gem "diaspora_federation-rails", "0.2.2"
|
||||
|
||||
# API and JSON
|
||||
|
||||
|
|
@ -292,7 +292,7 @@ group :test do
|
|||
gem "timecop", "0.9.1"
|
||||
gem "webmock", "3.0.1", require: false
|
||||
|
||||
gem "diaspora_federation-test", "0.2.1"
|
||||
gem "diaspora_federation-test", "0.2.2"
|
||||
|
||||
# Coverage
|
||||
gem "coveralls", "0.8.21", require: false
|
||||
|
|
|
|||
24
Gemfile.lock
24
Gemfile.lock
|
|
@ -166,20 +166,20 @@ GEM
|
|||
devise
|
||||
rails (>= 3.0.4)
|
||||
diaspora-prosody-config (0.0.7)
|
||||
diaspora_federation (0.2.1)
|
||||
faraday (>= 0.9.0, < 0.13.0)
|
||||
diaspora_federation (0.2.2)
|
||||
faraday (>= 0.9.0, < 0.14.0)
|
||||
faraday_middleware (>= 0.10.0, < 0.13.0)
|
||||
nokogiri (~> 1.6, >= 1.6.8)
|
||||
typhoeus (~> 1.0)
|
||||
valid (~> 1.0)
|
||||
diaspora_federation-json_schema (0.2.1)
|
||||
diaspora_federation-rails (0.2.1)
|
||||
diaspora_federation-json_schema (0.2.2)
|
||||
diaspora_federation-rails (0.2.2)
|
||||
actionpack (>= 4.2, < 6)
|
||||
diaspora_federation (= 0.2.1)
|
||||
diaspora_federation-test (0.2.1)
|
||||
diaspora_federation (= 0.2.1)
|
||||
fabrication (~> 2.16.0)
|
||||
uuid (~> 2.3.8)
|
||||
diaspora_federation (= 0.2.2)
|
||||
diaspora_federation-test (0.2.2)
|
||||
diaspora_federation (= 0.2.2)
|
||||
fabrication (~> 2.16)
|
||||
uuid (~> 2.3, >= 2.3.8)
|
||||
diff-lcs (1.3)
|
||||
docile (1.1.5)
|
||||
domain_name (0.5.20170404)
|
||||
|
|
@ -783,9 +783,9 @@ DEPENDENCIES
|
|||
devise (= 4.3.0)
|
||||
devise_lastseenable (= 0.0.6)
|
||||
diaspora-prosody-config (= 0.0.7)
|
||||
diaspora_federation-json_schema (= 0.2.1)
|
||||
diaspora_federation-rails (= 0.2.1)
|
||||
diaspora_federation-test (= 0.2.1)
|
||||
diaspora_federation-json_schema (= 0.2.2)
|
||||
diaspora_federation-rails (= 0.2.2)
|
||||
diaspora_federation-test (= 0.2.2)
|
||||
entypo-rails (= 3.0.0)
|
||||
eye (= 0.9.2)
|
||||
factory_girl_rails (= 4.8.0)
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ class PeopleController < ApplicationController
|
|||
end
|
||||
|
||||
def diaspora_id?(query)
|
||||
!(query.nil? || query.lstrip.empty?) && Validation::Rule::DiasporaId.new.valid_value?(query)
|
||||
!(query.nil? || query.lstrip.empty?) && Validation::Rule::DiasporaId.new.valid_value?(query.downcase).present?
|
||||
end
|
||||
|
||||
# view this profile on the home pod, if you don't want to sign in...
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ class Comment < ApplicationRecord
|
|||
include Diaspora::Taggable
|
||||
include Diaspora::Likeable
|
||||
include Diaspora::MentionsContainer
|
||||
include Reference::Source
|
||||
|
||||
acts_as_taggable_on :tags
|
||||
extract_tags_from :text
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ class Message < ApplicationRecord
|
|||
include Diaspora::Fields::Guid
|
||||
include Diaspora::Fields::Author
|
||||
|
||||
include Reference::Source
|
||||
|
||||
belongs_to :conversation, touch: true
|
||||
|
||||
delegate :name, to: :author, prefix: true
|
||||
|
|
|
|||
32
app/models/reference.rb
Normal file
32
app/models/reference.rb
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Reference < ApplicationRecord
|
||||
belongs_to :source, polymorphic: true
|
||||
belongs_to :target, polymorphic: true
|
||||
validates :target_id, uniqueness: {scope: %i[target_type source_id source_type]}
|
||||
|
||||
module Source
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
after_create :create_references
|
||||
has_many :references, as: :source, dependent: :destroy
|
||||
end
|
||||
|
||||
def create_references
|
||||
text&.scan(DiasporaFederation::Federation::DiasporaUrlParser::DIASPORA_URL_REGEX)&.each do |author, type, guid|
|
||||
class_name = DiasporaFederation::Entity.entity_class(type).to_s.rpartition("::").last
|
||||
entity = Diaspora::Federation::Mappings.model_class_for(class_name).find_by(guid: guid)
|
||||
references.find_or_create_by(target: entity) if entity.diaspora_handle == author
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Target
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_many :referenced_by, as: :target, class_name: "Reference", dependent: :destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -7,6 +7,9 @@
|
|||
class StatusMessage < Post
|
||||
include Diaspora::Taggable
|
||||
|
||||
include Reference::Source
|
||||
include Reference::Target
|
||||
|
||||
include PeopleHelper
|
||||
|
||||
acts_as_taggable_on :tags
|
||||
|
|
|
|||
|
|
@ -4,12 +4,25 @@
|
|||
# licensed under the Affero General Public License version 3 or later. See
|
||||
# the COPYRIGHT file.
|
||||
|
||||
|
||||
module Workers
|
||||
class ExportUser < Base
|
||||
sidekiq_options queue: :low
|
||||
|
||||
include Diaspora::Logging
|
||||
|
||||
def perform(user_id)
|
||||
if currently_running_exports >= AppConfig.settings.export_concurrency.to_i
|
||||
logger.info "Already the maximum number of parallel user exports running, " \
|
||||
"scheduling export for User:#{user_id} in 5 minutes."
|
||||
self.class.perform_in(5.minutes + rand(30), user_id)
|
||||
else
|
||||
export_user(user_id)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def export_user(user_id)
|
||||
@user = User.find(user_id)
|
||||
@user.perform_export!
|
||||
|
||||
|
|
@ -19,5 +32,13 @@ module Workers
|
|||
ExportMailer.export_failure_for(@user).deliver_now
|
||||
end
|
||||
end
|
||||
|
||||
def currently_running_exports
|
||||
return 0 if AppConfig.environment.single_process_mode?
|
||||
Sidekiq::Workers.new.count do |process_id, thread_id, work|
|
||||
!(Process.pid.to_s == process_id.split(":")[1] && Thread.current.object_id.to_s(36) == thread_id) &&
|
||||
work["payload"]["class"] == self.class.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ defaults:
|
|||
suggest_email:
|
||||
typhoeus_verbose: false
|
||||
typhoeus_concurrency: 20
|
||||
export_concurrency: 1
|
||||
username_blacklist:
|
||||
- 'admin'
|
||||
- 'administrator'
|
||||
|
|
|
|||
|
|
@ -455,6 +455,11 @@ configuration: ## Section
|
|||
## of your Sidekiq workers.
|
||||
#typhoeus_concurrency: 20
|
||||
|
||||
## Maximum number of parallel user data export jobs (default=1)
|
||||
## Be careful, exports of big/old profiles can use a lot of memory, running
|
||||
## many of them in parallel can be a problem for small servers.
|
||||
#export_concurrency: 1
|
||||
|
||||
## Captcha settings
|
||||
captcha: ## Section
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ DiasporaFederation.configure do |config|
|
|||
|
||||
config.certificate_authorities = AppConfig.environment.certificate_authorities.get
|
||||
|
||||
config.webfinger_http_fallback = Rails.env == "development"
|
||||
|
||||
config.http_concurrency = AppConfig.settings.typhoeus_concurrency.to_i
|
||||
config.http_verbose = AppConfig.settings.typhoeus_verbose?
|
||||
|
||||
|
|
|
|||
12
db/migrate/20170917163640_cleanup_invalid_o_embed_caches.rb
Normal file
12
db/migrate/20170917163640_cleanup_invalid_o_embed_caches.rb
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
class CleanupInvalidOEmbedCaches < ActiveRecord::Migration[5.1]
|
||||
class OEmbedCache < ApplicationRecord
|
||||
end
|
||||
class Post < ApplicationRecord
|
||||
end
|
||||
|
||||
def up
|
||||
ids = OEmbedCache.where("data LIKE '%!binary%'").ids
|
||||
Post.where(o_embed_cache_id: ids).update_all(o_embed_cache_id: nil) # rubocop:disable Rails/SkipsModelValidations
|
||||
OEmbedCache.where(id: ids).delete_all
|
||||
end
|
||||
end
|
||||
16
db/migrate/20170920214158_create_references_table.rb
Normal file
16
db/migrate/20170920214158_create_references_table.rb
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateReferencesTable < ActiveRecord::Migration[5.1]
|
||||
def change
|
||||
create_table :references do |t|
|
||||
t.integer :source_id, null: false
|
||||
t.string :source_type, limit: 60, null: false
|
||||
t.integer :target_id, null: false
|
||||
t.string :target_type, limit: 60, null: false
|
||||
end
|
||||
|
||||
add_index :references, %i[source_id source_type target_id target_type],
|
||||
name: :index_references_on_source_and_target, unique: true
|
||||
add_index :references, %i[source_id source_type], name: :index_references_on_source_id_and_source_type
|
||||
end
|
||||
end
|
||||
|
|
@ -176,13 +176,11 @@ module Diaspora
|
|||
|
||||
def self.reshare(reshare)
|
||||
DiasporaFederation::Entities::Reshare.new(
|
||||
root_author: reshare.root_diaspora_id,
|
||||
root_guid: reshare.root_guid,
|
||||
author: reshare.diaspora_handle,
|
||||
guid: reshare.guid,
|
||||
public: reshare.public,
|
||||
created_at: reshare.created_at,
|
||||
provider_display_name: reshare.provider_display_name
|
||||
root_author: reshare.root_diaspora_id,
|
||||
root_guid: reshare.root_guid,
|
||||
author: reshare.diaspora_handle,
|
||||
guid: reshare.guid,
|
||||
created_at: reshare.created_at
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -142,12 +142,10 @@ module Diaspora
|
|||
author = author_of(entity)
|
||||
ignore_existing_guid(Reshare, entity.guid, author) do
|
||||
Reshare.create!(
|
||||
author: author,
|
||||
guid: entity.guid,
|
||||
created_at: entity.created_at,
|
||||
provider_display_name: entity.provider_display_name,
|
||||
public: entity.public,
|
||||
root_guid: entity.root_guid
|
||||
author: author,
|
||||
guid: entity.guid,
|
||||
created_at: entity.created_at,
|
||||
root_guid: entity.root_guid
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -160,10 +158,10 @@ module Diaspora
|
|||
when Person
|
||||
User.find(recipient_id).disconnected_by(object)
|
||||
when Diaspora::Relayable
|
||||
if object.parent.author.local?
|
||||
parent_author = object.parent.author.owner
|
||||
if object.root.author.local?
|
||||
root_author = object.root.author.owner
|
||||
retraction = Retraction.for(object)
|
||||
retraction.defer_dispatch(parent_author, false)
|
||||
retraction.defer_dispatch(root_author, false)
|
||||
retraction.perform
|
||||
else
|
||||
object.destroy!
|
||||
|
|
@ -259,7 +257,7 @@ module Diaspora
|
|||
yield.tap do |relayable|
|
||||
retract_if_author_ignored(relayable)
|
||||
|
||||
relayable.signature = build_signature(klass, entity) if relayable.parent.author.local?
|
||||
relayable.signature = build_signature(klass, entity) if relayable.root.author.local?
|
||||
relayable.save!
|
||||
end
|
||||
end
|
||||
|
|
@ -274,18 +272,18 @@ module Diaspora
|
|||
end
|
||||
|
||||
private_class_method def self.retract_if_author_ignored(relayable)
|
||||
parent_author = relayable.parent.author.owner
|
||||
return unless parent_author && parent_author.ignored_people.include?(relayable.author)
|
||||
root_author = relayable.root.author.owner
|
||||
return unless root_author && root_author.ignored_people.include?(relayable.author)
|
||||
|
||||
retraction = Retraction.for(relayable)
|
||||
Diaspora::Federation::Dispatcher.build(parent_author, retraction, subscribers: [relayable.author]).dispatch
|
||||
Diaspora::Federation::Dispatcher.build(root_author, retraction, subscribers: [relayable.author]).dispatch
|
||||
|
||||
raise Diaspora::Federation::AuthorIgnored
|
||||
end
|
||||
|
||||
private_class_method def self.relay_relayable(relayable)
|
||||
parent_author = relayable.parent.author.owner
|
||||
Diaspora::Federation::Dispatcher.defer_dispatch(parent_author, relayable) if parent_author
|
||||
root_author = relayable.root.author.owner
|
||||
Diaspora::Federation::Dispatcher.defer_dispatch(root_author, relayable) if root_author
|
||||
end
|
||||
|
||||
# check if the object already exists, otherwise save it.
|
||||
|
|
|
|||
|
|
@ -95,6 +95,12 @@ module Diaspora
|
|||
def normalize
|
||||
@message = self.class.normalize(@message)
|
||||
end
|
||||
|
||||
def diaspora_links
|
||||
@message = @message.gsub(DiasporaFederation::Federation::DiasporaUrlParser::DIASPORA_URL_REGEX) {|match_str|
|
||||
Regexp.last_match(2) == "post" ? AppConfig.url_to("/posts/#{Regexp.last_match(3)}") : match_str
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
DEFAULTS = {mentioned_people: [],
|
||||
|
|
@ -158,6 +164,7 @@ module Diaspora
|
|||
def plain_text opts={}
|
||||
process(opts) {
|
||||
make_mentions_plain_text
|
||||
diaspora_links
|
||||
squish
|
||||
append_and_truncate
|
||||
}
|
||||
|
|
@ -167,6 +174,7 @@ module Diaspora
|
|||
def plain_text_without_markdown opts={}
|
||||
process(opts) {
|
||||
make_mentions_plain_text
|
||||
diaspora_links
|
||||
strip_markdown
|
||||
squish
|
||||
append_and_truncate
|
||||
|
|
@ -177,6 +185,7 @@ module Diaspora
|
|||
def plain_text_for_json opts={}
|
||||
process(opts) {
|
||||
normalize
|
||||
diaspora_links
|
||||
camo_urls if AppConfig.privacy.camo.proxy_markdown_images?
|
||||
}
|
||||
end
|
||||
|
|
@ -186,6 +195,7 @@ module Diaspora
|
|||
process(opts) {
|
||||
escape
|
||||
normalize
|
||||
diaspora_links
|
||||
render_mentions
|
||||
render_tags
|
||||
squish
|
||||
|
|
@ -198,6 +208,7 @@ module Diaspora
|
|||
process(opts) {
|
||||
process_newlines
|
||||
normalize
|
||||
diaspora_links
|
||||
camo_urls if AppConfig.privacy.camo.proxy_markdown_images?
|
||||
markdownify
|
||||
render_mentions
|
||||
|
|
|
|||
|
|
@ -17,9 +17,15 @@ module Diaspora
|
|||
end
|
||||
end
|
||||
|
||||
def root
|
||||
@root ||= parent
|
||||
@root = @root.parent while @root.is_a?(Relayable)
|
||||
@root
|
||||
end
|
||||
|
||||
def author_is_not_ignored
|
||||
unless new_record? && parent.present? && parent.author.local? &&
|
||||
parent.author.owner.ignored_people.include?(author)
|
||||
unless new_record? && root.present? && root.author.local? &&
|
||||
root.author.owner.ignored_people.include?(author)
|
||||
return
|
||||
end
|
||||
|
||||
|
|
@ -28,19 +34,19 @@ module Diaspora
|
|||
|
||||
# @return [Array<Person>]
|
||||
def subscribers
|
||||
if parent.author.local?
|
||||
if root.author.local?
|
||||
if author.local?
|
||||
parent.subscribers
|
||||
root.subscribers
|
||||
else
|
||||
parent.subscribers.select(&:remote?).reject {|person| person.pod_id == author.pod_id }
|
||||
root.subscribers.select(&:remote?).reject {|person| person.pod_id == author.pod_id }
|
||||
end
|
||||
else
|
||||
[parent.author, author]
|
||||
[root.author, author]
|
||||
end
|
||||
end
|
||||
|
||||
def sender_for_dispatch
|
||||
parent.author.owner if parent.author.local?
|
||||
root.author.owner if root.author.local?
|
||||
end
|
||||
|
||||
# @abstract
|
||||
|
|
|
|||
|
|
@ -81,7 +81,8 @@
|
|||
{
|
||||
"oneOf": [
|
||||
{ "$ref": "#/definitions/remote_subscription/public" },
|
||||
{ "$ref": "#/definitions/remote_subscription/private" }
|
||||
{ "$ref": "#/definitions/remote_subscription/private" },
|
||||
{ "$ref": "#/definitions/remote_subscription/reshare" }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -223,7 +224,7 @@
|
|||
}
|
||||
},
|
||||
"required": [
|
||||
"entity_data"
|
||||
"entity_data", "subscribed_pods_uris"
|
||||
]
|
||||
},
|
||||
|
||||
|
|
@ -248,7 +249,29 @@
|
|||
"public"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"entity_data", "subscribed_users_ids"
|
||||
]
|
||||
},
|
||||
|
||||
"reshare": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entity_type": {
|
||||
"type": "string",
|
||||
"pattern": "^reshare$"
|
||||
},
|
||||
"subscribed_pods_uris": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"entity_type", "subscribed_pods_uris"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,6 +258,11 @@ FactoryGirl.define do
|
|||
end
|
||||
end
|
||||
|
||||
factory :reference do
|
||||
association :source, factory: :status_message
|
||||
association :target, factory: :status_message
|
||||
end
|
||||
|
||||
factory(:notification, class: Notifications::AlsoCommented) do
|
||||
association :recipient, :factory => :user
|
||||
association :target, :factory => :comment
|
||||
|
|
|
|||
|
|
@ -428,7 +428,6 @@ describe "diaspora federation callbacks" do
|
|||
|
||||
expect(entity.guid).to eq(post.guid)
|
||||
expect(entity.author).to eq(alice.diaspora_handle)
|
||||
expect(entity.public).to be_truthy
|
||||
end
|
||||
|
||||
it "does not fetch a private post" do
|
||||
|
|
|
|||
|
|
@ -210,7 +210,6 @@ describe Diaspora::Exporter do
|
|||
"author": user.diaspora_handle,
|
||||
"guid": reshare.guid,
|
||||
"created_at": reshare.created_at.iso8601,
|
||||
"public": true,
|
||||
"root_author": reshare.root_author.diaspora_handle,
|
||||
"root_guid": reshare.root_guid
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,9 +212,7 @@ describe Diaspora::Federation::Entities do
|
|||
expect(federation_entity.guid).to eq(diaspora_entity.guid)
|
||||
expect(federation_entity.root_author).to eq(diaspora_entity.root.author.diaspora_handle)
|
||||
expect(federation_entity.root_guid).to eq(diaspora_entity.root.guid)
|
||||
expect(federation_entity.public).to be_truthy
|
||||
expect(federation_entity.created_at).to eq(diaspora_entity.created_at)
|
||||
expect(federation_entity.provider_display_name).to eq(diaspora_entity.provider_display_name)
|
||||
end
|
||||
|
||||
context "Retraction" do
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ describe Diaspora::Federation::Receive do
|
|||
|
||||
let(:entity) { comment_entity }
|
||||
it_behaves_like "it ignores existing object received twice", Comment
|
||||
it_behaves_like "it rejects if the parent author ignores the author", Comment
|
||||
it_behaves_like "it rejects if the root author ignores the author", Comment
|
||||
it_behaves_like "it relays relayables", Comment
|
||||
end
|
||||
|
||||
|
|
@ -241,8 +241,49 @@ describe Diaspora::Federation::Receive do
|
|||
|
||||
let(:entity) { like_entity }
|
||||
it_behaves_like "it ignores existing object received twice", Like
|
||||
it_behaves_like "it rejects if the parent author ignores the author", Like
|
||||
it_behaves_like "it rejects if the root author ignores the author", Like
|
||||
it_behaves_like "it relays relayables", Like
|
||||
|
||||
context "like for a comment" do
|
||||
let(:comment) { FactoryGirl.create(:comment, post: post) }
|
||||
let(:like_entity) {
|
||||
build_relayable_federation_entity(
|
||||
:like,
|
||||
{
|
||||
author: sender.diaspora_handle,
|
||||
parent_guid: comment.guid,
|
||||
parent_type: "Comment",
|
||||
author_signature: "aa"
|
||||
},
|
||||
"new_property" => "data"
|
||||
)
|
||||
}
|
||||
|
||||
it "attaches the like to the comment" do
|
||||
Diaspora::Federation::Receive.perform(like_entity)
|
||||
|
||||
like = Like.find_by!(guid: like_entity.guid)
|
||||
|
||||
expect(comment.likes).to include(like)
|
||||
expect(like.target).to eq(comment)
|
||||
end
|
||||
|
||||
it "saves the signature data" do
|
||||
Diaspora::Federation::Receive.perform(like_entity)
|
||||
|
||||
like = Like.find_by!(guid: like_entity.guid)
|
||||
|
||||
expect(like.signature).not_to be_nil
|
||||
expect(like.signature.author_signature).to eq("aa")
|
||||
expect(like.signature.additional_data).to eq("new_property" => "data")
|
||||
expect(like.signature.order).to eq(like_entity.signature_order.map(&:to_s))
|
||||
end
|
||||
|
||||
let(:entity) { like_entity }
|
||||
it_behaves_like "it ignores existing object received twice", Like
|
||||
it_behaves_like "it rejects if the root author ignores the author", Like
|
||||
it_behaves_like "it relays relayables", Like
|
||||
end
|
||||
end
|
||||
|
||||
describe ".message" do
|
||||
|
|
@ -408,7 +449,7 @@ describe Diaspora::Federation::Receive do
|
|||
|
||||
let(:entity) { poll_participation_entity }
|
||||
it_behaves_like "it ignores existing object received twice", PollParticipation
|
||||
it_behaves_like "it rejects if the parent author ignores the author", PollParticipation
|
||||
it_behaves_like "it rejects if the root author ignores the author", PollParticipation
|
||||
it_behaves_like "it relays relayables", PollParticipation
|
||||
end
|
||||
|
||||
|
|
@ -584,17 +625,6 @@ describe Diaspora::Federation::Receive do
|
|||
|
||||
Diaspora::Federation::Receive.perform(status_message_entity)
|
||||
end
|
||||
|
||||
it "finds the correct author if the author is not lowercase" do
|
||||
status_message_entity = Fabricate(:status_message_entity, author: sender.diaspora_handle.upcase)
|
||||
|
||||
received = Diaspora::Federation::Receive.perform(status_message_entity)
|
||||
|
||||
status_message = StatusMessage.find_by!(guid: status_message_entity.guid)
|
||||
|
||||
expect(received).to eq(status_message)
|
||||
expect(status_message.author).to eq(sender)
|
||||
end
|
||||
end
|
||||
|
||||
context "with poll" do
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe Diaspora::MessageRenderer do
|
||||
MESSAGE_NORMALIZTIONS = {
|
||||
"\u202a#\u200eUSA\u202c" => "#USA",
|
||||
"ള്" => "ള്"
|
||||
}
|
||||
|
||||
def message(text, opts={})
|
||||
Diaspora::MessageRenderer.new(text, opts)
|
||||
end
|
||||
|
|
@ -100,6 +95,20 @@ describe Diaspora::MessageRenderer do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with diaspora:// links" do
|
||||
it "replaces diaspora:// links with pod-local links" do
|
||||
target = FactoryGirl.create(:status_message)
|
||||
expect(
|
||||
message("Have a look at diaspora://#{target.diaspora_handle}/post/#{target.guid}.").html
|
||||
).to match(/Have a look at #{AppConfig.url_to("/posts/#{target.guid}")}./)
|
||||
end
|
||||
|
||||
it "doesn't touch invalid diaspora:// links" do
|
||||
text = "You can create diaspora://author/type/guid links!"
|
||||
expect(message(text).html).to match(/#{text}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#markdownified" do
|
||||
|
|
@ -128,8 +137,11 @@ describe Diaspora::MessageRenderer do
|
|||
end
|
||||
|
||||
it "normalizes" do
|
||||
MESSAGE_NORMALIZTIONS.each do |input, output|
|
||||
expect(message(input).plain_text_for_json).to eq output
|
||||
{
|
||||
"\u202a#\u200eUSA\u202c" => "<p><a class=\"tag\" href=\"/tags/USA\">#USA</a></p>\n",
|
||||
"ള്" => "<p>ള്</p>\n"
|
||||
}.each do |input, output|
|
||||
expect(message(input).markdownified).to eq output
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -180,6 +192,25 @@ describe Diaspora::MessageRenderer do
|
|||
entities = '& ß ' ' "'
|
||||
expect(message(entities).markdownified).to eq "<p>#{entities}</p>\n"
|
||||
end
|
||||
|
||||
context "with diaspora:// links" do
|
||||
it "replaces diaspora:// links with pod-local links" do
|
||||
target1 = FactoryGirl.create(:status_message)
|
||||
target2 = FactoryGirl.create(:status_message)
|
||||
text = "Have a look at [this post](diaspora://#{target1.diaspora_handle}/post/#{target1.guid}) and " \
|
||||
"this one too diaspora://#{target2.diaspora_handle}/post/#{target2.guid}."
|
||||
|
||||
rendered = message(text).markdownified
|
||||
|
||||
expect(rendered).to match(%r{at <a href="#{AppConfig.url_to("/posts/#{target1.guid}")}">this post</a> and})
|
||||
expect(rendered).to match(/this one too #{AppConfig.url_to("/posts/#{target2.guid}")}./)
|
||||
end
|
||||
|
||||
it "doesn't touch invalid diaspora:// links" do
|
||||
text = "You can create diaspora://author/type/guid links!"
|
||||
expect(message(text).markdownified).to match(/#{text}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -210,6 +241,25 @@ describe Diaspora::MessageRenderer do
|
|||
expect(msg.plain_text_without_markdown).to eq "@#{alice.diaspora_handle} is cool"
|
||||
end
|
||||
end
|
||||
|
||||
context "with diaspora:// links" do
|
||||
it "replaces diaspora:// links with pod-local links" do
|
||||
target1 = FactoryGirl.create(:status_message)
|
||||
target2 = FactoryGirl.create(:status_message)
|
||||
text = "Have a look at [this post](diaspora://#{target1.diaspora_handle}/post/#{target1.guid}) and " \
|
||||
"this one too diaspora://#{target2.diaspora_handle}/post/#{target2.guid}."
|
||||
|
||||
rendered = message(text).plain_text_without_markdown
|
||||
|
||||
expect(rendered).to match(/look at this post \(#{AppConfig.url_to("/posts/#{target1.guid}")}\) and/)
|
||||
expect(rendered).to match(/this one too #{AppConfig.url_to("/posts/#{target2.guid}")}./)
|
||||
end
|
||||
|
||||
it "doesn't touch invalid diaspora:// links" do
|
||||
text = "You can create diaspora://author/type/guid links!"
|
||||
expect(message(text).plain_text_without_markdown).to match(/#{text}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#urls" do
|
||||
|
|
@ -241,9 +291,31 @@ describe Diaspora::MessageRenderer do
|
|||
|
||||
describe "#plain_text_for_json" do
|
||||
it "normalizes" do
|
||||
MESSAGE_NORMALIZTIONS.each do |input, output|
|
||||
{
|
||||
"\u202a#\u200eUSA\u202c" => "#USA",
|
||||
"ള്" => "ള്"
|
||||
}.each do |input, output|
|
||||
expect(message(input).plain_text_for_json).to eq output
|
||||
end
|
||||
end
|
||||
|
||||
context "with diaspora:// links" do
|
||||
it "replaces diaspora:// links with pod-local links" do
|
||||
target1 = FactoryGirl.create(:status_message)
|
||||
target2 = FactoryGirl.create(:status_message)
|
||||
text = "Have a look at [this post](diaspora://#{target1.diaspora_handle}/post/#{target1.guid}) and " \
|
||||
"this one too diaspora://#{target2.diaspora_handle}/post/#{target2.guid}."
|
||||
|
||||
rendered = message(text).plain_text_for_json
|
||||
|
||||
expect(rendered).to match(/look at \[this post\]\(#{AppConfig.url_to("/posts/#{target1.guid}")}\) and/)
|
||||
expect(rendered).to match(/this one too #{AppConfig.url_to("/posts/#{target2.guid}")}./)
|
||||
end
|
||||
|
||||
it "doesn't touch invalid diaspora:// links" do
|
||||
text = "You can create diaspora://author/type/guid links!"
|
||||
expect(message(text).plain_text_for_json).to match(/#{text}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ describe Comment, type: :model do
|
|||
let(:comment_alice) { alice.comment!(status_bob, "why so formal?") }
|
||||
|
||||
it_behaves_like "it is mentions container"
|
||||
it_behaves_like "a reference source"
|
||||
|
||||
describe "#destroy" do
|
||||
it "should delete a participation" do
|
||||
|
|
|
|||
|
|
@ -62,4 +62,17 @@ describe Like, type: :model do
|
|||
let(:remote_object_on_local_parent) { FactoryGirl.create(:like, target: local_parent, author: remote_raphael) }
|
||||
let(:relayable) { Like::Generator.new(alice, status).build }
|
||||
end
|
||||
|
||||
context "like for a comment" do
|
||||
it_behaves_like "it is relayable" do
|
||||
let(:local_parent) { local_luke.post(:status_message, text: "hi", to: local_luke.aspects.first) }
|
||||
let(:remote_parent) { FactoryGirl.create(:status_message, author: remote_raphael) }
|
||||
let(:comment_on_local_parent) { FactoryGirl.create(:comment, post: local_parent) }
|
||||
let(:comment_on_remote_parent) { FactoryGirl.create(:comment, post: remote_parent) }
|
||||
let(:object_on_local_parent) { local_luke.like!(comment_on_local_parent) }
|
||||
let(:object_on_remote_parent) { local_luke.like!(comment_on_remote_parent) }
|
||||
let(:remote_object_on_local_parent) { FactoryGirl.create(:like, target: local_parent, author: remote_raphael) }
|
||||
let(:relayable) { Like::Generator.new(alice, status).build }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -52,4 +52,6 @@ describe Message, type: :model do
|
|||
expect(conf.reload.unread).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like "a reference source"
|
||||
end
|
||||
|
|
|
|||
22
spec/models/reference_spec.rb
Normal file
22
spec/models/reference_spec.rb
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
describe Reference, type: :model do
|
||||
context "validation" do
|
||||
it "validates a valid reference" do
|
||||
expect(FactoryGirl.build(:reference)).to be_valid
|
||||
end
|
||||
|
||||
it "requires a source" do
|
||||
expect(FactoryGirl.build(:reference, source: nil)).not_to be_valid
|
||||
end
|
||||
|
||||
it "requires a target" do
|
||||
expect(FactoryGirl.build(:reference, target: nil)).not_to be_valid
|
||||
end
|
||||
|
||||
it "disallows to link the same target twice from one source" do
|
||||
reference = FactoryGirl.create(:reference)
|
||||
expect(FactoryGirl.build(:reference, source: reference.source, target: reference.target)).not_to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -147,6 +147,9 @@ describe StatusMessage, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
it_behaves_like "a reference source"
|
||||
it_behaves_like "a reference target"
|
||||
|
||||
describe "#nsfw" do
|
||||
it "returns MatchObject (true) if the post contains #nsfw (however capitalised)" do
|
||||
status = FactoryGirl.build(:status_message, text: "This message is #nSFw")
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ shared_examples_for "it ignores existing object received twice" do |klass|
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples_for "it rejects if the parent author ignores the author" do |klass|
|
||||
shared_examples_for "it rejects if the root author ignores the author" do |klass|
|
||||
it "saves the relayable if the author is not ignored" do
|
||||
Diaspora::Federation::Receive.perform(entity)
|
||||
|
||||
|
|
|
|||
70
spec/shared_behaviors/references.rb
Normal file
70
spec/shared_behaviors/references.rb
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
shared_examples_for "a reference source" do
|
||||
let!(:source) { FactoryGirl.create(described_class.to_s.underscore.to_sym) }
|
||||
let!(:reference) { FactoryGirl.create(:reference, source: source) }
|
||||
|
||||
describe "references" do
|
||||
it "returns the references" do
|
||||
expect(source.references).to match_array([reference])
|
||||
end
|
||||
|
||||
it "destroys the reference when the source is destroyed" do
|
||||
source.destroy
|
||||
expect(Reference.where(id: reference.id)).not_to exist
|
||||
end
|
||||
end
|
||||
|
||||
describe "#create_references" do
|
||||
it "creates a reference for every referenced post after create" do
|
||||
target1 = FactoryGirl.create(:status_message)
|
||||
target2 = FactoryGirl.create(:status_message)
|
||||
text = "Have a look at [this post](diaspora://#{target1.diaspora_handle}/post/#{target1.guid}) and " \
|
||||
"this one too diaspora://#{target2.diaspora_handle}/post/#{target2.guid}."
|
||||
|
||||
post = FactoryGirl.build(described_class.to_s.underscore.to_sym, text: text)
|
||||
post.save
|
||||
|
||||
expect(post.references.map(&:target).map(&:guid)).to match_array([target1, target2].map(&:guid))
|
||||
end
|
||||
|
||||
it "only creates one reference, even when it is referenced twice" do
|
||||
target = FactoryGirl.create(:status_message)
|
||||
text = "Have a look at [this post](diaspora://#{target.diaspora_handle}/post/#{target.guid}) and " \
|
||||
"this one too diaspora://#{target.diaspora_handle}/post/#{target.guid}."
|
||||
|
||||
post = FactoryGirl.build(described_class.to_s.underscore.to_sym, text: text)
|
||||
post.save
|
||||
|
||||
expect(post.references.map(&:target).map(&:guid)).to match_array([target.guid])
|
||||
end
|
||||
|
||||
it "only creates references, when the author of the known entity matches" do
|
||||
target1 = FactoryGirl.create(:status_message)
|
||||
target2 = FactoryGirl.create(:status_message)
|
||||
text = "Have a look at [this post](diaspora://#{target1.diaspora_handle}/post/#{target1.guid}) and " \
|
||||
"this one too diaspora://#{target1.diaspora_handle}/post/#{target2.guid}."
|
||||
|
||||
post = FactoryGirl.build(described_class.to_s.underscore.to_sym, text: text)
|
||||
post.save
|
||||
|
||||
expect(post.references.map(&:target).map(&:guid)).to match_array([target1.guid])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples_for "a reference target" do
|
||||
let!(:target) { FactoryGirl.create(described_class.to_s.underscore.to_sym) }
|
||||
let!(:reference) { FactoryGirl.create(:reference, target: target) }
|
||||
|
||||
describe "referenced_by" do
|
||||
it "returns the references where the target is referenced" do
|
||||
expect(target.referenced_by).to match_array([reference])
|
||||
end
|
||||
|
||||
it "destroys the reference when the target is destroyed" do
|
||||
target.destroy
|
||||
expect(Reference.where(id: reference.id)).not_to exist
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -22,4 +22,68 @@ describe Workers::ExportUser do
|
|||
expect(ExportMailer).to receive(:export_failure_for).with(alice).and_call_original
|
||||
Workers::ExportUser.new.perform(alice.id)
|
||||
end
|
||||
|
||||
context "concurrency" do
|
||||
before do
|
||||
AppConfig.environment.single_process_mode = false
|
||||
AppConfig.settings.export_concurrency = 1
|
||||
end
|
||||
|
||||
after :all do
|
||||
AppConfig.environment.single_process_mode = true
|
||||
end
|
||||
|
||||
let(:pid) { "#{Socket.gethostname}:#{Process.pid}:#{SecureRandom.hex(6)}" }
|
||||
|
||||
it "schedules a job for later when already another parallel export job is running" do
|
||||
expect(Sidekiq::Workers).to receive(:new).and_return(
|
||||
[[pid, SecureRandom.hex(4), {"payload" => {"class" => "Workers::ExportUser"}}]]
|
||||
)
|
||||
|
||||
expect(Workers::ExportUser).to receive(:perform_in).with(kind_of(Integer), alice.id)
|
||||
expect(alice).not_to receive(:perform_export!)
|
||||
|
||||
Workers::ExportUser.new.perform(alice.id)
|
||||
end
|
||||
|
||||
it "runs the export when the own running job" do
|
||||
expect(Sidekiq::Workers).to receive(:new).and_return(
|
||||
[[pid, Thread.current.object_id.to_s(36), {"payload" => {"class" => "Workers::ExportUser"}}]]
|
||||
)
|
||||
|
||||
expect(Workers::ExportUser).not_to receive(:perform_in).with(kind_of(Integer), alice.id)
|
||||
expect(alice).to receive(:perform_export!)
|
||||
|
||||
Workers::ExportUser.new.perform(alice.id)
|
||||
end
|
||||
|
||||
it "runs the export when no other job is running" do
|
||||
expect(Sidekiq::Workers).to receive(:new).and_return([])
|
||||
|
||||
expect(Workers::ExportUser).not_to receive(:perform_in).with(kind_of(Integer), alice.id)
|
||||
expect(alice).to receive(:perform_export!)
|
||||
|
||||
Workers::ExportUser.new.perform(alice.id)
|
||||
end
|
||||
|
||||
it "runs the export when some other job is running" do
|
||||
expect(Sidekiq::Workers).to receive(:new).and_return(
|
||||
[[pid, SecureRandom.hex(4), {"payload" => {"class" => "Workers::OtherJob"}}]]
|
||||
)
|
||||
|
||||
expect(Workers::ExportUser).not_to receive(:perform_in).with(kind_of(Integer), alice.id)
|
||||
expect(alice).to receive(:perform_export!)
|
||||
|
||||
Workers::ExportUser.new.perform(alice.id)
|
||||
end
|
||||
|
||||
it "runs the export when diaspora is in single process mode" do
|
||||
AppConfig.environment.single_process_mode = true
|
||||
expect(Sidekiq::Workers).not_to receive(:new)
|
||||
expect(Workers::ExportUser).not_to receive(:perform_in).with(kind_of(Integer), alice.id)
|
||||
expect(alice).to receive(:perform_export!)
|
||||
|
||||
Workers::ExportUser.new.perform(alice.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue