Add server rules (#15769)

This commit is contained in:
Eugen Rochko 2021-02-21 19:50:12 +01:00 committed by GitHub
parent a74d700799
commit 4930e71ae7
21 changed files with 258 additions and 2 deletions

View file

@ -20,6 +20,7 @@ class AboutController < ApplicationController
toc_generator = TOCGenerator.new(@instance_presenter.site_extended_description) toc_generator = TOCGenerator.new(@instance_presenter.site_extended_description)
@rules = Rule.ordered
@contents = toc_generator.html @contents = toc_generator.html
@table_of_contents = toc_generator.toc @table_of_contents = toc_generator.toc
@blocks = DomainBlock.with_user_facing_limitations.by_severity if display_blocks? @blocks = DomainBlock.with_user_facing_limitations.by_severity if display_blocks?

View file

@ -0,0 +1,59 @@
# frozen_string_literal: true
module Admin
class RulesController < BaseController
before_action :set_rule, except: [:index, :create]
def index
authorize :rule, :index?
@rules = Rule.ordered
@rule = Rule.new
end
def create
authorize :rule, :create?
@rule = Rule.new(resource_params)
if @rule.save
redirect_to admin_rules_path
else
@rules = Rule.ordered
render :index
end
end
def edit
authorize @rule, :update?
end
def update
authorize @rule, :update?
if @rule.update(resource_params)
redirect_to admin_rules_path
else
render :edit
end
end
def destroy
authorize @rule, :destroy?
@rule.discard
redirect_to admin_rules_path
end
private
def set_rule
@rule = Rule.find(params[:id])
end
def resource_params
params.require(:rule).permit(:text, :priority)
end
end
end

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
class Api::V1::Instances::RulesController < Api::BaseController
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
before_action :set_rules
def index
render json: @rules, each_serializer: REST::RuleSerializer
end
private
def set_rules
@rules = Rule.ordered
end
end

View file

@ -884,3 +884,24 @@ $small-breakpoint: 960px;
} }
} }
.rules-list {
background: darken($ui-base-color, 2%);
border: 1px solid darken($ui-base-color, 8%);
border-radius: 4px;
padding: 0.5em 2.5em !important;
margin-top: 1.85em !important;
li {
border-bottom: 1px solid lighten($ui-base-color, 4%);
color: $dark-text-color;
padding: 1em;
&:last-child {
border-bottom: 0;
}
}
&__text {
color: $primary-text-color;
}
}

22
app/models/rule.rb Normal file
View file

@ -0,0 +1,22 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: rules
#
# id :bigint(8) not null, primary key
# priority :integer default(0), not null
# deleted_at :datetime
# text :text default(""), not null
# created_at :datetime not null
# updated_at :datetime not null
#
class Rule < ApplicationRecord
include Discard::Model
self.discard_column = :deleted_at
validates :text, presence: true, length: { maximum: 300 }
scope :ordered, -> { kept.order(priority: :asc) }
end

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
class RulePolicy < ApplicationPolicy
def index?
staff?
end
def create?
admin?
end
def update?
admin?
end
def destroy?
admin?
end
end

View file

@ -16,6 +16,10 @@ class InstancePresenter
Account.find_local(Setting.site_contact_username.strip.gsub(/\A@/, '')) Account.find_local(Setting.site_contact_username.strip.gsub(/\A@/, ''))
end end
def rules
Rule.ordered
end
def user_count def user_count
Rails.cache.fetch('user_count') { User.confirmed.joins(:account).merge(Account.without_suspended).count } Rails.cache.fetch('user_count') { User.confirmed.joins(:account).merge(Account.without_suspended).count }
end end

View file

@ -9,7 +9,9 @@ class REST::InstanceSerializer < ActiveModel::Serializer
has_one :contact_account, serializer: REST::AccountSerializer has_one :contact_account, serializer: REST::AccountSerializer
delegate :contact_account, to: :instance_presenter has_many :rules, serializer: REST::RuleSerializer
delegate :contact_account, :rules, to: :instance_presenter
def uri def uri
Rails.configuration.x.local_domain Rails.configuration.x.local_domain

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
class REST::RuleSerializer < ActiveModel::Serializer
attributes :id, :text
def id
object.id.to_s
end
end

View file

