From 08e4c78e78358c2847967e9cc34b4a6497be97e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B4=20Shoemake?= Date: Sun, 14 Jan 2018 19:33:06 -0800 Subject: [PATCH 1/6] Fix column headers accessibility (#6199) * Fix accessibility of column headers As a screen reader user new to Mastodon, I encountered the following issues with the column headers as designed: * Jumping between them was difficult. FOr instance, passing my home timeline to reach notification settings was difficult to impossible, especially considering infinite scrolling. * There doesn't appear to be any means for triggering the control via the keyboard. the `titleClick` handler only responds to mouse clicks. * I didn't even realize there was a Settings toggle until I made this change. Thanks for using ARIA in your designs. It's a huge help. But adding a `button` role doesn't add keyboard handling and other button behavior. Also, because the role was on the heading container, it obscured the controls within the container itself. This fix resolve that. It also exposes the headings as headings rather than buttons, enabling skipping columns by using screen readers' heading navigation commands. Since I myself am blind, if this fix requires additional visual styling, I'd like help applying that so it can be merged. I'd consider it an essential accessibility fix for my and other blind users' existence on the platform. Thanks! * Styling fixes * Fixed overflow issue --- .../mastodon/components/column_header.js | 19 ++++++++----------- .../styles/mastodon/components.scss | 13 +++++++++++++ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.js index 80a8fbdb3a6..c300db89b9d 100644 --- a/app/javascript/mastodon/components/column_header.js +++ b/app/javascript/mastodon/components/column_header.js @@ -23,7 +23,6 @@ export default class ColumnHeader extends React.PureComponent { icon: PropTypes.string.isRequired, active: PropTypes.bool, multiColumn: PropTypes.bool, - focusable: PropTypes.bool, showBackButton: PropTypes.bool, children: PropTypes.node, pinned: PropTypes.bool, @@ -32,10 +31,6 @@ export default class ColumnHeader extends React.PureComponent { onClick: PropTypes.func, }; - static defaultProps = { - focusable: true, - } - state = { collapsed: true, animating: false, @@ -68,7 +63,7 @@ export default class ColumnHeader extends React.PureComponent { } render () { - const { title, icon, active, children, pinned, onPin, multiColumn, focusable, showBackButton, intl: { formatMessage } } = this.props; + const { title, icon, active, children, pinned, onPin, multiColumn, showBackButton, intl: { formatMessage } } = this.props; const { collapsed, animating } = this.state; const wrapperClassName = classNames('column-header__wrapper', { @@ -135,11 +130,13 @@ export default class ColumnHeader extends React.PureComponent { return (
-

- - - {title} - +

+
{backButton} diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index dec489e9a58..e8adceb6681 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2350,6 +2350,19 @@ position: relative; z-index: 2; outline: 0; + overflow: hidden; + + & > button { + display: flex; + flex: auto; + margin: 0; + border: none; + padding: 0; + color: inherit; + background: transparent; + font: inherit; + text-align: left; + } &.active { box-shadow: 0 1px 0 rgba($ui-highlight-color, 0.3); From ed867eca9dbd7d798bc7179dbc13c4f1edc238ad Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 15 Jan 2018 04:34:28 +0100 Subject: [PATCH 2/6] Move e-mail digest task to sidekiq, reduce workload, improve hint (#6252) --- app/models/user.rb | 4 ++++ app/workers/digest_mailer_worker.rb | 6 +----- app/workers/scheduler/email_scheduler.rb | 24 ++++++++++++++++++++++++ config/locales/simple_form.en.yml | 2 +- config/sidekiq.yml | 3 +++ lib/tasks/mastodon.rake | 7 +++---- 6 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 app/workers/scheduler/email_scheduler.rb diff --git a/app/models/user.rb b/app/models/user.rb index 9459db7fe48..892a07bba03 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -170,6 +170,10 @@ class User < ApplicationRecord settings.default_privacy || (account.locked? ? 'private' : 'public') end + def allows_digest_emails? + settings.notification_emails['digest'] + end + def token_for_app(a) return nil if a.nil? || a.owner != self Doorkeeper::AccessToken diff --git a/app/workers/digest_mailer_worker.rb b/app/workers/digest_mailer_worker.rb index 028db89a9a4..21f1c357a0e 100644 --- a/app/workers/digest_mailer_worker.rb +++ b/app/workers/digest_mailer_worker.rb @@ -9,7 +9,7 @@ class DigestMailerWorker def perform(user_id) @user = User.find(user_id) - deliver_digest if user_receives_digest? + deliver_digest if @user.allows_digest_emails? end private @@ -18,8 +18,4 @@ class DigestMailerWorker NotificationMailer.digest(user.account).deliver_now! user.touch(:last_emailed_at) end - - def user_receives_digest? - user.settings.notification_emails['digest'] - end end diff --git a/app/workers/scheduler/email_scheduler.rb b/app/workers/scheduler/email_scheduler.rb new file mode 100644 index 00000000000..24d0c0ebef7 --- /dev/null +++ b/app/workers/scheduler/email_scheduler.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true +require 'sidekiq-scheduler' + +class Scheduler::EmailScheduler + include Sidekiq::Worker + + def perform + eligible_users.find_each do |user| + next unless user.allows_digest_emails? + DigestMailerWorker.perform_async(user.id) + end + end + + private + + def eligible_users + User.confirmed + .joins(:account) + .where(accounts: { silenced: false, suspended: false }) + .where(disabled: false) + .where('current_sign_in_at < ?', 20.days.ago) + .where('last_emailed_at IS NULL OR last_emailed_at < ?', 20.days.ago) + end +end diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index ff1a40ccd0f..143daaa2988 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -4,7 +4,7 @@ en: hints: defaults: avatar: PNG, GIF or JPG. At most 2MB. Will be downscaled to 120x120px - digest: Sent after a long period of inactivity with a summary of mentions you've received in your absence + digest: Only sent after a long period of inactivity and only if you have received any personal messages in your absence display_name: one: 1 character left other: %{count} characters left diff --git a/config/sidekiq.yml b/config/sidekiq.yml index 4c35dcd43a9..bfe29b8f818 100644 --- a/config/sidekiq.yml +++ b/config/sidekiq.yml @@ -27,3 +27,6 @@ ip_cleanup_scheduler: cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *' class: Scheduler::IpCleanupScheduler + email_scheduler: + cron: '0 10 * * 2' + class: Scheduler::EmailScheduler diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 33969d470c4..38dbed982a6 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -171,11 +171,10 @@ namespace :mastodon do end namespace :emails do - desc 'Send out digest e-mails' + desc 'Send out digest e-mails (deprecated)' task digest: :environment do - User.confirmed.joins(:account).where(accounts: { silenced: false, suspended: false }).where('current_sign_in_at < ?', 20.days.ago).find_each do |user| - DigestMailerWorker.perform_async(user.id) - end + # No-op + # This task is now executed via sidekiq-scheduler end end From dcc614f869b9484fc3aa601405d7e1988e166403 Mon Sep 17 00:00:00 2001 From: Jeong Arm Date: Mon, 15 Jan 2018 14:50:29 +0900 Subject: [PATCH 3/6] Add some browsers (#6246) Related: #6165 --- config/locales/en.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/locales/en.yml b/config/locales/en.yml index e4425b424f0..23dce73e907 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -552,12 +552,14 @@ en: blackberry: Blackberry chrome: Chrome edge: Microsoft Edge + electron: Electron firefox: Firefox generic: Unknown browser ie: Internet Explorer micro_messenger: MicroMessenger nokia: Nokia S40 Ovi Browser opera: Opera + otter: Otter phantom_js: PhantomJS qq: QQ Browser safari: Safari From 2091ae92be5d04cd4dadb2200c507ce8d8d2623e Mon Sep 17 00:00:00 2001 From: neetshin Date: Mon, 15 Jan 2018 05:51:00 +0000 Subject: [PATCH 4/6] Make columns-area unscrollable when modal opened (#6241) * Add aria-autocomplete='list' in Textaria ref: https://www.w3.org/TR/wai-aria-1.1/#aria-autocomplete * Make detect empty string brefore assign upload description * Change code elements in keyboard-shortcuts component to kbd * Add validation for onMuteNotifications * Make columns-area unscrollable when modal opend * Make columns-area unscrollable when modal opened --- .../mastodon/features/ui/components/columns_area.js | 5 +++-- .../features/ui/containers/columns_area_container.js | 1 + app/javascript/styles/mastodon/components.scss | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js index f00b74dfdcd..a01e5a390eb 100644 --- a/app/javascript/mastodon/features/ui/components/columns_area.js +++ b/app/javascript/mastodon/features/ui/components/columns_area.js @@ -37,6 +37,7 @@ export default class ColumnsArea extends ImmutablePureComponent { static propTypes = { intl: PropTypes.object.isRequired, columns: ImmutablePropTypes.list.isRequired, + isModalOpen: PropTypes.bool.isRequired, singleColumn: PropTypes.bool, children: PropTypes.node, }; @@ -144,7 +145,7 @@ export default class ColumnsArea extends ImmutablePureComponent { } render () { - const { columns, children, singleColumn } = this.props; + const { columns, children, singleColumn, isModalOpen } = this.props; const { shouldAnimate } = this.state; const columnIndex = getIndex(this.context.router.history.location.pathname); @@ -159,7 +160,7 @@ export default class ColumnsArea extends ImmutablePureComponent { } return ( -
+
{columns.map(column => { const params = column.get('params', null) === null ? null : column.get('params').toJS(); diff --git a/app/javascript/mastodon/features/ui/containers/columns_area_container.js b/app/javascript/mastodon/features/ui/containers/columns_area_container.js index 95f95618b2c..f3e82a8ac27 100644 --- a/app/javascript/mastodon/features/ui/containers/columns_area_container.js +++ b/app/javascript/mastodon/features/ui/containers/columns_area_container.js @@ -3,6 +3,7 @@ import ColumnsArea from '../components/columns_area'; const mapStateToProps = state => ({ columns: state.getIn(['settings', 'columns']), + isModalOpen: !!state.get('modal').modalType, }); export default connect(mapStateToProps, null, null, { withRef: true })(ColumnsArea); diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index e8adceb6681..dbb277af965 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1613,6 +1613,10 @@ justify-content: flex-start; overflow-x: auto; position: relative; + + &.unscrollable { + overflow-x: hidden; + } } @media screen and (min-width: 360px) { From 537d2939b10df9121e5a9f13a9d66c568ff681bf Mon Sep 17 00:00:00 2001 From: Patrick Figel Date: Mon, 15 Jan 2018 06:51:23 +0100 Subject: [PATCH 5/6] Suppress CSRF token warnings (#6240) CSRF token checking was enabled for API controllers in #6223, producing "Can't verify CSRF token authenticity" log spam. This disables logging of failed CSRF checks. This also changes the protection strategy for PushSubscriptionsController to use exceptions, making it consistent with other controllers that use sessions. --- app/controllers/api/web/push_subscriptions_controller.rb | 1 + config/initializers/suppress_csrf_warnings.rb | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 config/initializers/suppress_csrf_warnings.rb diff --git a/app/controllers/api/web/push_subscriptions_controller.rb b/app/controllers/api/web/push_subscriptions_controller.rb index 52e250d02da..68ccbd5e20d 100644 --- a/app/controllers/api/web/push_subscriptions_controller.rb +++ b/app/controllers/api/web/push_subscriptions_controller.rb @@ -4,6 +4,7 @@ class Api::Web::PushSubscriptionsController < Api::BaseController respond_to :json before_action :require_user! + protect_from_forgery with: :exception def create params.require(:subscription).require(:endpoint) diff --git a/config/initializers/suppress_csrf_warnings.rb b/config/initializers/suppress_csrf_warnings.rb new file mode 100644 index 00000000000..410ab585b46 --- /dev/null +++ b/config/initializers/suppress_csrf_warnings.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +ActionController::Base.log_warning_on_csrf_failure = false From 74c1c9ec01addc7474d74712f76fb6d6d5eecdf7 Mon Sep 17 00:00:00 2001 From: puckipedia Date: Mon, 15 Jan 2018 06:51:46 +0100 Subject: [PATCH 6/6] Allow attributedTo in a status to be an embedded object (#6238) --- app/services/activitypub/fetch_remote_status_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/activitypub/fetch_remote_status_service.rb b/app/services/activitypub/fetch_remote_status_service.rb index 7649bceca9a..503c175d8a6 100644 --- a/app/services/activitypub/fetch_remote_status_service.rb +++ b/app/services/activitypub/fetch_remote_status_service.rb @@ -30,7 +30,7 @@ class ActivityPub::FetchRemoteStatusService < BaseService end def actor_id - first_of_value(@json['attributedTo']) + value_or_id(first_of_value(@json['attributedTo'])) end def trustworthy_attribution?(uri, attributed_to)