about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorFire Demon <firedemon@creature.cafe>2020-06-27 20:48:54 -0500
committerFire Demon <firedemon@creature.cafe>2020-08-30 05:41:03 -0500
commitba3a399d8899c55f03b5648ed668c9f32f9b8a26 (patch)
treeedc1f6ba121ffb7026c72ab24e375eaf777f08b5 /app
parentc81fd632250ba6180d11c9fba01865f5caf71173 (diff)
[Privacy, Transparency] Reduce allowlist mode restrictions; make publishing trusted servers to about page possible.
Diffstat (limited to 'app')
-rw-r--r--app/controllers/about_controller.rb11
-rw-r--r--app/controllers/accounts_controller.rb2
-rw-r--r--app/controllers/activitypub/claims_controller.rb2
-rw-r--r--app/controllers/activitypub/inboxes_controller.rb2
-rw-r--r--app/controllers/api/base_controller.rb2
-rw-r--r--app/controllers/api/v1/instances/activity_controller.rb4
-rw-r--r--app/controllers/api/v1/instances/peers_controller.rb4
-rw-r--r--app/controllers/api/v1/instances_controller.rb2
-rw-r--r--app/controllers/concerns/account_owned_concern.rb2
-rw-r--r--app/controllers/home_controller.rb2
-rw-r--r--app/controllers/media_controller.rb4
-rw-r--r--app/controllers/media_proxy_controller.rb2
-rw-r--r--app/controllers/remote_interaction_controller.rb4
-rw-r--r--app/controllers/statuses_controller.rb2
-rw-r--r--app/controllers/tags_controller.rb4
-rw-r--r--app/models/form/admin_settings.rb4
-rw-r--r--app/views/about/_domain_allows.html.haml12
-rw-r--r--app/views/about/more.html.haml10
-rw-r--r--app/views/admin/settings/edit.html.haml47
-rw-r--r--app/views/layouts/public.html.haml7
20 files changed, 81 insertions, 48 deletions
diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb
index 5d5db937c..218dcb99e 100644
--- a/app/controllers/about_controller.rb
+++ b/app/controllers/about_controller.rb
@@ -4,7 +4,7 @@ class AboutController < ApplicationController
   before_action :set_pack
   layout 'public'
 
-  before_action :require_open_federation!, only: [:show, :more]
+  #before_action :require_open_federation!, only: [:show, :more]
   before_action :set_body_classes, only: :show
   before_action :set_instance_presenter
   before_action :set_expires_in, only: [:show, :more, :terms]
@@ -21,10 +21,13 @@ class AboutController < ApplicationController
     @contents          = toc_generator.html
     @table_of_contents = toc_generator.toc
     @blocks            = DomainBlock.with_user_facing_limitations.by_severity if display_blocks?
+    @allows            = DomainAllow.all if display_allows?
   end
 
   def terms; end
 
+  helper_method :display_allows?
+
   helper_method :display_blocks?
   helper_method :display_blocks_rationale?
   helper_method :public_fetch_mode?
@@ -66,4 +69,10 @@ class AboutController < ApplicationController
   def set_expires_in
     expires_in 0, public: true
   end
+
+  # Monsterfork additions
+
+  def display_allows?
+    Setting.show_domain_allows == 'all' || (Setting.show_domain_allows == 'users' && user_signed_in?)
+  end
 end
diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
index 5c8cdd174..f4fce456f 100644
--- a/app/controllers/accounts_controller.rb
+++ b/app/controllers/accounts_controller.rb
@@ -11,7 +11,7 @@ class AccountsController < ApplicationController
   before_action :set_body_classes
 
   skip_around_action :set_locale, if: -> { [:json, :rss].include?(request.format&.to_sym) }
-  skip_before_action :require_functional!, unless: :whitelist_mode?
+  skip_before_action :require_functional! #, unless: :whitelist_mode?
 
   def show
     respond_to do |format|
