commit: fa33750105389110a3395ca19167f789d21a149e
parent: 3b0bc18db928c455186273d9b9aa5b96d91e035e
Author: Eugen Rochko <eugen@zeonfederated.com>
Date: Tue, 23 Feb 2016 19:17:37 +0100
Adding reblogs, favourites, improving atom generation
Diffstat:
19 files changed, 251 insertions(+), 79 deletions(-)
diff --git a/app/controllers/profile_controller.rb b/app/controllers/profile_controller.rb
@@ -1,4 +1,7 @@
class ProfileController < ApplicationController
def show
end
+
+ def entry
+ end
end
diff --git a/app/helpers/atom_helper.rb b/app/helpers/atom_helper.rb
@@ -93,6 +93,87 @@ module AtomHelper
xml['poco'].note account.note
end
+ def in_reply_to(xml, uri, url)
+ xml['thr'].send('in-reply-to', { ref: uri, href: url, type: 'text/html' })
+ end
+
+ def disambiguate_uri(target)
+ if target.local?
+ if target.object_type == :person
+ profile_url(name: target.username)
+ else
+ unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type)
+ end
+ else
+ target.uri
+ end
+ end
+
+ def disambiguate_url(target)
+ if target.local?
+ if target.object_type == :person
+ profile_url(name: target.username)
+ else
+ status_url(name: target.stream_entry.account.username, id: target.stream_entry.id)
+ end
+ else
+ target.url
+ end
+ end
+
+ def link_mention(xml, account)
+ xml.link(rel: 'mentioned', href: disambiguate_uri(account))
+ end
+
+ def include_author(xml, account)
+ object_type xml, :person
+ uri xml, profile_url(name: account.username)
+ name xml, account.username
+ summary xml, account.note
+ link_alternate xml, profile_url(name: account.username)
+ portable_contact xml, account
+ end
+
+ def include_entry(xml, stream_entry)
+ unique_id xml, stream_entry.created_at, stream_entry.activity_id, stream_entry.activity_type
+ published_at xml, stream_entry.activity.created_at
+ updated_at xml, stream_entry.activity.updated_at
+ title xml, stream_entry.title
+ content xml, stream_entry.content
+ verb xml, stream_entry.verb
+ link_self xml, atom_entry_url(id: stream_entry.id)
+ object_type xml, stream_entry.object_type
+
+ # Comments need thread element
+ if stream_entry.threaded?
+ in_reply_to xml, disambiguate_uri(stream_entry.thread), disambiguate_url(stream_entry.thread)
+ end
+
+ if stream_entry.targeted?
+ target(xml) do
+ object_type xml, stream_entry.target.object_type
+ simple_id xml, disambiguate_uri(stream_entry.target)
+ title xml, stream_entry.target.title
+ link_alternate xml, disambiguate_url(stream_entry.target)
+
+ # People have summary and portable contacts information
+ if stream_entry.target.object_type == :person
+ summary xml, stream_entry.target.content
+ portable_contact xml, stream_entry.target
+ end
+
+ # Statuses have content
+ if [:note, :comment].include? stream_entry.target.object_type
+ content xml, stream_entry.target.content
+ end
+ end
+ end
+
+ stream_entry.mentions.each do |mentioned|
+ link_mention xml, mentioned
+ end
+ end
+
private
def root_tag(xml, tag, &block)
diff --git a/app/models/account.rb b/app/models/account.rb
@@ -5,6 +5,7 @@ class Account < ActiveRecord::Base
# Timelines
has_many :stream_entries, inverse_of: :account
has_many :statuses, inverse_of: :account
+ has_many :favourites, inverse_of: :account
# Follow relations
has_many :active_relationships, class_name: 'Follow', foreign_key: 'account_id', dependent: :destroy
@@ -41,7 +42,7 @@ class Account < ActiveRecord::Base
self.username
end
- def summary
+ def content
self.note
end
diff --git a/app/models/favourite.rb b/app/models/favourite.rb
@@ -0,0 +1,38 @@
+class Favourite < ActiveRecord::Base
+ belongs_to :account, inverse_of: :favourites
+ belongs_to :status, inverse_of: :favourites
+
+ has_one :stream_entry, as: :activity
+
+ def verb
+ :favorite
+ end
+
+ def title
+ "#{self.account.acct} favourited a status by #{self.status.account.acct}"
+ end
+
+ def content
+ title
+ end
+
+ def object_type
+ target.object_type
+ end
+
+ def target
+ self.status
+ end
+
+ def mentions
+ []
+ end
+
+ def thread
+ target
+ end
+
+ after_create do
+ self.account.stream_entries.create!(activity: self)
+ end
+end
diff --git a/app/models/follow.rb b/app/models/follow.rb
@@ -2,20 +2,23 @@ class Follow < ActiveRecord::Base
belongs_to :account
belongs_to :target_account, class_name: 'Account'
+ has_one :stream_entry, as: :activity
+
validates :account, :target_account, presence: true
+ validates :account_id, uniqueness: { scope: :target_account_id }
def verb
:follow
end
- def object_type
- :person
- end
-
def target
self.target_account
end
+ def object_type
+ target.object_type
+ end
+
def content
"#{self.account.acct} started following #{self.target_account.acct}"
end
@@ -24,6 +27,10 @@ class Follow < ActiveRecord::Base
content
end
+ def mentions
+ []
+ end
+
after_create do
self.account.stream_entries.create!(activity: self)
end
diff --git a/app/models/status.rb b/app/models/status.rb
@@ -1,24 +1,56 @@
class Status < ActiveRecord::Base
belongs_to :account, inverse_of: :statuses
+ belongs_to :thread, foreign_key: 'in_reply_to_id', class_name: 'Status'
+ belongs_to :reblog, foreign_key: 'reblog_of_id', class_name: 'Status'
+
+ has_one :stream_entry, as: :activity
+ has_many :favourites, inverse_of: :status
+
validates :account, presence: true
+ validates :uri, uniqueness: true, unless: 'local?'
+
+ def local?
+ self.uri.nil?
+ end
+
+ def reblog?
+ !self.reblog_of_id.nil?
+ end
+
+ def reply?
+ !self.in_reply_to_id.nil?
+ end
def verb
- :post
+ reblog? ? :share : :post
end
def object_type
- :note
+ reply? ? :comment : :note
end
def content
- self.text
+ reblog? ? self.reblog.text : self.text
+ end
+
+ def target
+ self.reblog
end
def title
content.truncate(80, omission: "...")
end
+ def mentions
+ m = []
+
+ m << thread.account if reply?
+ m << reblog.account if reblog?
+
+ m
+ end
+
after_create do
self.account.stream_entries.create!(activity: self)
end
diff --git a/app/models/stream_entry.rb b/app/models/stream_entry.rb
@@ -5,7 +5,7 @@ class StreamEntry < ActiveRecord::Base
validates :account, :activity, presence: true
def object_type
- self.activity.object_type
+ targeted? ? :activity : self.activity.object_type
end
def verb
@@ -13,7 +13,7 @@ class StreamEntry < ActiveRecord::Base
end
def targeted?
- [:follow].include? self.verb
+ [:follow, :share, :favorite].include? verb
end
def target
@@ -27,4 +27,16 @@ class StreamEntry < ActiveRecord::Base
def content
self.activity.content
end
+
+ def threaded?
+ [:favorite, :comment].include? verb
+ end
+
+ def thread
+ self.activity.thread
+ end
+
+ def mentions
+ self.activity.mentions
+ end
end
diff --git a/app/services/follow_remote_account_service.rb b/app/services/follow_remote_account_service.rb
@@ -15,6 +15,7 @@ class FollowRemoteAccountService
account.remote_url = data.link('http://schemas.google.com/g/2010#updates-from').href
account.salmon_url = data.link('salmon').href
+ account.url = data.link('http://webfinger.net/rel/profile-page').href
account.public_key = magic_key_to_pem(data.link('magic-public-key').href)
account.private_key = nil
diff --git a/app/services/process_interaction_service.rb b/app/services/process_interaction_service.rb
@@ -3,10 +3,10 @@ class ProcessInteractionService
body = salmon.unpack(envelope)
xml = Nokogiri::XML(body)
- return if !involves_target_account(xml, target_account) || xml.at_xpath('//author/name').nil? || xml.at_xpath('//author/uri').nil?
+ return if !involves_target_account(xml, target_account) || xml.at_xpath('//xmlns:author/xmlns:name').nil? || xml.at_xpath('//xmlns:author/xmlns:uri').nil?
- username = xml.at_xpath('//author/name').content
- url = xml.at_xpath('//author/uri').content
+ username = xml.at_xpath('//xmlns:author/xmlns:name').content
+ url = xml.at_xpath('//xmlns:author/xmlns:uri').content
domain = Addressable::URI.parse(url).host
account = Account.find_by(username: username, domain: domain)
diff --git a/app/views/atom/entry.xml.ruby b/app/views/atom/entry.xml.ruby
@@ -1,37 +1,9 @@
Nokogiri::XML::Builder.new do |xml|
entry(xml, true) do
- unique_id xml, @entry.created_at, @entry.activity_id, @entry.activity_type
- published_at xml, @entry.activity.created_at
- updated_at xml, @entry.activity.updated_at
- title xml, @entry.title
- content xml, @entry.content
- verb xml, @entry.verb
-
author(xml) do
- object_type xml, :person
- uri xml, profile_url(name: @entry.account.username)
- name xml, @entry.account.username
- summary xml, @entry.account.note
- link_alternate xml, profile_url(name: @entry.account.username)
- portable_contact xml, @entry.account
- end
-
- if @entry.targeted?
- target(xml) do
- object_type xml, @entry.target.object_type
- simple_id xml, @entry.target.uri
- title xml, @entry.target.title
- summary xml, @entry.target.summary
- link_alternate xml, @entry.target.uri
-
- if @entry.target.object_type == :person
- portable_contact xml, @entry.target
- end
- end
- else
- object_type xml, @entry.object_type
+ include_author xml, @entry.account
end
- link_self xml, atom_entry_url(id: @entry.id)
+ include_entry xml, @entry
end
-end
+end.to_xml
diff --git a/app/views/atom/user_stream.xml.ruby b/app/views/atom/user_stream.xml.ruby
@@ -6,12 +6,7 @@ Nokogiri::XML::Builder.new do |xml|
updated_at xml, stream_updated_at
author(xml) do
- object_type xml, :person
- uri xml, profile_url(name: @account.username)
- name xml, @account.username
- summary xml, @account.note
- link_alternate xml, profile_url(name: @account.username)
- portable_contact xml, @account
+ include_author xml, @account
end
link_alternate xml, profile_url(name: @account.username)
@@ -21,29 +16,7 @@ Nokogiri::XML::Builder.new do |xml|
@account.stream_entries.order('id desc').each do |stream_entry|
entry(xml, false) do
- unique_id xml, stream_entry.created_at, stream_entry.activity_id, stream_entry.activity_type
- published_at xml, stream_entry.activity.created_at
- updated_at xml, stream_entry.activity.updated_at
- title xml, stream_entry.title
- content xml, stream_entry.content
- verb xml, stream_entry.verb
- link_self xml, atom_entry_url(id: stream_entry.id)
-
- if stream_entry.targeted?
- target(xml) do
- object_type xml, stream_entry.target.object_type
- simple_id xml, stream_entry.target.uri
- title xml, stream_entry.target.title
- summary xml, stream_entry.target.summary
- link_alternate xml, stream_entry.target.uri
-
- if stream_entry.target.object_type == :person
- portable_contact xml, stream_entry.target
- end
- end
- else
- object_type xml, stream_entry.object_type
- end
+ include_entry xml, stream_entry
end
end
end
diff --git a/config/routes.rb b/config/routes.rb
@@ -5,6 +5,7 @@ Rails.application.routes.draw do
get 'atom/entries/:id', to: 'atom#entry', as: :atom_entry
get 'atom/users/:id', to: 'atom#user_stream', as: :atom_user_stream
get 'users/:name', to: 'profile#show', as: :profile
+ get 'users/:name/:id', to: 'profile#entry', as: :status
mount Mastodon::API => '/api/'
diff --git a/db/migrate/20160223162837_add_metadata_to_statuses.rb b/db/migrate/20160223162837_add_metadata_to_statuses.rb
@@ -0,0 +1,6 @@
+class AddMetadataToStatuses < ActiveRecord::Migration
+ def change
+ add_column :statuses, :in_reply_to_id, :integer, null: true
+ add_column :statuses, :reblog_of_id, :integer, null: true
+ end
+end
diff --git a/db/migrate/20160223164502_make_uris_nullable_in_statuses.rb b/db/migrate/20160223164502_make_uris_nullable_in_statuses.rb
@@ -0,0 +1,5 @@
+class MakeUrisNullableInStatuses < ActiveRecord::Migration
+ def change
+ change_column :statuses, :uri, :string, null: true, default: nil
+ end
+end
diff --git a/db/migrate/20160223165723_add_url_to_statuses.rb b/db/migrate/20160223165723_add_url_to_statuses.rb
@@ -0,0 +1,5 @@
+class AddUrlToStatuses < ActiveRecord::Migration
+ def change
+ add_column :statuses, :url, :string, null: true, default: nil
+ end
+end
diff --git a/db/migrate/20160223165855_add_url_to_accounts.rb b/db/migrate/20160223165855_add_url_to_accounts.rb
@@ -0,0 +1,5 @@
+class AddUrlToAccounts < ActiveRecord::Migration
+ def change
+ add_column :accounts, :url, :string, null: true, default: nil
+ end
+end
diff --git a/db/migrate/20160223171800_create_favourites.rb b/db/migrate/20160223171800_create_favourites.rb
@@ -0,0 +1,12 @@
+class CreateFavourites < ActiveRecord::Migration
+ def change
+ create_table :favourites do |t|
+ t.integer :account_id, null: false
+ t.integer :status_id, null: false
+
+ t.timestamps null: false
+ end
+
+ add_index :favourites, [:account_id, :status_id], unique: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20160222143943) do
+ActiveRecord::Schema.define(version: 20160223171800) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -31,10 +31,20 @@ ActiveRecord::Schema.define(version: 20160222143943) do
t.text "note", default: "", null: false
t.string "display_name", default: "", null: false
t.string "uri", default: "", null: false
+ t.string "url"
end
add_index "accounts", ["username", "domain"], name: "index_accounts_on_username_and_domain", unique: true, using: :btree
+ create_table "favourites", force: :cascade do |t|
+ t.integer "account_id", null: false
+ t.integer "status_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
+ add_index "favourites", ["account_id", "status_id"], name: "index_favourites_on_account_id_and_status_id", unique: true, using: :btree
+
create_table "follows", force: :cascade do |t|
t.integer "account_id", null: false
t.integer "target_account_id", null: false
@@ -45,11 +55,14 @@ ActiveRecord::Schema.define(version: 20160222143943) do
add_index "follows", ["account_id", "target_account_id"], name: "index_follows_on_account_id_and_target_account_id", unique: true, using: :btree
create_table "statuses", force: :cascade do |t|
- t.string "uri", default: "", null: false
- t.integer "account_id", null: false
- t.text "text", default: "", null: false
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.string "uri"
+ t.integer "account_id", null: false
+ t.text "text", default: "", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.integer "in_reply_to_id"
+ t.integer "reblog_of_id"
+ t.string "url"
end
add_index "statuses", ["uri"], name: "index_statuses_on_uri", unique: true, using: :btree
diff --git a/spec/models/favourite_spec.rb b/spec/models/favourite_spec.rb
@@ -0,0 +1,5 @@
+require 'rails_helper'
+
+RSpec.describe Favourite, type: :model do
+ pending "add some examples to (or delete) #{__FILE__}"
+end