federails 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +171 -7
  3. data/Rakefile +5 -5
  4. data/app/controllers/federails/application_controller.rb +19 -0
  5. data/app/controllers/federails/client/activities_controller.rb +21 -0
  6. data/app/controllers/federails/client/actors_controller.rb +37 -0
  7. data/app/controllers/federails/client/followings_controller.rb +93 -0
  8. data/app/controllers/federails/server/activities_controller.rb +65 -0
  9. data/app/controllers/federails/server/actors_controller.rb +34 -0
  10. data/app/controllers/federails/server/followings_controller.rb +18 -0
  11. data/app/controllers/federails/server/nodeinfo_controller.rb +22 -0
  12. data/app/controllers/federails/server/server_controller.rb +17 -0
  13. data/app/controllers/federails/server/web_finger_controller.rb +38 -0
  14. data/app/jobs/federails/notify_inbox_job.rb +12 -0
  15. data/app/mailers/federails/application_mailer.rb +2 -2
  16. data/app/models/concerns/federails/entity.rb +46 -0
  17. data/app/models/federails/activity.rb +40 -0
  18. data/app/models/federails/actor.rb +152 -0
  19. data/app/models/federails/following.rb +43 -0
  20. data/app/policies/federails/client/activity_policy.rb +6 -0
  21. data/app/policies/federails/client/actor_policy.rb +15 -0
  22. data/app/policies/federails/client/following_policy.rb +35 -0
  23. data/app/policies/federails/federails_policy.rb +59 -0
  24. data/app/policies/federails/server/activity_policy.rb +6 -0
  25. data/app/policies/federails/server/actor_policy.rb +23 -0
  26. data/app/policies/federails/server/following_policy.rb +6 -0
  27. data/app/views/federails/client/activities/_activity.html.erb +5 -0
  28. data/app/views/federails/client/activities/_activity.json.jbuilder +1 -0
  29. data/app/views/federails/client/activities/_index.json.jbuilder +1 -0
  30. data/app/views/federails/client/activities/feed.html.erb +4 -0
  31. data/app/views/federails/client/activities/feed.json.jbuilder +1 -0
  32. data/app/views/federails/client/activities/index.html.erb +5 -0
  33. data/app/views/federails/client/activities/index.json.jbuilder +1 -0
  34. data/app/views/federails/client/actors/_actor.json.jbuilder +14 -0
  35. data/app/views/federails/client/actors/_lookup_form.html.erb +5 -0
  36. data/app/views/federails/client/actors/index.html.erb +24 -0
  37. data/app/views/federails/client/actors/index.json.jbuilder +1 -0
  38. data/app/views/federails/client/actors/show.html.erb +100 -0
  39. data/app/views/federails/client/actors/show.json.jbuilder +1 -0
  40. data/app/views/federails/client/followings/_follow.html.erb +4 -0
  41. data/app/views/federails/client/followings/_follower.html.erb +7 -0
  42. data/app/views/federails/client/followings/_following.json.jbuilder +1 -0
  43. data/app/views/federails/client/followings/_form.html.erb +21 -0
  44. data/app/views/federails/client/followings/index.html.erb +29 -0
  45. data/app/views/federails/client/followings/index.json.jbuilder +1 -0
  46. data/app/views/federails/client/followings/show.html.erb +21 -0
  47. data/app/views/federails/client/followings/show.json.jbuilder +1 -0
  48. data/app/views/federails/server/activities/_activity.json.jbuilder +9 -0
  49. data/app/views/federails/server/activities/outbox.json.jbuilder +18 -0
  50. data/app/views/federails/server/activities/show.json.jbuilder +1 -0
  51. data/app/views/federails/server/actors/_actor.json.jbuilder +11 -0
  52. data/app/views/federails/server/actors/followers.json.jbuilder +18 -0
  53. data/app/views/federails/server/actors/following.json.jbuilder +18 -0
  54. data/app/views/federails/server/actors/show.json.jbuilder +1 -0
  55. data/app/views/federails/server/followings/_following.json.jbuilder +7 -0
  56. data/app/views/federails/server/followings/show.json.jbuilder +1 -0
  57. data/app/views/federails/server/nodeinfo/index.json.jbuilder +6 -0
  58. data/app/views/federails/server/nodeinfo/show.json.jbuilder +19 -0
  59. data/app/views/federails/server/web_finger/find.json.jbuilder +20 -0
  60. data/app/views/federails/server/web_finger/host_meta.xml.erb +5 -0
  61. data/config/routes.rb +43 -0
  62. data/db/migrate/20200712133150_create_federails_actors.rb +24 -0
  63. data/db/migrate/20200712143127_create_federails_followings.rb +14 -0
  64. data/db/migrate/20200712174938_create_federails_activities.rb +11 -0
  65. data/db/migrate/20240731145400_change_actor_entity_rel_to_polymorphic.rb +11 -0
  66. data/lib/federails/configuration.rb +88 -0
  67. data/lib/federails/engine.rb +6 -0
  68. data/lib/federails/utils/host.rb +54 -0
  69. data/lib/federails/version.rb +1 -1
  70. data/lib/federails.rb +33 -3
  71. data/lib/fediverse/inbox.rb +71 -0
  72. data/lib/fediverse/notifier.rb +21 -0
  73. data/lib/fediverse/request.rb +38 -0
  74. data/lib/fediverse/webfinger.rb +93 -0
  75. data/lib/generators/federails/install/USAGE +9 -0
  76. data/lib/generators/federails/install/install_generator.rb +10 -0
  77. data/lib/generators/federails/install/templates/federails.rb +1 -0
  78. data/lib/generators/federails/install/templates/federails.yml +23 -0
  79. data/lib/tasks/factory_bot.rake +15 -0
  80. metadata +165 -10
  81. data/app/views/layouts/federails/application.html.erb +0 -15
