diff options
Diffstat (limited to 'app')
21 files changed, 260 insertions, 12 deletions
diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb index 5ff6990d7..620c0ff78 100644 --- a/app/controllers/about_controller.rb +++ b/app/controllers/about_controller.rb @@ -22,6 +22,7 @@ class AboutController < ApplicationController toc_generator = TOCGenerator.new(@instance_presenter.site_extended_description) + @rules = Rule.ordered @contents = toc_generator.html @table_of_contents = toc_generator.toc @blocks = DomainBlock.with_user_facing_limitations.by_severity if display_blocks? diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index dfe94af7d..ab7e1f077 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -136,15 +136,15 @@ class AccountsController < ApplicationController end def media_requested? - request.path.split('.').first.ends_with?('/media') && !tag_requested? + request.path.split('.').first.end_with?('/media') && !tag_requested? end def replies_requested? - request.path.split('.').first.ends_with?('/with_replies') && !tag_requested? + request.path.split('.').first.end_with?('/with_replies') && !tag_requested? end def tag_requested? - request.path.split('.').first.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize) + request.path.split('.').first.end_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize) end def cached_filtered_status_page diff --git a/app/controllers/admin/rules_controller.rb b/app/controllers/admin/rules_controller.rb new file mode 100644 index 000000000..f3bed3ad8 --- /dev/null +++ b/app/controllers/admin/rules_controller.rb @@ -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 diff --git a/app/controllers/api/v1/accounts/lookup_controller.rb b/app/controllers/api/v1/accounts/lookup_controller.rb new file mode 100644 index 000000000..aee6be18a --- /dev/null +++ b/app/controllers/api/v1/accounts/lookup_controller.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class Api::V1::Accounts::LookupController < Api::BaseController + before_action -> { authorize_if_got_token! :read, :'read:accounts' } + before_action :set_account + + def show + render json: @account, serializer: REST::AccountSerializer + end + + private + + def set_account + @account = ResolveAccountService.new.call(params[:acct], skip_webfinger: true) || raise(ActiveRecord::RecordNotFound) + end +end diff --git a/app/controllers/api/v1/instances/rules_controller.rb b/app/controllers/api/v1/instances/rules_controller.rb new file mode 100644 index 000000000..93cf3c759 --- /dev/null +++ b/app/controllers/api/v1/instances/rules_controller.rb @@ -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 diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index a4b740b89..7e97009cf 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -44,7 +44,7 @@ class ApplicationController < ActionController::Base private def https_enabled? - Rails.env.production? && !request.path.start_with?('/health') && !request.headers["Host"].ends_with?(".onion") + Rails.env.production? && !request.path.start_with?('/health') && !request.headers["Host"].end_with?(".onion") end def authorized_fetch_mode? diff --git a/app/controllers/media_proxy_controller.rb b/app/controllers/media_proxy_controller.rb index 0b1d09de9..1b610318d 100644 --- a/app/controllers/media_proxy_controller.rb +++ b/app/controllers/media_proxy_controller.rb @@ -37,7 +37,7 @@ class MediaProxyController < ApplicationController end def version - if request.path.ends_with?('/small') + if request.path.end_with?('/small') :small else :original diff --git a/app/javascript/flavours/glitch/components/autosuggest_input.js b/app/javascript/flavours/glitch/components/autosuggest_input.js index cc0ff7dea..b40a2ff35 100644 --- a/app/javascript/flavours/glitch/components/autosuggest_input.js +++ b/app/javascript/flavours/glitch/components/autosuggest_input.js @@ -6,7 +6,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import ImmutablePureComponent from 'react-immutable-pure-component'; import classNames from 'classnames'; -import { List as ImmutableList } from 'immutable'; const textAtCursorMatchesToken = (str, caretPosition, searchTokens) => { let word; @@ -55,7 +54,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { static defaultProps = { autoFocus: true, - searchTokens: ImmutableList(['@', ':', '#']), + searchTokens: ['@', ':', '#'], }; state = { diff --git a/app/javascript/flavours/glitch/styles/about.scss b/app/javascript/flavours/glitch/styles/about.scss index de821fbe4..cdf2d116b 100644 --- a/app/javascript/flavours/glitch/styles/about.scss +++ b/app/javascript/flavours/glitch/styles/about.scss @@ -886,3 +886,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; + } +} diff --git a/app/javascript/mastodon/components/autosuggest_input.js b/app/javascript/mastodon/components/autosuggest_input.js index 5187f95c8..12d44b5d0 100644 --- a/app/javascript/mastodon/components/autosuggest_input.js +++ b/app/javascript/mastodon/components/autosuggest_input.js @@ -6,7 +6,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import ImmutablePureComponent from 'react-immutable-pure-component'; import classNames from 'classnames'; -import { List as ImmutableList } from 'immutable'; const textAtCursorMatchesToken = (str, caretPosition, searchTokens) => { let word; @@ -55,7 +54,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { static defaultProps = { autoFocus: true, - searchTokens: ImmutableList(['@', ':', '#']), + searchTokens: ['@', ':', '#'], }; state = { diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss index d6bd9e3c6..281f5b2bf 100644 --- a/app/javascript/styles/mastodon/about.scss +++ b/app/javascript/styles/mastodon/about.scss @@ -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; + } +} diff --git a/app/lib/webfinger.rb b/app/lib/webfinger.rb index 40795a7aa..e0e022cea 100644 --- a/app/lib/webfinger.rb +++ b/app/lib/webfinger.rb @@ -88,7 +88,7 @@ class Webfinger end def standard_url - if @domain.ends_with? ".onion" + if @domain.end_with? ".onion" "http://#{@domain}/.well-known/webfinger?resource=#{@uri}" else "https://#{@domain}/.well-known/webfinger?resource=#{@uri}" @@ -96,7 +96,7 @@ class Webfinger end def host_meta_url - if @domain.ends_with? ".onion" + if @domain.end_with? ".onion" "http://#{@domain}/.well-known/host-meta" else "https://#{@domain}/.well-known/host-meta" diff --git a/app/models/rule.rb b/app/models/rule.rb new file mode 100644 index 000000000..7b62f2b35 --- /dev/null +++ b/app/models/rule.rb @@ -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 diff --git a/app/policies/rule_policy.rb b/app/policies/rule_policy.rb new file mode 100644 index 000000000..6a4def009 --- /dev/null +++ b/app/policies/rule_policy.rb @@ -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 diff --git a/app/presenters/instance_presenter.rb b/app/presenters/instance_presenter.rb index a37d904dc..345a5e5e9 100644 --- a/app/presenters/instance_presenter.rb +++ b/app/presenters/instance_presenter.rb @@ -16,6 +16,10 @@ class InstancePresenter Account.find_local(Setting.site_contact_username.strip.gsub(/\A@/, '')) end + def rules + Rule.ordered + end + def user_count Rails.cache.fetch('user_count') { User.confirmed.joins(:account).merge(Account.without_suspended).count } end diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb index 54e7c450c..ae8b80fb7 100644 --- a/app/serializers/rest/instance_serializer.rb +++ b/app/serializers/rest/instance_serializer.rb @@ -9,7 +9,9 @@ class REST::InstanceSerializer < ActiveModel::Serializer 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 Rails.configuration.x.local_domain diff --git a/app/serializers/rest/rule_serializer.rb b/app/serializers/rest/rule_serializer.rb new file mode 100644 index 000000000..fc925925a --- /dev/null +++ b/app/serializers/rest/rule_serializer.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class REST::RuleSerializer < ActiveModel::Serializer + attributes :id, :text + + def id + object.id.to_s + end +end diff --git a/app/views/about/more.html.haml b/app/views/about/more.html.haml index 78f54ec5a..45675224b 100644 --- a/app/views/about/more.html.haml +++ b/app/views/about/more.html.haml @@ -47,6 +47,16 @@ - else .box-widget .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 - if display_blocks? && !@blocks.empty? @@ -69,6 +79,9 @@ .column-4 %ul.table-of-contents + - unless @rules.empty? + %li= link_to t('about.rules'), '#rules' + - @table_of_contents.each do |item| %li = link_to item.title, "##{item.anchor}" diff --git a/app/views/admin/rules/_rule.html.haml b/app/views/admin/rules/_rule.html.haml new file mode 100644 index 000000000..f8a9ac786 --- /dev/null +++ b/app/views/admin/rules/_rule.html.haml @@ -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) diff --git a/app/views/admin/rules/edit.html.haml b/app/views/admin/rules/edit.html.haml new file mode 100644 index 000000000..ba7e6451a --- /dev/null +++ b/app/views/admin/rules/edit.html.haml @@ -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 diff --git a/app/views/admin/rules/index.html.haml b/app/views/admin/rules/index.html.haml new file mode 100644 index 000000000..3b069d083 --- /dev/null +++ b/app/views/admin/rules/index.html.haml @@ -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 |