about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2016-02-29 19:42:08 +0100
committerEugen Rochko <eugen@zeonfederated.com>2016-02-29 19:42:08 +0100
commit0e8f59c16fcb21301c736ecbc4424cb4c5388c42 (patch)
tree344ac1e0b2d165ba4fe3870f786e854710970ce1 /app
parent11ff92c9d7b27c2c9ed86f649aef8d956cc8b989 (diff)
Refactoring Grape API methods into normal controllers & other things
Diffstat (limited to 'app')
-rw-r--r--app/api/mastodon/api.rb8
-rw-r--r--app/api/mastodon/entities.rb54
-rw-r--r--app/api/mastodon/ostatus.rb62
-rw-r--r--app/api/mastodon/rest.rb44
-rw-r--r--app/assets/javascripts/atom.coffee3
-rw-r--r--app/assets/javascripts/home.coffee3
-rw-r--r--app/assets/javascripts/profile.coffee3
-rw-r--r--app/assets/javascripts/xrd.coffee3
-rw-r--r--app/assets/stylesheets/accounts.scss39
-rw-r--r--app/assets/stylesheets/api/salmon.scss (renamed from app/assets/stylesheets/atom.scss)2
-rw-r--r--app/assets/stylesheets/api/subscriptions.scss (renamed from app/assets/stylesheets/xrd.scss)2
-rw-r--r--app/assets/stylesheets/application.scss5
-rw-r--r--app/assets/stylesheets/stream_entries.scss (renamed from app/assets/stylesheets/profile.scss)81
-rw-r--r--app/controllers/accounts_controller.rb16
-rw-r--r--app/controllers/api/salmon_controller.rb14
-rw-r--r--app/controllers/api/subscriptions_controller.rb28
-rw-r--r--app/controllers/atom_controller.rb18
-rw-r--r--app/controllers/profile_controller.rb17
-rw-r--r--app/controllers/stream_entries_controller.rb23
-rw-r--r--app/controllers/xrd_controller.rb2
-rw-r--r--app/helpers/accounts_helper.rb3
-rw-r--r--app/helpers/api/salmon_helper.rb2
-rw-r--r--app/helpers/api/subscriptions_helper.rb2
-rw-r--r--app/helpers/application_helper.rb22
-rw-r--r--app/helpers/atom_builder_helper.rb (renamed from app/helpers/atom_helper.rb)32
-rw-r--r--app/helpers/stream_entries_helper.rb (renamed from app/helpers/profile_helper.rb)2
-rw-r--r--app/models/account.rb4
-rw-r--r--app/models/user.rb5
-rw-r--r--app/services/base_service.rb1
-rw-r--r--app/services/follow_remote_account_service.rb2
-rw-r--r--app/services/follow_service.rb2
-rw-r--r--app/services/post_status_service.rb2
-rw-r--r--app/services/process_interaction_service.rb2
-rw-r--r--app/services/reblog_service.rb2
-rw-r--r--app/services/send_interaction_service.rb2
-rw-r--r--app/views/accounts/show.atom.ruby (renamed from app/views/atom/user_stream.xml.ruby)8
-rw-r--r--app/views/accounts/show.html.haml (renamed from app/views/profile/show.html.haml)6
-rw-r--r--app/views/profile/_status_footer.html.haml2
-rw-r--r--app/views/profile/_status_header.html.haml7
-rw-r--r--app/views/profile/entry.html.haml5
-rw-r--r--app/views/stream_entries/_favourite.html.haml (renamed from app/views/profile/_favourite.html.haml)0
-rw-r--r--app/views/stream_entries/_follow.html.haml (renamed from app/views/profile/_follow.html.haml)0
-rw-r--r--app/views/stream_entries/_status.html.haml (renamed from app/views/profile/_status.html.haml)14
-rw-r--r--app/views/stream_entries/show.atom.ruby (renamed from app/views/atom/entry.xml.ruby)0
-rw-r--r--app/views/stream_entries/show.html.haml5
-rw-r--r--app/views/xrd/webfinger.xml.ruby6
46 files changed, 205 insertions, 360 deletions
diff --git a/app/api/mastodon/api.rb b/app/api/mastodon/api.rb
deleted file mode 100644
index 39469dc65..000000000
--- a/app/api/mastodon/api.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module Mastodon
-  class API < Grape::API
-    rescue_from :all
-
-    mount Mastodon::Ostatus
-    mount Mastodon::Rest
-  end
-end
diff --git a/app/api/mastodon/entities.rb b/app/api/mastodon/entities.rb
deleted file mode 100644
index 1c0e768f8..000000000
--- a/app/api/mastodon/entities.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-module Mastodon
-  module Entities
-    class Account < Grape::Entity
-      include ApplicationHelper
-
-      expose :id
-      expose :username
-
-      expose :domain do |account|
-        account.local? ? LOCAL_DOMAIN : account.domain
-      end
-
-      expose :display_name
-      expose :note
-
-      expose :url do |account|
-        account.local? ? profile_url(name: account.username) : account.url
-      end
-    end
-
-    class Status < Grape::Entity
-      include ApplicationHelper
-
-      format_with(:iso_timestamp) { |dt| dt.iso8601 }
-
-      expose :id
-
-      expose :uri do |status|
-        status.local? ? unique_tag(status.stream_entry.created_at, status.stream_entry.activity_id, status.stream_entry.activity_type) : status.uri
-      end
-
-      expose :url do |status|
-        status.local? ? status_url(name: status.account.username, id: status.id) : status.url
-      end
-
-      expose :text
-      expose :in_reply_to_id
-
-      expose :reblog_of_id
-      expose :reblog, using: Mastodon::Entities::Status
-
-      expose :account, using: Mastodon::Entities::Account
-
-      with_options(format_with: :iso_timestamp) do
-        expose :created_at
-        expose :updated_at
-      end
-    end
-
-    class StreamEntry < Grape::Entity
-      expose :activity, using: Mastodon::Entities::Status
-    end
-  end
-end
diff --git a/app/api/mastodon/ostatus.rb b/app/api/mastodon/ostatus.rb
deleted file mode 100644
index 4676bc429..000000000
--- a/app/api/mastodon/ostatus.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-module Mastodon
-  class Ostatus < Grape::API
-    format :txt
-
-    before do
-      @account = Account.find(params[:id])
-    end
-
-    resource :subscriptions do
-      helpers do
-        include ApplicationHelper
-      end
-
-      desc 'Receive updates from an account'
-
-      params do
-        requires :id, type: String, desc: 'Account ID'
-      end
-
-      post ':id' do
-        body = request.body.read
-
-        if @account.subscription(subscription_url(@account)).verify(body, env['HTTP_X_HUB_SIGNATURE'])
-          ProcessFeedService.new.(body, @account)
-          status 201
-        else
-          status 202
-        end
-      end
-
-      desc 'Confirm PuSH subscription to an account'
-
-      params do
-        requires :id, type: String, desc: 'Account ID'
-        requires 'hub.topic', type: String, desc: 'Topic URL'
-        requires 'hub.verify_token', type: String, desc: 'Verification token'
-        requires 'hub.challenge', type: String, desc: 'Hub challenge'
-      end
-
-      get ':id' do
-        if @account.subscription(subscription_url(@account)).valid?(params['hub.topic'], params['hub.verify_token'])
-          params['hub.challenge']
-        else
-          error! :not_found, 404
-        end
-      end
-    end
-
-    resource :salmon do
-      desc 'Receive Salmon updates targeted to account'
-
-      params do
-        requires :id, type: String, desc: 'Account ID'
-      end
-
-      post ':id' do
-        ProcessInteractionService.new.(request.body.read, @account)
-        status 201
-      end
-    end
-  end
-end
diff --git a/app/api/mastodon/rest.rb b/app/api/mastodon/rest.rb
deleted file mode 100644
index eb5232165..000000000
--- a/app/api/mastodon/rest.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-module Mastodon
-  class Rest < Grape::API
-    version 'v1', using: :path
-    format :json
-
-    helpers do
-      def current_user
-        User.first
-      end
-    end
-
-    resource :timelines do
-      desc 'Return a public timeline'
-
-      get :public do
-        # todo
-      end
-
-      desc 'Return the home timeline of a logged in user'
-
-      get :home do
-        present current_user.timeline, with: Mastodon::Entities::StreamEntry
-      end
-
-      desc 'Return the notifications timeline of a logged in user'
-
-      get :notifications do
-        # todo
-      end
-    end
-
-    resource :accounts do
-      desc 'Return a user profile'
-
-      params do
-        requires :id, type: String, desc: 'Account ID'
-      end
-
-      get ':id' do
-        present Account.find(params[:id]), with: Mastodon::Entities::Account
-      end
-    end
-  end
-end
diff --git a/app/assets/javascripts/atom.coffee b/app/assets/javascripts/atom.coffee
deleted file mode 100644
index 24f83d18b..000000000
--- a/app/assets/javascripts/atom.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/home.coffee b/app/assets/javascripts/home.coffee
deleted file mode 100644
index 24f83d18b..000000000
--- a/app/assets/javascripts/home.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/profile.coffee b/app/assets/javascripts/profile.coffee
deleted file mode 100644
index 24f83d18b..000000000
--- a/app/assets/javascripts/profile.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/xrd.coffee b/app/assets/javascripts/xrd.coffee
deleted file mode 100644
index 24f83d18b..000000000
--- a/app/assets/javascripts/xrd.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/stylesheets/accounts.scss b/app/assets/stylesheets/accounts.scss
new file mode 100644
index 000000000..a777633f1
--- /dev/null
+++ b/app/assets/stylesheets/accounts.scss
@@ -0,0 +1,39 @@
+.card {
+  display: flex;
+  background: $primary-color;
+  box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
+
+  .bio {
+    flex-grow: 1;
+  }
+
+  .name {
+    font-size: 20px;
+    line-height: 18px * 1.5;
+    color: $quaternary-color;
+
+    small {
+      display: block;
+      font-size: 14px;
+      color: $quaternary-color;
+    }
+  }
+
+  .avatar {
+    width: 96px;
+    float: left;
+    margin-right: 10px;
+    padding: 10px;
+    padding-right: 0;
+    padding-left: 9px;
+    margin-top: -30px;
+
+    img {
+      width: 94px;
+      height: 94px;
+      display: block;
+      border-radius: 5px;
+      box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
+    }
+  }
+}
diff --git a/app/assets/stylesheets/atom.scss b/app/assets/stylesheets/api/salmon.scss
index 888698db3..13cb648a8 100644
--- a/app/assets/stylesheets/atom.scss
+++ b/app/assets/stylesheets/api/salmon.scss
@@ -1,3 +1,3 @@
-// Place all the styles related to the Atom controller here.
+// Place all the styles related to the API::Salmon controller here.
 // They will automatically be included in application.css.
 // You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/xrd.scss b/app/assets/stylesheets/api/subscriptions.scss
