diff options
author | Thibaut Girka <thib@sitedethib.com> | 2020-11-07 18:19:34 +0100 |
---|---|---|
committer | Thibaut Girka <thib@sitedethib.com> | 2020-11-07 18:23:02 +0100 |
commit | 412218af2ecd4cd004708d781e574208742dea4c (patch) | |
tree | c0bf823109c7481ec9242a0263e091ec4aaeafeb /app | |
parent | 5a9fc749c3eab8d3c93dd282fa89c20a5cb0e994 (diff) | |
parent | ee8cf246cfe8e05914ad7dcf81596f8535b3e161 (diff) |
Merge branch 'master' into glitch-soc/merge-upstream
Diffstat (limited to 'app')
33 files changed, 193 insertions, 72 deletions
diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index b9b75727d..1dd7430e0 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -53,6 +53,13 @@ module Admin redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.destroyed_msg', username: @account.acct) end + def unsensitive + authorize @account, :unsensitive? + @account.unsensitize! + log_action :unsensitive, @account + redirect_to admin_account_path(@account.id) + end + def unsilence authorize @account, :unsilence? @account.unsilence! diff --git a/app/controllers/admin/announcements_controller.rb b/app/controllers/admin/announcements_controller.rb index 494fd13d0..351b9a991 100644 --- a/app/controllers/admin/announcements_controller.rb +++ b/app/controllers/admin/announcements_controller.rb @@ -71,7 +71,7 @@ class Admin::AnnouncementsController < Admin::BaseController private def set_announcements - @announcements = AnnouncementFilter.new(filter_params).results.page(params[:page]) + @announcements = AnnouncementFilter.new(filter_params).results.reverse_chronological.page(params[:page]) end def set_announcement diff --git a/app/controllers/api/v1/admin/accounts_controller.rb b/app/controllers/api/v1/admin/accounts_controller.rb index 3af572f25..63cc521ed 100644 --- a/app/controllers/api/v1/admin/accounts_controller.rb +++ b/app/controllers/api/v1/admin/accounts_controller.rb @@ -22,6 +22,7 @@ class Api::V1::Admin::AccountsController < Api::BaseController active pending disabled + sensitized silenced suspended username @@ -68,6 +69,13 @@ class Api::V1::Admin::AccountsController < Api::BaseController render json: @account, serializer: REST::Admin::AccountSerializer end + def unsensitive + authorize @account, :unsensitive? + @account.unsensitize! + log_action :unsensitive, @account + render json: @account, serializer: REST::Admin::AccountSerializer + end + def unsilence authorize @account, :unsilence? @account.unsilence! diff --git a/app/helpers/statuses_helper.rb b/app/helpers/statuses_helper.rb index a51597cf3..daed9048f 100644 --- a/app/helpers/statuses_helper.rb +++ b/app/helpers/statuses_helper.rb @@ -4,8 +4,12 @@ module StatusesHelper EMBEDDED_CONTROLLER = 'statuses' EMBEDDED_ACTION = 'embed' - def link_to_more(url) - link_to t('statuses.show_more'), url, class: 'load-more load-gap' + def link_to_newer(url) + link_to t('statuses.show_newer'), url, class: 'load-more load-gap' + end + + def link_to_older(url) + link_to t('statuses.show_older'), url, class: 'load-more load-gap' end def nothing_here(extra_classes = '') @@ -117,6 +121,14 @@ module StatusesHelper end end + def sensitized?(status, account) + if !account.nil? && account.id == status.account_id + status.sensitive + else + status.account.sensitized? || status.sensitive + end + end + private def simplified_text(text) diff --git a/app/javascript/mastodon/components/column.js b/app/javascript/mastodon/components/column.js index 55e3bfd5e..239824a4f 100644 --- a/app/javascript/mastodon/components/column.js +++ b/app/javascript/mastodon/components/column.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import detectPassiveEvents from 'detect-passive-events'; +import { supportsPassiveEvents } from 'detect-passive-events'; import { scrollTop } from '../scroll'; export default class Column extends React.PureComponent { @@ -35,9 +35,9 @@ export default class Column extends React.PureComponent { componentDidMount () { if (this.props.bindToDocument) { - document.addEventListener('wheel', this.handleWheel, detectPassiveEvents.hasSupport ? { passive: true } : false); + document.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false); } else { - this.node.addEventListener('wheel', this.handleWheel, detectPassiveEvents.hasSupport ? { passive: true } : false); + this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false); } } diff --git a/app/javascript/mastodon/components/dropdown_menu.js b/app/javascript/mastodon/components/dropdown_menu.js index 09e3c9df8..c6b4b1187 100644 --- a/app/javascript/mastodon/components/dropdown_menu.js +++ b/app/javascript/mastodon/components/dropdown_menu.js @@ -5,9 +5,9 @@ import IconButton from './icon_button'; import Overlay from 'react-overlays/lib/Overlay'; import Motion from '../features/ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; -import detectPassiveEvents from 'detect-passive-events'; +import { supportsPassiveEvents } from 'detect-passive-events'; -const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false; +const listenerOptions = supportsPassiveEvents ? { passive: true } : false; let id = 0; class DropdownMenu extends React.PureComponent { diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js index bac136e5e..dc4f48060 100644 --- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js +++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js @@ -5,7 +5,7 @@ import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components' import Overlay from 'react-overlays/lib/Overlay'; import classNames from 'classnames'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import detectPassiveEvents from 'detect-passive-events'; +import { supportsPassiveEvents } from 'detect-passive-events'; import { buildCustomEmojis, categoriesFromEmojis } from '../../emoji/emoji'; import { assetHost } from 'mastodon/utils/config'; @@ -29,7 +29,7 @@ const messages = defineMessages({ let EmojiPicker, Emoji; // load asynchronously const backgroundImageFn = () => `${assetHost}/emoji/sheet_10.png`; -const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false; +const listenerOptions = supportsPassiveEvents ? { passive: true } : false; class ModifierPickerMenu extends React.PureComponent { diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js index 5223025fb..309f46290 100644 --- a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js +++ b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js @@ -5,7 +5,7 @@ import IconButton from '../../../components/icon_button'; import Overlay from 'react-overlays/lib/Overlay'; import Motion from '../../ui/util/optional_motion'; import spring from 'react-motion/lib/spring'; -import detectPassiveEvents from 'detect-passive-events'; +import { supportsPassiveEvents } from 'detect-passive-events'; import classNames from 'classnames'; import Icon from 'mastodon/components/icon'; @@ -21,7 +21,7 @@ const messages = defineMessages({ change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' }, }); -const listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false; +const listenerOptions = supportsPassiveEvents ? { passive: true } : false; class PrivacyDropdownMenu extends React.PureComponent { diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js index ecc0b8f0b..36a84fcbf 100644 --- a/app/javascript/mastodon/features/ui/components/columns_area.js +++ b/app/javascript/mastodon/features/ui/components/columns_area.js @@ -31,7 +31,7 @@ import Icon from 'mastodon/components/icon'; import ComposePanel from './compose_panel'; import NavigationPanel from './navigation_panel'; -import detectPassiveEvents from 'detect-passive-events'; +import { supportsPassiveEvents } from 'detect-passive-events'; import { scrollRight } from '../../../scroll'; const componentMap = { @@ -80,7 +80,7 @@ class ColumnsArea extends ImmutablePureComponent { componentDidMount() { if (!this.props.singleColumn) { - this.node.addEventListener('wheel', this.handleWheel, detectPassiveEvents.hasSupport ? { passive: true } : false); + this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false); } this.lastIndex = getIndex(this.context.router.history.location.pathname); @@ -97,7 +97,7 @@ class ColumnsArea extends ImmutablePureComponent { componentDidUpdate(prevProps) { if (this.props.singleColumn !== prevProps.singleColumn && !this.props.singleColumn) { - this.node.addEventListener('wheel', this.handleWheel, detectPassiveEvents.hasSupport ? { passive: true } : false); + this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false); } this.lastIndex = getIndex(this.context.router.history.location.pathname); this.setState({ shouldAnimate: true }); diff --git a/app/javascript/mastodon/features/ui/components/zoomable_image.js b/app/javascript/mastodon/features/ui/components/zoomable_image.js index 402196727..1cf263cb9 100644 --- a/app/javascript/mastodon/features/ui/components/zoomable_image.js +++ b/app/javascript/mastodon/features/ui/components/zoomable_image.js @@ -113,7 +113,8 @@ class ZoomableImage extends React.PureComponent { state = { scale: MIN_SCALE, zoomMatrix: { - type: null, // 'full-width' 'full-height' + type: null, // 'width' 'height' + fullScreen: null, // bool rate: null, // full screen scale rate clientWidth: null, clientHeight: null, @@ -122,12 +123,15 @@ class ZoomableImage extends React.PureComponent { clientHeightFixed: null, scrollTop: null, scrollLeft: null, + translateX: null, + translateY: null, }, zoomState: 'expand', // 'expand' 'compress' navigationHidden: false, dragPosition: { top: 0, left: 0, x: 0, y: 0 }, dragged: false, lockScroll: { x: 0, y: 0 }, + lockTranslate: { x: 0, y: 0 }, } removers = []; @@ -168,18 +172,24 @@ class ZoomableImage extends React.PureComponent { } componentDidUpdate () { + this.setState({ zoomState: this.state.scale >= this.state.zoomMatrix.rate ? 'compress' : 'expand' }); + + if (this.state.scale === MIN_SCALE) { + this.container.style.removeProperty('cursor'); + } + } + + UNSAFE_componentWillReceiveProps () { + // reset when slide to next image if (this.props.zoomButtonHidden) { - this.setState({ scale: MIN_SCALE }, () => { + this.setState({ + scale: MIN_SCALE, + lockTranslate: { x: 0, y: 0 }, + }, () => { this.container.scrollLeft = 0; this.container.scrollTop = 0; }); } - - this.setState({ zoomState: this.state.scale >= this.state.zoomMatrix.rate ? 'compress' : 'expand' }); - - if (this.state.scale === 1) { - this.container.style.removeProperty('cursor'); - } } removeEventListeners () { @@ -192,7 +202,7 @@ class ZoomableImage extends React.PureComponent { const event = normalizeWheel(e); - if (this.state.zoomMatrix.type === 'full-width') { + if (this.state.zoomMatrix.type === 'width') { // full width, scroll vertical this.container.scrollTop = Math.max(this.container.scrollTop + event.pixelY, this.state.lockScroll.y); } else { @@ -268,7 +278,7 @@ class ZoomableImage extends React.PureComponent { } zoom(nextScale, midpoint) { - const { scale } = this.state; + const { scale, zoomMatrix } = this.state; const { scrollLeft, scrollTop } = this.container; // math memo: @@ -283,6 +293,15 @@ class ZoomableImage extends React.PureComponent { this.setState({ scale: nextScale }, () => { this.container.scrollLeft = nextScrollLeft; this.container.scrollTop = nextScrollTop; + // reset the translateX/Y constantly + if (nextScale < zoomMatrix.rate) { + this.setState({ + lockTranslate: { + x: zoomMatrix.fullScreen ? 0 : zoomMatrix.translateX * ((nextScale - MIN_SCALE) / (zoomMatrix.rate - MIN_SCALE)), + y: zoomMatrix.fullScreen ? 0 : zoomMatrix.translateY * ((nextScale - MIN_SCALE) / (zoomMatrix.rate - MIN_SCALE)), + }, + }); + } }); } @@ -307,14 +326,18 @@ class ZoomableImage extends React.PureComponent { const { offsetWidth, offsetHeight } = this.image; const clientHeightFixed = clientHeight - NAV_BAR_HEIGHT; - const type = width/height < clientWidth / clientHeightFixed ? 'full-width' : 'full-height'; - const rate = type === 'full-width' ? clientWidth / offsetWidth : clientHeightFixed / offsetHeight; - const scrollTop = type === 'full-width' ? (clientHeight - offsetHeight) / 2 - NAV_BAR_HEIGHT : (clientHeightFixed - offsetHeight) / 2; + const type = width / height < clientWidth / clientHeightFixed ? 'width' : 'height'; + const fullScreen = type === 'width' ? width > clientWidth : height > clientHeightFixed; + const rate = type === 'width' ? Math.min(clientWidth, width) / offsetWidth : Math.min(clientHeightFixed, height) / offsetHeight; + const scrollTop = type === 'width' ? (clientHeight - offsetHeight) / 2 - NAV_BAR_HEIGHT : (clientHeightFixed - offsetHeight) / 2; const scrollLeft = (clientWidth - offsetWidth) / 2; + const translateX = type === 'width' ? (width - offsetWidth) / (2 * rate) : 0; + const translateY = type === 'height' ? (height - offsetHeight) / (2 * rate) : 0; this.setState({ zoomMatrix: { type: type, + fullScreen: fullScreen, rate: rate, clientWidth: clientWidth, clientHeight: clientHeight, @@ -323,6 +346,8 @@ class ZoomableImage extends React.PureComponent { clientHeightFixed: clientHeightFixed, scrollTop: scrollTop, scrollLeft: scrollLeft, + translateX: translateX, + translateY: translateY, }, }); } @@ -340,6 +365,10 @@ class ZoomableImage extends React.PureComponent { x: 0, y: 0, }, + lockTranslate: { + x: 0, + y: 0, + }, }, () => { this.container.scrollLeft = 0; this.container.scrollTop = 0; @@ -351,6 +380,10 @@ class ZoomableImage extends React.PureComponent { x: zoomMatrix.scrollLeft, y: zoomMatrix.scrollTop, }, + lockTranslate: { + x: zoomMatrix.fullScreen ? 0 : zoomMatrix.translateX, + y: zoomMatrix.fullScreen ? 0 : zoomMatrix.translateY, + }, }, () => { this.container.scrollLeft = zoomMatrix.scrollLeft; this.container.scrollTop = zoomMatrix.scrollTop; @@ -371,15 +404,15 @@ class ZoomableImage extends React.PureComponent { render () { const { alt, src, width, height, intl } = this.props; - const { scale } = this.state; - const overflow = scale === 1 ? 'hidden' : 'scroll'; - const zoomButtonSshouldHide = !this.state.navigationHidden && !this.props.zoomButtonHidden ? '' : 'media-modal__zoom-button--hidden'; + const { scale, lockTranslate } = this.state; + const overflow = scale === MIN_SCALE ? 'hidden' : 'scroll'; + const zoomButtonShouldHide = this.state.navigationHidden || this.props.zoomButtonHidden || this.state.zoomMatrix.rate <= MIN_SCALE ? 'media-modal__zoom-button--hidden' : ''; const zoomButtonTitle = this.state.zoomState === 'compress' ? intl.formatMessage(messages.compress) : intl.formatMessage(messages.expand); return ( <React.Fragment> <IconButton - className={`media-modal__zoom-button ${zoomButtonSshouldHide}`} + className={`media-modal__zoom-button ${zoomButtonShouldHide}`} title={zoomButtonTitle} icon={this.state.zoomState} onClick={this.handleZoomClick} @@ -402,7 +435,7 @@ class ZoomableImage extends React.PureComponent { width={width} height={height} style={{ - transform: `scale(${scale})`, + transform: `scale(${scale}) translate(-${lockTranslate.x}px, -${lockTranslate.y}px)`, transformOrigin: '0 0', }} draggable={false} diff --git a/app/javascript/mastodon/is_mobile.js b/app/javascript/mastodon/is_mobile.js index f96df1ebb..5a8c3db08 100644 --- a/app/javascript/mastodon/is_mobile.js +++ b/app/javascript/mastodon/is_mobile.js @@ -1,4 +1,4 @@ -import detectPassiveEvents from 'detect-passive-events'; +import { supportsPassiveEvents } from 'detect-passive-events'; const LAYOUT_BREAKPOINT = 630; @@ -9,7 +9,7 @@ export function isMobile(width) { const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; let userTouching = false; -let listenerOptions = detectPassiveEvents.hasSupport ? { passive: true } : false; +let listenerOptions = supportsPassiveEvents ? { passive: true } : false; function touchListener() { userTouching = true; diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 3a9f83978..d56d47a2d 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -111,7 +111,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity created_at: @object['published'], override_timestamps: @options[:override_timestamps], reply: @object['inReplyTo'].present?, - sensitive: @object['sensitive'] || false, + sensitive: @account.sensitized? || @object['sensitive'] || false, visibility: visibility_from_audience, thread: replied_to_status, conversation: conversation_from_uri(@object['conversation']), diff --git a/app/models/account.rb b/app/models/account.rb index 38f235baa..14df68058 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -50,6 +50,7 @@ # avatar_storage_schema_version :integer # header_storage_schema_version :integer # devices_url :string +# sensitized_at :datetime # class Account < ApplicationRecord @@ -96,6 +97,7 @@ class Account < ApplicationRecord scope :partitioned, -> { order(Arel.sql('row_number() over (partition by domain)')) } scope :silenced, -> { where.not(silenced_at: nil) } scope :suspended, -> { where.not(suspended_at: nil) } + scope :sensitized, -> { where.not(sensitized_at: nil) } scope :without_suspended, -> { where(suspended_at: nil) } scope :without_silenced, -> { where(silenced_at: nil) } scope :recent, -> { reorder(id: :desc) } @@ -238,6 +240,18 @@ class Account < ApplicationRecord end end + def sensitized? + sensitized_at.present? + end + + def sensitize!(date = Time.now.utc) + update!(sensitized_at: date) + end + + def unsensitize! + update!(sensitized_at: nil) + end + def memorialize! update!(memorial: true) end diff --git a/app/models/account_warning.rb b/app/models/account_warning.rb index 157e6c04d..5efc924d5 100644 --- a/app/models/account_warning.rb +++ b/app/models/account_warning.rb @@ -13,7 +13,7 @@ # class AccountWarning < ApplicationRecord - enum action: %i(none disable silence suspend), _suffix: :action + enum action: %i(none disable sensitive silence suspend), _suffix: :action belongs_to :account, inverse_of: :account_warnings belongs_to :target_account, class_name: 'Account', inverse_of: :targeted_account_warnings diff --git a/app/models/admin/account_action.rb b/app/models/admin/account_action.rb index c4ac09520..11ce737f3 100644 --- a/app/models/admin/account_action.rb +++ b/app/models/admin/account_action.rb @@ -8,6 +8,7 @@ class Admin::AccountAction TYPES = %w( none disable + sensitive silence suspend ).freeze @@ -64,6 +65,8 @@ class Admin::AccountAction case type when 'disable' handle_disable! + when 'sensitive' + handle_sensitive! when 'silence' handle_silence! when 'suspend' @@ -109,6 +112,12 @@ class Admin::AccountAction target_account.user&.disable! end + def handle_sensitive! + authorize(target_account, :sensitive?) + log_action(:sensitive, target_account) + target_account.sensitize! + end + def handle_silence! authorize(target_account, :silence?) log_action(:silence, target_account) diff --git a/app/models/admin/action_log_filter.rb b/app/models/admin/action_log_filter.rb index 0ba7e1609..3a1b67e06 100644 --- a/app/models/admin/action_log_filter.rb +++ b/app/models/admin/action_log_filter.rb @@ -35,9 +35,11 @@ class Admin::ActionLogFilter reopen_report: { target_type: 'Report', action: 'reopen' }.freeze, reset_password_user: { target_type: 'User', action: 'reset_password' }.freeze, resolve_report: { target_type: 'Report', action: 'resolve' }.freeze, + sensitive_account: { target_type: 'Account', action: 'sensitive' }.freeze, silence_account: { target_type: 'Account', action: 'silence' }.freeze, suspend_account: { target_type: 'Account', action: 'suspend' }.freeze, unassigned_report: { target_type: 'Report', action: 'unassigned' }.freeze, + unsensitive_account: { target_type: 'Account', action: 'unsensitive' }.freeze, unsilence_account: { target_type: 'Account', action: 'unsilence' }.freeze, unsuspend_account: { target_type: 'Account', action: 'unsuspend' }.freeze, update_announcement: { target_type: 'Announcement', action: 'update' }.freeze, diff --git a/app/models/announcement.rb b/app/models/announcement.rb index c493604c2..f8183aabc 100644 --- a/app/models/announcement.rb +++ b/app/models/announcement.rb @@ -22,6 +22,7 @@ class Announcement < ApplicationRecord scope :published, -> { where(published: true) } scope :without_muted, ->(account) { joins("LEFT OUTER JOIN announcement_mutes ON announcement_mutes.announcement_id = announcements.id AND announcement_mutes.account_id = #{account.id}").where('announcement_mutes.id IS NULL') } scope :chronological, -> { order(Arel.sql('COALESCE(announcements.starts_at, announcements.scheduled_at, announcements.published_at, announcements.created_at) ASC')) } + scope :reverse_chronological, -> { order(Arel.sql('COALESCE(announcements.starts_at, announcements.scheduled_at, announcements.published_at, announcements.created_at) DESC')) } has_many :announcement_mutes, dependent: :destroy has_many :announcement_reactions, dependent: :destroy diff --git a/app/policies/account_policy.rb b/app/policies/account_policy.rb index 1b105e92a..679119075 100644 --- a/app/policies/account_policy.rb +++ b/app/policies/account_policy.rb @@ -25,6 +25,14 @@ class AccountPolicy < ApplicationPolicy staff? end + def sensitive? + staff? && !record.user&.staff? + end + + def unsensitive? + staff? + end + def silence? staff? && !record.user&.staff? end diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb index a0965790e..4ac699ddf 100644 --- a/app/serializers/activitypub/note_serializer.rb +++ b/app/serializers/activitypub/note_serializer.rb @@ -110,6 +110,10 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer ActivityPub::TagManager.instance.cc(object) end + def sensitive + object.account.sensitized? || object.sensitive + end + def virtual_tags object.active_mentions.to_a.sort_by(&:id) + object.tags + object.emojis end diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb index 58e7bd4e4..b5dcf6208 100644 --- a/app/serializers/rest/status_serializer.rb +++ b/app/serializers/rest/status_serializer.rb @@ -60,6 +60,14 @@ class REST::StatusSerializer < ActiveModel::Serializer end end + def sensitive + if current_user? && current_user.account_id == object.account_id + object.sensitive + else + object.account.sensitized? || object.sensitive + end + end + def uri ActivityPub::TagManager.instance.uri_for(object) end diff --git a/app/services/suspend_account_service.rb b/app/services/suspend_account_service.rb index 5a079c3ac..f08c41e17 100644 --- a/app/services/suspend_account_service.rb +++ b/app/services/suspend_account_service.rb @@ -18,7 +18,7 @@ class SuspendAccountService < BaseService def unmerge_from_home_timelines! @account.followers_for_local_distribution.find_each do |follower| - FeedManager.instance.unmerge_from_timeline(@account, follower) + FeedManager.instance.unmerge_from_home(@account, follower) end end @@ -39,11 +39,15 @@ class SuspendAccountService < BaseService styles.each do |style| case Paperclip::Attachment.default_options[:storage] when :s3 - attachment.s3_object(style).acl.put(:private) + attachment.s3_object(style).acl.put(acl: 'private') when :fog # Not supported when :filesystem - FileUtils.chmod(0o600 & ~File.umask, attachment.path(style)) + begin + FileUtils.chmod(0o600 & ~File.umask, attachment.path(style)) unless attachment.path(style).nil? + rescue Errno::ENOENT + Rails.logger.warn "Tried to change permission on non-existent file #{attachment.path(style)}" + end end end end diff --git a/app/services/unsuspend_account_service.rb b/app/services/unsuspend_account_service.rb index 3e731ddd9..91dbc9c18 100644 --- a/app/services/unsuspend_account_service.rb +++ b/app/services/unsuspend_account_service.rb @@ -18,7 +18,7 @@ class UnsuspendAccountService < BaseService def merge_into_home_timelines! @account.followers_for_local_distribution.find_each do |follower| - FeedManager.instance.merge_into_timeline(@account, follower) + FeedManager.instance.merge_into_home(@account, follower) end end @@ -39,11 +39,15 @@ class UnsuspendAccountService < BaseService styles.each do |style| case Paperclip::Attachment.default_options[:storage] when :s3 - attachment.s3_object(style).acl.put(Paperclip::Attachment.default_options[:s3_permissions]) + attachment.s3_object(style).acl.put(acl: Paperclip::Attachment.default_options[:s3_permissions]) when :fog # Not supported when :filesystem - FileUtils.chmod(0o666 & ~File.umask, attachment.path(style)) + begin + FileUtils.chmod(0o666 & ~File.umask, attachment.path(style)) unless attachment.path(style).nil? + rescue Errno::ENOENT + Rails.logger.warn "Tried to change permission on non-existent file #{attachment.path(style)}" + end end end end diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml index c9688ea88..1a81b96f6 100644 --- a/app/views/accounts/show.html.haml +++ b/app/views/accounts/show.html.haml @@ -39,12 +39,12 @@ = render partial: 'statuses/status', collection: @pinned_statuses, as: :status, locals: { pinned: true } - if @newer_url - .entry= link_to_more @newer_url + .entry= link_to_newer @newer_url = render partial: 'statuses/status', collection: @statuses, as: :status - if @older_url - .entry= link_to_more @older_url + .entry= link_to_older @older_url .column-1 - if @account.memorial? diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index f0a216f6b..d5978eddd 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -69,6 +69,8 @@ = t('admin.accounts.confirming') - elsif @account.local? && !@account.user_approved? = t('admin.accounts.pending') + - elsif @account.sensitized? + = t('admin.accounts.sensitive') - else = t('admin.accounts.no_limits_imposed') .dashboard__counters__label= t 'admin.accounts.login_status' @@ -192,6 +194,11 @@ - else = link_to t('admin.accounts.disable'), new_admin_account_action_path(@account.id, type: 'disable'), class: 'button' if can?(:disable, @account.user) + - if @account.sensitized? + = link_to t('admin.accounts.undo_sensitized'), unsensitive_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unsensitive, @account) + - elsif !@account.local? || @account.user_approved? + = link_to t('admin.accounts.sensitive'), new_admin_account_action_path(@account.id, type: 'sensitive'), class: 'button' if can?(:sensitive, @account) + - if @account.silenced? = link_to t('admin.accounts.undo_silenced'), unsilence_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unsilence, @account) - elsif !@account.local? || @account.user_approved? diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 9530e612a..ce47418d4 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,8 +1,8 @@ - content_for :header_tags do - = preload_link_tag asset_pack_path('features/getting_started.js'), crossorigin: 'anonymous' - = preload_link_tag asset_pack_path('features/compose.js'), crossorigin: 'anonymous' - = preload_link_tag asset_pack_path('features/home_timeline.js'), crossorigin: 'anonymous' - = preload_link_tag asset_pack_path('features/notifications.js'), crossorigin: 'anonymous' + = preload_pack_asset 'features/getting_started.js', crossorigin: 'anonymous' + = preload_pack_asset 'features/compose.js', crossorigin: 'anonymous' + = preload_pack_asset 'features/home_timeline.js', crossorigin: 'anonymous' + = preload_pack_asset 'features/notifications.js', crossorigin: 'anonymous' %meta{name: 'applicationServerKey', content: Rails.configuration.x.vapid_public_key} = render_initial_state diff --git a/app/views/layouts/_theme.html.haml b/app/views/layouts/_theme.html.haml index 066d9de42..92de64b0d 100644 --- a/app/views/layouts/_theme.html.haml +++ b/app/views/layouts/_theme.html.haml @@ -2,12 +2,12 @@ - if theme[:pack] != 'common' && theme[:common] = render partial: 'layouts/theme', object: theme[:common] - if theme[:pack] - = javascript_pack_tag theme[:flavour] ? "flavours/#{theme[:flavour]}/#{theme[:pack]}" : "core/#{theme[:pack]}", integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag theme[:flavour] ? "flavours/#{theme[:flavour]}/#{theme[:pack]}" : "core/#{theme[:pack]}", crossorigin: 'anonymous' - if theme[:skin] - if !theme[:flavour] || theme[:skin] == 'default' - = stylesheet_pack_tag theme[:flavour] ? "flavours/#{theme[:flavour]}/#{theme[:pack]}" : "core/#{theme[:pack]}", integrity: true, media: 'all' + = stylesheet_pack_tag theme[:flavour] ? "flavours/#{theme[:flavour]}/#{theme[:pack]}" : "core/#{theme[:pack]}", media: 'all', crossorigin: 'anonymous' - else - = stylesheet_pack_tag "skins/#{theme[:flavour]}/#{theme[:skin]}/#{theme[:pack]}" + = stylesheet_pack_tag "skins/#{theme[:flavour]}/#{theme[:skin]}/#{theme[:pack]}", crossorigin: 'anonymous' - if theme[:preload] - theme[:preload].each do |link| %link{ href: asset_pack_path("#{link}.js"), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 1481f6973..32681773f 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -21,12 +21,12 @@ %title= content_for?(:page_title) ? safe_join([yield(:page_title).chomp.html_safe, title], ' - ') : title - = javascript_pack_tag "locales", integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag "locales", crossorigin: 'anonymous' - if @theme - if @theme[:supported_locales].include? I18n.locale.to_s - = javascript_pack_tag "locales/#{@theme[:flavour]}/#{I18n.locale}", integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag "locales/#{@theme[:flavour]}/#{I18n.locale}", crossorigin: 'anonymous' - elsif @theme[:supported_locales].include? 'en' - = javascript_pack_tag "locales/#{@theme[:flavour]}/en", integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag "locales/#{@theme[:flavour]}/en", crossorigin: 'anonymous' = csrf_meta_tags %meta{ name: 'style-nonce', content: request.content_security_policy_nonce } diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml index 69b206f69..431bd260c 100644 --- a/app/views/layouts/embedded.html.haml +++ b/app/views/layouts/embedded.html.haml @@ -12,12 +12,12 @@ %link{ rel: 'dns-prefetch', href: storage_host }/ = render_initial_state - = javascript_pack_tag "locales", integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag "locales", crossorigin: 'anonymous' - if @theme - if @theme[:supported_locales].include? I18n.locale.to_s - = javascript_pack_tag "locales/#{@theme[:flavour]}/#{I18n.locale}", integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag "locales/#{@theme[:flavour]}/#{I18n.locale}", crossorigin: 'anonymous' - elsif @theme[:supported_locales].include? 'en' - = javascript_pack_tag "locales/#{@theme[:flavour]}/en", integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag "locales/#{@theme[:flavour]}/en", crossorigin: 'anonymous' = render partial: 'layouts/theme', object: @core = render partial: 'layouts/theme', object: @theme diff --git a/app/views/layouts/error.html.haml b/app/views/layouts/error.html.haml index f8315afb5..55da5de3f 100644 --- a/app/views/layouts/error.html.haml +++ b/app/views/layouts/error.html.haml @@ -5,7 +5,7 @@ %meta{ charset: 'utf-8' }/ %title= safe_join([yield(:page_title), Setting.default_settings['site_title']], ' - ') %meta{ content: 'width=device-width,initial-scale=1', name: 'viewport' }/ - = javascript_pack_tag "locales", integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag "locales", crossorigin: 'anonymous' = render partial: 'layouts/theme', object: (@core || { pack: 'common' }) = render partial: 'layouts/theme', object: (@theme || { pack: 'error', flavour: 'glitch', common: { pack: 'common', flavour: 'glitch', skin: 'default' } }) %body.error diff --git a/app/views/media/player.html.haml b/app/views/media/player.html.haml index bd5610a0b..7369628a4 100644 --- a/app/views/media/player.html.haml +++ b/app/views/media/player.html.haml @@ -1,11 +1,11 @@ - content_for :header_tags do = render_initial_state - = javascript_pack_tag "locales", integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag "locales", crossorigin: 'anonymous' - if @theme - if @theme[:supported_locales].include? I18n.locale.to_s - = javascript_pack_tag "locales/#{@theme[:flavour]}/#{I18n.locale}", integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag "locales/#{@theme[:flavour]}/#{I18n.locale}", crossorigin: 'anonymous' - elsif @theme[:supported_locales].include? 'en' - = javascript_pack_tag "locales/#{@theme[:flavour]}/en", integrity: true, crossorigin: 'anonymous' + = javascript_pack_tag "locales/#{@theme[:flavour]}/en", crossorigin: 'anonymous' = render partial: 'layouts/theme', object: @core = render partial: 'layouts/theme', object: @theme diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml index b3e9c44fc..a4dd8534f 100644 --- a/app/views/statuses/_detailed_status.html.haml +++ b/app/views/statuses/_detailed_status.html.haml @@ -29,17 +29,17 @@ - if !status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first - = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: status.sensitive?, width: 670, height: 380, detailed: true, inline: true, alt: video.description do + = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: sensitized?(status, current_account), width: 670, height: 380, detailed: true, inline: true, alt: video.description do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.media_attachments.first.audio? - audio = status.media_attachments.first = react_component :audio, src: full_asset_url(audio.file.url(:original)), poster: full_asset_url(audio.thumbnail.present? ? audio.thumbnail.url : status.account.avatar_static_url), backgroundColor: audio.file.meta.dig('colors', 'background'), foregroundColor: audio.file.meta.dig('colors', 'foreground'), accentColor: audio.file.meta.dig('colors', 'accent'), width: 670, height: 380, alt: audio.description, duration: audio.file.meta.dig('original', 'duration') do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - else - = react_component :media_gallery, height: 380, sensitive: status.sensitive?, standalone: true, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do + = react_component :media_gallery, height: 380, sensitive: sensitized?(status, current_account), standalone: true, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.preview_card - = react_component :card, sensitive: status.sensitive?, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json + = react_component :card, sensitive: sensitized?(status, current_account), 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json .detailed-status__meta %data.dt-published{ value: status.created_at.to_time.iso8601 } diff --git a/app/views/statuses/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml index d095a1613..199061c46 100644 --- a/app/views/statuses/_simple_status.html.haml +++ b/app/views/statuses/_simple_status.html.haml @@ -35,17 +35,17 @@ - if !status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first - = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: status.sensitive?, width: 610, height: 343, inline: true, alt: video.description do + = react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: sensitized?(status, current_account), width: 610, height: 343, inline: true, alt: video.description do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.media_attachments.first.audio? - audio = status.media_attachments.first = react_component :audio, src: full_asset_url(audio.file.url(:original)), poster: full_asset_url(audio.thumbnail.present? ? audio.thumbnail.url : status.account.avatar_static_url), backgroundColor: audio.file.meta.dig('colors', 'background'), foregroundColor: audio.file.meta.dig('colors', 'foreground'), accentColor: audio.file.meta.dig('colors', 'accent'), width: 610, height: 343, alt: audio.description, duration: audio.file.meta.dig('original', 'duration') do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - else - = react_component :media_gallery, height: 343, sensitive: status.sensitive?, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do + = react_component :media_gallery, height: 343, sensitive: sensitized?(status, current_account), autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do = render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments } - elsif status.preview_card - = react_component :card, sensitive: status.sensitive?, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json + = react_component :card, sensitive: sensitized?(status, current_account), 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json - if !status.in_reply_to_id.nil? && status.in_reply_to_account_id == status.account.id = link_to ActivityPub::TagManager.instance.url_for(status), class: 'status__content__read-more-button', target: stream_link_target, rel: 'noopener noreferrer' do diff --git a/app/views/statuses/_status.html.haml b/app/views/statuses/_status.html.haml index 0e3652503..650f9b679 100644 --- a/app/views/statuses/_status.html.haml +++ b/app/views/statuses/_status.html.haml @@ -17,7 +17,7 @@ - if status.reply? && include_threads - if @next_ancestor .entry{ class: entry_classes } - = link_to_more ActivityPub::TagManager.instance.url_for(@next_ancestor) + = link_to_older ActivityPub::TagManager.instance.url_for(@next_ancestor) = render partial: 'statuses/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id }, autoplay: autoplay @@ -44,16 +44,16 @@ - if include_threads - if @since_descendant_thread_id .entry{ class: entry_classes } - = link_to_more short_account_status_url(status.account.username, status, max_descendant_thread_id: @since_descendant_thread_id + 1) + = link_to_newer short_account_status_url(status.account.username, status, max_descendant_thread_id: @since_descendant_thread_id + 1) - @descendant_threads.each do |thread| = render partial: 'statuses/status', collection: thread[:statuses], as: :status, locals: { is_successor: true, parent_id: status.id }, autoplay: autoplay - if thread[:next_status] .entry{ class: entry_classes } - = link_to_more ActivityPub::TagManager.instance.url_for(thread[:next_status]) + = link_to_newer ActivityPub::TagManager.instance.url_for(thread[:next_status]) - if @next_descendant_thread .entry{ class: entry_classes } - = link_to_more short_account_status_url(status.account.username, status, since_descendant_thread_id: @max_descendant_thread_id - 1) + = link_to_newer short_account_status_url(status.account.username, status, since_descendant_thread_id: @max_descendant_thread_id - 1) - if include_threads && !embedded_view? && !user_signed_in? .entry{ class: entry_classes } |