Commit 9f9971f5 authored by ikuradon's avatar ikuradon

Merge remote-tracking branch 'upstream/master' into comm.cx

parents 90b36053 874bd3ac
Pipeline #317 passed with stage
in 35 minutes and 7 seconds
......@@ -15,25 +15,25 @@ GIT
GEM
remote: https://rubygems.org/
specs:
actioncable (5.2.2)
actionpack (= 5.2.2)
actioncable (5.2.2.1)
actionpack (= 5.2.2.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailer (5.2.2)
actionpack (= 5.2.2)
actionview (= 5.2.2)
activejob (= 5.2.2)
actionmailer (5.2.2.1)
actionpack (= 5.2.2.1)
actionview (= 5.2.2.1)
activejob (= 5.2.2.1)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (5.2.2)
actionview (= 5.2.2)
activesupport (= 5.2.2)
actionpack (5.2.2.1)
actionview (= 5.2.2.1)
activesupport (= 5.2.2.1)
rack (~> 2.0)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.2.2)
activesupport (= 5.2.2)
actionview (5.2.2.1)
activesupport (= 5.2.2.1)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
......@@ -43,21 +43,21 @@ GEM
activemodel (>= 4.1, < 6)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
active_record_query_trace (1.6.1)
activejob (5.2.2)
activesupport (= 5.2.2)
active_record_query_trace (1.6.2)
activejob (5.2.2.1)
activesupport (= 5.2.2.1)
globalid (>= 0.3.6)
activemodel (5.2.2)
activesupport (= 5.2.2)
activerecord (5.2.2)
activemodel (= 5.2.2)
activesupport (= 5.2.2)
activemodel (5.2.2.1)
activesupport (= 5.2.2.1)
activerecord (5.2.2.1)
activemodel (= 5.2.2.1)
activesupport (= 5.2.2.1)
arel (>= 9.0)
activestorage (5.2.2)
actionpack (= 5.2.2)
activerecord (= 5.2.2)
activestorage (5.2.2.1)
actionpack (= 5.2.2.1)
activerecord (= 5.2.2.1)
marcel (~> 0.3.1)
activesupport (5.2.2)
activesupport (5.2.2.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
......@@ -232,7 +232,7 @@ GEM
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
get_process_mem (0.2.3)
globalid (0.4.1)
globalid (0.4.2)
activesupport (>= 4.2.0)
goldfinger (2.1.0)
addressable (~> 2.5)
......@@ -269,7 +269,7 @@ GEM
httplog (1.2.1)
rack (>= 1.0)
rainbow (>= 2.0.0)
i18n (1.5.3)
i18n (1.6.0)
concurrent-ruby (~> 1.0)
i18n-tasks (0.9.28)
activesupport (>= 4.0.2)
......@@ -341,7 +341,7 @@ GEM
mime-types (3.2.2)
mime-types-data (~> 3.2015)
mime-types-data (3.2018.0812)
mimemagic (0.3.2)
mimemagic (0.3.3)
mini_mime (1.0.1)
mini_portile2 (2.4.0)
minitest (5.11.3)
......@@ -434,18 +434,18 @@ GEM
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (5.2.2)
actioncable (= 5.2.2)
actionmailer (= 5.2.2)
actionpack (= 5.2.2)
actionview (= 5.2.2)
activejob (= 5.2.2)
activemodel (= 5.2.2)
activerecord (= 5.2.2)
activestorage (= 5.2.2)
activesupport (= 5.2.2)
rails (5.2.2.1)
actioncable (= 5.2.2.1)
actionmailer (= 5.2.2.1)
actionpack (= 5.2.2.1)
actionview (= 5.2.2.1)
activejob (= 5.2.2.1)
activemodel (= 5.2.2.1)
activerecord (= 5.2.2.1)
activestorage (= 5.2.2.1)
activesupport (= 5.2.2.1)
bundler (>= 1.3.0)
railties (= 5.2.2)
railties (= 5.2.2.1)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.4)
actionpack (>= 5.0.1.x)
......@@ -461,9 +461,9 @@ GEM
railties (>= 5.0, < 6)
rails-settings-cached (0.6.6)
rails (>= 4.2.0)
railties (5.2.2)
actionpack (= 5.2.2)
activesupport (= 5.2.2)
railties (5.2.2.1)
actionpack (= 5.2.2.1)
activesupport (= 5.2.2.1)
method_source
rake (>= 0.8.7)
thor (>= 0.19.0, < 2.0)
......
......@@ -80,11 +80,17 @@ class AccountsController < ApplicationController
end
def hashtag_scope
Status.tagged_with(Tag.find_by(name: params[:tag].downcase)&.id)
tag = Tag.find_normalized(params[:tag])
if tag
Status.tagged_with(tag.id)
else
Status.none
end
end
def set_account
@account = Account.find_local!(params[:username])
def username_param
params[:username]
end
def older_url
......
......@@ -2,9 +2,9 @@
module Admin
class AccountsController < BaseController
before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :remove_header, :enable, :unsilence, :unsuspend, :memorialize]
before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :remove_header, :enable, :unsilence, :unsuspend, :memorialize, :approve, :reject]
before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload]
before_action :require_local_account!, only: [:enable, :memorialize]
before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject]
def index
authorize :account, :index?
......@@ -45,6 +45,18 @@ module Admin
redirect_to admin_account_path(@account.id)
end
def approve
authorize @account.user, :approve?
@account.user.approve!
redirect_to admin_accounts_path(pending: '1')
end
def reject
authorize @account.user, :reject?
SuspendAccountService.new.call(@account, including_user: true, destroy: true)
redirect_to admin_accounts_path(pending: '1')
end
def unsilence
authorize @account, :unsilence?
@account.unsilence!
......@@ -114,6 +126,7 @@ module Admin
:remote,
:by_domain,
:active,
:pending,
:silenced,
:suspended,
:username,
......
......@@ -10,7 +10,7 @@ module Admin
@interactions_week = Redis.current.get("activity:interactions:#{current_week}") || 0
@relay_enabled = Relay.enabled.exists?
@single_user_mode = Rails.configuration.x.single_user_mode
@registrations_enabled = Setting.open_registrations
@registrations_enabled = Setting.registrations_mode != 'none'
@deletions_enabled = Setting.open_deletion
@invites_enabled = Setting.min_invite_role == 'user'
@search_enabled = Chewy.enabled?
......
......@@ -10,7 +10,7 @@ module Admin
site_description
site_extended_description
site_terms
open_registrations
registrations_mode
closed_registrations_message
open_deletion
timeline_preview
......@@ -30,7 +30,6 @@ module Admin
).freeze
BOOLEAN_SETTINGS = %w(
open_registrations
open_deletion
timeline_preview
show_staff_badge
......
......@@ -73,7 +73,9 @@ class Api::BaseController < ApplicationController
elsif current_user.disabled?
render json: { error: 'Your login is currently disabled' }, status: 403
elsif !current_user.confirmed?
render json: { error: 'Email confirmation is not completed' }, status: 403
render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403
elsif !current_user.approved?
render json: { error: 'Your login is currently pending approval' }, status: 403
else
set_user_activity
end
......
......@@ -69,7 +69,13 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
end
def hashtag_scope
Status.tagged_with(Tag.find_by(name: params[:tagged])&.id)
tag = Tag.find_normalized(params[:tagged])
if tag
Status.tagged_with(tag.id)
else
Status.none
end
end
def pagination_params(core_params)
......
......@@ -80,6 +80,10 @@ class Api::V1::AccountsController < Api::BaseController
end
def check_enabled_registrations
forbidden if single_user_mode? || !Setting.open_registrations
forbidden if single_user_mode? || !allowed_registrations?
end
def allowed_registrations?
Setting.registrations_mode != 'none'
end
end
......@@ -14,7 +14,7 @@ class Api::V1::Timelines::TagController < Api::BaseController
private
def load_tag
@tag = Tag.find_by(name: params[:id].downcase)
@tag = Tag.find_normalized(params[:id])
end
def load_statuses
......
......@@ -65,7 +65,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
end
def allowed_registrations?
Setting.open_registrations || @invite&.valid_for_use?
Setting.registrations_mode != 'none' || @invite&.valid_for_use?
end
def invite_code
......
......@@ -7,16 +7,18 @@ module AccountControllerConcern
included do
layout 'public'
before_action :set_account
before_action :check_account_approval
before_action :check_account_suspension
before_action :set_instance_presenter
before_action :set_link_headers
before_action :check_account_suspension
end
private
def set_account
@account = Account.find_local!(params[:account_username])
@account = Account.find_local!(username_param)
end
def set_instance_presenter
......@@ -33,6 +35,10 @@ module AccountControllerConcern
)
end
def username_param
params[:account_username]
end
def webfinger_account_link
[
webfinger_account_url,
......@@ -58,6 +64,10 @@ module AccountControllerConcern
webfinger_url(resource: @account.to_webfinger_s)
end
def check_account_approval
not_found if @account.user_pending?
end
def check_account_suspension
gone if @account.suspended?
end
......
......@@ -9,7 +9,7 @@ class TagsController < ApplicationController
before_action :set_instance_presenter
def show
@tag = Tag.find_by!(name: params[:id].downcase)
@tag = Tag.find_normalized!(params[:id])
respond_to do |format|
format.html do
......
# frozen_string_literal: true
module Admin::FilterHelper
ACCOUNT_FILTERS = %i(local remote by_domain active silenced suspended username display_name email ip staff).freeze
ACCOUNT_FILTERS = %i(local remote by_domain active pending silenced suspended username display_name email ip staff).freeze
REPORT_FILTERS = %i(resolved account_id target_account_id).freeze
INVITE_FILTER = %i(available expired).freeze
CUSTOM_EMOJI_FILTERS = %i(local remote by_domain shortcode).freeze
......
......@@ -20,7 +20,23 @@ module ApplicationHelper
end
def open_registrations?
Setting.open_registrations
Setting.registrations_mode == 'open'
end
def approved_registrations?
Setting.registrations_mode == 'approved'
end
def closed_registrations?
Setting.registrations_mode == 'none'
end
def available_sign_up_path
if closed_registrations?
'https://joinmastodon.org/#getting-started'
else
new_user_registration_path
end
end
def open_deletion?
......
......@@ -64,4 +64,14 @@ module HomeHelper
content_tag(:div, &block)
end
end
def sign_up_message
if closed_registrations?
t('auth.registration_closed', instance: site_hostname)
elsif open_registrations?
t('auth.register')
elsif approved_registrations?
t('auth.apply_for_account')
end
end
end
......@@ -89,6 +89,17 @@ export default class ColumnSettings extends React.PureComponent {
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'reblog']} onChange={onChange} label={soundStr} />
</div>
</div>
<div role='group' aria-labelledby='notifications-poll'>
<span id='notifications-poll' className='column-settings__section'><FormattedMessage id='notifications.column_settings.poll' defaultMessage='Poll results:' /></span>
<div className='column-settings__row'>
<SettingToggle prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'poll']} onChange={onChange} label={alertStr} />
{showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'poll']} onChange={this.onPushChange} label={pushStr} />}
<SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'poll']} onChange={onChange} label={showStr} />
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'poll']} onChange={onChange} label={soundStr} />
</div>
</div>
</div>
);
}
......
......@@ -7,6 +7,7 @@ const tooltips = defineMessages({
mentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' },
favourites: { id: 'notifications.filter.favourites', defaultMessage: 'Favourites' },
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' },
polls: { id: 'notifications.filter.polls', defaultMessage: 'Poll results' },
follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
});
......@@ -79,6 +80,13 @@ class FilterBar extends React.PureComponent {
>
<Icon id='retweet' fixedWidth />
</button>
<button
className={selectedFilter === 'poll' ? 'active' : ''}
onClick={this.onClick('poll')}
title={intl.formatMessage(tooltips.polls)}
>
<Icon id='tasks' fixedWidth />
</button>
<button
className={selectedFilter === 'follow' ? 'active' : ''}
onClick={this.onClick('follow')}
......
......@@ -210,14 +210,14 @@ class Notification extends ImmutablePureComponent {
return (
<HotKeys handlers={this.getHandlers()}>
<div className='notification notification-poll focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.poll', defaultMessage: 'Your poll has ended' }), notification.get('created_at'))}>
<div className='notification notification-poll focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' }), notification.get('created_at'))}>
<div className='notification__message'>
<div className='notification__favourite-icon-wrapper'>
<Icon id='tasks' fixedWidth />
</div>
<span title={notification.get('created_at')}>
<FormattedMessage id='notification.poll' defaultMessage='Your poll has ended' />
<FormattedMessage id='notification.poll' defaultMessage='A poll you have voted in has ended' />
</span>
</div>
......
......@@ -17,6 +17,10 @@
{
"defaultMessage": "File upload limit exceeded.",
"id": "upload_error.limit"
},
{
"defaultMessage": "File upload not allowed with polls.",
"id": "upload_error.poll"
}
],
"path": "app/javascript/mastodon/actions/compose.json"
......@@ -907,6 +911,52 @@
],
"path": "app/javascript/mastodon/features/compose/components/navigation_bar.json"
},
{
"descriptors": [
{
"defaultMessage": "Add a poll",
"id": "poll_button.add_poll"
},
{
"defaultMessage": "Remove poll",
"id": "poll_button.remove_poll"
}
],
"path": "app/javascript/mastodon/features/compose/components/poll_button.json"
},
{
"descriptors": [
{
"defaultMessage": "Choice {number}",
"id": "compose_form.poll.option_placeholder"
},
{
"defaultMessage": "Add a choice",
"id": "compose_form.poll.add_option"
},
{
"defaultMessage": "Remove this choice",
"id": "compose_form.poll.remove_option"
},
{
"defaultMessage": "Poll duration",
"id": "compose_form.poll.duration"
},
{
"defaultMessage": "{number, plural, one {# minute} other {# minutes}}",
"id": "intervals.full.minutes"
},
{
"defaultMessage": "{number, plural, one {# hour} other {# hours}}",
"id": "intervals.full.hours"
},
{
"defaultMessage": "{number, plural, one {# day} other {# days}}",
"id": "intervals.full.days"
}
],
"path": "app/javascript/mastodon/features/compose/components/poll_form.json"
},
{
"descriptors": [
{
......@@ -1853,6 +1903,10 @@
{
"defaultMessage": "{name} boosted your status",
"id": "notification.reblog"
},
{
"defaultMessage": "Your poll has ended",
"id": "notification.poll"
}
],
"path": "app/javascript/mastodon/features/notifications/components/notification.json"
......@@ -1914,24 +1968,6 @@
],
"path": "app/javascript/mastodon/features/reblogs/index.json"
},
{
"descriptors": [
{
"defaultMessage": "A look inside...",
"id": "standalone.public_title"
}
],
"path": "app/javascript/mastodon/features/standalone/community_timeline/index.json"
},
{
"descriptors": [
{
"defaultMessage": "A look inside...",
"id": "standalone.public_title"
}
],
"path": "app/javascript/mastodon/features/standalone/public_timeline/index.json"
},
{
"descriptors": [
{
......
......@@ -73,6 +73,10 @@
"compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
"compose_form.lock_disclaimer.lock": "locked",
"compose_form.placeholder": "What's on your mind?",
"compose_form.poll.add_option": "Add a choice",
"compose_form.poll.duration": "Poll duration",
"compose_form.poll.option_placeholder": "Choice {number}",
"compose_form.poll.remove_option": "Remove this choice",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.marked": "Media is marked as sensitive",
......@@ -151,6 +155,9 @@
"home.column_settings.basic": "Basic",
"home.column_settings.show_reblogs": "Show boosts",
"home.column_settings.show_replies": "Show replies",
"intervals.full.days": "{number, plural, one {# day} other {# days}}",
"intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
"intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
"introduction.federation.action": "Next",
"introduction.federation.federated.headline": "Federated",
"introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.",
......@@ -240,7 +247,7 @@
"notification.favourite": "{name} favourited your status",
"notification.follow": "{name} followed you",
"notification.mention": "{name} mentioned you",
"notification.poll": "Your poll has ended",
"notification.poll": "A poll you have voted in has ended",
"notification.reblog": "{name} boosted your status",
"notifications.clear": "Clear notifications",
"notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
......@@ -265,6 +272,8 @@
"poll.refresh": "Refresh",
"poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
"poll.vote": "Vote",
"poll_button.add_poll": "Add a poll",
"poll_button.remove_poll": "Remove poll",
"privacy.change": "Adjust status privacy",
"privacy.direct.long": "Post to mentioned users only",
"privacy.direct.short": "Direct",
......@@ -299,7 +308,6 @@
"search_results.hashtags": "Hashtags",
"search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"standalone.public_title": "A look inside...",
"status.admin_account": "Open moderation interface for @{name}",
"status.admin_status": "Open this status in the moderation interface",
"status.block": "Block @{name}",
......@@ -357,6 +365,7 @@
"upload_area.title": "Drag & drop to upload",
"upload_button.label": "Add media (JPEG, PNG, GIF, WebM, MP4, MOV)",
"upload_error.limit": "File upload limit exceeded.",
"upload_error.poll": "File upload not allowed with polls.",
"upload_form.description": "Describe for the visually impaired",
"upload_form.focus": "Change preview",
"upload_form.undo": "Delete",
......
......@@ -73,6 +73,10 @@
"compose_form.lock_disclaimer": "あなたのアカウントは{locked}になっていません。誰でもあなたをフォローすることができ、フォロワー限定の投稿を見ることができます。",
"compose_form.lock_disclaimer.lock": "承認制",
"compose_form.placeholder": "今なにしてる?",
"compose_form.poll.add_option": "Add a choice",
"compose_form.poll.duration": "Poll duration",
"compose_form.poll.option_placeholder": "Choice {number}",
"compose_form.poll.remove_option": "Remove this choice",
"compose_form.publish": "トゥート",
"compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.marked": "メディアに閲覧注意が設定されています",
......@@ -151,6 +155,9 @@
"home.column_settings.basic": "基本設定",
"home.column_settings.show_reblogs": "ブースト表示",
"home.column_settings.show_replies": "返信表示",
"intervals.full.days": "{number, plural, one {# day} other {# days}}",
"intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
"intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
"introduction.federation.action": "次へ",
"introduction.federation.federated.headline": "連合タイムライン",
"introduction.federation.federated.text": "Fediverseの他のサーバーからの公開投稿は連合タイムラインに表示されます。",
......@@ -240,6 +247,7 @@
"notification.favourite": "{name}さんがあなたのトゥートをお気に入りに登録しました",
"notification.follow": "{name}さんにフォローされました",
"notification.mention": "{name}さんがあなたに返信しました",
"notification.poll": "Your poll has ended",
"notification.reblog": "{name}さんがあなたのトゥートをブーストしました",
"notifications.clear": "通知を消去",
"notifications.clear_confirmation": "本当に通知を消去しますか?",
......@@ -264,6 +272,8 @@
"poll.refresh": "Refresh",
"poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
"poll.vote": "Vote",
"poll_button.add_poll": "Add a poll",
"poll_button.remove_poll": "Remove poll",
"privacy.change": "公開範囲を変更",
"privacy.direct.long": "メンションしたユーザーだけに公開",
"privacy.direct.short": "ダイレクト",
......@@ -298,7 +308,6 @@
"search_results.hashtags": "ハッシュタグ",
"search_results.statuses": "トゥート",
"search_results.total": "{count, number}件の結果",
"standalone.public_title": "今こんな話をしています...",
"status.admin_account": "@{name} のモデレーション画面を開く",
"status.admin_status": "このトゥートをモデレーション画面で開く",
"status.block": "@{name}さんをブロック",
......@@ -356,6 +365,7 @@
"upload_area.title": "ドラッグ&ドロップでアップロード",
"upload_button.label": "メディアを追加 (JPEG, PNG, GIF, WebM, MP4, MOV)",
"upload_error.limit": "アップロードできる上限を超えています。",
"upload_error.poll": "File upload not allowed with polls.",
"upload_form.description": "視覚障害者のための説明",
"upload_form.focus": "焦点",
"upload_form.undo": "削除",
......
......@@ -9,6 +9,7 @@ const initialState = Immutable.Map({
favourite: false,
reblog: false,
mention: false,
poll: false,
}),
isSubscribed: false,
browserSupport: false,
......
......@@ -689,3 +689,11 @@ a.name-tag,
overflow: hidden;
text-overflow: ellipsis;
}
.ellipsized-ip {
display: inline-block;
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
}