index 62391c7d3..9bee8c9bd 100644
--- a/app/assets/stylesheets/xrd.scss
+++ b/app/assets/stylesheets/api/subscriptions.scss
@@ -1,3 +1,3 @@
-// Place all the styles related to the XRD controller here.
+// Place all the styles related to the API::Subscriptions controller here.
 // They will automatically be included in application.css.
 // You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 9787c6be1..552356a22 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -20,7 +20,7 @@ body {
 }
 
 .container {
-  width: 800px;
+  width: 700px;
   margin: 0 auto;
   margin-top: 40px;
 }
@@ -40,4 +40,5 @@ body {
 
 
 @import 'home';
-@import 'profile';
+@import 'accounts';
+@import 'stream_entries';
diff --git a/app/assets/stylesheets/profile.scss b/app/assets/stylesheets/stream_entries.scss
index 49ce98d02..5689880ca 100644
--- a/app/assets/stylesheets/profile.scss
+++ b/app/assets/stylesheets/stream_entries.scss
@@ -1,59 +1,32 @@
-.card {
-  display: flex;
-  background: $darker-background-color;
-  border: 1px solid darken($darker-background-color, 15%);
-  box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
-
-  .bio {
-    flex-grow: 1;
-  }
-
-  .name {
-    font-size: 24px;
-    line-height: 18px * 1.5;
-    color: $text-color;
-
-    small {
-      display: block;
-      font-size: 14px;
-      color: $lighter-text-color;
-    }
-  }
-
-  .avatar {
-    width: 96px;
-    float: left;
-    margin-right: 10px;
-    padding: 10px;
-    padding-left: 9px;
-    margin-top: -30px;
-
-    img {
-      width: 94px;
-      height: 94px;
-      display: block;
-      border: 2px solid $lighter-text-color;
-      border-radius: 5px;
-    }
-  }
-}
 
 .activity-stream {
   clear: both;
   box-shadow: 4px 3px 0 rgba(0, 0, 0, 0.1);
 
   .entry {
-    border-bottom: 1px solid darken($background-color, 10%);
+    border-bottom: 1px solid $darker-background-color;
     background: $background-color;
     border-left: 2px solid $primary-color;
 
     &.entry-reblog {
       border-left: 2px solid $tertiary-color;
+
+      .content {
+        a {
+          color: $tertiary-color;
+        }
+      }
     }
 
     &.entry-predecessor, &.entry-successor {
       border-left: 2px solid $lighter-text-color;
       background: darken($background-color, 5%);
+
+      .content {
+        a {
+          color: $lighter-text-color;
+        }
+      }
     }
 
     &.entry-follow, &.entry-favourite {
@@ -92,9 +65,10 @@
   }
 
   .header {
-    margin-bottom: 10px;
+    margin-bottom: 5px;
     padding: 10px;
     padding-bottom: 0;
+    padding-left: 8px;
 
     .name {
       text-decoration: none;
@@ -113,7 +87,7 @@
   }
 
   .pre-header {
-    border-bottom: 1px solid darken($background-color, 10%);
+    border-bottom: 1px solid darken($background-color, 5%);
     color: $tertiary-color;
     padding: 5px 10px;
     padding-left: 8px;
@@ -131,9 +105,10 @@
   }
 
   .content {
-    font-size: 16px;
+    font-size: 14px;
     padding: 0 10px;
     padding-left: 8px;
+    padding-bottom: 25px;
 
     a {
       color: $primary-color;
@@ -153,24 +128,4 @@
       text-decoration: underline;
     }
   }
-
-  .counters {
-    margin-top: 15px;
-    color: $lighter-text-color;
-    cursor: default;
-    padding: 10px;
-    padding-top: 0;
-
-    .counter {
-      display: inline-block;
-      margin-right: 10px;
-      color: $lighter-text-color;
-    }
-
-    .conversation-link {
-      color: $primary-color;
-      text-decoration: underline;
-      float: right;
-    }
-  }
 }
diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
new file mode 100644
index 000000000..e96ef4a1c
--- /dev/null
+++ b/app/controllers/accounts_controller.rb
@@ -0,0 +1,16 @@
+class AccountsController < ApplicationController
+  before_action :set_account
+
+  def show
+    respond_to do |format|
+      format.html
+      format.atom
+    end
+  end
+
+  private
+
+  def set_account
+    @account = Account.find_by!(username: params[:username], domain: nil)
+  end
+end
diff --git a/app/controllers/api/salmon_controller.rb b/app/controllers/api/salmon_controller.rb
new file mode 100644
index 000000000..99ec15bff
--- /dev/null
+++ b/app/controllers/api/salmon_controller.rb
@@ -0,0 +1,14 @@
+class Api::SalmonController < ApplicationController
+  before_action :set_account
+
+  def update
+    ProcessInteractionService.new.(request.body.read, @account)
+    render nothing: true, status: 201
+  end
+
+  private
+
+  def set_account
+    @account = Account.find(params[:id])
+  end
+end
diff --git a/app/controllers/api/subscriptions_controller.rb b/app/controllers/api/subscriptions_controller.rb
new file mode 100644
index 000000000..56deae10c
--- /dev/null
+++ b/app/controllers/api/subscriptions_controller.rb
@@ -0,0 +1,28 @@
+class Api::SubscriptionsController < ApplicationController
+  before_action :set_account
+
+  def show
+    if @account.subscription(api_subscription_url(@account.id)).valid?(params['hub.topic'], params['hub.verify_token'])
+      render text: params['hub.challenge'], status: 200
+    else
+      render nothing: true, status: 404
+    end
+  end
+
+  def update
+    body = request.body.read
+
+    if @account.subscription(api_subscription_url(@account.id)).verify(body, env['HTTP_X_HUB_SIGNATURE'])
+      ProcessFeedService.new.(body, @account)
+      render nothing: true, status: 201
+    else
+      render nothing: true, status: 202
+    end
+  end
+
+  private
+
+  def set_account
+    @account = Account.find(params[:id])
+  end
+end
diff --git a/app/controllers/atom_controller.rb b/app/controllers/atom_controller.rb
deleted file mode 100644
index f9d8a4582..000000000
--- a/app/controllers/atom_controller.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-class AtomController < ApplicationController
-  before_filter :set_format
-
-  def user_stream
-    @account = Account.find_by!(id: params[:id], domain: nil)
-  end
-
-  def entry
-    @entry = StreamEntry.find(params[:id])
-  end
-
-  private
-
-  def set_format
-    request.format = 'xml'
-    response.headers['Content-Type'] = 'application/atom+xml'
-  end
-end
diff --git a/app/controllers/profile_controller.rb b/app/controllers/profile_controller.rb
deleted file mode 100644
index 30bd1a6b6..000000000
--- a/app/controllers/profile_controller.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-class ProfileController < ApplicationController
-  before_action :set_account
-
-  def show
-  end
-
-  def entry
-    @entry = @account.stream_entries.find(params[:id])
-    @type  = @entry.activity_type.downcase
-  end
-
-  private
-
-  def set_account
-    @account = Account.find_by!(username: params[:name], domain: nil)
-  end
-end
diff --git a/app/controllers/stream_entries_controller.rb b/app/controllers/stream_entries_controller.rb
new file mode 100644
index 000000000..c5aebb9da
--- /dev/null
+++ b/app/controllers/stream_entries_controller.rb
@@ -0,0 +1,23 @@
+class StreamEntriesController < ApplicationController
+  before_action :set_account
+  before_action :set_stream_entry
+
+  def show
+    @type = @stream_entry.activity_type.downcase
+
+    respond_to do |format|
+      format.html
+      format.atom
+    end
+  end
+
+  private
+
+  def set_account
+    @account = Account.find_by!(username: params[:account_username], domain: nil)
+  end
+
+  def set_stream_entry
+    @stream_entry = @account.stream_entries.find(params[:id])
+  end
+end
diff --git a/app/controllers/xrd_controller.rb b/app/controllers/xrd_controller.rb
index 417d4f4fa..28e0a47b8 100644
--- a/app/controllers/xrd_controller.rb
+++ b/app/controllers/xrd_controller.rb
@@ -9,6 +9,8 @@ class XrdController < ApplicationController
     @account = Account.find_by!(username: username_from_resource, domain: nil)
     @canonical_account_uri = "acct:#{@account.username}@#{LOCAL_DOMAIN}"
     @magic_key = pem_to_magic_key(@account.keypair.public_key)
+  rescue ActiveRecord::RecordNotFound
+    render nothing: true, status: 404
   end
 
   private
diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb
new file mode 100644
index 000000000..51fe6bc4e
--- /dev/null
+++ b/app/helpers/accounts_helper.rb
@@ -0,0 +1,3 @@
+module AccountsHelper
+
+end
diff --git a/app/helpers/api/salmon_helper.rb b/app/helpers/api/salmon_helper.rb
new file mode 100644
index 000000000..513c6fb7d
--- /dev/null
+++ b/app/helpers/api/salmon_helper.rb
@@ -0,0 +1,2 @@
+module Api::SalmonHelper
+end
diff --git a/app/helpers/api/subscriptions_helper.rb b/app/helpers/api/subscriptions_helper.rb
new file mode 100644
index 000000000..3796aee42
--- /dev/null
+++ b/app/helpers/api/subscriptions_helper.rb
@@ -0,0 +1,2 @@
+module Api::SubscriptionsHelper
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 90b45a025..5d696316d 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,6 +1,4 @@
 module ApplicationHelper
-  include RoutingHelper
-
   def unique_tag(date, id, type)
     "tag:#{LOCAL_DOMAIN},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}"
   end
@@ -13,24 +11,4 @@ module ApplicationHelper
   def local_id?(id)
     id.start_with?("tag:#{LOCAL_DOMAIN}")
   end
-
-  def subscription_url(account)
-    add_base_url_prefix subscriptions_path(id: account.id, format: '')
-  end
-
-  def salmon_url(account)
-    add_base_url_prefix salmon_path(id: account.id, format: '')
-  end
-
-  def profile_url(account)
-    account.local? ? super(name: account.username) : account.url
-  end
-
-  def status_url(status)
-    status.local? ? super(name: status.account.username, id: status.stream_entry.id) : status.url
-  end
-
-  def add_base_url_prefix(suffix)
-    File.join(root_url, "api", suffix)
-  end
 end
diff --git a/app/helpers/atom_helper.rb b/app/helpers/atom_builder_helper.rb
index bd7aee9a0..8e3652c70 100644
--- a/app/helpers/atom_helper.rb
+++ b/app/helpers/atom_builder_helper.rb
@@ -1,4 +1,4 @@
-module AtomHelper
+module AtomBuilderHelper
   def stream_updated_at
     @account.stream_entries.last ? (@account.updated_at > @account.stream_entries.last.created_at ? @account.updated_at : @account.stream_entries.last.created_at) : @account.updated_at
   end
@@ -97,10 +97,10 @@ module AtomHelper
     xml['thr'].send('in-reply-to', { ref: uri, href: url, type: 'text/html' })
   end
 
-  def disambiguate_uri(target)
+  def uri_for_target(target)
     if target.local?
       if target.object_type == :person
-        profile_url(target)
+        account_url(target)
       else
         unique_tag(target.stream_entry.created_at, target.stream_entry.activity_id, target.stream_entry.activity_type)
       end
@@ -109,12 +109,12 @@ module AtomHelper
     end
   end
 
-  def disambiguate_url(target)
+  def url_for_target(target)
     if target.local?
       if target.object_type == :person
-        profile_url(target)
+        account_url(target)
       else
-        status_url(target)
+        account_stream_entry_url(target.account, target.stream_entry)
       end
     else
       target.url
@@ -122,13 +122,13 @@ module AtomHelper
   end
 
   def link_mention(xml, account)
-    xml.link(rel: 'mentioned', href: disambiguate_uri(account))
+    xml.link(rel: 'mentioned', href: uri_for_target(account))
   end
 
   def link_avatar(xml, account)
-    xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '300', 'media:height' =>'300', 'href' => asset_url(account.avatar.url(:large)))
-    xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '96', 'media:height' =>'96', 'href' => asset_url(account.avatar.url(:medium)))
-    xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '48', 'media:height' =>'48', 'href' => asset_url(account.avatar.url(:small)))
+    xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '300', 'media:height' =>'300', 'href' => asset_url(account.avatar.url(:large, false)))
+    xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '96', 'media:height' =>'96', 'href' => asset_url(account.avatar.url(:medium, false)))
+    xml.link('rel' => 'avatar', 'type' => account.avatar_content_type, 'media:width' => '48', 'media:height' =>'48', 'href' => asset_url(account.avatar.url(:small, false)))
   end
 
   def logo(xml, url)
