about summary refs log tree commit diff
path: root/app/views
diff options
context:
space:
mode:
Diffstat (limited to 'app/views')
-rw-r--r--app/views/about/index.html.haml75
-rw-r--r--app/views/accounts/_header.html.haml10
-rw-r--r--app/views/accounts/show.atom.ruby3
-rw-r--r--app/views/accounts/show.html.haml5
-rw-r--r--app/views/admin/accounts/index.html.haml14
-rw-r--r--app/views/admin/accounts/show.html.haml50
-rw-r--r--app/views/admin/domain_blocks/index.html.haml1
-rw-r--r--app/views/admin/domain_blocks/new.html.haml18
-rw-r--r--app/views/admin/reports/index.html.haml32
-rw-r--r--app/views/admin/reports/show.html.haml44
-rw-r--r--app/views/admin/settings/index.html.haml18
-rw-r--r--app/views/api/v1/accounts/relationship.rabl1
-rw-r--r--app/views/api/v1/accounts/show.rabl8
-rw-r--r--app/views/api/v1/instances/show.rabl6
-rw-r--r--app/views/api/v1/media/create.rabl4
-rw-r--r--app/views/api/v1/mutes/index.rabl2
-rw-r--r--app/views/api/v1/notifications/show.rabl2
-rw-r--r--app/views/api/v1/reports/index.rabl2
-rw-r--r--app/views/api/v1/reports/show.rabl2
-rw-r--r--app/views/api/v1/search/index.rabl13
-rw-r--r--app/views/api/v1/statuses/_show.rabl4
-rw-r--r--app/views/doorkeeper/authorized_applications/index.html.haml23
-rw-r--r--app/views/layouts/admin.html.haml9
-rw-r--r--app/views/layouts/application.html.haml2
-rw-r--r--app/views/layouts/mailer.text.erb2
-rw-r--r--app/views/notification_mailer/_status.text.erb4
-rw-r--r--app/views/notification_mailer/digest.text.erb15
-rw-r--r--app/views/notification_mailer/favourite.text.erb4
-rw-r--r--app/views/notification_mailer/follow.text.erb4
-rw-r--r--app/views/notification_mailer/follow_request.text.erb4
-rw-r--r--app/views/notification_mailer/mention.text.erb4
-rw-r--r--app/views/notification_mailer/reblog.text.erb4
-rw-r--r--app/views/settings/exports/show.html.haml17
-rw-r--r--app/views/settings/imports/show.html.haml11
-rw-r--r--app/views/settings/preferences/show.html.haml3
-rw-r--r--app/views/settings/two_factor_auths/show.html.haml8
-rw-r--r--app/views/shared/_landing_strip.html.haml2
-rw-r--r--app/views/stream_entries/_detailed_status.html.haml16
-rw-r--r--app/views/stream_entries/_favourite.html.haml5
-rw-r--r--app/views/stream_entries/_follow.html.haml5
-rw-r--r--app/views/stream_entries/_media.html.haml4
-rw-r--r--app/views/stream_entries/_simple_status.html.haml21
-rw-r--r--app/views/stream_entries/_status.html.haml4
-rw-r--r--app/views/stream_entries/show.html.haml5
-rw-r--r--app/views/tags/show.html.haml14
45 files changed, 410 insertions, 94 deletions
diff --git a/app/views/about/index.html.haml b/app/views/about/index.html.haml
index 88bfe3d61..ebca4213a 100644
--- a/app/views/about/index.html.haml
+++ b/app/views/about/index.html.haml
@@ -20,18 +20,81 @@
     Mastodon
 
   %p= t('about.about_mastodon').html_safe
-  %p= t('about.about_instance', instance: Rails.configuration.x.local_domain).html_safe
 
-  .screenshot= image_tag 'screenshot.png'
+  .screenshot-with-signup
+    .mascot= image_tag 'fluffy-elephant-friend.png'
+
+    - if @open_registrations
+      = simple_form_for(@user, url: user_registration_path) do |f|
+        = f.simple_fields_for :account do |ff|
+          = ff.input :username, autofocus: true, placeholder: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username') }
+
+        = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }
+        = f.input :password, autocomplete: "off", placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password') }
+        = f.input :password_confirmation, autocomplete: "off", placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password') }
+
+        .actions
+          = f.button :button, t('about.get_started'), type: :submit
+
+        .info
+          = link_to t('auth.login'), new_user_session_path, class: 'webapp-btn'
+          ·
+          = link_to t('about.about_this'), about_more_path
+    - else
+      .closed-registrations-message
+        - if @closed_registrations_message.blank?
+          %p= t('about.closed_registrations')
+        - else
+          = @closed_registrations_message.html_safe
+        .info
+          = link_to t('auth.login'), new_user_session_path, class: 'webapp-btn'
+          ·
+          = link_to t('about.other_instances'), 'https://github.com/tootsuite/mastodon/blob/master/docs/Using-Mastodon/List-of-Mastodon-instances.md'
+          ·
+          = link_to t('about.about_this'), about_more_path
+
+  %h3= t('about.features_headline')
+
+  .features-list
+    .features-list__column
+      %ul.fa-ul
+        %li
+          = fa_icon('li check-square')
+          = t 'about.features.chronology'
+        %li
+          = fa_icon('li check-square')
+          = t 'about.features.public'
+        %li
+          = fa_icon('li check-square')
+          = t 'about.features.characters'
+        %li
+          = fa_icon('li check-square')
+          = t 'about.features.gifv'
+    .features-list__column
+      %ul.fa-ul
+        %li
+          = fa_icon('li check-square')
+          = t 'about.features.privacy'
+        %li
+          = fa_icon('li check-square')
+          = t 'about.features.blocks'
+        %li
+          = fa_icon('li check-square')
+          = t 'about.features.ethics'
+        %li
+          = fa_icon('li check-square')
+          = t 'about.features.api'
 
   - unless @description.blank?
