diff --git a/.env.production.sample b/.env.production.sample
index d7c04e23..fbb28470 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 62c3e61e..71877fb2 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 fdd9c0e0..568422ff 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 696e8941..9aead00b 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 7fd43489..04e7ddac 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 da18474c..45487311 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 dadf2126..742717ba 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 8a5b81e6..22b93fe7 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 3b8e8c07..73cfaf10 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 6f48de04..cbd98732 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 7bbc5419..71c05433 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 4ece7e70..1976ce33 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 0446b9e4..e55e7d71 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 cbba8b5b..c59c7600 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 2de3bf98..8c12f57c 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 f6b0c166..8a0d00da 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 2665f1a2..6c45f1a2 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 9d33450f..dfa3f9f7 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 159973f0..810b8327 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 dc5dd4af..d299e4f4 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 ce44d041..3b46b01e 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 be109df9..cfc9083d 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 9727f3b7..9a9c1b6d 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 9cbecf07..b0a13aa7 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 09b0f1df..8bcfe7c9 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 79dcb722..5dc7f156 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 4282649e..f49de962 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 93a45459..0c3b2b04 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 360bbc16..7a5b8ec8 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 00000000..0f318d9c
--- /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 00000000..22ce1cf5
--- /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