@@ -137,10 +137,10 @@ module AtomHelper
 
   def include_author(xml, account)
     object_type      xml, :person
-    uri              xml, profile_url(account)
+    uri              xml, url_for_target(account)
     name             xml, account.username
     summary          xml, account.note
-    link_alternate   xml, profile_url(account)
+    link_alternate   xml, url_for_target(account)
     link_avatar      xml, account
     portable_contact xml, account
   end
@@ -152,20 +152,20 @@ module AtomHelper
     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)
+    link_self    xml, account_stream_entry_url(stream_entry.account, stream_entry, format: 'atom')
     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)
+      in_reply_to xml, uri_for_target(stream_entry.thread), url_for_target(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)
+        simple_id      xml, uri_for_target(stream_entry.target)
         title          xml, stream_entry.target.title
-        link_alternate xml, disambiguate_url(stream_entry.target)
+        link_alternate xml, url_for_target(stream_entry.target)
 
         # People have summary and portable contacts information
         if stream_entry.target.object_type == :person
diff --git a/app/helpers/profile_helper.rb b/app/helpers/stream_entries_helper.rb
index 3a34dfbd8..c588f5ce7 100644
--- a/app/helpers/profile_helper.rb
+++ b/app/helpers/stream_entries_helper.rb
@@ -1,4 +1,4 @@
-module ProfileHelper
+module StreamEntriesHelper
   def display_name(account)
     account.display_name.blank? ? account.username : account.display_name
   end