+    %h3= t('about.description_headline', domain: Rails.configuration.x.local_domain)
     %p= @description.html_safe
 
   .actions
     .info
-      = link_to t('about.learn_more'), about_more_path
       = link_to t('about.terms'), terms_path
+      ·
+      = link_to t('about.apps'), 'https://github.com/tootsuite/mastodon/blob/master/docs/Using-Mastodon/Apps.md'
+      ·
       = link_to t('about.source_code'), 'https://github.com/tootsuite/mastodon'
-
-    = link_to t('about.get_started'), new_user_registration_path, class: 'button webapp-btn'
-    = link_to t('auth.login'), new_user_session_path, class: 'button webapp-btn'
+      ·
+      = link_to t('about.other_instances'), 'https://github.com/tootsuite/mastodon/blob/master/docs/Using-Mastodon/List-of-Mastodon-instances.md'
diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml
index f575e855e..0d43fba30 100644
--- a/app/views/accounts/_header.html.haml
+++ b/app/views/accounts/_header.html.haml
@@ -20,15 +20,15 @@
       .account__header__content.p-note.emojify= Formatter.instance.simplified_format(@account)
 
     .details-counters
-      .counter{ class: active_nav_class(account_url(@account)) }
-        = link_to account_url(@account), class: 'u-url u-uid' do
+      .counter{ class: active_nav_class(short_account_url(@account)) }
+        = link_to short_account_url(@account), class: 'u-url u-uid' do
           %span.counter-label= t('accounts.posts')
-          %span.counter-number= number_with_delimiter @account.statuses.count
+          %span.counter-number= number_with_delimiter @account.statuses_count
       .counter{ class: active_nav_class(following_account_url(@account)) }
         = link_to following_account_url(@account) do
           %span.counter-label= t('accounts.following')
-          %span.counter-number= number_with_delimiter @account.following.count
+          %span.counter-number= number_with_delimiter @account.following_count
       .counter{ class: active_nav_class(followers_account_url(@account)) }
         = link_to followers_account_url(@account) do
           %span.counter-label= t('accounts.followers')
-          %span.counter-number= number_with_delimiter @account.followers.count
+          %span.counter-number= number_with_delimiter @account.followers_count
diff --git a/app/views/accounts/show.atom.ruby b/app/views/accounts/show.atom.ruby
index a22568396..e15021178 100644
--- a/app/views/accounts/show.atom.ruby
+++ b/app/views/accounts/show.atom.ruby
@@ -6,7 +6,7 @@ Nokogiri::XML::Builder.new do |xml|
     title      xml, @account.display_name
     subtitle   xml, @account.note
     updated_at xml, stream_updated_at
-    logo       xml, full_asset_url(@account.avatar.url( :original))
+    logo       xml, full_asset_url(@account.avatar.url(:original))
 
     author(xml) do
       include_author xml, @account
@@ -14,6 +14,7 @@ Nokogiri::XML::Builder.new do |xml|
 
     link_alternate xml, TagManager.instance.url_for(@account)
     link_self      xml, account_url(@account, format: 'atom')
+    link_next      xml, account_url(@account, format: 'atom', max_id: @entries.last.id) if @entries.size == 20
     link_hub       xml, api_push_url
     link_salmon    xml, api_salmon_url(@account.id)
 
diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml
index c194ce33d..3b8c67b45 100644
--- a/app/views/accounts/show.html.haml
+++ b/app/views/accounts/show.html.haml
@@ -14,6 +14,9 @@
   %meta{ property: 'og:image:height', content: '120' }/
   %meta{ property: 'twitter:card', content: 'summary' }/
 
