Adding reblogs, favourites, improving atom generation
This commit is contained in:
		| @@ -1,4 +1,7 @@ | ||||
| class ProfileController < ApplicationController | ||||
|   def show | ||||
|   end | ||||
|  | ||||
|   def entry | ||||
|   end | ||||
| end | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
							
								
								
									
										38
									
								
								app/models/favourite.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								app/models/favourite.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|       include_author 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 | ||||
|     include_entry xml, @entry | ||||
|   end | ||||
|       end | ||||
|     else | ||||
|       object_type xml, @entry.object_type | ||||
|     end | ||||
|  | ||||
|     link_self xml, atom_entry_url(id: @entry.id) | ||||
|   end | ||||
| end | ||||
| end.to_xml | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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/' | ||||
|  | ||||
|   | ||||
							
								
								
									
										6
									
								
								db/migrate/20160223162837_add_metadata_to_statuses.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								db/migrate/20160223162837_add_metadata_to_statuses.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| @@ -0,0 +1,5 @@ | ||||
| class MakeUrisNullableInStatuses < ActiveRecord::Migration | ||||
|   def change | ||||
|     change_column :statuses, :uri, :string, null: true, default: nil | ||||
|   end | ||||
| end | ||||
							
								
								
									
										5
									
								
								db/migrate/20160223165723_add_url_to_statuses.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								db/migrate/20160223165723_add_url_to_statuses.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| class AddUrlToStatuses < ActiveRecord::Migration | ||||
|   def change | ||||
|     add_column :statuses, :url, :string, null: true, default: nil | ||||
|   end | ||||
| end | ||||
							
								
								
									
										5
									
								
								db/migrate/20160223165855_add_url_to_accounts.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								db/migrate/20160223165855_add_url_to_accounts.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| class AddUrlToAccounts < ActiveRecord::Migration | ||||
|   def change | ||||
|     add_column :accounts, :url, :string, null: true, default: nil | ||||
|   end | ||||
| end | ||||
							
								
								
									
										12
									
								
								db/migrate/20160223171800_create_favourites.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								db/migrate/20160223171800_create_favourites.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										17
									
								
								db/schema.rb
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								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.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 | ||||
|   | ||||
							
								
								
									
										5
									
								
								spec/models/favourite_spec.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								spec/models/favourite_spec.rb
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| require 'rails_helper' | ||||
|  | ||||
| RSpec.describe Favourite, type: :model do | ||||
|   pending "add some examples to (or delete) #{__FILE__}" | ||||
| end | ||||
		Reference in New Issue
	
	Block a user