diff --git a/app/models/account.rb b/app/models/account.rb
index 6cb638b3e..6a4aa16b4 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -76,6 +76,10 @@ class Account < ActiveRecord::Base
     @avatar_remote_url = url
   end
 
+  def to_param
+    self.username
+  end
+
   before_create do
     if local?
       keypair = OpenSSL::PKey::RSA.new(Rails.env.test? ? 1024 : 2048)
diff --git a/app/models/user.rb b/app/models/user.rb
index c54a5fab3..d871a760a 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,9 +1,4 @@
 class User < ActiveRecord::Base
   belongs_to :account, inverse_of: :user
-
   validates :account, presence: true
-
-  def timeline
-    StreamEntry.where(account_id: self.account.following, activity_type: 'Status').order('id desc')
-  end
 end
diff --git a/app/services/base_service.rb b/app/services/base_service.rb
index 0816b3503..c89d6eabc 100644
--- a/app/services/base_service.rb
+++ b/app/services/base_service.rb
@@ -1,3 +1,4 @@
 class BaseService
+  include RoutingHelper
   include ApplicationHelper
 end
diff --git a/app/services/follow_remote_account_service.rb b/app/services/follow_remote_account_service.rb
index f52a3a222..2f842e329 100644
--- a/app/services/follow_remote_account_service.rb
+++ b/app/services/follow_remote_account_service.rb
@@ -38,7 +38,7 @@ class FollowRemoteAccountService < BaseService
       account.secret       = SecureRandom.hex
       account.verify_token = SecureRandom.hex
 