@@ -0,0 +1,40 @@
1
+ module Federails
2
+ class Activity < ApplicationRecord
3
+ belongs_to :entity, polymorphic: true
4
+ belongs_to :actor
5
+
6
+ scope :feed_for, lambda { |actor|
7
+ actor_ids = []
8
+ Following.accepted.where(actor: actor).find_each do |following|
9
+ actor_ids << following.target_actor_id
10
+ end
11
+ where(actor_id: actor_ids)
12
+ }
13
+
14
+ after_create_commit :post_to_inboxes
15
+
16
+ def recipients # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
17
+ return [] unless actor.local?
18
+
19
+ actors = []
20
+ case action
21
+ when 'Create'
22
+ actors.push(entity.target_actor) if entity_type == 'Federails::Following'
23
+ # FIXME: Move this to dummy, somehow
24
+ actors.push(*actor.followers) if entity_type == 'Note'
25
+ when 'Accept'
26
+ actors.push(entity.actor) if entity_type == 'Federails::Following'
27
+ when 'Undo'
28
+ actors.push(entity.target_actor) if entity_type == 'Federails::Following'
29
+ end
30
+
31
+ actors
32
+ end
33
+
34
+ private
35
+
36
+ def post_to_inboxes
37
+ NotifyInboxJob.perform_later(self)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,152 @@
1
+ require 'federails/utils/host'
2
+ require 'fediverse/webfinger'
3
+
4
+ module Federails
5
+ class Actor < ApplicationRecord # rubocop:disable Metrics/ClassLength
6
+ validates :federated_url, presence: { unless: :entity }, uniqueness: { unless: :entity }
7
+ validates :username, presence: { unless: :entity }
8
+ validates :server, presence: { unless: :entity }
9
+ validates :inbox_url, presence: { unless: :entity }
10
+ validates :outbox_url, presence: { unless: :entity }
11
+ validates :followers_url, presence: { unless: :entity }
12
+ validates :followings_url, presence: { unless: :entity }
13
+ validates :profile_url, presence: { unless: :entity }
14
+ validates :entity_id, uniqueness: { scope: :entity_type }, if: :local?
15
+
16
+ belongs_to :entity, polymorphic: true, optional: true
17
+ # FIXME: Handle this with something like undelete
18
+ has_many :activities, dependent: :destroy
19
+ has_many :activities_as_entity, class_name: 'Federails::Activity', as: :entity, dependent: :destroy
20
+ has_many :following_followers, class_name: 'Federails::Following', foreign_key: :target_actor_id, dependent: :destroy, inverse_of: :target_actor
21
+ has_many :following_follows, class_name: 'Federails::Following', dependent: :destroy, inverse_of: :actor
22
+ has_many :followers, source: :actor, through: :following_followers
23
+ has_many :follows, source: :target_actor, through: :following_follows
24
+
25
+ scope :local, -> { where.not(entity: nil) }
26
+
27
+ def local?
28
+ entity.present?
29
+ end
30
+
31
+ def federated_url
32
+ local? ? Federails::Engine.routes.url_helpers.server_actor_url(self) : attributes['federated_url'].presence
33
+ end
34
+
35
+ def username
36
+ return attributes['username'] unless local?
37
+
38
+ entity.send(entity_configuration[:username_field]).to_s
39
+ end
40
+
41
+ def name
42
+ value = (entity.send(entity_configuration[:name_field]).to_s if local?)
43
+
44
+ value || attributes['name'] || username
45
+ end
46
+
47
+ def server
48
+ local? ? Utils::Host.localhost : attributes['server']
49
+ end
50
+
51
+ def inbox_url
52
+ local? ? Federails::Engine.routes.url_helpers.server_actor_inbox_url(self) : attributes['inbox_url']
53
+ end
54
+
55
+ def outbox_url
56
+ local? ? Federails::Engine.routes.url_helpers.server_actor_outbox_url(self) : attributes['outbox_url']
57
+ end
58
+
59
+ def followers_url
60
+ local? ? Federails::Engine.routes.url_helpers.followers_server_actor_url(self) : attributes['followers_url']
61
+ end
62
+
63
+ def followings_url
64
+ local? ? Federails::Engine.routes.url_helpers.following_server_actor_url(self) : attributes['followings_url']
65
+ end
66
+
67
+ def profile_url
68
+ return attributes['profile_url'].presence unless local?
69
+
70
+ method = entity_configuration[:profile_url_method]
71
+ return Federails::Engine.routes.url_helpers.server_actor_url self unless method
72
+
73
+ Rails.application.routes.url_helpers.send method, [entity]
74
+ end
75
+
76
+ def at_address
77
+ "#{username}@#{server}"
78
+ end
79
+
80
+ def short_at_address
81
+ local? ? "@#{username}" : at_address
82
+ end
83
+
84
+ def follows?(actor)
85
+ list = following_follows.where target_actor: actor
86
+ return list.first if list.count == 1
87
+
88
+ false
89
+ end
90
+
91
+ def entity_configuration
92
+ Federails::Configuration.entity_types[entity.class.name]
93
+ end
94
+
95
+ class << self
96
+ def find_by_account(account) # rubocop:todo Metrics/AbcSize
97
+ parts = Fediverse::Webfinger.split_account account
98
+
99
+ if Fediverse::Webfinger.local_user? parts
100
+ actor = nil
101
+ Federails::Configuration.entity_types.each_value do |entity|
102
+ actor ||= entity[:class].find_by(entity[:username_field] => parts[:username])&.actor
103
+ end
104
+ raise ActiveRecord::RecordNotFound if actor.nil?
105
+ else
106
+ actor = find_by username: parts[:username], server: parts[:domain]
107
+ actor ||= Fediverse::Webfinger.fetch_actor(parts[:username], parts[:domain])
108
+ end
109
+
110
+ actor
111
+ end
112
+
113
+ def find_by_federation_url(federated_url)
114
+ local_route = Utils::Host.local_route federated_url
115
+ return find local_route[:id] if local_route && local_route[:controller] == 'federails/server/actors' && local_route[:action] == 'show'
116
+
117
+ actor = find_by federated_url: federated_url
118
+ return actor if actor
119
+
120
+ Fediverse::Webfinger.fetch_actor_url(federated_url)
121
+ end
122
+
123
+ def find_or_create_by_account(account)
124
+ actor = find_by_account account
125
+ # Create/update distant actors
126
+ actor.save! unless actor.local?
127
+
128
+ actor
129
+ end
130
+
131
+ def find_or_create_by_federation_url(url)
132
+ actor = find_by_federation_url url
133
+ # Create/update distant actors
134
+ actor.save! unless actor.local?
135
+
136
+ actor
137
+ end
138
+
139
+ # Find or create actor from a given actor hash or actor id (actor's URL)
140
+ def find_or_create_by_object(object)
141
+ case object
142
+ when String
143
+ find_or_create_by_federation_url object
144
+ when Hash
145
+ find_or_create_by_federation_url object['id']
146
+ else
147
+ raise "Unsupported object type for actor (#{object.class})"
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,43 @@
1
+ module Federails
2
+ class Following < ApplicationRecord
3
+ enum status: { pending: 0, accepted: 1 }
4
+
5
+ validates :target_actor_id, uniqueness: { scope: [:actor_id, :target_actor_id] }
6
+
7
+ belongs_to :actor
8
+ belongs_to :target_actor, class_name: 'Federails::Actor'
9
+ # FIXME: Handle this with something like undelete
10
+ has_many :activities, as: :entity, dependent: :destroy
11
+
12
+ after_create :create_activity
13
+ after_destroy :destroy_activity
14
+
15
+ scope :with_actor, ->(actor) { where(actor_id: actor.id).or(where(target_actor_id: actor.id)) }
16
+
17
+ def federated_url
18
+ attributes['federated_url'].presence || Federails::Engine.routes.url_helpers.server_actor_following_url(actor_id: actor_id, id: id)
19
+ end
20
+
21
+ def accept!
22
+ update! status: :accepted
23
+ Activity.create! actor: target_actor, action: 'Accept', entity: self
24
+ end
25
+
26
+ class << self
27
+ def new_from_account(account, actor:)
28
+ target_actor = Actor.find_or_create_by_account account
29
+ new actor: actor, target_actor: target_actor
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def create_activity
36
+ Activity.create! actor: actor, action: 'Create', entity: self
37
+ end
38
+
39
+ def destroy_activity
40
+ Activity.create! actor: actor, action: 'Undo', entity: self
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,6 @@
1
+ module Federails
2
+ module Client
3
+ class ActivityPolicy < Federails::FederailsPolicy
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,15 @@
1
+ module Federails
2
+ module Client
3
+ class ActorPolicy < Federails::FederailsPolicy
4
+ def lookup?
5
+ true
6
+ end
7
+
8
+ class Scope < Scope
9
+ def resolve
10
+ scope.local
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,35 @@
1
+ module Federails
2
+ module Client
3
+ class FollowingPolicy < Federails::FederailsPolicy
4
+ def show?
5
+ in_following?
6
+ end
7
+
8
+ def destroy?
9
+ in_following?
10
+ end
11
+
12
+ def accept?
13
+ in_following? && @record.target_actor_id == @user.actor.id
14
+ end
15
+
16
+ def follow?
17
+ create?
18
+ end
19
+
20
+ class Scope < Scope
21
+ def resolve
22
+ scope.with_actor(@user.actor)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def in_following?
29
+ return false if @user.blank?
30
+
31
+ @record.actor_id == @user.actor.id || @record.target_actor_id == @user.actor.id
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,59 @@
1
+ module Federails
2
+ class FederailsPolicy
3
+ attr_reader :user, :record
4
+
5
+ def initialize(user, record)
6
+ @user = user
7
+ @record = record
8
+ end
9
+
10
+ def index?
11
+ true
12
+ end
13
+
14
+ def show?
15
+ true
16
+ end
17
+
18
+ def create?
19
+ @user.present?
20
+ end
21
+
22
+ def new?
23
+ create?
24
+ end
25
+
26
+ def update?
27
+ owner?
28
+ end
29
+
30
+ def edit?
31
+ update?
32
+ end
33
+
34
+ def destroy?
35
+ owner?
36
+ end
37
+
38
+ class Scope
39
+ attr_reader :user, :scope
40
+
41
+ def initialize(user, scope)
42
+ @user = user
43
+ @scope = scope
44
+ end
45
+
46
+ def resolve
47
+ scope.all
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def owner?
54
+ return false unless @user
55
+
56
+ @record.actor_id == @user.actor.id
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,6 @@
1
+ module Federails
2
+ module Server
3
+ class ActivityPolicy < Federails::FederailsPolicy
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,23 @@
1
+ module Federails
2
+ module Server
3
+ class ActorPolicy < Federails::FederailsPolicy
4
+ def show?
5
+ @record.local?
6
+ end
7
+
8
+ def following?
9
+ true
10
+ end
11
+
12
+ def followers?
13
+ true
14
+ end
15
+
16
+ class Scope < Scope
17
+ def resolve
18
+ scope.local
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ module Federails
2
+ module Server
3
+ class FollowingPolicy < Federails::FederailsPolicy
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ <div>
2
+ <b><%= activity.actor.name %></b>
3
+ <code><%= activity.action %></code>
4
+ <!-- < %= link_to activity.entity_type, activity.entity %>-->
5
+ </div>
@@ -0,0 +1 @@
1
+ json.extract! activity, :id, :entity_id, :entity_type, :action, :actor_id, :created_at
@@ -0,0 +1 @@
1
+ json.array! @activities, partial: 'federails/client/activities/activity', as: :activity
@@ -0,0 +1,4 @@
1
+ <h1>Your feed !</h1>
2
+ <% @activities.each do |activity| %>
3
+ <%= render 'activity', activity: activity %>
4
+ <% end %>
@@ -0,0 +1 @@
1
+ json.partial! 'federails/client/activities/index'
@@ -0,0 +1,5 @@
1
+ <h1>Listing activities</h1>
2
+
3
+ <%= @activities.each do |activity| %>
4
+ <%= render 'activity', activity: activity %>
5
+ <% end %>
@@ -0,0 +1 @@
1
+ json.partial! 'federails/client/activities/index'
@@ -0,0 +1,14 @@
1
+ json.extract! actor,
2
+ :id,
3
+ :name,
4
+ :federated_url,
5
+ :username,
6
+ :inbox_url,
7
+ :outbox_url,
8
+ :followers_url,
9
+ :followings_url,
10
+ :profile_url,
11
+ :at_address,
12
+ :user_id,
13
+ :created_at,
14
+ :updated_at
@@ -0,0 +1,5 @@
1
+ <%= form_tag federails.lookup_client_actors_url, method: :get do %>
2
+ <%= label_tag :account %>
3
+ <%= text_field_tag :account, nil, placeholder: 'user@domain.tld or username', required: true %>
4
+ <%= submit_tag 'Search' %>
5
+ <% end %>
@@ -0,0 +1,24 @@
1
+ <h1>Listing actors</h1>
2
+
3
+ <table>
4
+ <thead>
5
+ <tr>
6
+ <th>Name</th>
7
+ <th>Username</th>
8
+ <th>Federation address</th>
9
+ <th>Local?</th>
10
+ <th></th>
11
+ </tr>
12
+ </thead>
13
+ <tbody>
14
+ <% @actors.each do |actor| %>
15
+ <tr>
16
+ <td><%= actor.name %></td>
17
+ <td><%= actor.username %></td>
18
+ <td><%= actor.at_address %></td>
19
+ <td><%= actor.local? %></td>
20
+ <td><%= link_to 'Show', federails.client_actor_url(actor) %></td>
21
+ </tr>
22
+ <% end %>
23
+ </tbody>
24
+ </table>
@@ -0,0 +1 @@
1
+ json.array! @actors, partial: 'federails/client/actors/actor', as: :actor
@@ -0,0 +1,100 @@
1
+ <h2><%= @actor.name %></h2>
2
+
3
+ <% if Federails::Client::FollowingPolicy.new(current_user, Federails::Following).create? %>
4
+ <p>
5
+ <%
6
+ follow = current_user.actor.follows? @actor
7
+ if @actor.entity == current_user
8
+ %>
9
+ <button role="button" disabled="disabled">That's you</button>
10
+ <% elsif follow %>
11
+ Already following (<%= follow.status %>)
12
+ <%= button_to 'Revoke', federails.client_following_path(follow), method: :delete %>
13
+ <% else %>
14
+ <%= button_to 'Follow!', federails.follow_client_followings_path, params: { account: @actor.at_address }, method: :post %>
15
+ <% end %>
16
+ </p>
17
+ <% end %>
18
+
19
+ <p>
20
+ <b>Federated url:</b>
21
+ <%= @actor.federated_url %>
22
+ </p>
23
+
24
+ <p>
25
+ <b>Username:</b>
26
+ <%= @actor.username %>
27
+ </p>
28
+
29
+ <p>
30
+ <b>Inbox URL:</b>
31
+ <%= @actor.inbox_url %>
32
+ </p>
33
+
34
+ <p>
35
+ <b>Outbox URL:</b>
36
+ <%= @actor.outbox_url %>
37
+ </p>
38
+
39
+ <p>
40
+ <b>Followers URL:</b>
41
+ <%= @actor.followers_url %>
42
+ </p>
43
+
44
+ <p>
45
+ <b>Followings URL:</b>
46
+ <%= @actor.followings_url %>
47
+ </p>
48
+
49
+ <p>
50
+ <b>Profile url:</b>
51
+ <% if @actor.profile_url %>
52
+ <%= link_to 'Profile', @actor.profile_url %>
53
+ <% end %>
54
+ </p>
55
+
56
+ <p>
57
+ <b>Federation address:</b>
58
+ <%= @actor.at_address %>
59
+ </p>
60
+
61
+ <p>
62
+ <b>Home page:</b>
63
+ <% if @actor.local? && Federails::Configuration.user_profile_url_method %>
64
+ <%= link_to @actor.send(Federails::Configuration.user_username_field), send(Federails::Configuration.user_profile_url_method, @actor.user) %>
65
+ <% elsif @actor.profile_url %>
66
+ <%= link_to @actor.name, @actor.profile_url %>
67
+ <% else %>
68
+ (No homepage)
69
+ <% end %>
70
+ </p>
71
+
72
+ <hr>
73
+
74
+ <h2>Follows</h2>
75
+ <% if current_user && current_user == @actor.entity %>
76
+ <%= render 'federails/client/followings/form', following: Federails::Following.new %>
77
+ <% end %>
78
+
79
+ <% @actor.following_follows.each do |following| %>
80
+ <%= render 'federails/client/followings/follow', following: following %>
81
+ <% end %>
82
+
83
+ <h2>Followers</h2>
84
+ <% @actor.following_followers.each do |following| %>
85
+ <%= render 'federails/client/followings/follower', following: following %>
86
+ <% end %>
87
+
88
+ <%= link_to 'Back', federails.client_actors_url %>
89
+
90
+ <!-- FIXME: Fetch distant content somehow -->
91
+ <h2>10 last activities</h2>
92
+ <% if @actor.local? %>
93
+ <%= link_to 'All', federails.client_actor_activities_path(@actor) %>
94
+ <% elsif @actor.profile_url %>
95
+ <%= link_to 'Visit profile', @actor.profile_url %>
96
+ <% end %>
97
+
98
+ <% @actor.activities.last(10).each do |activity| %>
99
+ <%= render 'federails/client/activities/activity', activity: activity %>
100
+ <% end %>
@@ -0,0 +1 @@
1
+ json.partial! 'federails/client/actors/actor', actor: @actor
@@ -0,0 +1,4 @@
1
+ <div>
2
+ <b><%= link_to following.target_actor.name, federails.client_actor_url(following.target_actor) %></b>
3
+ (<%= following.target_actor.at_address %>) (<%= following.status %>)
4
+ </div>
@@ -0,0 +1,7 @@
1
+ <div>
2
+ <b><%= link_to following.actor.name, federails.client_actor_url(following.actor) %></b>
3
+ (<%= following.actor.at_address %>) (<%= following.status %>)
4
+ <% if following.pending? && following.target_actor == current_user.actor %>
5
+ <%= button_to 'Accept', federails.accept_client_following_path(following), method: :put %>
6
+ <% end %>
7
+ </div>
@@ -0,0 +1 @@
1
+ json.extract! following, :id, :actor_id, :target_actor_id, :status, :created_at, :updated_at
@@ -0,0 +1,21 @@
1
+ <%= form_for following, url: federails.client_following_url do |f| %>
2
+ <% if following.errors.any? %>
3
+ <div class="error_explanation">
4
+ <h2><%= pluralize(following.errors.count, 'error') %> prohibited this following from being saved:</h2>
5
+ <ul>
6
+ <% following.errors.full_messages.each do |message| %>
7
+ <li><%= message %></li>
8
+ <% end %>
9
+ </ul>
10
+ </div>
11
+ <% end %>
12
+
13
+ <div class="field">
14
+ <%= f.label :target_actor %>
15
+ <%= f.collection_select :target_actor_id, Federails::Actor.all, :id, :name %>
16
+ </div>
17
+
18
+ <div class="actions">
19
+ <%= f.submit 'Save' %>
20
+ </div>
21
+ <% end %>
@@ -0,0 +1,29 @@
1
+ <h1>Listing followings</h1>
2
+
3
+ <table>
4
+ <thead>
5
+ <tr>
6
+ <th>Actor</th>
7
+ <th>Target actor</th>
8
+ <th>Status</th>
9
+ <th></th>
10
+ <th></th>
11
+ <th></th>
12
+ </tr>
13
+ </thead>
14
+
15
+ <tbody>
16
+ <% @followings.each do |following| %>
17
+ <tr>
18
+ <td><%= following.actor %></td>
19
+ <td><%= following.target_actor %></td>
20
+ <td><%= following.status %></td>
21
+ <td><%= link_to 'Show', following %></td>
22
+ <td><%= link_to 'Edit', edit_following_path(following) %></td>
23
+ <td><%= link_to 'Destroy', following, method: :delete, data: { confirm: 'Are you sure?' } %></td>
24
+ </tr>
25
+ <% end %>
26
+ </tbody>
27
+ </table>
28
+
29
+ <%= link_to 'New Following', new_following_path %>
@@ -0,0 +1 @@
1
+ json.array! @followings, partial: 'federails/client/followings/following', as: :following
@@ -0,0 +1,21 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <p>
4
+ <b>Actor:</b>
5
+ <%= @following.actor %>
6
+ </p>
7
+
8
+ <p>
9
+ <b>Target actor:</b>
10
+ <%= @following.target_actor %>
11
+ </p>
12
+
13
+ <p>
14
+ <b>Status:</b>
15
+ <%= @following.status %>
16
+ </p>
17
+
18
+
19
+ <%= link_to 'Edit', edit_following_path(@following) %>
20
+ \|
21
+ <%= link_to 'Back', followings_path %>