+- if !user_signed_in? && !Rails.configuration.x.single_user_mode
+  = render partial: 'shared/landing_strip', locals: { account: @account }
+
 .h-feed
   %data.p-name{ value: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/
 
@@ -28,4 +31,4 @@
 
   .pagination
     - if @statuses.size == 20
-      = link_to safe_join([t('pagination.next'), fa_icon('chevron-right')], ' '), account_url(@account, max_id: @statuses.last.id), class: 'next_page', rel: 'next'
+      = link_to safe_join([t('pagination.next'), fa_icon('chevron-right')], ' '), short_account_url(@account, max_id: @statuses.last.id), class: 'next_page', rel: 'next'
diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml
index a93aa9143..f8ed4ef97 100644
--- a/app/views/admin/accounts/index.html.haml
+++ b/app/views/admin/accounts/index.html.haml
@@ -25,9 +25,7 @@
     %tr
       %th Username
       %th Domain
-      %th Subscribed
-      %th Silenced
-      %th Suspended
+      %th= fa_icon 'paper-plane-o'
       %th
   %tbody
     - @accounts.each do |account|
@@ -44,16 +42,6 @@
           - else
             %i.fa.fa-times
         %td
-          - if account.silenced?
-            %i.fa.fa-check
-          - else
-            %i.fa.fa-times
-        %td
-          - if account.suspended?
-            %i.fa.fa-check
-          - else
-            %i.fa.fa-times
-        %td
           = table_link_to 'circle', 'Web', web_path("accounts/#{account.id}")
           = table_link_to 'globe', 'Public', TagManager.instance.url_for(account)
           = table_link_to 'pencil', 'Edit', admin_account_path(account.id)
diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml
index 7d3f449e5..ba1c3bae7 100644
--- a/app/views/admin/accounts/show.html.haml
+++ b/app/views/admin/accounts/show.html.haml
@@ -18,8 +18,15 @@
         %th E-mail
         %td= @account.user.email
       %tr
-        %th Current IP
+        %th Most recent IP
         %td= @account.user.current_sign_in_ip
+      %tr
+        %th Most recent activity
+        %td
+          - if @account.user.current_sign_in_at
+            = l @account.user.current_sign_in_at
+          - else
+            Never
     - else
       %tr
         %th Profile URL
@@ -27,14 +34,39 @@
       %tr
         %th Feed URL
         %td= link_to @account.remote_url
+      %tr
+        %th PuSH subscription expires
+        %td
+          - if @account.subscribed?
+            = l @account.subscription_expires_at
+          - else
+            Not subscribed
+      %tr
+        %th Salmon URL
+        %td= link_to @account.salmon_url
 
-= simple_form_for @account, url: admin_account_path(@account.id) do |f|
-  = render 'shared/error_messages', object: @account
-
-  = f.input :silenced, as: :boolean, wrapper: :with_label
-  = f.input :suspended, as: :boolean, wrapper: :with_label
+    %tr
+      %th Follows
+      %td= @account.following_count
+    %tr
+      %th Followers
+      %td= @account.followers_count
+    %tr
+      %th Statuses
+      %td= @account.statuses_count
+    %tr
+      %th Media attachments
+      %td
+        = @account.media_attachments.count
+        = surround '(', ')' do
+          = number_to_human_size @account.media_attachments.sum('file_file_size')
 
-  .actions
-    = f.button :button, t('generic.save_changes'), type: :submit
+- if @account.silenced?
+  = link_to 'Undo silence', unsilence_admin_account_path(@account.id), method: :post, class: 'button'
+- else
+  = link_to 'Silence', silence_admin_account_path(@account.id), method: :post, class: 'button'
 
-= link_to 'Perform full suspension', suspend_admin_account_path(@account.id), method: :post, data: { confirm: 'Are you sure?' }, class: 'button'
+- if @account.suspended?
+  = link_to 'Undo suspension', unsuspend_admin_account_path(@account.id), method: :post, class: 'button'
+- else
+  = link_to 'Perform full suspension', suspend_admin_account_path(@account.id), method: :post, data: { confirm: 'Are you sure?' }, class: 'button'
diff --git a/app/views/admin/domain_blocks/index.html.haml b/app/views/admin/domain_blocks/index.html.haml
index dbaeb4716..eb7894b86 100644
--- a/app/views/admin/domain_blocks/index.html.haml
+++ b/app/views/admin/domain_blocks/index.html.haml
@@ -14,3 +14,4 @@
         %td= block.severity
 
 = will_paginate @blocks, pagination_options
+= link_to 'Add new', new_admin_domain_block_path, class: 'button'
diff --git a/app/views/admin/domain_blocks/new.html.haml b/app/views/admin/domain_blocks/new.html.haml
new file mode 100644
index 000000000..fbd39d6cf
--- /dev/null
+++ b/app/views/admin/domain_blocks/new.html.haml
@@ -0,0 +1,18 @@
+- content_for :page_title do
+  New domain block
+
+= simple_form_for @domain_block, url: admin_domain_blocks_path do |f|
+  = render 'shared/error_messages', object: @domain_block
+
+  %p.hint The domain block will not prevent creation of account entries in the database, but will retroactively and automatically apply specific moderation methods on those accounts.
+
+  = f.input :domain, placeholder: 'Domain'
+  = f.input :severity, collection: DomainBlock.severities.keys, wrapper: :with_label, include_blank: false
+
+  %p.hint
+    %strong Silence
+    will make the account's posts invisible to anyone who isn't following them.
+    %strong Suspend
+    will remove all of the account's content, media, and profile data.
+  .actions
+    = f.button :button, 'Create block', type: :submit
diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml
new file mode 100644
index 000000000..839259dc2
--- /dev/null
+++ b/app/views/admin/reports/index.html.haml
@@ -0,0 +1,32 @@
+- content_for :page_title do
+  Reports
+
+.filters
+  .filter-subset
+    %strong Status
+    %ul
+      %li= filter_link_to 'Unresolved', action_taken: nil
+      %li= filter_link_to 'Resolved', action_taken: '1'
+
+= form_tag do
+
+  %table.table
+    %thead
+      %tr
+        %th
+        %th ID
+        %th Target
+        %th Reported by
+        %th Comment
+        %th
+    %tbody
+      - @reports.each do |report|
+        %tr
+          %td= check_box_tag 'select', report.id
+          %td= "##{report.id}"
+          %td= link_to report.target_account.acct, admin_account_path(report.target_account.id)
+          %td= link_to report.account.acct, admin_account_path(report.account.id)
+          %td= truncate(report.comment, length: 30, separator: ' ')
+          %td= table_link_to 'circle', 'View', admin_report_path(report)
+
+= will_paginate @reports, pagination_options
diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml
new file mode 100644
index 000000000..caa8415df
--- /dev/null
+++ b/app/views/admin/reports/show.html.haml
@@ -0,0 +1,44 @@
+- content_for :page_title do
+  = "Report ##{@report.id}"
+
+.report-accounts
+  .report-accounts__item
+    %strong Reported account:
+    = render partial: 'authorize_follow/card', locals: { account: @report.target_account }
+  .report-accounts__item
+    %strong Reported by:
+    = render partial: 'authorize_follow/card', locals: { account: @report.account }
+
+%p
+  %strong Comment:
+  - if @report.comment.blank?
+    None
+  - else
+    = @report.comment
+
+- unless @statuses.empty?
+  %hr/
+
+  - @statuses.each do |status|
+    .report-status
+      .activity-stream.activity-stream-headless
+        .entry= render partial: 'stream_entries/simple_status', locals: { status: status }
+      .report-status__actions
+        = link_to remove_admin_report_path(@report, status_id: status.id), method: :post, class: 'icon-button', style: 'font-size: 24px; width: 24px; height: 24px', title: 'Delete' do
+          = fa_icon 'trash'
+
+- if !@report.action_taken?
+  %hr/
+
+  %div{ style: 'overflow: hidden' }
+    %div{ style: 'float: right' }
+      = link_to 'Silence account', silence_admin_report_path(@report), method: :post, class: 'button'
+      = link_to 'Suspend account', suspend_admin_report_path(@report), method: :post, class: 'button'
+    %div{ style: 'float: left' }
+      = link_to 'Mark as resolved', resolve_admin_report_path(@report), method: :post, class: 'button'
+- elsif !@report.action_taken_by_account.nil?
+  %hr/
+
+  %p
+    %strong Action taken by:
+    = @report.action_taken_by_account.acct
diff --git a/app/views/admin/settings/index.html.haml b/app/views/admin/settings/index.html.haml
index 5b482213b..02faac8c2 100644
--- a/app/views/admin/settings/index.html.haml
+++ b/app/views/admin/settings/index.html.haml
@@ -17,6 +17,10 @@
       %td= best_in_place @settings['site_contact_email'], :value, url: admin_setting_path(@settings['site_contact_email']), place_holder: 'Enter a public e-mail address'
     %tr
       %td
+        %strong Site title
+      %td= best_in_place @settings['site_title'], :value, url: admin_setting_path(@settings['site_title'])
+    %tr
+      %td
         %strong Site description
         %br/
         Displayed as a paragraph on the frontpage and used as a meta tag.
@@ -33,4 +37,16 @@
         Displayed on extended information page
         %br/
         You can use HTML tags
-      %td= best_in_place @settings['site_extended_description'], :value, as: :textarea, url: admin_setting_path(@settings['site_extended_description'])
\ No newline at end of file
+      %td= best_in_place @settings['site_extended_description'], :value, as: :textarea, url: admin_setting_path(@settings['site_extended_description'])
+    %tr
+      %td
+        %strong Open registration
+      %td= best_in_place @settings['open_registrations'], :value, as: :checkbox, collection: { false: 'Disabled', true: 'Enabled'}, url: admin_setting_path(@settings['open_registrations'])
+    %tr
+      %td
+        %strong Closed registration message
+        %br/
+        Displayed on frontpage when registrations are closed
+        %br/
+        You can use HTML tags
+      %td= best_in_place @settings['closed_registrations_message'], :value, as: :textarea, url: admin_setting_path(@settings['closed_registrations_message'])
diff --git a/app/views/api/v1/accounts/relationship.rabl b/app/views/api/v1/accounts/relationship.rabl
index 22b37586e..d6f1dd48a 100644
--- a/app/views/api/v1/accounts/relationship.rabl
+++ b/app/views/api/v1/accounts/relationship.rabl
@@ -4,4 +4,5 @@ attribute :id
 node(:following)   { |account| @following[account.id]   || false }
 node(:followed_by) { |account| @followed_by[account.id] || false }
 node(:blocking)    { |account| @blocking[account.id]    || false }
+node(:muting)      { |account| @muting[account.id]      || false }
 node(:requested)   { |account| @requested[account.id]   || false }
diff --git a/app/views/api/v1/accounts/show.rabl b/app/views/api/v1/accounts/show.rabl
index 151a5080d..32df0457a 100644
--- a/app/views/api/v1/accounts/show.rabl
+++ b/app/views/api/v1/accounts/show.rabl
@@ -1,11 +1,11 @@
 object @account
 
-attributes :id, :username, :acct, :display_name, :locked
+attributes :id, :username, :acct, :display_name, :locked, :created_at
 
 node(:note)            { |account| Formatter.instance.simplified_format(account) }
 node(:url)             { |account| TagManager.instance.url_for(account) }
 node(:avatar)          { |account| full_asset_url(account.avatar.url(:original)) }
 node(:header)          { |account| full_asset_url(account.header.url(:original)) }
-node(:followers_count) { |account| defined?(@followers_counts_map) ? (@followers_counts_map[account.id] || 0) : (account.try(:followers_count) || account.followers.count) }
-node(:following_count) { |account| defined?(@following_counts_map) ? (@following_counts_map[account.id] || 0) : (account.try(:following_count) || account.following.count) }
-node(:statuses_count)  { |account| defined?(@statuses_counts_map)  ? (@statuses_counts_map[account.id]  || 0) : (account.try(:statuses_count)  || account.statuses.count) }
+node(:followers_count) { |account| defined?(@followers_counts_map) ? (@followers_counts_map[account.id] || 0) : account.followers_count }
+node(:following_count) { |account| defined?(@following_counts_map) ? (@following_counts_map[account.id] || 0) : account.following_count }
+node(:statuses_count)  { |account| defined?(@statuses_counts_map)  ? (@statuses_counts_map[account.id]  || 0) : account.statuses_count }
diff --git a/app/views/api/v1/instances/show.rabl b/app/views/api/v1/instances/show.rabl
new file mode 100644
index 000000000..88eb08a9e
--- /dev/null
+++ b/app/views/api/v1/instances/show.rabl
@@ -0,0 +1,6 @@
+object false
+
+node(:uri)         { Rails.configuration.x.local_domain }
+node(:title)       { Setting.site_title }
+node(:description) { Setting.site_description }
+node(:email)       { Setting.site_contact_email }
diff --git a/app/views/api/v1/media/create.rabl b/app/views/api/v1/media/create.rabl
index 0b42e6e3d..916217cbd 100644
--- a/app/views/api/v1/media/create.rabl
+++ b/app/views/api/v1/media/create.rabl
@@ -1,5 +1,5 @@
 object @media
 attribute :id, :type
-node(:url) { |media| full_asset_url(media.file.url( :original)) }
-node(:preview_url) { |media| full_asset_url(media.file.url( :small)) }
+node(:url) { |media| full_asset_url(media.file.url(:original)) }
+node(:preview_url) { |media| full_asset_url(media.file.url(:small)) }
 node(:text_url) { |media| medium_url(media) }
diff --git a/app/views/api/v1/mutes/index.rabl b/app/views/api/v1/mutes/index.rabl
new file mode 100644
index 000000000..9f3b13a53
--- /dev/null
+++ b/app/views/api/v1/mutes/index.rabl
@@ -0,0 +1,2 @@
+collection @accounts
+extends 'api/v1/accounts/show'
diff --git a/app/views/api/v1/notifications/show.rabl b/app/views/api/v1/notifications/show.rabl
index fe2218ed7..ca34f2d5d 100644
--- a/app/views/api/v1/notifications/show.rabl
+++ b/app/views/api/v1/notifications/show.rabl
@@ -1,6 +1,6 @@
 object @notification
 
-attributes :id, :type
+attributes :id, :type, :created_at
 
 child from_account: :account do
   extends 'api/v1/accounts/show'
diff --git a/app/views/api/v1/reports/index.rabl b/app/views/api/v1/reports/index.rabl
new file mode 100644
index 000000000..4f0794027
--- /dev/null
+++ b/app/views/api/v1/reports/index.rabl
@@ -0,0 +1,2 @@
+collection @reports
+extends 'api/v1/reports/show'
diff --git a/app/views/api/v1/reports/show.rabl b/app/views/api/v1/reports/show.rabl
new file mode 100644
index 000000000..006db51e3
--- /dev/null
+++ b/app/views/api/v1/reports/show.rabl
@@ -0,0 +1,2 @@
+object @report
+attributes :id, :action_taken
diff --git a/app/views/api/v1/search/index.rabl b/app/views/api/v1/search/index.rabl
new file mode 100644
index 000000000..8d1640f2d
--- /dev/null
+++ b/app/views/api/v1/search/index.rabl
@@ -0,0 +1,13 @@
+object @search
+
+child :accounts, object_root: false do
+  extends 'api/v1/accounts/show'
+end
+
+node(:hashtags) do |search|
+  search.hashtags.map(&:name)
+end
+
+child :statuses, object_root: false do
+  extends 'api/v1/statuses/show'
+end
diff --git a/app/views/api/v1/statuses/_show.rabl b/app/views/api/v1/statuses/_show.rabl
index 059e0d13f..54e8a86d8 100644
--- a/app/views/api/v1/statuses/_show.rabl
+++ b/app/views/api/v1/statuses/_show.rabl
@@ -3,8 +3,8 @@ attributes :id, :created_at, :in_reply_to_id, :in_reply_to_account_id, :sensitiv
 node(:uri)              { |status| TagManager.instance.uri_for(status) }
 node(:content)          { |status| Formatter.instance.format(status) }
 node(:url)              { |status| TagManager.instance.url_for(status) }
-node(:reblogs_count)    { |status| defined?(@reblogs_counts_map)    ? (@reblogs_counts_map[status.id]    || 0) : status.reblogs.count }
-node(:favourites_count) { |status| defined?(@favourites_counts_map) ? (@favourites_counts_map[status.id] || 0) : status.favourites.count }
+node(:reblogs_count)    { |status| defined?(@reblogs_counts_map)    ? (@reblogs_counts_map[status.id]    || 0) : status.reblogs_count }
+node(:favourites_count) { |status| defined?(@favourites_counts_map) ? (@favourites_counts_map[status.id] || 0) : status.favourites_count }
 
 child :application do
   extends 'api/v1/apps/show'
diff --git a/app/views/doorkeeper/authorized_applications/index.html.haml b/app/views/doorkeeper/authorized_applications/index.html.haml
new file mode 100644
index 000000000..d4719881c
--- /dev/null
+++ b/app/views/doorkeeper/authorized_applications/index.html.haml
@@ -0,0 +1,23 @@
+- content_for :page_title do
+  = t('doorkeeper.authorized_applications.index.title')
+
+%table.table
+  %thead
+    %tr
+      %th= t('doorkeeper.authorized_applications.index.application')
+      %th= t('doorkeeper.authorized_applications.index.scopes')
+      %th= t('doorkeeper.authorized_applications.index.created_at')
+      %th
+  %tbody
+    - @applications.each do |application|
+      %tr
+        %td
+          - if application.website.blank?
+            = application.name
+          - else
+            = link_to application.name, application.website
+        %th= application.scopes.map { |scope| t(scope, scope: [:doorkeeper, :scopes]) }.join('<br />').html_safe
+        %td= l application.created_at
+        %td
+          - unless application.superapp?
+            = table_link_to 'times', t('doorkeeper.authorized_applications.buttons.revoke'), oauth_authorized_application_path(application), method: :delete, data: { confirm: t('doorkeeper.authorized_applications.confirmations.revoke') }
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index 750d6036f..59fe078df 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -12,6 +12,15 @@
     .content-wrapper
       .content
         %h2= yield :page_title
+
+        - if flash[:notice]
+          .flash-message.notice
+            %strong= flash[:notice]
+
+        - if flash[:alert]
+          .flash-message.alert
+            %strong= flash[:alert]
+
         = yield
 
 = render template: "layouts/application", locals: { body_classes: 'admin' }
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index e122e1c55..7eae6982b 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -13,7 +13,7 @@
 
     %title
       = "#{yield(:page_title)} - " if content_for?(:page_title)
-      Mastodon
+      = Setting.site_title
 
     = stylesheet_link_tag 'application', media: 'all'
     = csrf_meta_tags
diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb
index ae52173b5..21bf444c3 100644
--- a/app/views/layouts/mailer.text.erb
+++ b/app/views/layouts/mailer.text.erb
@@ -1,5 +1,5 @@
 <%= yield %>
-
 ---
 
 <%= t('application_mailer.signature', instance: Rails.configuration.x.local_domain) %>
+<%= t('application_mailer.settings', link: settings_preferences_url) %>
diff --git a/app/views/notification_mailer/_status.text.erb b/app/views/notification_mailer/_status.text.erb
index b089a7b73..85a0136b7 100644
--- a/app/views/notification_mailer/_status.text.erb
+++ b/app/views/notification_mailer/_status.text.erb
@@ -1,3 +1,3 @@
-<%= strip_tags(@status.content) %>
+<%= raw Formatter.instance.plaintext(status) %>
 
-<%= web_url("statuses/#{@status.id}") %>
+<%= raw t('application_mailer.view')%> <%= web_url("statuses/#{status.id}") %>
diff --git a/app/views/notification_mailer/digest.text.erb b/app/views/notification_mailer/digest.text.erb
new file mode 100644
index 000000000..95aed6793
--- /dev/null
+++ b/app/views/notification_mailer/digest.text.erb
@@ -0,0 +1,15 @@
+<%= display_name(@me) %>,
+
+<%= raw t('notification_mailer.digest.body', since: @since, instance: root_url) %>
+<% @notifications.each do |notification| %>
+
+* <%= raw t('notification_mailer.digest.mention', name: notification.from_account.acct) %>
+
+  <%= raw Formatter.instance.plaintext(notification.target_status) %>
+
+  <%= raw t('application_mailer.view')%> <%= web_url("statuses/#{notification.target_status.id}") %>
+<% end %>
+<% if @follows_since > 0 %>
+
+<%= raw t('notification_mailer.digest.new_followers_summary', count: @follows_since) %>
+<% end %>
diff --git a/app/views/notification_mailer/favourite.text.erb b/app/views/notification_mailer/favourite.text.erb
index b2e1e3e9e..99852592f 100644
--- a/app/views/notification_mailer/favourite.text.erb
+++ b/app/views/notification_mailer/favourite.text.erb
@@ -1,5 +1,5 @@
 <%= display_name(@me) %>,
 
-<%= t('notification_mailer.favourite.body', name: @account.acct) %>
+<%= raw t('notification_mailer.favourite.body', name: @account.acct) %>
 
-<%= render partial: 'status' %>
+<%= render partial: 'status', locals: { status: @status } %>
diff --git a/app/views/notification_mailer/follow.text.erb b/app/views/notification_mailer/follow.text.erb
index 4b2ec142c..af41a3080 100644
--- a/app/views/notification_mailer/follow.text.erb
+++ b/app/views/notification_mailer/follow.text.erb
@@ -1,5 +1,5 @@
 <%= display_name(@me) %>,
 
-<%= t('notification_mailer.follow.body', name: @account.acct) %>
+<%= raw t('notification_mailer.follow.body', name: @account.acct) %>
 
-<%= web_url("accounts/#{@account.id}") %>
+<%= raw t('application_mailer.view')%> <%= web_url("accounts/#{@account.id}") %>
diff --git a/app/views/notification_mailer/follow_request.text.erb b/app/views/notification_mailer/follow_request.text.erb
index c0d38ec67..49087a575 100644
--- a/app/views/notification_mailer/follow_request.text.erb
+++ b/app/views/notification_mailer/follow_request.text.erb
@@ -1,5 +1,5 @@
 <%= display_name(@me) %>,
 
-<%= t('notification_mailer.follow_request.body', name: @account.acct) %>
+<%= raw t('notification_mailer.follow_request.body', name: @account.acct) %>
 
-<%= web_url("follow_requests") %>
+<%= raw t('application_mailer.view')%> <%= web_url("follow_requests") %>
diff --git a/app/views/notification_mailer/mention.text.erb b/app/views/notification_mailer/mention.text.erb
index 31a294bb9..c0d4be1d8 100644
--- a/app/views/notification_mailer/mention.text.erb
+++ b/app/views/notification_mailer/mention.text.erb
@@ -1,5 +1,5 @@
 <%= display_name(@me) %>,
 
-<%= t('notification_mailer.mention.body', name: @status.account.acct) %>
+<%= raw t('notification_mailer.mention.body', name: @status.account.acct) %>
 
-<%= render partial: 'status' %>
+<%= render partial: 'status', locals: { status: @status } %>
diff --git a/app/views/notification_mailer/reblog.text.erb b/app/views/notification_mailer/reblog.text.erb
index 7af8052ca..c32b48650 100644
--- a/app/views/notification_mailer/reblog.text.erb
+++ b/app/views/notification_mailer/reblog.text.erb
@@ -1,5 +1,5 @@
 <%= display_name(@me) %>,
 
-<%= t('notification_mailer.reblog.body', name: @account.acct) %>
+<%= raw t('notification_mailer.reblog.body', name: @account.acct) %>
 
-<%= render partial: 'status' %>
+<%= render partial: 'status', locals: { status: @status } %>
diff --git a/app/views/settings/exports/show.html.haml b/app/views/settings/exports/show.html.haml
new file mode 100644
index 000000000..0a0ff8633
--- /dev/null
+++ b/app/views/settings/exports/show.html.haml
@@ -0,0 +1,17 @@
+- content_for :page_title do
+  = t('settings.export')
+
+%table.table
+  %tbody
+    %tr
+      %th= t('exports.storage')
+      %td= number_to_human_size @total_storage
+      %td
+    %tr
+      %th= t('exports.follows')
+      %td= @total_follows
+      %td= table_link_to 'download', t('exports.csv'), follows_settings_export_path(format: :csv)
+    %tr
+      %th= t('exports.blocks')
+      %td= @total_blocks
+      %td= table_link_to 'download', t('exports.csv'), blocks_settings_export_path(format: :csv)
diff --git a/app/views/settings/imports/show.html.haml b/app/views/settings/imports/show.html.haml
new file mode 100644
index 000000000..8502913dc
--- /dev/null
+++ b/app/views/settings/imports/show.html.haml
@@ -0,0 +1,11 @@
+- content_for :page_title do
+  = t('settings.import')
+
+%p.hint= t('imports.preface')
+
+= simple_form_for @import, url: settings_import_path do |f|
+  = f.input :type, collection: Import.types.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |type| I18n.t("imports.types.#{type}") }
+  = f.input :data, wrapper: :with_label, hint: t('simple_form.hints.imports.data')
+
+  .actions
+    = f.button :button, t('imports.upload'), type: :submit
diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml
index aee0540d2..64cf32c3a 100644
--- a/app/views/settings/preferences/show.html.haml
+++ b/app/views/settings/preferences/show.html.haml
@@ -7,7 +7,7 @@
   .fields-group
     = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| human_locale(locale) }
 