-      subscription = account.subscription(subscription_url(account))
+      subscription = account.subscription(api_subscription_url(account.id))
       subscription.subscribe
 
       account.save!
diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb
index 55cb9bdca..b0bfdf4f2 100644
--- a/app/services/follow_service.rb
+++ b/app/services/follow_service.rb
@@ -9,7 +9,7 @@ class FollowService < BaseService
 
     follow = source_account.follow!(target_account)
     send_interaction_service.(follow.stream_entry, target_account)
-    source_account.ping!(atom_user_stream_url(id: source_account.id), [HUB_URL])
+    source_account.ping!(account_url(account, format: 'atom'), [HUB_URL])
   end
 
   private
diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb
index e19c0584e..aa25de4dc 100644
--- a/app/services/post_status_service.rb
+++ b/app/services/post_status_service.rb
@@ -7,7 +7,7 @@ class PostStatusService < BaseService
   def call(account, text, in_reply_to = nil)
     status = account.statuses.create!(text: text, thread: in_reply_to)
     process_mentions_service.(status)
-    account.ping!(atom_user_stream_url(id: account.id), [HUB_URL])
+    account.ping!(account_url(account, format: 'atom'), [HUB_URL])
     status
   end
 
diff --git a/app/services/process_interaction_service.rb b/app/services/process_interaction_service.rb
index 2ebaa5296..30cf5a771 100644
--- a/app/services/process_interaction_service.rb
+++ b/app/services/process_interaction_service.rb
@@ -43,7 +43,7 @@ class ProcessInteractionService < BaseService
   end
 
   def mentions_account?(xml, account)
