diff --git a/.env.production.sample b/.env.production.sample
index d7c04e2354..fbb2847078 100644
--- a/.env.production.sample
+++ b/.env.production.sample
@@ -63,3 +63,7 @@ SMTP_FROM_ADDRESS=notifications@example.com
# Streaming API integration
# STREAMING_API_BASE_URL=
+
+# Advanced settings
+# If you need to use pgBouncer, you need to disable prepared statements:
+# PREPARED_STATEMENTS=false
diff --git a/app/assets/javascripts/components/features/notifications/components/clear_column_button.jsx b/app/assets/javascripts/components/features/notifications/components/clear_column_button.jsx
index 62c3e61e07..71877fb2bc 100644
--- a/app/assets/javascripts/components/features/notifications/components/clear_column_button.jsx
+++ b/app/assets/javascripts/components/features/notifications/components/clear_column_button.jsx
@@ -25,7 +25,7 @@ const ClearColumnButton = React.createClass({
const { intl } = this.props;
return (
-
+
);
diff --git a/app/assets/javascripts/components/locales/fr.jsx b/app/assets/javascripts/components/locales/fr.jsx
index fdd9c0e007..568422ff35 100644
--- a/app/assets/javascripts/components/locales/fr.jsx
+++ b/app/assets/javascripts/components/locales/fr.jsx
@@ -14,6 +14,7 @@ const fr = {
"status.show_less": "Replier",
"status.open": "Déplier ce status",
"status.report": "Signaler @{name}",
+ "status.load_more": "Charger plus",
"video_player.toggle_sound": "Mettre/Couper le son",
"account.mention": "Mentionner",
"account.edit_profile": "Modifier le profil",
@@ -41,6 +42,7 @@ const fr = {
"column.notifications": "Notifications",
"column.blocks": "Utilisateurs bloqués",
"column.favourites": "Favoris",
+ "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres utilisateurs⋅trices pour débuter la conversation.",
"tabs_bar.compose": "Composer",
"tabs_bar.home": "Accueil",
"tabs_bar.mentions": "Mentions",
diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss
index 696e89418a..9aead00b5f 100644
--- a/app/assets/stylesheets/components.scss
+++ b/app/assets/stylesheets/components.scss
@@ -714,7 +714,15 @@ a.status__content__spoiler-link {
@media screen and (min-width: 360px) {
.columns-area {
- margin: 10px;
+ margin: 0;
+ }
+
+ .column:first-child, .drawer:first-child {
+ margin-left: 0;
+ }
+
+ .column:last-child, .drawer:last-child {
+ margin-right: 0;
}
}
@@ -816,6 +824,7 @@ a.status__content__spoiler-link {
}
.column, .drawer {
+ margin: 10px;
margin-left: 5px;
margin-right: 5px;
flex: 0 0 auto;
@@ -823,11 +832,11 @@ a.status__content__spoiler-link {
}
.column:first-child, .drawer:first-child {
- margin-left: 0;
+ margin-left: 10px;
}
.column:last-child, .drawer:last-child {
- margin-right: 0;
+ margin-right: 10px;
}
@media screen and (max-width: 1024px) {
@@ -885,6 +894,10 @@ a.status__content__spoiler-link {
}
@media screen and (min-width: 360px) {
+ .columns-area {
+ margin: 10px;
+ }
+
.tabs-bar {
margin: 10px;
margin-bottom: 0;
@@ -895,6 +908,12 @@ a.status__content__spoiler-link {
}
}
+@media screen and (min-width: 1024px) {
+ .columns-area {
+ margin: 0;
+ }
+}
+
@media screen and (min-width: 600px) {
.tabs-bar__link {
.fa {
diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb
index 7fd43489f2..04e7ddacf7 100644
--- a/app/controllers/about_controller.rb
+++ b/app/controllers/about_controller.rb
@@ -2,30 +2,25 @@
class AboutController < ApplicationController
before_action :set_body_classes
+ before_action :set_instance_presenter, only: [:show, :more]
- def index
- @description = Setting.site_description
- @open_registrations = Setting.open_registrations
- @closed_registrations_message = Setting.closed_registrations_message
+ def show; end
- @user = User.new
- @user.build_account
- end
-
- def more
- @description = Setting.site_description
- @extended_description = Setting.site_extended_description
- @contact_account = Account.find_local(Setting.site_contact_username)
- @contact_email = Setting.site_contact_email
- @user_count = Rails.cache.fetch('user_count') { User.count }
- @status_count = Rails.cache.fetch('local_status_count') { Status.local.count }
- @domain_count = Rails.cache.fetch('distinct_domain_count') { Account.distinct.count(:domain) }
- end
+ def more; end
def terms; end
private
+ def new_user
+ User.new.tap(&:build_account)
+ end
+ helper_method :new_user
+
+ def set_instance_presenter
+ @instance_presenter = InstancePresenter.new
+ end
+
def set_body_classes
@body_classes = 'about-body'
end
diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb
index da18474cbd..454873116a 100644
--- a/app/controllers/api/v1/accounts_controller.rb
+++ b/app/controllers/api/v1/accounts_controller.rb
@@ -20,10 +20,8 @@ class Api::V1::AccountsController < ApiController
accounts = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |f| accounts[f.target_account_id] }
- # set_account_counters_maps(@accounts)
-
- next_path = following_api_v1_account_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
- prev_path = following_api_v1_account_url(since_id: results.first.id) unless results.empty?
+ next_path = following_api_v1_account_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
+ prev_path = following_api_v1_account_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
@@ -35,10 +33,8 @@ class Api::V1::AccountsController < ApiController
accounts = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |f| accounts[f.account_id] }
- # set_account_counters_maps(@accounts)
-
- next_path = followers_api_v1_account_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
- prev_path = followers_api_v1_account_url(since_id: results.first.id) unless results.empty?
+ next_path = followers_api_v1_account_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
+ prev_path = followers_api_v1_account_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
@@ -52,11 +48,9 @@ class Api::V1::AccountsController < ApiController
@statuses = cache_collection(@statuses, Status)
set_maps(@statuses)
- # set_counters_maps(@statuses)
- # set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
- next_path = statuses_api_v1_account_url(max_id: @statuses.last.id) unless @statuses.empty?
- prev_path = statuses_api_v1_account_url(since_id: @statuses.first.id) unless @statuses.empty?
+ next_path = statuses_api_v1_account_url(statuses_pagination_params(max_id: @statuses.last.id)) unless @statuses.empty?
+ prev_path = statuses_api_v1_account_url(statuses_pagination_params(since_id: @statuses.first.id)) unless @statuses.empty?
set_pagination_headers(next_path, prev_path)
end
@@ -117,8 +111,6 @@ class Api::V1::AccountsController < ApiController
def search
@accounts = AccountSearchService.new.call(params[:q], limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:resolve] == 'true', current_account)
- # set_account_counters_maps(@accounts) unless @accounts.nil?
-
render action: :index
end
@@ -135,4 +127,12 @@ class Api::V1::AccountsController < ApiController
@muting = Account.muting_map([@account.id], current_user.account_id)
@requested = Account.requested_map([@account.id], current_user.account_id)
end
+
+ def pagination_params(core_params)
+ params.permit(:limit).merge(core_params)
+ end
+
+ def statuses_pagination_params(core_params)
+ params.permit(:limit, :only_media, :exclude_replies).merge(core_params)
+ end
end
diff --git a/app/controllers/api/v1/blocks_controller.rb b/app/controllers/api/v1/blocks_controller.rb
index dadf212656..742717ba2f 100644
--- a/app/controllers/api/v1/blocks_controller.rb
+++ b/app/controllers/api/v1/blocks_controller.rb
@@ -11,11 +11,15 @@ class Api::V1::BlocksController < ApiController
accounts = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |f| accounts[f.target_account_id] }.compact
- # set_account_counters_maps(@accounts)
-
- next_path = api_v1_blocks_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
- prev_path = api_v1_blocks_url(since_id: results.first.id) unless results.empty?
+ next_path = api_v1_blocks_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
+ prev_path = api_v1_blocks_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
end
+
+ private
+
+ def pagination_params(core_params)
+ params.permit(:limit).merge(core_params)
+ end
end
diff --git a/app/controllers/api/v1/favourites_controller.rb b/app/controllers/api/v1/favourites_controller.rb
index 8a5b81e63b..22b93fe79a 100644
--- a/app/controllers/api/v1/favourites_controller.rb
+++ b/app/controllers/api/v1/favourites_controller.rb
@@ -11,11 +11,16 @@ class Api::V1::FavouritesController < ApiController
@statuses = cache_collection(Status.where(id: results.map(&:status_id)), Status)
set_maps(@statuses)
- # set_counters_maps(@statuses)
- next_path = api_v1_favourites_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_STATUSES_LIMIT)
- prev_path = api_v1_favourites_url(since_id: results.first.id) unless results.empty?
+ next_path = api_v1_favourites_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_STATUSES_LIMIT)
+ prev_path = api_v1_favourites_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
end
+
+ private
+
+ def pagination_params(core_params)
+ params.permit(:limit).merge(core_params)
+ end
end
diff --git a/app/controllers/api/v1/follow_requests_controller.rb b/app/controllers/api/v1/follow_requests_controller.rb
index 3b8e8c0785..73cfaf10a6 100644
--- a/app/controllers/api/v1/follow_requests_controller.rb
+++ b/app/controllers/api/v1/follow_requests_controller.rb
@@ -9,10 +9,8 @@ class Api::V1::FollowRequestsController < ApiController
accounts = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |f| accounts[f.account_id] }
- # set_account_counters_maps(@accounts)
-
- next_path = api_v1_follow_requests_url(max_id: results.last.id) if results.size == DEFAULT_ACCOUNTS_LIMIT
- prev_path = api_v1_follow_requests_url(since_id: results.first.id) unless results.empty?
+ next_path = api_v1_follow_requests_url(pagination_params(max_id: results.last.id)) if results.size == DEFAULT_ACCOUNTS_LIMIT
+ prev_path = api_v1_follow_requests_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
end
@@ -26,4 +24,10 @@ class Api::V1::FollowRequestsController < ApiController
RejectFollowService.new.call(Account.find(params[:id]), current_account)
render_empty
end
+
+ private
+
+ def pagination_params(core_params)
+ params.permit(:limit).merge(core_params)
+ end
end
diff --git a/app/controllers/api/v1/mutes_controller.rb b/app/controllers/api/v1/mutes_controller.rb
index 6f48de0403..cbd98732bb 100644
--- a/app/controllers/api/v1/mutes_controller.rb
+++ b/app/controllers/api/v1/mutes_controller.rb
@@ -11,11 +11,15 @@ class Api::V1::MutesController < ApiController
accounts = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |f| accounts[f.target_account_id] }
- # set_account_counters_maps(@accounts)
-
- next_path = api_v1_mutes_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
- prev_path = api_v1_mutes_url(since_id: results.first.id) unless results.empty?
+ next_path = api_v1_mutes_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
+ prev_path = api_v1_mutes_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
end
+
+ private
+
+ def pagination_params(core_params)
+ params.permit(:limit).merge(core_params)
+ end
end
diff --git a/app/controllers/api/v1/notifications_controller.rb b/app/controllers/api/v1/notifications_controller.rb
index 7bbc5419c5..71c054334e 100644
--- a/app/controllers/api/v1/notifications_controller.rb
+++ b/app/controllers/api/v1/notifications_controller.rb
@@ -14,11 +14,9 @@ class Api::V1::NotificationsController < ApiController
statuses = @notifications.select { |n| !n.target_status.nil? }.map(&:target_status)
set_maps(statuses)
- # set_counters_maps(statuses)
- # set_account_counters_maps(@notifications.map(&:from_account))
- next_path = api_v1_notifications_url(max_id: @notifications.last.id) unless @notifications.empty?
- prev_path = api_v1_notifications_url(since_id: @notifications.first.id) unless @notifications.empty?
+ next_path = api_v1_notifications_url(pagination_params(max_id: @notifications.last.id)) unless @notifications.empty?
+ prev_path = api_v1_notifications_url(pagination_params(since_id: @notifications.first.id)) unless @notifications.empty?
set_pagination_headers(next_path, prev_path)
end
@@ -31,4 +29,10 @@ class Api::V1::NotificationsController < ApiController
Notification.where(account: current_account).delete_all
render_empty
end
+
+ private
+
+ def pagination_params(core_params)
+ params.permit(:limit).merge(core_params)
+ end
end
diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb
index 4ece7e7028..1976ce330d 100644
--- a/app/controllers/api/v1/statuses_controller.rb
+++ b/app/controllers/api/v1/statuses_controller.rb
@@ -23,7 +23,6 @@ class Api::V1::StatusesController < ApiController
statuses = [@status] + @context[:ancestors] + @context[:descendants]
set_maps(statuses)
- # set_counters_maps(statuses)
end
def card
@@ -36,10 +35,8 @@ class Api::V1::StatusesController < ApiController
accounts = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |r| accounts[r.account_id] }
- # set_account_counters_maps(@accounts)
-
- next_path = reblogged_by_api_v1_status_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
- prev_path = reblogged_by_api_v1_status_url(since_id: results.first.id) unless results.empty?
+ next_path = reblogged_by_api_v1_status_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
+ prev_path = reblogged_by_api_v1_status_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
@@ -51,10 +48,8 @@ class Api::V1::StatusesController < ApiController
accounts = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |f| accounts[f.account_id] }
- # set_account_counters_maps(@accounts)
-
- next_path = favourited_by_api_v1_status_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
- prev_path = favourited_by_api_v1_status_url(since_id: results.first.id) unless results.empty?
+ next_path = favourited_by_api_v1_status_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
+ prev_path = favourited_by_api_v1_status_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
@@ -115,4 +110,8 @@ class Api::V1::StatusesController < ApiController
def status_params
params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, media_ids: [])
end
+
+ def pagination_params(core_params)
+ params.permit(:limit).merge(core_params)
+ end
end
diff --git a/app/controllers/api/v1/timelines_controller.rb b/app/controllers/api/v1/timelines_controller.rb
index 0446b9e4dd..e55e7d7181 100644
--- a/app/controllers/api/v1/timelines_controller.rb
+++ b/app/controllers/api/v1/timelines_controller.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
class Api::V1::TimelinesController < ApiController
- before_action -> { doorkeeper_authorize! :read }
- before_action :require_user!, only: [:home, :mentions]
+ before_action -> { doorkeeper_authorize! :read }, only: [:home]
+ before_action :require_user!, only: [:home]
respond_to :json
@@ -11,11 +11,9 @@ class Api::V1::TimelinesController < ApiController
@statuses = cache_collection(@statuses)
set_maps(@statuses)
- # set_counters_maps(@statuses)
- # set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
- next_path = api_v1_home_timeline_url(max_id: @statuses.last.id) unless @statuses.empty?
- prev_path = api_v1_home_timeline_url(since_id: @statuses.first.id) unless @statuses.empty?
+ next_path = api_v1_home_timeline_url(pagination_params(max_id: @statuses.last.id)) unless @statuses.empty?
+ prev_path = api_v1_home_timeline_url(pagination_params(since_id: @statuses.first.id)) unless @statuses.empty?
set_pagination_headers(next_path, prev_path)
@@ -27,11 +25,9 @@ class Api::V1::TimelinesController < ApiController
@statuses = cache_collection(@statuses)
set_maps(@statuses)
- # set_counters_maps(@statuses)
- # set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
- next_path = api_v1_public_timeline_url(max_id: @statuses.last.id) unless @statuses.empty?
- prev_path = api_v1_public_timeline_url(since_id: @statuses.first.id) unless @statuses.empty?
+ next_path = api_v1_public_timeline_url(pagination_params(max_id: @statuses.last.id)) unless @statuses.empty?
+ prev_path = api_v1_public_timeline_url(pagination_params(since_id: @statuses.first.id)) unless @statuses.empty?
set_pagination_headers(next_path, prev_path)
@@ -44,11 +40,9 @@ class Api::V1::TimelinesController < ApiController
@statuses = cache_collection(@statuses)
set_maps(@statuses)
- # set_counters_maps(@statuses)
- # set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
- next_path = api_v1_hashtag_timeline_url(params[:id], max_id: @statuses.last.id) unless @statuses.empty?
- prev_path = api_v1_hashtag_timeline_url(params[:id], since_id: @statuses.first.id) unless @statuses.empty?
+ next_path = api_v1_hashtag_timeline_url(params[:id], pagination_params(max_id: @statuses.last.id)) unless @statuses.empty?
+ prev_path = api_v1_hashtag_timeline_url(params[:id], pagination_params(since_id: @statuses.first.id)) unless @statuses.empty?
set_pagination_headers(next_path, prev_path)
@@ -60,4 +54,8 @@ class Api::V1::TimelinesController < ApiController
def cache_collection(raw)
super(raw, Status)
end
+
+ def pagination_params(core_params)
+ params.permit(:local, :limit).merge(core_params)
+ end
end
diff --git a/app/models/account.rb b/app/models/account.rb
index cbba8b5b6d..c59c760095 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -203,7 +203,7 @@ class Account < ApplicationRecord
end
def triadic_closures(account, limit = 5)
- sql = <
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
diff --git a/app/views/about/more.html.haml b/app/views/about/more.html.haml
index 2de3bf986f..8c12f57c10 100644
--- a/app/views/about/more.html.haml
+++ b/app/views/about/more.html.haml
@@ -7,42 +7,42 @@
.panel
%h2= Rails.configuration.x.local_domain
- - unless @description.blank?
- %p= @description.html_safe
+ - unless @instance_presenter.site_description.blank?
+ %p= @instance_presenter.site_description.html_safe
.information-board
.section
%span= t 'about.user_count_before'
- %strong= number_with_delimiter @user_count
+ %strong= number_with_delimiter @instance_presenter.user_count
%span= t 'about.user_count_after'
.section
%span= t 'about.status_count_before'
- %strong= number_with_delimiter @status_count
+ %strong= number_with_delimiter @instance_presenter.status_count
%span= t 'about.status_count_after'
.section
%span= t 'about.domain_count_before'
- %strong= number_with_delimiter @domain_count
+ %strong= number_with_delimiter @instance_presenter.domain_count
%span= t 'about.domain_count_after'
- - unless @extended_description.blank?
- .panel= @extended_description.html_safe
+ - unless @instance_presenter.site_extended_description.blank?
+ .panel= @instance_presenter.site_extended_description.html_safe
.sidebar
.panel
.panel-header= t 'about.contact'
.panel-body
- - if @contact_account
+ - if @instance_presenter.contact_account
.owner
- .avatar= image_tag @contact_account.avatar.url
+ .avatar= image_tag @instance_presenter.contact_account.avatar.url
.name
- = link_to TagManager.instance.url_for(@contact_account) do
- %span.display_name.emojify= display_name(@contact_account)
- %span.username= "@#{@contact_account.acct}"
+ = link_to TagManager.instance.url_for(@instance_presenter.contact_account) do
+ %span.display_name.emojify= display_name(@instance_presenter.contact_account)
+ %span.username= "@#{@instance_presenter.contact_account.acct}"
- - unless @contact_email.blank?
+ - unless @instance_presenter.contact_email.blank?
.contact-email
= t 'about.business_email'
- %strong= @contact_email
+ %strong= @instance_presenter.contact_email
.panel
.panel-header= t 'about.links'
.panel-list
diff --git a/app/views/about/index.html.haml b/app/views/about/show.html.haml
similarity index 60%
rename from app/views/about/index.html.haml
rename to app/views/about/show.html.haml
index f6b0c16685..8a0d00daa9 100644
--- a/app/views/about/index.html.haml
+++ b/app/views/about/show.html.haml
@@ -8,7 +8,7 @@
%meta{ property: 'og:site_name', content: site_title }/
%meta{ property: 'og:type', content: 'website' }/
%meta{ property: 'og:title', content: Rails.configuration.x.local_domain }/
- %meta{ property: 'og:description', content: @description.blank? ? "Mastodon is a free, open-source social network server. A decentralized alternative to commercial platforms, it avoids the risks of a single company monopolizing your communication. Anyone can run Mastodon and participate in the social network seamlessly" : strip_tags(@description) }/
+ %meta{ property: 'og:description', content: strip_tags(@instance_presenter.site_description.blank? ? t('about.about_mastodon') : @instance_presenter.site_description) }/
%meta{ property: 'og:image', content: asset_url('mastodon_small.jpg') }/
%meta{ property: 'og:image:width', content: '400' }/
%meta{ property: 'og:image:height', content: '400' }/
@@ -24,28 +24,14 @@
.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
+ - if @instance_presenter.open_registrations
+ = render 'registration'
- else
.closed-registrations-message
- - if @closed_registrations_message.blank?
+ - if @instance_presenter.closed_registrations_message.blank?
%p= t('about.closed_registrations')
- else
- = @closed_registrations_message.html_safe
+ = @instance_presenter.closed_registrations_message.html_safe
.info
= link_to t('auth.login'), new_user_session_path, class: 'webapp-btn'
·
@@ -85,9 +71,9 @@
= fa_icon('li check-square')
= t 'about.features.api'
- - unless @description.blank?
+ - unless @instance_presenter.site_description.blank?
%h3= t('about.description_headline', domain: Rails.configuration.x.local_domain)
- %p= @description.html_safe
+ %p= @instance_presenter.site_description.html_safe
.actions
.info
diff --git a/app/views/user_mailer/confirmation_instructions.fr.html.erb b/app/views/user_mailer/confirmation_instructions.fr.html.erb
index 2665f1a20b..6c45f1a214 100644
--- a/app/views/user_mailer/confirmation_instructions.fr.html.erb
+++ b/app/views/user_mailer/confirmation_instructions.fr.html.erb
@@ -1,5 +1,5 @@
Bienvenue <%= @resource.email %> !
-Vous pouvez confirmer l'email de votre compte Mastodon en cliquant sur le lien ci-dessous :
+Vous pouvez confirmer le courriel de votre compte Mastodon en cliquant sur le lien ci-dessous :
<%= link_to 'Confirmer mon compte', confirmation_url(@resource, confirmation_token: @token) %>
diff --git a/app/views/user_mailer/confirmation_instructions.fr.text.erb b/app/views/user_mailer/confirmation_instructions.fr.text.erb
index 9d33450f80..dfa3f9f7c3 100644
--- a/app/views/user_mailer/confirmation_instructions.fr.text.erb
+++ b/app/views/user_mailer/confirmation_instructions.fr.text.erb
@@ -1,5 +1,5 @@
Bienvenue <%= @resource.email %> !
-Vous pouvez confirmer l'email de votre compte Mastodon en cliquant sur le lien ci-dessous :
+Vous pouvez confirmer le courriel de votre compte Mastodon en cliquant sur le lien ci-dessous :
<%= confirmation_url(@resource, confirmation_token: @token) %>
diff --git a/config/database.yml b/config/database.yml
index 159973f0d3..810b832780 100644
--- a/config/database.yml
+++ b/config/database.yml
@@ -22,4 +22,4 @@ production:
password: <%= ENV['DB_PASS'] || '' %>
host: <%= ENV['DB_HOST'] || 'localhost' %>
port: <%= ENV['DB_PORT'] || 5432 %>
- prepared_statements: false
+ prepared_statements: <%= ENV['PREPARED_STATEMENTS'] || 'true' %>
diff --git a/config/environments/production.rb b/config/environments/production.rb
index dc5dd4afd4..d299e4f4ca 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -38,9 +38,9 @@ Rails.application.configure do
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = false
- # Use the lowest log level to ensure availability of diagnostic information
+ # By default, use the lowest log level to ensure availability of diagnostic information
# when problems arise.
- config.log_level = :debug
+ config.log_level = ENV.fetch('RAILS_LOG_LEVEL', 'debug').to_sym
# Prepend all log lines with the following tags.
config.log_tags = [:request_id]
diff --git a/config/locales/devise.fr.yml b/config/locales/devise.fr.yml
index ce44d041aa..3b46b01e3a 100644
--- a/config/locales/devise.fr.yml
+++ b/config/locales/devise.fr.yml
@@ -58,4 +58,4 @@ fr:
not_locked: n'était pas verrouillé(e)
not_saved:
one: '1 erreur a empêché ce(tte) %{resource} d''être sauvegardé(e) :'
- other: '%{count} erreurs ont empêché ce(tte) %{resource} d''être sauvegardé(e): '
+ other: '%{count} erreurs ont empêché ce(tte) %{resource} d''être sauvegardé(e) : '
diff --git a/config/locales/doorkeeper.fr.yml b/config/locales/doorkeeper.fr.yml
index be109df9cc..cfc9083d74 100644
--- a/config/locales/doorkeeper.fr.yml
+++ b/config/locales/doorkeeper.fr.yml
@@ -23,11 +23,11 @@ fr:
edit: Modifier
submit: Envoyer
confirmations:
- destroy: Êtes-vous certain?
+ destroy: Êtes-vous certain ?
edit:
title: Modifier l'application
form:
- error: Oups! Vérifier votre formulaire pour des erreurs possibles
+ error: Oups ! Vérifier votre formulaire pour des erreurs possibles
help:
native_redirect_uri: Utiliser %{native_redirect_uri} pour les tests locaux
redirect_uri: Utiliser une ligne par URL
@@ -54,7 +54,7 @@ fr:
title: Une erreur est survenue
new:
able_to: Cette application pourra
- prompt: Autoriser %{client_name} à utiliser votre compte?
+ prompt: Autoriser %{client_name} à utiliser votre compte ?
title: Autorisation requise
show:
title: Code d'autorisation
@@ -109,5 +109,5 @@ fr:
title: Autorisation OAuth requise
scopes:
follow: s’abonner, se désabonner, bloquer, et débloquer des comptes
- read: lire les données de votre compte
+ read: lire les données de votre compte
write: poster en tant que vous
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 9727f3b7e3..9a9c1b6ded 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -4,7 +4,7 @@ fr:
about_mastodon: Mastodon est un serveur libre de réseautage social. Alternative décentralisée aux plateformes commerciales, la monopolisation de vos communications par une entreprise unique est évitée. Tout un chacun peut faire tourner Mastodon et participer au réseau social de manière transparente.
about_this: À propos de cette instance
apps: Applications
- business_email: E-mail professionnel
+ business_email: Courriel professionnel
closed_registrations: Les inscriptions sont actuellement fermées sur cette instance.
contact: Contact
description_headline: Qu'est-ce que %{domain} ?
@@ -40,9 +40,9 @@ fr:
remote_follow: Suivre à distance
unfollow: Ne plus suivre
application_mailer:
- settings: 'Changer les préférences e-mail: ${link}'
+ settings: 'Changer les préférences courriel : ${link}'
signature: Notifications de Mastodon depuis %{instance}
- view: 'Voir:'
+ view: 'Voir :'
applications:
invalid_url: L'URL fournie est invalide
auth:
@@ -58,21 +58,27 @@ fr:
authorize_follow:
error: Malheureusement, il y a eu une erreur en cherchant les détails du compte distant
follow: Suivre
- prompt_html: 'Vous (%{self}) avez demandé à suivre:'
+ prompt_html: 'Vous (%{self}) avez demandé à suivre :'
title: Suivre %{acct}
datetime:
distance_in_words:
about_x_hours: "%{count}h"
- about_x_months: "%{count}mo"
- about_x_years: "%{count}y"
- almost_x_years: "%{count}y"
+ about_x_months: "%{count}mois"
+ about_x_years:
+ one: un an
+ other: "%{count} ans"
+ almost_x_years:
+ one: un an
+ other: "%{count} ans"
half_a_minute: A l'instant
- less_than_x_minutes: "%{count}m"
+ less_than_x_minutes: "%{count}min"
less_than_x_seconds: A l'instant
- over_x_years: "%{count}y"
- x_days: "%{count}d"
- x_minutes: "%{count}m"
- x_months: "%{count}mo"
+ over_x_years:
+ one: un an
+ other: "%{count} ans"
+ x_days: "%{count}j"
+ x_minutes: "%{count}min"
+ x_months: "%{count}mois"
x_seconds: "%{count}s"
exports:
blocks: Vous bloquez
@@ -96,7 +102,7 @@ fr:
landing_strip_html: %{name} utilise %{domain}. Vous pouvez le/la suivre et interagir si vous possédez un compte quelque part dans le "fediverse". Si ce n'est pas le cas, vous pouvez en créer un ici.
notification_mailer:
digest:
- body: 'Voici ce que vous avez raté sur ${instance} depuis votre dernière visite (%{}):'
+ body: 'Voici ce que vous avez raté sur ${instance} depuis votre dernière visite (%{}) :'
mention: '%{name} vous a mentionné⋅e'
new_followers_summary:
one: Vous avez un⋅e nouvel⋅le abonné⋅e ! Youpi !
@@ -156,10 +162,10 @@ fr:
disable: Désactiver
enable: Activer
instructions_html: "Scannez ce QR code grâce à Google Authenticator, Authy ou une application similaire sur votre téléphone. Désormais, cette application générera des jetons que vous devrez saisir à chaque connexion."
- plaintext_secret_html: 'Code secret en clair: %{secret}'
+ plaintext_secret_html: 'Code secret en clair : %{secret}'
warning: Si vous ne pouvez pas configurer une application d'authentification maintenant, vous devriez cliquer sur "Désactiver" pour ne pas bloquer l'accès à votre compte.
users:
- invalid_email: L'adresse e-mail est invalide
+ invalid_email: L'adresse courriel est invalide
invalid_otp_token: Le code d'authentification à deux facteurs est invalide
will_paginate:
page_gap: "…"
diff --git a/config/routes.rb b/config/routes.rb
index 9cbecf0779..b0a13aa78a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -188,11 +188,14 @@ Rails.application.routes.draw do
get '/web/(*any)', to: 'home#index', as: :web
- get '/about', to: 'about#index'
+ get '/about', to: 'about#show'
get '/about/more', to: 'about#more'
get '/terms', to: 'about#terms'
root 'home#index'
- match '*unmatched_route', via: :all, to: 'application#raise_not_found'
+ match '*unmatched_route',
+ via: :all,
+ to: 'application#raise_not_found',
+ format: false
end
diff --git a/docs/Running-Mastodon/Administration-guide.md b/docs/Running-Mastodon/Administration-guide.md
index 09b0f1df12..8bcfe7c9e1 100644
--- a/docs/Running-Mastodon/Administration-guide.md
+++ b/docs/Running-Mastodon/Administration-guide.md
@@ -35,3 +35,11 @@ You are able to set the following settings:
- Site extended description
You may wish to use the extended description (shown at https://yourmastodon.instance/about/more ) to display content guidelines or a user agreement (see https://mastodon.social/about/more for an example).
+
+## Confirming Users Manually
+
+The following rake task:
+
+ RAILS_ENV=production bundle exec rails mastodon:confirm_email USER_EMAIL=alice@alice.com
+
+Will confirm a user manually, in case they don't have access to their confirmation email for whatever reason.
diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake
index 79dcb722ae..5dc7f15678 100644
--- a/lib/tasks/mastodon.rake
+++ b/lib/tasks/mastodon.rake
@@ -10,6 +10,15 @@ namespace :mastodon do
puts "Congrats! #{user.account.username} is now an admin. \\o/\nNavigate to #{admin_settings_url} to get started"
end
+ desc 'Manually confirms a user with associated user email address stored in USER_EMAIL environment variable.'
+ task confirm_email: :environment do
+ email = ENV.fetch('USER_EMAIL')
+ user = User.where(email: email)
+ user.update(confirmed_at: Time.now.utc)
+
+ puts "User #{email} confirmed."
+ end
+
namespace :media do
desc 'Removes media attachments that have not been assigned to any status for longer than a day'
task clear: :environment do
diff --git a/spec/controllers/about_controller_spec.rb b/spec/controllers/about_controller_spec.rb
index 4282649e1a..f49de96221 100644
--- a/spec/controllers/about_controller_spec.rb
+++ b/spec/controllers/about_controller_spec.rb
@@ -3,9 +3,16 @@ require 'rails_helper'
RSpec.describe AboutController, type: :controller do
render_views
- describe 'GET #index' do
+ describe 'GET #show' do
it 'returns http success' do
- get :index
+ get :show
+ expect(response).to have_http_status(:success)
+ end
+ end
+
+ describe 'GET #more' do
+ it 'returns http success' do
+ get :more
expect(response).to have_http_status(:success)
end
end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 93a45459d9..0c3b2b0428 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -170,6 +170,61 @@ RSpec.describe Account, type: :model do
end
end
+ describe '.search_for' do
+ before do
+ @match = Fabricate(
+ :account,
+ display_name: "Display Name",
+ username: "username",
+ domain: "example.com"
+ )
+ _missing = Fabricate(
+ :account,
+ display_name: "Missing",
+ username: "missing",
+ domain: "missing.com"
+ )
+ end
+
+ it 'finds accounts with matching display_name' do
+ results = Account.search_for("display")
+ expect(results).to eq [@match]
+ end
+
+ it 'finds accounts with matching username' do
+ results = Account.search_for("username")
+ expect(results).to eq [@match]
+ end
+
+ it 'finds accounts with matching domain' do
+ results = Account.search_for("example")
+ expect(results).to eq [@match]
+ end
+
+ it 'ranks multiple matches higher' do
+ account = Fabricate(
+ :account,
+ username: "username",
+ display_name: "username"
+ )
+ results = Account.search_for("username")
+ expect(results).to eq [account, @match]
+ end
+ end
+
+ describe '.advanced_search_for' do
+ it 'ranks followed accounts higher' do
+ account = Fabricate(:account)
+ match = Fabricate(:account, username: "Matching")
+ followed_match = Fabricate(:account, username: "Matcher")
+ Fabricate(:follow, account: account, target_account: followed_match)
+
+ results = Account.advanced_search_for("match", account)
+ expect(results).to eq [followed_match, match]
+ expect(results.first.rank).to be > results.last.rank
+ end
+ end
+
describe '.find_local' do
before do
Fabricate(:account, username: 'Alice')
diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb
index 360bbc16de..7a5b8ec897 100644
--- a/spec/models/tag_spec.rb
+++ b/spec/models/tag_spec.rb
@@ -12,4 +12,15 @@ RSpec.describe Tag, type: :model do
expect(subject.match('https://en.wikipedia.org/wiki/Ghostbusters_(song)#Lawsuit')).to be_nil
end
end
+
+ describe '.search_for' do
+ it 'finds tag records with matching names' do
+ tag = Fabricate(:tag, name: "match")
+ _miss_tag = Fabricate(:tag, name: "miss")
+
+ results = Tag.search_for("match")
+
+ expect(results).to eq [tag]
+ end
+ end
end
diff --git a/spec/presenters/instance_presenter_spec.rb b/spec/presenters/instance_presenter_spec.rb
new file mode 100644
index 0000000000..0f318d9c31
--- /dev/null
+++ b/spec/presenters/instance_presenter_spec.rb
@@ -0,0 +1,74 @@
+require 'rails_helper'
+
+describe InstancePresenter do
+ let(:instance_presenter) { InstancePresenter.new }
+
+ it "delegates site_description to Setting" do
+ Setting.site_description = "Site desc"
+
+ expect(instance_presenter.site_description).to eq "Site desc"
+ end
+
+ it "delegates site_extended_description to Setting" do
+ Setting.site_extended_description = "Extended desc"
+
+ expect(instance_presenter.site_extended_description).to eq "Extended desc"
+ end
+
+ it "delegates open_registrations to Setting" do
+ Setting.open_registrations = false
+
+ expect(instance_presenter.open_registrations).to eq false
+ end
+
+ it "delegates closed_registrations_message to Setting" do
+ Setting.closed_registrations_message = "Closed message"
+
+ expect(instance_presenter.closed_registrations_message).to eq "Closed message"
+ end
+
+ it "delegates contact_email to Setting" do
+ Setting.contact_email = "admin@example.com"
+
+ expect(instance_presenter.contact_email).to eq "admin@example.com"
+ end
+
+ describe "contact_account" do
+ it "returns the account for the site contact username" do
+ Setting.site_contact_username = "aaa"
+ account = Fabricate(:account, username: "aaa")
+
+ expect(instance_presenter.contact_account).to eq(account)
+ end
+ end
+
+ describe "user_count" do
+ it "returns the number of site users" do
+ cache = double
+ allow(Rails).to receive(:cache).and_return(cache)
+ allow(cache).to receive(:fetch).with("user_count").and_return(123)
+
+ expect(instance_presenter.user_count).to eq(123)
+ end
+ end
+
+ describe "status_count" do
+ it "returns the number of local statuses" do
+ cache = double
+ allow(Rails).to receive(:cache).and_return(cache)
+ allow(cache).to receive(:fetch).with("local_status_count").and_return(234)
+
+ expect(instance_presenter.status_count).to eq(234)
+ end
+ end
+
+ describe "domain_count" do
+ it "returns the number of known domains" do
+ cache = double
+ allow(Rails).to receive(:cache).and_return(cache)
+ allow(cache).to receive(:fetch).with("distinct_domain_count").and_return(345)
+
+ expect(instance_presenter.domain_count).to eq(345)
+ end
+ end
+end
diff --git a/spec/requests/catch_all_route_request_spec.rb b/spec/requests/catch_all_route_request_spec.rb
new file mode 100644
index 0000000000..22ce1cf59f
--- /dev/null
+++ b/spec/requests/catch_all_route_request_spec.rb
@@ -0,0 +1,21 @@
+require "rails_helper"
+
+describe "The catch all route" do
+ describe "with a simple value" do
+ it "returns a 404 page as html" do
+ get "/test"
+
+ expect(response.status).to eq 404
+ expect(response.content_type).to eq "text/html"
+ end
+ end
+
+ describe "with an implied format" do
+ it "returns a 404 page as html" do
+ get "/test.test"
+
+ expect(response.status).to eq 404
+ expect(response.content_type).to eq "text/html"
+ end
+ end
+end