-    = f.input :setting_default_privacy, collection: Status.visibilities.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| I18n.t("statuses.visibilities.#{visibility}") }, required: false
+    = f.input :setting_default_privacy, collection: Status.visibilities.keys - ['direct'], wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| I18n.t("statuses.visibilities.#{visibility}") }, required: false
 
   .fields-group
     = f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff|
@@ -16,6 +16,7 @@
       = ff.input :reblog, as: :boolean, wrapper: :with_label
       = ff.input :favourite, as: :boolean, wrapper: :with_label
       = ff.input :mention, as: :boolean, wrapper: :with_label
+      = ff.input :digest, as: :boolean, wrapper: :with_label
 
   = f.simple_fields_for :interactions, hash_to_object(current_user.settings.interactions) do |ff|
     = ff.input :must_be_follower, as: :boolean, wrapper: :with_label
diff --git a/app/views/settings/two_factor_auths/show.html.haml b/app/views/settings/two_factor_auths/show.html.haml
index bad359f8f..87bfadc69 100644
--- a/app/views/settings/two_factor_auths/show.html.haml
+++ b/app/views/settings/two_factor_auths/show.html.haml
@@ -3,11 +3,15 @@
 
 .simple_form
   - if current_user.otp_required_for_login
-    %p= t('two_factor_auth.instructions_html')
+    %p.hint= t('two_factor_auth.instructions_html')
 
     .qr-code= raw @qrcode.as_svg(padding: 0, module_size: 5)
 