-    xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]').each { |mention_link| return true if mention_link.attribute('href').value == profile_url(account) }
+    xml.xpath('/xmlns:entry/xmlns:link[@rel="mentioned"]').each { |mention_link| return true if mention_link.attribute('href').value == url_for_target(account) }
     false
   end
 
diff --git a/app/services/reblog_service.rb b/app/services/reblog_service.rb
index 4c76e5038..e3e091fa7 100644
--- a/app/services/reblog_service.rb
+++ b/app/services/reblog_service.rb
@@ -5,7 +5,7 @@ class ReblogService < BaseService
   # @return [Status]
   def call(account, reblogged_status)
     reblog = account.statuses.create!(reblog: reblogged_status, text: '')
-    account.ping!(atom_user_stream_url(id: account.id), [HUB_URL])
+    account.ping!(account_url(account, format: 'atom'), [HUB_URL])
     return reblog if reblogged_status.local?
     send_interaction_service.(reblog.stream_entry, reblogged_status.account)
     reblog
diff --git a/app/services/send_interaction_service.rb b/app/services/send_interaction_service.rb
index 5385831ed..e6708498f 100644
--- a/app/services/send_interaction_service.rb
+++ b/app/services/send_interaction_service.rb
@@ -1,5 +1,5 @@
 class SendInteractionService < BaseService