@ -48,6 +48,16 @@
- else - else
.box-widget .box-widget
.rich-formatting .rich-formatting
- unless @rules.empty?
%h2#rules= t('about.rules')
%p= t('about.rules_html')
%ol.rules-list
- @rules.each do |rule|
%li
.rules-list__text= rule.text
= @contents.html_safe = @contents.html_safe
- if display_blocks? && !@blocks.empty? - if display_blocks? && !@blocks.empty?
@ -70,6 +80,9 @@
.column-4 .column-4
%ul.table-of-contents %ul.table-of-contents
- unless @rules.empty?
%li= link_to t('about.rules'), '#rules'
- @table_of_contents.each do |item| - @table_of_contents.each do |item|
%li %li
= link_to item.title, "##{item.anchor}" = link_to item.title, "##{item.anchor}"

View file

@ -0,0 +1,11 @@
.announcements-list__item
= link_to edit_admin_rule_path(rule), class: 'announcements-list__item__title' do
= "#{rule_counter + 1}."
= truncate(rule.text)
.announcements-list__item__action-bar
.announcements-list__item__meta
= rule.text
%div
= table_link_to 'trash', t('admin.rules.delete'), admin_rule_path(rule), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') } if can?(:destroy, rule)

View file

@ -0,0 +1,11 @@
- content_for :page_title do
= t('admin.rules.edit')
= simple_form_for @rule, url: admin_rule_path(@rule) do |f|
= render 'shared/error_messages', object: @rule
.fields-group
= f.input :text, wrapper: :with_block_label
.actions
= f.button :button, t('generic.save_changes'), type: :submit

View file

@ -0,0 +1,24 @@
- content_for :page_title do
= t('admin.rules.title')
.simple_form
%p.hint= t('admin.rules.description')
- if can? :create, :rule
= simple_form_for @rule, url: admin_rules_path do |f|
= render 'shared/error_messages', object: @rule
.fields-group
= f.input :text, wrapper: :with_block_label
.actions
= f.button :button, t('admin.rules.add_new'), type: :submit
%hr.spacer/
- if @rules.empty?
%div.muted-hint.center-text
= t 'admin.rules.empty'
- else
.announcements-list
= render partial: 'rule', collection: @rules

View file

@ -26,6 +26,8 @@ en:
It is used for federation purposes and should not be blocked unless you want to block the whole instance, in which case you should use a domain block. It is used for federation purposes and should not be blocked unless you want to block the whole instance, in which case you should use a domain block.
learn_more: Learn more learn_more: Learn more
privacy_policy: Privacy policy privacy_policy: Privacy policy
rules: Server rules
rules_html: 'Below is a summary of rules you need to follow if you want to have an account on this server of Mastodon:'
see_whats_happening: See what's happening see_whats_happening: See what's happening
server_stats: 'Server stats:' server_stats: 'Server stats:'
source_code: Source code source_code: Source code
@ -542,6 +544,11 @@ en:
unassign: Unassign unassign: Unassign
unresolved: Unresolved unresolved: Unresolved
updated_at: Updated updated_at: Updated
rules:
add_new: Add rule
description: While most claim to have read and agree to the terms of service, usually people do not read through until after a problem arises. Make it easier to see your server's rules at a glance by providing them in a flat bullet point list. Try to keep individual rules short and simple, but try not to split them up into many separate items either.
edit: Edit rule
title: Server rules
settings: settings:
activity_api_enabled: activity_api_enabled:
desc_html: Counts of locally posted statuses, active users, and new registrations in weekly buckets desc_html: Counts of locally posted statuses, active users, and new registrations in weekly buckets

View file

@ -73,6 +73,8 @@ en:
no_access: Block access to all resources no_access: Block access to all resources
sign_up_requires_approval: New sign-ups will require your approval sign_up_requires_approval: New sign-ups will require your approval
severity: Choose what will happen with requests from this IP severity: Choose what will happen with requests from this IP
rule:
text: Describe a rule or requirement for users on this server. Try to keep it short and simple
sessions: sessions:
otp: 'Enter the two-factor code generated by your phone app or use one of your recovery codes:' otp: 'Enter the two-factor code generated by your phone app or use one of your recovery codes:'
webauthn: If it's an USB key be sure to insert it and, if necessary, tap it. webauthn: If it's an USB key be sure to insert it and, if necessary, tap it.
@ -197,6 +199,8 @@ en:
reblog: Someone boosted your status reblog: Someone boosted your status
report: New report is submitted report: New report is submitted
trending_tag: An unreviewed hashtag is trending trending_tag: An unreviewed hashtag is trending
rule:
text: Rule
tag: tag:
listable: Allow this hashtag to appear in searches and on the profile directory listable: Allow this hashtag to appear in searches and on the profile directory
name: Hashtag name: Hashtag

View file