+    %p.hint= t('two_factor_auth.plaintext_secret_html', secret: current_user.otp_secret)
+
+    %p.hint= t('two_factor_auth.warning')
+
     = link_to t('two_factor_auth.disable'), disable_settings_two_factor_auth_path, data: { method: 'POST' }, class: 'block-button'
   - else
-    %p= t('two_factor_auth.description_html')
+    %p.hint= t('two_factor_auth.description_html')
     = link_to t('two_factor_auth.enable'), enable_settings_two_factor_auth_path, data: { method: 'POST' }, class: 'block-button'
diff --git a/app/views/shared/_landing_strip.html.haml b/app/views/shared/_landing_strip.html.haml
new file mode 100644
index 000000000..bb081e544
--- /dev/null
+++ b/app/views/shared/_landing_strip.html.haml
@@ -0,0 +1,2 @@
+.landing-strip
+  = t('landing_strip_html', name: display_name(account), domain: Rails.configuration.x.local_domain, sign_up_path: new_user_registration_path)
diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml
index 235dc6086..8495f28b9 100644
--- a/app/views/stream_entries/_detailed_status.html.haml
+++ b/app/views/stream_entries/_detailed_status.html.haml
@@ -9,8 +9,10 @@
 
   .status__content.e-content.p-name.emojify<
     - unless status.spoiler_text.blank?