-  include AtomHelper
+  include AtomBuilderHelper
 
   # Send an Atom representation of an interaction to a remote Salmon endpoint
   # @param [StreamEntry] stream_entry
diff --git a/app/views/atom/user_stream.xml.ruby b/app/views/accounts/show.atom.ruby
index 00b2cdc14..12d2bb233 100644
--- a/app/views/atom/user_stream.xml.ruby
+++ b/app/views/accounts/show.atom.ruby
@@ -1,6 +1,6 @@
 Nokogiri::XML::Builder.new do |xml|
   feed(xml) do
-    simple_id  xml, atom_user_stream_url(id: @account.id)
+    simple_id  xml, account_url(@account, format: 'atom')
     title      xml, @account.display_name
     subtitle   xml, @account.note
     updated_at xml, stream_updated_at
@@ -10,10 +10,10 @@ Nokogiri::XML::Builder.new do |xml|
       include_author xml, @account
     end
 
-    link_alternate xml, profile_url(@account)
-    link_self      xml, atom_user_stream_url(id: @account.id)
+    link_alternate xml, url_for_target(@account)
+    link_self      xml, account_url(@account, format: 'atom')
     link_hub       xml, HUB_URL
-    link_salmon    xml, salmon_url(@account)
+    link_salmon    xml, api_salmon_url(@account.id)
 
     @account.stream_entries.order('id desc').each do |stream_entry|
       entry(xml, false) do
diff --git a/app/views/profile/show.html.haml b/app/views/accounts/show.html.haml
index c02bdddcf..113db12f6 100644
--- a/app/views/profile/show.html.haml
+++ b/app/views/accounts/show.html.haml
@@ -1,6 +1,6 @@
 - content_for :header_tags do
-  %link{ rel: 'salmon', href: salmon_url(@account) }/
-  %link{ rel: 'alternate', type: 'application/atom+xml', href: atom_user_stream_url(id: @account.id) }/
+  %link{ rel: 'salmon', href: api_salmon_url(@account.id) }/
+  %link{ rel: 'alternate', type: 'application/atom+xml', href: account_url(@account, format: 'atom') }/
 
 .card
   .avatar= image_tag @account.avatar.url(:medium)
@@ -11,4 +11,4 @@
 
 .activity-stream
   - @account.statuses.order('id desc').each do |status|