@ -47,6 +47,7 @@ SimpleNavigation::Configuration.run do |navigation|
n.item :admin, safe_join([fa_icon('cogs fw'), t('admin.title')]), admin_dashboard_url, if: proc { current_user.staff? } do |s| n.item :admin, safe_join([fa_icon('cogs fw'), t('admin.title')]), admin_dashboard_url, if: proc { current_user.staff? } do |s|
s.item :dashboard, safe_join([fa_icon('tachometer fw'), t('admin.dashboard.title')]), admin_dashboard_url s.item :dashboard, safe_join([fa_icon('tachometer fw'), t('admin.dashboard.title')]), admin_dashboard_url
s.item :settings, safe_join([fa_icon('cogs fw'), t('admin.settings.title')]), edit_admin_settings_url, if: -> { current_user.admin? }, highlights_on: %r{/admin/settings} s.item :settings, safe_join([fa_icon('cogs fw'), t('admin.settings.title')]), edit_admin_settings_url, if: -> { current_user.admin? }, highlights_on: %r{/admin/settings}
s.item :rules, safe_join([fa_icon('gavel fw'), t('admin.rules.title')]), admin_rules_path, highlights_on: %r{/admin/rules}
s.item :announcements, safe_join([fa_icon('bullhorn fw'), t('admin.announcements.title')]), admin_announcements_path, highlights_on: %r{/admin/announcements} s.item :announcements, safe_join([fa_icon('bullhorn fw'), t('admin.announcements.title')]), admin_announcements_path, highlights_on: %r{/admin/announcements}
s.item :custom_emojis, safe_join([fa_icon('smile-o fw'), t('admin.custom_emojis.title')]), admin_custom_emojis_url, highlights_on: %r{/admin/custom_emojis} s.item :custom_emojis, safe_join([fa_icon('smile-o fw'), t('admin.custom_emojis.title')]), admin_custom_emojis_url, highlights_on: %r{/admin/custom_emojis}
s.item :relays, safe_join([fa_icon('exchange fw'), t('admin.relays.title')]), admin_relays_url, if: -> { current_user.admin? && !whitelist_mode? }, highlights_on: %r{/admin/relays} s.item :relays, safe_join([fa_icon('exchange fw'), t('admin.relays.title')]), admin_relays_url, if: -> { current_user.admin? && !whitelist_mode? }, highlights_on: %r{/admin/relays}

View file

@ -220,6 +220,7 @@ Rails.application.routes.draw do
end end
resources :instances, only: [:index, :show], constraints: { id: /[^\/]+/ } resources :instances, only: [:index, :show], constraints: { id: /[^\/]+/ }
resources :rules
resources :reports, only: [:index, :show] do resources :reports, only: [:index, :show] do
member do member do
@ -405,6 +406,7 @@ Rails.application.routes.draw do
resource :instance, only: [:show] do resource :instance, only: [:show] do
resources :peers, only: [:index], controller: 'instances/peers' resources :peers, only: [:index], controller: 'instances/peers'
resource :activity, only: [:show], controller: 'instances/activity' resource :activity, only: [:show], controller: 'instances/activity'
resources :rules, only: [:index], controller: 'instances/rules'
end end
resource :domain_blocks, only: [:show, :create, :destroy] resource :domain_blocks, only: [:show, :create, :destroy]

View file

@ -0,0 +1,11 @@
class CreateRules < ActiveRecord::Migration[5.2]
def change
create_table :rules do |t|
t.integer :priority, null: false, default: 0
t.datetime :deleted_at
t.text :text, null: false, default: ''
t.timestamps
end
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_12_18_054746) do ActiveRecord::Schema.define(version: 2021_02_21_045109) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -723,6 +723,14 @@ ActiveRecord::Schema.define(version: 2020_12_18_054746) do
t.index ["target_account_id"], name: "index_reports_on_target_account_id" t.index ["target_account_id"], name: "index_reports_on_target_account_id"
end end
create_table "rules", force: :cascade do |t|
t.integer "priority", default: 0, null: false
t.datetime "deleted_at"
t.text "text", default: "", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "scheduled_statuses", force: :cascade do |t| create_table "scheduled_statuses", force: :cascade do |t|
t.bigint "account_id" t.bigint "account_id"
t.datetime "scheduled_at" t.datetime "scheduled_at"

View file

@ -0,0 +1,5 @@
Fabricator(:rule) do
priority ""
deleted_at "2021-02-21 05:51:09"
text "MyText"
end

5
spec/models/rule_spec.rb Normal file
View file

@ -0,0 +1,5 @@
require 'rails_helper'
RSpec.describe Rule, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end