-      %p= status.spoiler_text
-    = Formatter.instance.format(status)
+      %p{ style: 'margin-bottom: 0' }<
+        %span>= "#{status.spoiler_text} "
+        %a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more')
+    %div{ style: "display: #{status.spoiler_text.blank? ? 'block' : 'none'}; direction: #{rtl?(status.content) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status)
 
   - unless status.media_attachments.empty?
     - if status.media_attachments.first.video?
@@ -22,9 +24,9 @@
       .detailed-status__attachments
         - if status.sensitive?
           = render partial: 'stream_entries/content_spoiler'
-        - status.media_attachments.each do |media|
-          .media-item
-            = link_to '', (media.remote_url.blank? ? media.file.url(:original) : media.remote_url), style: "background-image: url(#{media.file.url(:original)})", target: '_blank', rel: 'noopener', class: "u-#{media.video? ? 'video' : 'photo'}"
+        .status__attachments__inner
+          - status.media_attachments.each do |media|
+            = render partial: 'stream_entries/media', locals: { media: media }
 
   %div.detailed-status__meta
     %data.dt-published{ value: status.created_at.to_time.iso8601 }
@@ -39,11 +41,11 @@
       ·
     %span<
       = fa_icon('retweet')
-      %span= status.reblogs.count
+      %span= status.reblogs_count
     ·
     %span<
       = fa_icon('star')
-      %span= status.favourites.count
+      %span= status.favourites_count
 
     - if user_signed_in?
       ·
diff --git a/app/views/stream_entries/_favourite.html.haml b/app/views/stream_entries/_favourite.html.haml
deleted file mode 100644
index ea4879328..000000000
--- a/app/views/stream_entries/_favourite.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-.entry.entry-favourite
-  .content.emojify
-    %strong= favourite.account.acct
-    = t('stream_entries.favourited')
-    %strong= favourite.status.account.acct
diff --git a/app/views/stream_entries/_follow.html.haml b/app/views/stream_entries/_follow.html.haml
deleted file mode 100644
index da6d062f0..000000000
--- a/app/views/stream_entries/_follow.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-.entry.entry-follow
-  .content.emojify
-    %strong= link_to follow.account.acct, account_path(follow.account)
-    = t('stream_entries.is_now_following')
-    %strong= link_to follow.target_account.acct, TagManager.instance.url_for(follow.target_account)
diff --git a/app/views/stream_entries/_media.html.haml b/app/views/stream_entries/_media.html.haml
new file mode 100644
index 000000000..cd7faa700
--- /dev/null
+++ b/app/views/stream_entries/_media.html.haml
@@ -0,0 +1,4 @@
+.media-item
+  = link_to media.remote_url.blank? ? media.file.url(:original) : media.remote_url, style: media.image? ? "background-image: url(#{media.file.url(:original)})" : "", target: '_blank', rel: 'noopener', class: "u-#{media.video? || media.gifv? ? 'video' : 'photo'}" do
+    - unless media.image?
+      %video{ src: media.file.url(:original), autoplay: true, loop: true }/
diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml
index 95f90abd9..2eb9bf166 100644
--- a/app/views/stream_entries/_simple_status.html.haml
+++ b/app/views/stream_entries/_simple_status.html.haml
@@ -14,19 +14,22 @@
 
   .status__content.e-content.p-name.emojify<
     - unless status.spoiler_text.blank?
-      %p= status.spoiler_text
-    = Formatter.instance.format(status)
+      %p{ style: 'margin-bottom: 0' }<
+        %span>= "#{status.spoiler_text} "
+        %a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more')
+    %div{ style: "display: #{status.spoiler_text.blank? ? 'block' : 'none'}; direction: #{rtl?(status.content) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status)
 
   - unless status.media_attachments.empty?
     .status__attachments
       - if status.sensitive?
         = render partial: 'stream_entries/content_spoiler'
       - if status.media_attachments.first.video?
-        .video-item
-          = link_to (status.media_attachments.first.remote_url.blank? ? status.media_attachments.first.file.url(:original) : status.media_attachments.first.remote_url), style: "background-image: url(#{status.media_attachments.first.file.url(:small)})", target: '_blank', rel: 'noopener', class: 'u-video' do
-            .video-item__play
-              = fa_icon('play')
+        .status__attachments__inner
+          .video-item
+            = link_to (status.media_attachments.first.remote_url.blank? ? status.media_attachments.first.file.url(:original) : status.media_attachments.first.remote_url), style: "background-image: url(#{status.media_attachments.first.file.url(:small)})", target: '_blank', rel: 'noopener', class: 'u-video' do
+              .video-item__play
+                = fa_icon('play')
       - else
-        - status.media_attachments.each do |media|
-          .media-item
-            = link_to '', (media.remote_url.blank? ? media.file.url(:original) : media.remote_url), style: "background-image: url(#{media.file.url(:original)})", target: '_blank', rel: 'noopener', class: "u-#{media.video? ? 'video' : 'photo'}"
+        .status__attachments__inner
+          - status.media_attachments.each do |media|
+            = render partial: 'stream_entries/media', locals: { media: media }
diff --git a/app/views/stream_entries/_status.html.haml b/app/views/stream_entries/_status.html.haml
index f70e2c890..cdd0dde3b 100644
--- a/app/views/stream_entries/_status.html.haml
+++ b/app/views/stream_entries/_status.html.haml
@@ -4,7 +4,7 @@
 - centered        ||= include_threads && !is_predecessor && !is_successor
 
 - if status.reply? && include_threads
-  = render partial: 'status', collection: @ancestors, as: :status, locals: { is_predecessor: true }
+  = render partial: 'stream_entries/status', collection: @ancestors, as: :status, locals: { is_predecessor: true }
 
 .entry{ class: entry_classes(status, is_predecessor, is_successor, include_threads) }
   - if status.reblog?
@@ -19,4 +19,4 @@
   = render partial: centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status', locals: { status: proper_status(status) }
 
 - if include_threads
-  = render partial: 'status', collection: @descendants, as: :status, locals: { is_successor: true }
+  = render partial: 'stream_entries/status', collection: @descendants, as: :status, locals: { is_successor: true }
diff --git a/app/views/stream_entries/show.html.haml b/app/views/stream_entries/show.html.haml
index 6bad45705..c109ff4b8 100644
--- a/app/views/stream_entries/show.html.haml
+++ b/app/views/stream_entries/show.html.haml
@@ -20,5 +20,8 @@
 
   %meta{ property: 'twitter:card', content: 'summary' }/
 
+- if !user_signed_in? && !Rails.configuration.x.single_user_mode
+  = render partial: 'shared/landing_strip', locals: { account: @stream_entry.account }
+
 .activity-stream.activity-stream-headless
-  = render partial: @type, locals: { @type.to_sym => @stream_entry.activity, include_threads: true }
+  = render partial: "stream_entries/#{@type}", locals: { @type.to_sym => @stream_entry.activity, include_threads: true }
diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml
index 412ec4fa5..32a50e158 100644
--- a/app/views/tags/show.html.haml
+++ b/app/views/tags/show.html.haml
@@ -1,10 +1,18 @@
+- content_for :page_title do
+  = "##{@tag.name}"
+
+.compact-header
+  %h1<
+    = link_to 'Mastodon', root_path
+    %small= "##{@tag.name}"
+
 - if @statuses.empty?
   .accounts-grid
     = render partial: 'accounts/nothing_here'
 - else
   .activity-stream.h-feed
-    = render partial: 'stream_entries/status', collection: @statuses, as: :status, cached: true
+    = render partial: 'stream_entries/status', collection: @statuses, as: :status
 
-.pagination
-  - if @statuses.size == 20
+- if @statuses.size == 20
+  .pagination
     = link_to safe_join([t('pagination.next'), fa_icon('chevron-right')], ' '), tag_url(@tag, max_id: @statuses.last.id), class: 'next_page', rel: 'next'