-    = render partial: 'status', locals: { status: status, include_threads: false, is_successor: false, is_predecessor: false }
+    = render partial: 'stream_entries/status', locals: { status: status, include_threads: false, is_successor: false, is_predecessor: false }
diff --git a/app/views/profile/_status_footer.html.haml b/app/views/profile/_status_footer.html.haml
deleted file mode 100644
index a2333df15..000000000
--- a/app/views/profile/_status_footer.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-- if status.reply?
-  = link_to "In response to #{status.thread.account.acct}", status_url(status.thread), class: 'conversation-link'
diff --git a/app/views/profile/_status_header.html.haml b/app/views/profile/_status_header.html.haml
deleted file mode 100644
index 225a89d71..000000000
--- a/app/views/profile/_status_header.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-= link_to profile_url(status.account), class: 'name' do
-  %strong= display_name(status.account)
-  = "@#{status.account.acct}"
-
-= link_to status_url(status), class: 'time' do
-  %span{ title: status.created_at }
-    = relative_time(status.created_at)
diff --git a/app/views/profile/entry.html.haml b/app/views/profile/entry.html.haml
deleted file mode 100644
index e4d5db300..000000000
--- a/app/views/profile/entry.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-- content_for :header_tags do
-  %link{ rel: 'alternate', type: 'application/atom+xml', href: atom_entry_url(id: @entry.id) }/
-
-.activity-stream
-  = render partial: @type, locals: { @type.to_sym => @entry.activity, include_threads: true, is_predecessor: false, is_successor: false }
diff --git a/app/views/profile/_favourite.html.haml b/app/views/stream_entries/_favourite.html.haml
index 85e3a0824..85e3a0824 100644
--- a/app/views/profile/_favourite.html.haml
+++ b/app/views/stream_entries/_favourite.html.haml
diff --git a/app/views/profile/_follow.html.haml b/app/views/stream_entries/_follow.html.haml
index c1c081374..c1c081374 100644
--- a/app/views/profile/_follow.html.haml
+++ b/app/views/stream_entries/_follow.html.haml
diff --git a/app/views/profile/_status.html.haml b/app/views/stream_entries/_status.html.haml
index b089036b1..89dd53613 100644
--- a/app/views/profile/_status.html.haml
+++ b/app/views/stream_entries/_status.html.haml
@@ -6,17 +6,23 @@
     .pre-header
       %i.fa.fa-retweet
       Shared by
-      = link_to display_name(status.account), profile_url(status.account), class: 'name'
+      = link_to display_name(status.account), url_for_target(status.account), class: 'name'
+
   .entry__container
     .avatar
       = image_tag avatar_for_status_url(status)
+
     .entry__container__container
       .header
-        = render partial: 'status_header', locals: { status: status.reblog? ? status.reblog : status }
+        = link_to url_for_target(status.reblog? ? status.reblog.account : status.account), class: 'name' do
+          %strong= display_name(status.reblog? ? status.reblog.account : status.account)
+          = "@#{status.reblog? ? status.reblog.account.acct : status.account.acct}"
+        = link_to url_for_target(status.reblog? ? status.reblog : status), class: 'time' do
+          %span{ title: status.reblog? ? status.reblog.created_at : status.created_at }
+            = relative_time(status.reblog? ? status.reblog.created_at : status.created_at)
+
       .content
         = status.content.html_safe
-      .counters
-        = render partial: 'status_footer', locals: { status: status.reblog? ? status.reblog : status }
 
 - if include_threads
   - status.replies.each do |status|
diff --git a/app/views/atom/entry.xml.ruby b/app/views/stream_entries/show.atom.ruby
index e0e089f46..e0e089f46 100644
--- a/app/views/atom/entry.xml.ruby
+++ b/app/views/stream_entries/show.atom.ruby
diff --git a/app/views/stream_entries/show.html.haml b/app/views/stream_entries/show.html.haml
new file mode 100644
index 000000000..6286daf53
--- /dev/null
+++ b/app/views/stream_entries/show.html.haml
@@ -0,0 +1,5 @@
+- content_for :header_tags do
+  %link{ rel: 'alternate', type: 'application/atom+xml', href: account_stream_entry_url(@account, @stream_entry, format: 'atom') }/
+
+.activity-stream
+  = render partial: @type, locals: { @type.to_sym => @stream_entry.activity, include_threads: true, is_predecessor: false, is_successor: false }
diff --git a/app/views/xrd/webfinger.xml.ruby b/app/views/xrd/webfinger.xml.ruby
index 42adc6551..ac0fd3d0e 100644
--- a/app/views/xrd/webfinger.xml.ruby
+++ b/app/views/xrd/webfinger.xml.ruby
@@ -1,10 +1,10 @@
 Nokogiri::XML::Builder.new do |xml|
   xml.XRD(xmlns: 'http://docs.oasis-open.org/ns/xri/xrd-1.0') do
     xml.Subject @canonical_account_uri
-    xml.Alias profile_url(@account)
-    xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: profile_url(@account))
+    xml.Alias url_for_target(@account)
+    xml.Link(rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: url_for_target(@account))
     xml.Link(rel: 'http://schemas.google.com/g/2010#updates-from', type: 'application/atom+xml', href: atom_user_stream_url(id: @account.id))
-    xml.Link(rel: 'salmon', href: salmon_url(@account))
+    xml.Link(rel: 'salmon', href: api_salmon_url(@account.id))
     xml.Link(rel: 'magic-public-key', href: @magic_key)
   end
 end.to_xml