diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index 5c47158e02c..5134bfb94a6 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -2,9 +2,9 @@ class Api::V1::AccountsController < Api::BaseController before_action -> { authorize_if_got_token! :read, :'read:accounts' }, except: [:create, :follow, :unfollow, :remove_from_followers, :block, :unblock, :mute, :unmute] - before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, only: [:follow, :unfollow, :remove_from_followers] - before_action -> { doorkeeper_authorize! :follow, :'write:mutes' }, only: [:mute, :unmute] - before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, only: [:block, :unblock] + before_action -> { doorkeeper_authorize! :follow, :write, :'write:follows' }, only: [:follow, :unfollow, :remove_from_followers] + before_action -> { doorkeeper_authorize! :follow, :write, :'write:mutes' }, only: [:mute, :unmute] + before_action -> { doorkeeper_authorize! :follow, :write, :'write:blocks' }, only: [:block, :unblock] before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, only: [:create] before_action :require_user!, except: [:show, :create] diff --git a/app/controllers/api/v1/blocks_controller.rb b/app/controllers/api/v1/blocks_controller.rb index 586cdfca9d4..a65e762c9f8 100644 --- a/app/controllers/api/v1/blocks_controller.rb +++ b/app/controllers/api/v1/blocks_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Api::V1::BlocksController < Api::BaseController - before_action -> { doorkeeper_authorize! :follow, :'read:blocks' } + before_action -> { doorkeeper_authorize! :follow, :read, :'read:blocks' } before_action :require_user! after_action :insert_pagination_headers diff --git a/app/controllers/api/v1/domain_blocks_controller.rb b/app/controllers/api/v1/domain_blocks_controller.rb index 5bb02d834cf..1891261b9ca 100644 --- a/app/controllers/api/v1/domain_blocks_controller.rb +++ b/app/controllers/api/v1/domain_blocks_controller.rb @@ -3,8 +3,8 @@ class Api::V1::DomainBlocksController < Api::BaseController BLOCK_LIMIT = 100 - before_action -> { doorkeeper_authorize! :follow, :'read:blocks' }, only: :show - before_action -> { doorkeeper_authorize! :follow, :'write:blocks' }, except: :show + before_action -> { doorkeeper_authorize! :follow, :read, :'read:blocks' }, only: :show + before_action -> { doorkeeper_authorize! :follow, :write, :'write:blocks' }, except: :show before_action :require_user! after_action :insert_pagination_headers, only: :show diff --git a/app/controllers/api/v1/follow_requests_controller.rb b/app/controllers/api/v1/follow_requests_controller.rb index f4b2a74d0ae..54ff0e11d03 100644 --- a/app/controllers/api/v1/follow_requests_controller.rb +++ b/app/controllers/api/v1/follow_requests_controller.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true class Api::V1::FollowRequestsController < Api::BaseController - before_action -> { doorkeeper_authorize! :follow, :'read:follows' }, only: :index - before_action -> { doorkeeper_authorize! :follow, :'write:follows' }, except: :index + before_action -> { doorkeeper_authorize! :follow, :read, :'read:follows' }, only: :index + before_action -> { doorkeeper_authorize! :follow, :write, :'write:follows' }, except: :index before_action :require_user! after_action :insert_pagination_headers, only: :index @@ -13,7 +13,7 @@ class Api::V1::FollowRequestsController < Api::BaseController def authorize AuthorizeFollowService.new.call(account, current_account) - NotifyService.new.call(current_account, :follow, Follow.find_by(account: account, target_account: current_account)) + LocalNotificationWorker.perform_async(current_account.id, Follow.find_by(account: account, target_account: current_account).id, 'Follow', 'follow') render json: account, serializer: REST::RelationshipSerializer, relationships: relationships end diff --git a/app/controllers/api/v1/media_controller.rb b/app/controllers/api/v1/media_controller.rb index 72094790fde..f9c935bf3e1 100644 --- a/app/controllers/api/v1/media_controller.rb +++ b/app/controllers/api/v1/media_controller.rb @@ -31,7 +31,7 @@ class Api::V1::MediaController < Api::BaseController end def set_media_attachment - @media_attachment = current_account.media_attachments.unattached.find(params[:id]) + @media_attachment = current_account.media_attachments.where(status_id: nil).find(params[:id]) end def check_processing diff --git a/app/controllers/api/v1/mutes_controller.rb b/app/controllers/api/v1/mutes_controller.rb index fd52511d7eb..6cde53a2a7d 100644 --- a/app/controllers/api/v1/mutes_controller.rb +++ b/app/controllers/api/v1/mutes_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Api::V1::MutesController < Api::BaseController - before_action -> { doorkeeper_authorize! :follow, :'read:mutes' } + before_action -> { doorkeeper_authorize! :follow, :read, :'read:mutes' } before_action :require_user! after_action :insert_pagination_headers diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js index 5508adb80e1..07572c530d9 100644 --- a/app/javascript/mastodon/features/getting_started/index.js +++ b/app/javascript/mastodon/features/getting_started/index.js @@ -7,7 +7,7 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import { me, profile_directory, showTrends } from '../../initial_state'; +import { me, showTrends } from '../../initial_state'; import { fetchFollowRequests } from 'mastodon/actions/accounts'; import { List as ImmutableList } from 'immutable'; import NavigationContainer from '../compose/containers/navigation_container'; @@ -35,7 +35,6 @@ const messages = defineMessages({ personal: { id: 'navigation_bar.personal', defaultMessage: 'Personal' }, security: { id: 'navigation_bar.security', defaultMessage: 'Security' }, menu: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, - profile_directory: { id: 'getting_started.directory', defaultMessage: 'Profile directory' }, }); const mapStateToProps = state => ({ @@ -104,25 +103,11 @@ class GettingStarted extends ImmutablePureComponent { height += 34 + 48*2; - if (profile_directory) { - navItems.push( - , - ); - - height += 48; - } - navItems.push( , ); height += 34; - } else if (profile_directory) { - navItems.push( - , - ); - - height += 48; } if (multiColumn && !columns.find(item => item.get('id') === 'HOME')) { diff --git a/app/javascript/mastodon/features/ui/components/link_footer.js b/app/javascript/mastodon/features/ui/components/link_footer.js index 4a9243c9e12..edf1104c4ed 100644 --- a/app/javascript/mastodon/features/ui/components/link_footer.js +++ b/app/javascript/mastodon/features/ui/components/link_footer.js @@ -3,7 +3,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; import { Link } from 'react-router-dom'; -import { invitesEnabled, limitedFederationMode, version, repository, source_url } from 'mastodon/initial_state'; +import { invitesEnabled, limitedFederationMode, version, repository, source_url, profile_directory as profileDirectory } from 'mastodon/initial_state'; import { logOut } from 'mastodon/utils/log_out'; import { openModal } from 'mastodon/actions/modal'; @@ -52,6 +52,7 @@ class LinkFooter extends React.PureComponent { {withHotkeys &&
  • ·
  • }
  • ·
  • {!limitedFederationMode &&
  • ·
  • } + {profileDirectory &&
  • ·
  • }
  • ·
  • ·
  • ·
  • diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.js b/app/javascript/mastodon/features/ui/components/navigation_panel.js index a70e5ab61cd..51a0757bcfe 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.js +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.js @@ -2,7 +2,7 @@ import React from 'react'; import { NavLink, withRouter } from 'react-router-dom'; import { FormattedMessage } from 'react-intl'; import Icon from 'mastodon/components/icon'; -import { profile_directory, showTrends } from 'mastodon/initial_state'; +import { showTrends } from 'mastodon/initial_state'; import NotificationsCounterIcon from './notifications_counter_icon'; import FollowRequestsNavLink from './follow_requests_nav_link'; import ListPanel from './list_panel'; @@ -20,7 +20,6 @@ const NavigationPanel = () => ( - {profile_directory && } diff --git a/app/lib/activitypub/activity/announce.rb b/app/lib/activitypub/activity/announce.rb index 7cd5a41e8e9..0674b108337 100644 --- a/app/lib/activitypub/activity/announce.rb +++ b/app/lib/activitypub/activity/announce.rb @@ -35,7 +35,7 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity def distribute # Notify the author of the original status if that status is local - NotifyService.new.call(@status.reblog.account, :reblog, @status) if reblog_of_local_account?(@status) && !reblog_by_following_group_account?(@status) + LocalNotificationWorker.perform_async(@status.reblog.account_id, @status.id, 'Status', 'reblog') if reblog_of_local_account?(@status) && !reblog_by_following_group_account?(@status) # Distribute into home and list feeds ::DistributionWorker.perform_async(@status.id) if @options[:override_timestamps] || @status.within_realtime_window? diff --git a/app/lib/activitypub/activity/follow.rb b/app/lib/activitypub/activity/follow.rb index 4efb84b8c98..97e41ab7895 100644 --- a/app/lib/activitypub/activity/follow.rb +++ b/app/lib/activitypub/activity/follow.rb @@ -31,10 +31,10 @@ class ActivityPub::Activity::Follow < ActivityPub::Activity follow_request = FollowRequest.create!(account: @account, target_account: target_account, uri: @json['id']) if target_account.locked? || @account.silenced? - NotifyService.new.call(target_account, :follow_request, follow_request) + LocalNotificationWorker.perform_async(target_account.id, follow_request.id, 'FollowRequest', 'follow_request') else AuthorizeFollowService.new.call(@account, target_account) - NotifyService.new.call(target_account, :follow, ::Follow.find_by(account: @account, target_account: target_account)) + LocalNotificationWorker.perform_async(target_account.id, ::Follow.find_by(account: @account, target_account: target_account).id, 'Follow', 'follow') end end diff --git a/app/lib/activitypub/activity/like.rb b/app/lib/activitypub/activity/like.rb index ebbda15b9f6..aa1dc304035 100644 --- a/app/lib/activitypub/activity/like.rb +++ b/app/lib/activitypub/activity/like.rb @@ -8,7 +8,7 @@ class ActivityPub::Activity::Like < ActivityPub::Activity favourite = original_status.favourites.create!(account: @account) - NotifyService.new.call(original_status.account, :favourite, favourite) + LocalNotificationWorker.perform_async(original_status.account_id, favourite.id, 'Favourite', 'favourite') Trends.statuses.register(original_status) end end diff --git a/app/lib/activitypub/activity/update.rb b/app/lib/activitypub/activity/update.rb index 0bfead55b27..36ad5f4465b 100644 --- a/app/lib/activitypub/activity/update.rb +++ b/app/lib/activitypub/activity/update.rb @@ -26,11 +26,6 @@ class ActivityPub::Activity::Update < ActivityPub::Activity return if @status.nil? - forwarder.forward! if forwarder.forwardable? ActivityPub::ProcessStatusUpdateService.new.call(@status, @object) end - - def forwarder - @forwarder ||= ActivityPub::Forwarder.new(@account, @json, @status) - end end diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index 94d149da375..dfa493ed5e6 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -90,6 +90,8 @@ class Formatter end def simplified_format(account, **options) + return '' if account.note.blank? + html = account.local? ? linkify(account.note) : reformat(account.note) html = encode_custom_emojis(html, account.emojis, options[:autoplay]) if options[:custom_emojify] html.html_safe # rubocop:disable Rails/OutputSafety diff --git a/app/models/account_statuses_cleanup_policy.rb b/app/models/account_statuses_cleanup_policy.rb index 0f78c1a5467..36512365314 100644 --- a/app/models/account_statuses_cleanup_policy.rb +++ b/app/models/account_statuses_cleanup_policy.rb @@ -23,6 +23,7 @@ class AccountStatusesCleanupPolicy < ApplicationRecord include Redisable ALLOWED_MIN_STATUS_AGE = [ + 1.week.seconds, 2.weeks.seconds, 1.month.seconds, 2.months.seconds, diff --git a/app/services/bootstrap_timeline_service.rb b/app/services/bootstrap_timeline_service.rb index 312c163e47e..a02e55a6df8 100644 --- a/app/services/bootstrap_timeline_service.rb +++ b/app/services/bootstrap_timeline_service.rb @@ -18,7 +18,7 @@ class BootstrapTimelineService < BaseService def notify_staff! User.staff.includes(:account).find_each do |user| - NotifyService.new.call(user.account, :'admin.sign_up', @source_account) + LocalNotificationWorker.perform_async(user.account_id, @source_account.id, 'Account', 'admin.sign_up') end end end diff --git a/app/services/favourite_service.rb b/app/services/favourite_service.rb index 0ca0081b48a..dc7fe88552a 100644 --- a/app/services/favourite_service.rb +++ b/app/services/favourite_service.rb @@ -31,7 +31,7 @@ class FavouriteService < BaseService status = favourite.status if status.account.local? - NotifyService.new.call(status.account, :favourite, favourite) + LocalNotificationWorker.perform_async(status.account_id, favourite.id, 'Favourite', 'favourite') elsif status.account.activitypub? ActivityPub::DeliveryWorker.perform_async(build_json(favourite), favourite.account_id, status.account.inbox_url) end diff --git a/app/views/about/_logged_in.html.haml b/app/views/about/_logged_in.html.haml new file mode 100644 index 00000000000..e1bcfffb316 --- /dev/null +++ b/app/views/about/_logged_in.html.haml @@ -0,0 +1,10 @@ +.simple_form + %p.lead= t('about.logged_in_as_html', username: content_tag(:strong, current_account.username)) + + .actions + = link_to t('about.continue_to_web'), root_url, class: 'button button-primary' + +.form-footer + %ul.no-list + %li= link_to t('about.get_apps'), 'https://joinmastodon.org/apps', target: '_blank', rel: 'noopener noreferrer' + %li= link_to t('auth.logout'), destroy_user_session_path, data: { method: :delete } diff --git a/app/views/about/_registration.html.haml b/app/views/about/_registration.html.haml index e4d614d71e7..b452e493689 100644 --- a/app/views/about/_registration.html.haml +++ b/app/views/about/_registration.html.haml @@ -1,17 +1,20 @@ -.simple_form__overlay-area{ class: (closed_registrations? && @instance_presenter.closed_registrations_message.present?) ? 'simple_form__overlay-area__blurred' : '' } +- disabled = closed_registrations? || omniauth_only? || current_account.present? +- show_message = disabled && (current_user.present? || @instance_presenter.closed_registrations_message.present?) + +.simple_form__overlay-area{ class: show_message ? 'simple_form__overlay-area__blurred' : '' } = simple_form_for(new_user, url: user_registration_path, namespace: 'registration', html: { novalidate: false }) do |f| %p.lead= t('about.federation_hint_html', instance: content_tag(:strong, site_hostname)) .fields-group = f.simple_fields_for :account do |account_fields| - = account_fields.input :username, wrapper: :with_label, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username'), pattern: '[a-zA-Z0-9_]+', maxlength: 30 }, append: "@#{site_hostname}", hint: false, disabled: closed_registrations? + = account_fields.input :username, wrapper: :with_label, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username'), pattern: '[a-zA-Z0-9_]+', maxlength: 30 }, append: "@#{site_hostname}", hint: false, disabled: disabled - = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? - = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, hint: false, disabled: closed_registrations? - = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? + = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false, disabled: disabled + = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, hint: false, disabled: disabled + = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false, disabled: disabled - = f.input :confirm_password, as: :string, placeholder: t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? - = f.input :website, as: :url, placeholder: t('simple_form.labels.defaults.honeypot', label: 'Website'), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: 'Website'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? + = f.input :confirm_password, as: :string, placeholder: t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), :autocomplete => 'off' }, hint: false, disabled: disabled + = f.input :website, as: :url, placeholder: t('simple_form.labels.defaults.honeypot', label: 'Website'), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: 'Website'), :autocomplete => 'off' }, hint: false, disabled: disabled - if approved_registrations? .fields-group @@ -19,13 +22,16 @@ = invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: Setting.require_invite_text .fields-group - = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true, disabled: closed_registrations? + = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true, disabled: disabled .actions - = f.button :button, sign_up_message, type: :submit, class: 'button button-primary', disabled: closed_registrations? + = f.button :button, sign_up_message, type: :submit, class: 'button button-primary', disabled: disabled - - if closed_registrations? && @instance_presenter.closed_registrations_message.present? + - if show_message .simple_form__overlay-area__overlay .simple_form__overlay-area__overlay__content.rich-formatting .block-icon= fa_icon 'warning' - = @instance_presenter.closed_registrations_message.html_safe + - if current_account.present? + = t('about.logout_before_registering') + - else + = @instance_presenter.closed_registrations_message.html_safe diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml index 6ae9e6ae070..32144009604 100644 --- a/app/views/about/show.html.haml +++ b/app/views/about/show.html.haml @@ -46,7 +46,10 @@ .landing__grid__column.landing__grid__column-login .box-widget - = render 'login' + - if current_user.present? + = render 'logged_in' + - else + = render 'login' .hero-widget .hero-widget__img diff --git a/app/workers/feed_insert_worker.rb b/app/workers/feed_insert_worker.rb index b81b09cac67..9483510aafd 100644 --- a/app/workers/feed_insert_worker.rb +++ b/app/workers/feed_insert_worker.rb @@ -74,7 +74,7 @@ class FeedInsertWorker end def perform_notify - NotifyService.new.call(@follower, :status, @status) + LocalNotificationWorker.perform_async(@follower.id, @status.id, 'Status', 'status') end def update? diff --git a/app/workers/poll_expiration_notify_worker.rb b/app/workers/poll_expiration_notify_worker.rb index 7613ed5f150..0e29a5f60be 100644 --- a/app/workers/poll_expiration_notify_worker.rb +++ b/app/workers/poll_expiration_notify_worker.rb @@ -38,12 +38,14 @@ class PollExpirationNotifyWorker def notify_remote_voters_and_owner! ActivityPub::DistributePollUpdateWorker.perform_async(@poll.status.id) - NotifyService.new.call(@poll.account, :poll, @poll) + LocalNotificationWorker.perform_async(@poll.account_id, @poll.id, 'Poll', 'poll') end def notify_local_voters! - @poll.voters.merge(Account.local).find_each do |account| - NotifyService.new.call(account, :poll, @poll) + @poll.voters.merge(Account.local).select(:id).find_in_batches do |accounts| + LocalNotificationWorker.push_bulk(accounts) do |account| + [account.id, @poll.id, 'Poll', 'poll'] + end end end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 6a559418582..2367d5ded41 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -16,6 +16,7 @@ en: contact: Contact contact_missing: Not set contact_unavailable: N/A + continue_to_web: Continue to web app discover_users: Discover users documentation: Documentation federation_hint_html: With an account on %{instance} you'll be able to follow people on any Mastodon server and beyond. @@ -25,6 +26,8 @@ en: This account is a virtual actor used to represent the server itself and not any individual user. 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 + logged_in_as_html: You are currently logged in as %{username}. + logout_before_registering: You are already logged in. 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:' @@ -1484,6 +1487,7 @@ en: '2629746': 1 month '31556952': 1 year '5259492': 2 months + '604800': 1 week '63113904': 2 years '7889238': 3 months min_age_label: Age threshold diff --git a/spec/views/about/show.html.haml_spec.rb b/spec/views/about/show.html.haml_spec.rb index d608bbf5d3f..140f3fd41cb 100644 --- a/spec/views/about/show.html.haml_spec.rb +++ b/spec/views/about/show.html.haml_spec.rb @@ -10,6 +10,7 @@ describe 'about/show.html.haml', without_verify_partial_doubles: true do allow(view).to receive(:site_title).and_return('example site') allow(view).to receive(:new_user).and_return(User.new) allow(view).to receive(:use_seamless_external_login?).and_return(false) + allow(view).to receive(:current_account).and_return(nil) end it 'has valid open graph tags' do