diff --git a/app/controllers/activitypub/claims_controller.rb b/app/controllers/activitypub/claims_controller.rb
index 08ad952df..5009a9f05 100644
--- a/app/controllers/activitypub/claims_controller.rb
+++ b/app/controllers/activitypub/claims_controller.rb
@@ -4,7 +4,7 @@ class ActivityPub::ClaimsController < ActivityPub::BaseController
   include SignatureVerification
   include AccountOwnedConcern
 
-  skip_before_action :authenticate_user!
+  #skip_before_action :authenticate_user!
 
   before_action :require_signature!
   before_action :set_claim_result
diff --git a/app/controllers/activitypub/inboxes_controller.rb b/app/controllers/activitypub/inboxes_controller.rb
index 0a561e7f0..3e67f3909 100644
--- a/app/controllers/activitypub/inboxes_controller.rb
+++ b/app/controllers/activitypub/inboxes_controller.rb
@@ -7,7 +7,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
 
   before_action :skip_unknown_actor_delete
   before_action :require_signature!
-  skip_before_action :authenticate_user!
+  #skip_before_action :authenticate_user!
 
   def create
     upgrade_account
diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb
index 045e7dd26..ca5bda8f1 100644
--- a/app/controllers/api/base_controller.rb
+++ b/app/controllers/api/base_controller.rb
@@ -7,7 +7,7 @@ class Api::BaseController < ApplicationController
   include RateLimitHeaders
 
   skip_before_action :store_current_location
-  skip_before_action :require_functional!, unless: :whitelist_mode?
+  skip_before_action :require_functional! #, unless: :whitelist_mode?
 
   before_action :require_authenticated_user!, if: :disallow_unauthenticated_api_access?
   before_action :set_cache_headers
diff --git a/app/controllers/api/v1/instances/activity_controller.rb b/app/controllers/api/v1/instances/activity_controller.rb
index 4f6b4bcbf..f2ac902e1 100644
--- a/app/controllers/api/v1/instances/activity_controller.rb
+++ b/app/controllers/api/v1/instances/activity_controller.rb
@@ -4,7 +4,7 @@ class Api::V1::Instances::ActivityController < Api::BaseController
   before_action :require_enabled_api!
 
   skip_before_action :set_cache_headers
-  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
+  skip_before_action :require_authenticated_user! #, unless: :whitelist_mode?
 
   def show
     expires_in 1.day, public: true
@@ -33,6 +33,6 @@ class Api::V1::Instances::ActivityController < Api::BaseController
   end
 
   def require_enabled_api!
-    head 404 unless Setting.activity_api_enabled && !whitelist_mode?
+    head 404 unless Setting.activity_api_enabled #&& !whitelist_mode?
   end
 end
diff --git a/app/controllers/api/v1/instances/peers_controller.rb b/app/controllers/api/v1/instances/peers_controller.rb
index 9fa440935..d30ef1fe9 100644
--- a/app/controllers/api/v1/instances/peers_controller.rb
+++ b/app/controllers/api/v1/instances/peers_controller.rb
@@ -4,7 +4,7 @@ class Api::V1::Instances::PeersController < Api::BaseController
   before_action :require_enabled_api!
 
   skip_before_action :set_cache_headers
-  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
+  skip_before_action :require_authenticated_user! #, unless: :whitelist_mode?
 
   def index
     expires_in 1.day, public: true
@@ -14,6 +14,6 @@ class Api::V1::Instances::PeersController < Api::BaseController
   private
 
   def require_enabled_api!
-    head 404 unless Setting.peers_api_enabled && !whitelist_mode?
+    head 404 unless Setting.peers_api_enabled #&& !whitelist_mode?
   end
 end
diff --git a/app/controllers/api/v1/instances_controller.rb b/app/controllers/api/v1/instances_controller.rb
index 5b5058a7b..844bab68a 100644
--- a/app/controllers/api/v1/instances_controller.rb
+++ b/app/controllers/api/v1/instances_controller.rb
@@ -2,7 +2,7 @@
 
 class Api::V1::InstancesController < Api::BaseController
   skip_before_action :set_cache_headers
-  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
+  skip_before_action :require_authenticated_user! #, unless: :whitelist_mode?
 
   def show
     expires_in 3.minutes, public: true
diff --git a/app/controllers/concerns/account_owned_concern.rb b/app/controllers/concerns/account_owned_concern.rb
index 460f71f65..65168efff 100644
--- a/app/controllers/concerns/account_owned_concern.rb
+++ b/app/controllers/concerns/account_owned_concern.rb
@@ -4,7 +4,7 @@ module AccountOwnedConcern
   extend ActiveSupport::Concern
 
   included do
-    before_action :authenticate_user!, if: -> { whitelist_mode? && request.format != :json }
+    #before_action :authenticate_user!, if: -> { whitelist_mode? && request.format != :json }
     before_action :set_account, if: :account_required?
     before_action :check_account_approval, if: :account_required?
     before_action :check_account_suspension, if: :account_required?
diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb
index c9b840881..d15adbf62 100644
--- a/app/controllers/home_controller.rb
+++ b/app/controllers/home_controller.rb
@@ -47,7 +47,7 @@ class HomeController < ApplicationController
   end
 
   def default_redirect_path
-    if request.path.start_with?('/web') || whitelist_mode?
+    if request.path.start_with?('/web') #|| whitelist_mode?
       new_user_session_path
     elsif single_user_mode?
       short_account_path(Account.local.without_suspended.where('id > 0').first)
diff --git a/app/controllers/media_controller.rb b/app/controllers/media_controller.rb
index 772fc42cb..ae63bb7c5 100644
--- a/app/controllers/media_controller.rb
+++ b/app/controllers/media_controller.rb
@@ -4,9 +4,9 @@ class MediaController < ApplicationController
   include Authorization
 
   skip_before_action :store_current_location
-  skip_before_action :require_functional!, unless: :whitelist_mode?
+  skip_before_action :require_functional! #, unless: :whitelist_mode?
 
-  before_action :authenticate_user!, if: :whitelist_mode?
+  #before_action :authenticate_user!, if: :whitelist_mode?
   before_action :set_media_attachment
   before_action :verify_permitted_status!
   before_action :check_playable, only: :player
diff --git a/app/controllers/media_proxy_controller.rb b/app/controllers/media_proxy_controller.rb
index 0b1d09de9..8f9c2e14d 100644
--- a/app/controllers/media_proxy_controller.rb
+++ b/app/controllers/media_proxy_controller.rb
@@ -7,7 +7,7 @@ class MediaProxyController < ApplicationController
   skip_before_action :store_current_location
   skip_before_action :require_functional!
 
-  before_action :authenticate_user!, if: :whitelist_mode?
+  #before_action :authenticate_user!, if: :whitelist_mode?
 
   rescue_from ActiveRecord::RecordInvalid, with: :not_found
   rescue_from Mastodon::UnexpectedResponseError, with: :not_found
diff --git a/app/controllers/remote_interaction_controller.rb b/app/controllers/remote_interaction_controller.rb
index a277bfa10..5ead3aaa0 100644
--- a/app/controllers/remote_interaction_controller.rb
+++ b/app/controllers/remote_interaction_controller.rb
@@ -5,13 +5,13 @@ class RemoteInteractionController < ApplicationController
 
   layout 'modal'
 
-  before_action :authenticate_user!, if: :whitelist_mode?
+  #before_action :authenticate_user!, if: :whitelist_mode?
   before_action :set_interaction_type
   before_action :set_status
   before_action :set_body_classes
   before_action :set_pack
 
-  skip_before_action :require_functional!, unless: :whitelist_mode?
+  skip_before_action :require_functional! #, unless: :whitelist_mode?
 
   def new
     @remote_follow = RemoteFollow.new(session_params)
diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb
index a6ab8828f..0360dc390 100644
--- a/app/controllers/statuses_controller.rb
+++ b/app/controllers/statuses_controller.rb
@@ -19,7 +19,7 @@ class StatusesController < ApplicationController
   before_action :set_autoplay, only: :embed
 
   skip_around_action :set_locale, if: -> { request.format == :json }
-  skip_before_action :require_functional!, only: [:show, :embed], unless: :whitelist_mode?
+  skip_before_action :require_functional!, only: [:show, :embed] #, unless: :whitelist_mode?
 
   content_security_policy only: :embed do |p|
     p.frame_ancestors(false)
diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb
index 69db89eb3..0b8ac7c6d 100644
--- a/app/controllers/tags_controller.rb
+++ b/app/controllers/tags_controller.rb
@@ -9,13 +9,13 @@ class TagsController < ApplicationController
   layout 'public'
 
   before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
-  before_action :authenticate_user!, if: :whitelist_mode?
+  #before_action :authenticate_user!, if: :whitelist_mode?
   before_action :set_tag
   before_action :set_local
   before_action :set_body_classes
   before_action :set_instance_presenter
 
-  skip_before_action :require_functional!, unless: :whitelist_mode?
+  skip_before_action :require_functional! #, unless: :whitelist_mode?
 
   def show
     respond_to do |format|
diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb
index fcec3e686..e36974519 100644
--- a/app/models/form/admin_settings.rb
+++ b/app/models/form/admin_settings.rb
@@ -4,6 +4,8 @@ class Form::AdminSettings
   include ActiveModel::Model
 
   KEYS = %i(
+    show_domain_allows
+
     site_contact_username
     site_contact_email
     site_title
@@ -76,6 +78,8 @@ class Form::AdminSettings
 
   attr_accessor(*KEYS)
 
+  validates :show_domain_allows, inclusion: { in: %w(disabled users all) }
+
   validates :site_short_description, :site_description, html: { wrap_with: :p }
   validates :site_extended_description, :site_terms, :closed_registrations_message, html: true
   validates :registrations_mode, inclusion: { in: %w(open approved none) }
diff --git a/app/views/about/_domain_allows.html.haml b/app/views/about/_domain_allows.html.haml
new file mode 100644
index 000000000..ab5755b41
--- /dev/null
+++ b/app/views/about/_domain_allows.html.haml
@@ -0,0 +1,12 @@
+%table
+  %thead
+    %tr
+      %th{colspan: 3}= t('about.unavailable_content_description.domain')
+  %tbody
+    - domain_allows.in_groups_of(3) do |group|
+      %tr
+      - group.each do |domain_allow|
+        %td.nowrap
+          - unless domain_allow.nil?
+            %span
+              %a{ title: domain_allow.domain, href: "https://#{domain_allow.domain}", rel: 'noopener nofollow' }= domain_allow.domain
diff --git a/app/views/about/more.html.haml b/app/views/about/more.html.haml
index 0a12ab8d6..0e4465a4a 100644
--- a/app/views/about/more.html.haml
+++ b/app/views/about/more.html.haml
@@ -42,13 +42,18 @@
   .column-3
     = render 'application/flashes'
 
-    - if @contents.blank? && (!display_blocks? || @blocks&.empty?)
+    - if @contents.blank? && ((!display_allows? || @allows&.empty?) && (!display_blocks? || @blocks&.empty?))
       = nothing_here
     - else
       .box-widget
         .rich-formatting
           = @contents.html_safe
 
+          - if display_allows? && !@allows.empty?
+            %h2#available-content= t('about.available_content')
+            %p= t('about.available_content_html')
+            = render partial: 'domain_allows', locals: { domain_allows: @allows }
+
           - if display_blocks? && !@blocks.empty?
             %h2#unavailable-content= t('about.unavailable_content')
 
@@ -78,5 +83,8 @@
               - item.children.each do |sub_item|
                 %li= link_to sub_item.title, "##{sub_item.anchor}"
 
+      - if display_allows? && !@allows.empty?
+        %li= link_to t('about.available_content'), '#available-content'
+
       - if display_blocks? && !@blocks.empty?
         %li= link_to t('about.unavailable_content'), '#unavailable-content'
diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml
index 108846ca9..0d36e4551 100644
--- a/app/views/admin/settings/edit.html.haml
+++ b/app/views/admin/settings/edit.html.haml
@@ -47,12 +47,11 @@
 
   %hr.spacer/
 
-  - unless whitelist_mode?
-    .fields-group
-      = f.input :timeline_preview, as: :boolean, wrapper: :with_label, label: t('admin.settings.timeline_preview.title'), hint: t('admin.settings.timeline_preview.desc_html')
+  .fields-group
+    = f.input :timeline_preview, as: :boolean, wrapper: :with_label, label: t('admin.settings.timeline_preview.title'), hint: t('admin.settings.timeline_preview.desc_html')
 
-    .fields-group
-      = f.input :show_known_fediverse_at_about_page, as: :boolean, wrapper: :with_label, label: t('admin.settings.show_known_fediverse_at_about_page.title'), hint: t('admin.settings.show_known_fediverse_at_about_page.desc_html')
+  .fields-group
+    = f.input :show_known_fediverse_at_about_page, as: :boolean, wrapper: :with_label, label: t('admin.settings.show_known_fediverse_at_about_page.title'), hint: t('admin.settings.show_known_fediverse_at_about_page.desc_html')
 
   .fields-group
     = f.input :show_staff_badge, as: :boolean, wrapper: :with_label, label: t('admin.settings.show_staff_badge.title'), hint: t('admin.settings.show_staff_badge.desc_html')
@@ -60,27 +59,26 @@
   .fields-group
     = f.input :open_deletion, as: :boolean, wrapper: :with_label, label: t('admin.settings.registrations.deletion.title'), hint: t('admin.settings.registrations.deletion.desc_html')
 
-  - unless whitelist_mode?
-    .fields-group
-      = f.input :activity_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.activity_api_enabled.title'), hint: t('admin.settings.activity_api_enabled.desc_html')
+  .fields-group
+    = f.input :activity_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.activity_api_enabled.title'), hint: t('admin.settings.activity_api_enabled.desc_html')
 
-    .fields-group
-      = f.input :peers_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.peers_api_enabled.title'), hint: t('admin.settings.peers_api_enabled.desc_html')
+  .fields-group
+    = f.input :peers_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.peers_api_enabled.title'), hint: t('admin.settings.peers_api_enabled.desc_html')
 
-    .fields-group
-      = f.input :preview_sensitive_media, as: :boolean, wrapper: :with_label, label: t('admin.settings.preview_sensitive_media.title'), hint: t('admin.settings.preview_sensitive_media.desc_html')
+  .fields-group
+    = f.input :preview_sensitive_media, as: :boolean, wrapper: :with_label, label: t('admin.settings.preview_sensitive_media.title'), hint: t('admin.settings.preview_sensitive_media.desc_html')
 
-    .fields-group
-      = f.input :profile_directory, as: :boolean, wrapper: :with_label, label: t('admin.settings.profile_directory.title'), hint: t('admin.settings.profile_directory.desc_html')
+  .fields-group
+    = f.input :profile_directory, as: :boolean, wrapper: :with_label, label: t('admin.settings.profile_directory.title'), hint: t('admin.settings.profile_directory.desc_html')
 
-    .fields-group
-      = f.input :trends, as: :boolean, wrapper: :with_label, label: t('admin.settings.trends.title'), hint: t('admin.settings.trends.desc_html')
+  .fields-group
+    = f.input :trends, as: :boolean, wrapper: :with_label, label: t('admin.settings.trends.title'), hint: t('admin.settings.trends.desc_html')
 
-    .fields-group
-      = f.input :trendable_by_default, as: :boolean, wrapper: :with_label, label: t('admin.settings.trendable_by_default.title'), hint: t('admin.settings.trendable_by_default.desc_html')
+  .fields-group
+    = f.input :trendable_by_default, as: :boolean, wrapper: :with_label, label: t('admin.settings.trendable_by_default.title'), hint: t('admin.settings.trendable_by_default.desc_html')
 
-    .fields-group
-      = f.input :noindex, as: :boolean, wrapper: :with_label, label: t('admin.settings.default_noindex.title'), hint: t('admin.settings.default_noindex.desc_html')
+  .fields-group
+    = f.input :noindex, as: :boolean, wrapper: :with_label, label: t('admin.settings.default_noindex.title'), hint: t('admin.settings.default_noindex.desc_html')
 
   .fields-group
     = f.input :hide_followers_count, as: :boolean, wrapper: :with_label, label: t('admin.settings.hide_followers_count.title'), hint: t('admin.settings.hide_followers_count.desc_html')
@@ -99,8 +97,11 @@
 
   %hr.spacer/
 
-  .fields-group
-    = f.input :min_invite_role, wrapper: :with_label, collection: %i(disabled user moderator admin), label: t('admin.settings.registrations.min_invite_role.title'), label_method: lambda { |role| role == :disabled ? t('admin.settings.registrations.min_invite_role.disabled') : t("admin.accounts.roles.#{role}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
+  .fields-row
+    .fields-row__column.fields-row__column-6.fields-group
+      = f.input :min_invite_role, wrapper: :with_label, collection: %i(disabled user moderator admin), label: t('admin.settings.registrations.min_invite_role.title'), label_method: lambda { |role| role == :disabled ? t('admin.settings.registrations.min_invite_role.disabled') : t("admin.accounts.roles.#{role}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
+    .fields-row__column.fields-row__column-6.fields-group
+      = f.input :show_domain_allows, wrapper: :with_label, collection: %i(disabled users all), label: t('admin.settings.domain_allows.title'), label_method: lambda { |value| t("admin.settings.domain_blocks.#{value}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
 
   .fields-row
     .fields-row__column.fields-row__column-6.fields-group
@@ -112,7 +113,7 @@
     = f.input :outgoing_spoilers, wrapper: :with_label, label: t('admin.settings.outgoing_spoilers.title'), hint: t('admin.settings.outgoing_spoilers.desc_html')
 
   .fields-group
-    = f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 } unless whitelist_mode?
+    = f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 }
     = f.input :closed_registrations_message, as: :text, wrapper: :with_block_label, label: t('admin.settings.registrations.closed_message.title'), hint: t('admin.settings.registrations.closed_message.desc_html'), input_html: { rows: 8 }
     = f.input :site_terms, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_terms.title'), hint: t('admin.settings.site_terms.desc_html'), input_html: { rows: 8 }
     = f.input :custom_css, wrapper: :with_block_label, as: :text, input_html: { rows: 8 }, label: t('admin.settings.custom_css.title'), hint: t('admin.settings.custom_css.desc_html')
diff --git a/app/views/layouts/public.html.haml b/app/views/layouts/public.html.haml
index eaa0437c2..e820285cb 100644
--- a/app/views/layouts/public.html.haml
+++ b/app/views/layouts/public.html.haml
@@ -10,10 +10,9 @@
             = link_to root_url, class: 'brand' do
               = svg_logo_full
 
-            - unless whitelist_mode?
-              = link_to t('directories.directory'), explore_path, class: 'nav-link optional' if Setting.profile_directory
-              = link_to t('about.about_this'), about_more_path, class: 'nav-link optional'
-              = link_to t('about.apps'), 'https://joinmastodon.org/apps', class: 'nav-link optional'
+            = link_to t('directories.directory'), explore_path, class: 'nav-link optional' if Setting.profile_directory
+            = link_to t('about.about_this'), about_more_path, class: 'nav-link optional'
+            = link_to t('about.apps'), 'https://joinmastodon.org/apps', class: 'nav-link optional'
 
           .nav-center