From 2f4c5f504fa5edc2a826afa9645371f529ae904d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 9 Dec 2017 01:32:29 +0100 Subject: Limit users to 50 lists, remove pagination from lists API (#5933) --- app/controllers/api/v1/lists_controller.rb | 38 +----------------------------- 1 file changed, 1 insertion(+), 37 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/api/v1/lists_controller.rb b/app/controllers/api/v1/lists_controller.rb index 9437373bd..180a91d81 100644 --- a/app/controllers/api/v1/lists_controller.rb +++ b/app/controllers/api/v1/lists_controller.rb @@ -1,18 +1,14 @@ # frozen_string_literal: true class Api::V1::ListsController < Api::BaseController - LISTS_LIMIT = 50 - before_action -> { doorkeeper_authorize! :read }, only: [:index, :show] before_action -> { doorkeeper_authorize! :write }, except: [:index, :show] before_action :require_user! before_action :set_list, except: [:index, :create] - after_action :insert_pagination_headers, only: :index - def index - @lists = List.where(account: current_account).paginate_by_max_id(limit_param(LISTS_LIMIT), params[:max_id], params[:since_id]) + @lists = List.where(account: current_account).all render json: @lists, each_serializer: REST::ListSerializer end @@ -44,36 +40,4 @@ class Api::V1::ListsController < Api::BaseController def list_params params.permit(:title) end - - def insert_pagination_headers - set_pagination_headers(next_path, prev_path) - end - - def next_path - if records_continue? - api_v1_lists_url pagination_params(max_id: pagination_max_id) - end - end - - def prev_path - unless @lists.empty? - api_v1_lists_url pagination_params(since_id: pagination_since_id) - end - end - - def pagination_max_id - @lists.last.id - end - - def pagination_since_id - @lists.first.id - end - - def records_continue? - @lists.size == limit_param(LISTS_LIMIT) - end - - def pagination_params(core_params) - params.permit(:limit).merge(core_params) - end end -- cgit From 99242b92bcc1965b2a72c01216bce7c232322f15 Mon Sep 17 00:00:00 2001 From: abcang Date: Sat, 9 Dec 2017 10:31:37 +0900 Subject: Keep WebPush settings (#5879) --- .../api/web/push_subscriptions_controller.rb | 2 + .../mastodon/actions/push_notifications.js | 11 +++-- app/javascript/mastodon/web_push_subscription.js | 51 ++++++++++++++++++++-- .../api/web/push_subscriptions_controller_spec.rb | 17 ++++++++ 4 files changed, 74 insertions(+), 7 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/api/web/push_subscriptions_controller.rb b/app/controllers/api/web/push_subscriptions_controller.rb index d66237feb..52e250d02 100644 --- a/app/controllers/api/web/push_subscriptions_controller.rb +++ b/app/controllers/api/web/push_subscriptions_controller.rb @@ -28,6 +28,8 @@ class Api::Web::PushSubscriptionsController < Api::BaseController }, } + data.deep_merge!(params[:data]) if params[:data] + web_subscription = ::Web::PushSubscription.create!( endpoint: params[:subscription][:endpoint], key_p256dh: params[:subscription][:keys][:p256dh], diff --git a/app/javascript/mastodon/actions/push_notifications.js b/app/javascript/mastodon/actions/push_notifications.js index 55661d2b0..cfe419888 100644 --- a/app/javascript/mastodon/actions/push_notifications.js +++ b/app/javascript/mastodon/actions/push_notifications.js @@ -1,4 +1,5 @@ import axios from 'axios'; +import { setSettingsToLocalStorage } from '../web_push_subscription'; export const SET_BROWSER_SUPPORT = 'PUSH_NOTIFICATIONS_SET_BROWSER_SUPPORT'; export const SET_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_SET_SUBSCRIPTION'; @@ -42,11 +43,15 @@ export function saveSettings() { const state = getState().get('push_notifications'); const subscription = state.get('subscription'); const alerts = state.get('alerts'); + const data = { alerts }; axios.put(`/api/web/push_subscriptions/${subscription.get('id')}`, { - data: { - alerts, - }, + data, + }).then(() => { + const me = getState().getIn(['meta', 'me']); + if (me) { + setSettingsToLocalStorage(me, data); + } }); }; } diff --git a/app/javascript/mastodon/web_push_subscription.js b/app/javascript/mastodon/web_push_subscription.js index 3dbed09ea..114d9c3b3 100644 --- a/app/javascript/mastodon/web_push_subscription.js +++ b/app/javascript/mastodon/web_push_subscription.js @@ -35,16 +35,35 @@ const subscribe = (registration) => const unsubscribe = ({ registration, subscription }) => subscription ? subscription.unsubscribe().then(() => registration) : registration; -const sendSubscriptionToBackend = (subscription) => - axios.post('/api/web/push_subscriptions', { - subscription, - }).then(response => response.data); +const sendSubscriptionToBackend = (subscription) => { + const params = { subscription }; + + const me = store.getState().getIn(['meta', 'me']); + if (me) { + const data = getSettingsFromLocalStorage(me); + if (data) { + params.data = data; + } + } + + return axios.post('/api/web/push_subscriptions', params).then(response => response.data); +}; // Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload const supportsPushNotifications = ('serviceWorker' in navigator && 'PushManager' in window && 'getKey' in PushSubscription.prototype); +const SUBSCRIPTION_DATA_STORAGE_KEY = 'mastodon_push_notification_data'; + export function register () { store.dispatch(setBrowserSupport(supportsPushNotifications)); + const me = store.getState().getIn(['meta', 'me']); + + if (me && !getSettingsFromLocalStorage(me)) { + const alerts = store.getState().getIn(['push_notifications', 'alerts']); + if (alerts) { + setSettingsToLocalStorage(me, { alerts: alerts }); + } + } if (supportsPushNotifications) { if (!getApplicationServerKey()) { @@ -79,6 +98,9 @@ export function register () { // it means that the backend subscription is valid (and was set during hydration) if (!(subscription instanceof PushSubscription)) { store.dispatch(setSubscription(subscription)); + if (me) { + setSettingsToLocalStorage(me, { alerts: subscription.alerts }); + } } }) .catch(error => { @@ -90,6 +112,9 @@ export function register () { // Clear alerts and hide UI settings store.dispatch(clearSubscription()); + if (me) { + removeSettingsFromLocalStorage(me); + } try { getRegistration() @@ -103,3 +128,21 @@ export function register () { console.warn('Your browser does not support Web Push Notifications.'); } } + +export function setSettingsToLocalStorage(id, data) { + try { + localStorage.setItem(`${SUBSCRIPTION_DATA_STORAGE_KEY}_${id}`, JSON.stringify(data)); + } catch (e) {} +} + +export function getSettingsFromLocalStorage(id) { + try { + return JSON.parse(localStorage.getItem(`${SUBSCRIPTION_DATA_STORAGE_KEY}_${id}`)); + } catch (e) {} + + return null; +} + +export function removeSettingsFromLocalStorage(id) { + localStorage.removeItem(`${SUBSCRIPTION_DATA_STORAGE_KEY}_${id}`); +} diff --git a/spec/controllers/api/web/push_subscriptions_controller_spec.rb b/spec/controllers/api/web/push_subscriptions_controller_spec.rb index 7e83b801d..bbf94c5c6 100644 --- a/spec/controllers/api/web/push_subscriptions_controller_spec.rb +++ b/spec/controllers/api/web/push_subscriptions_controller_spec.rb @@ -48,6 +48,23 @@ describe Api::Web::PushSubscriptionsController do expect(push_subscription['key_p256dh']).to eq(create_payload[:subscription][:keys][:p256dh]) expect(push_subscription['key_auth']).to eq(create_payload[:subscription][:keys][:auth]) end + + context 'with initial data' do + it 'saves alert settings' do + sign_in(user) + + stub_request(:post, create_payload[:subscription][:endpoint]).to_return(status: 200) + + post :create, format: :json, params: create_payload.merge(alerts_payload) + + push_subscription = Web::PushSubscription.find_by(endpoint: create_payload[:subscription][:endpoint]) + + expect(push_subscription.data['follow']).to eq(alerts_payload[:data][:follow]) + expect(push_subscription.data['favourite']).to eq(alerts_payload[:data][:favourite]) + expect(push_subscription.data['reblog']).to eq(alerts_payload[:data][:reblog]) + expect(push_subscription.data['mention']).to eq(alerts_payload[:data][:mention]) + end + end end describe 'PUT #update' do -- cgit From a865b62efc0f7025f789f4413a03ccacd57db7bb Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 9 Dec 2017 14:20:02 +0100 Subject: Rate limit by user instead of IP when API user is authenticated (#5923) * Fix #668 - Rate limit by user instead of IP when API user is authenticated * Fix code style issue * Use request decorator provided by Doorkeeper --- app/controllers/concerns/rate_limit_headers.rb | 3 +- config/initializers/rack_attack.rb | 55 ++++++++++++++++------ .../concerns/rate_limit_headers_spec.rb | 2 +- 3 files changed, 44 insertions(+), 16 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/concerns/rate_limit_headers.rb b/app/controllers/concerns/rate_limit_headers.rb index 36cb91075..ac9b58f5d 100644 --- a/app/controllers/concerns/rate_limit_headers.rb +++ b/app/controllers/concerns/rate_limit_headers.rb @@ -44,7 +44,8 @@ module RateLimitHeaders end def api_throttle_data - request.env['rack.attack.throttle_data']['api'] + request.env['rack.attack.throttle_data']['throttle_authenticated_api'] || + request.env['rack.attack.throttle_data']['throttle_unauthenticated_api'] end def request_time diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb index 53cb106ca..fddcbc52c 100644 --- a/config/initializers/rack_attack.rb +++ b/config/initializers/rack_attack.rb @@ -1,6 +1,41 @@ # frozen_string_literal: true class Rack::Attack + class Request + def authenticated_token + return @token if defined?(@token) + + @token = Doorkeeper::OAuth::Token.authenticate( + Doorkeeper::Grape::AuthorizationDecorator.new(self), + *Doorkeeper.configuration.access_token_methods + ) + end + + def authenticated_user_id + authenticated_token&.resource_owner_id + end + + def unauthenticated? + !authenticated_user_id + end + + def api_request? + path.start_with?('/api') + end + + def web_request? + !api_request? + end + end + + PROTECTED_PATHS = %w( + /auth/sign_in + /auth + /auth/password + ).freeze + + PROTECTED_PATHS_REGEX = Regexp.union(PROTECTED_PATHS.map { |path| /\A#{Regexp.escape(path)}/ }) + # Always allow requests from localhost # (blocklist & throttles are skipped) Rack::Attack.safelist('allow from localhost') do |req| @@ -8,24 +43,16 @@ class Rack::Attack '127.0.0.1' == req.ip || '::1' == req.ip end - # Rate limits for the API - throttle('api', limit: 300, period: 5.minutes) do |req| - req.ip if req.path =~ /\A\/api\/v/ - end - - # Rate limit logins - throttle('login', limit: 5, period: 5.minutes) do |req| - req.ip if req.path == '/auth/sign_in' && req.post? + throttle('throttle_authenticated_api', limit: 300, period: 5.minutes) do |req| + req.api_request? && req.authenticated_user_id end - # Rate limit sign-ups - throttle('register', limit: 5, period: 5.minutes) do |req| - req.ip if req.path == '/auth' && req.post? + throttle('throttle_unauthenticated_api', limit: 300, period: 5.minutes) do |req| + req.ip if req.api_request? && req.unauthenticated? end - # Rate limit forgotten passwords - throttle('reminder', limit: 5, period: 5.minutes) do |req| - req.ip if req.path == '/auth/password' && req.post? + throttle('protected_paths', limit: 5, period: 5.minutes) do |req| + req.ip if req.post? && req.path =~ PROTECTED_PATHS_REGEX end self.throttled_response = lambda do |env| diff --git a/spec/controllers/concerns/rate_limit_headers_spec.rb b/spec/controllers/concerns/rate_limit_headers_spec.rb index 719978dc2..00a9a2080 100644 --- a/spec/controllers/concerns/rate_limit_headers_spec.rb +++ b/spec/controllers/concerns/rate_limit_headers_spec.rb @@ -34,7 +34,7 @@ describe ApplicationController do let(:start_time) { DateTime.new(2017, 1, 1, 12, 0, 0).utc } before do - request.env['rack.attack.throttle_data'] = { 'api' => { limit: 100, count: 20, period: 10 } } + request.env['rack.attack.throttle_data'] = { 'throttle_authenticated_api' => { limit: 100, count: 20, period: 10 } } travel_to start_time do get 'show' end -- cgit From abe95b614b6355e61dd2955e09115ac473417c84 Mon Sep 17 00:00:00 2001 From: cwm Date: Sat, 9 Dec 2017 10:26:22 -0600 Subject: add initial components based off of tootsuite pr #1507 --- app/controllers/settings/preferences_controller.rb | 1 + .../flavours/glitch/components/status.js | 5 +- .../flavours/glitch/containers/status_container.js | 16 +++-- .../features/status/components/action_bar.js | 4 +- .../flavours/glitch/features/status/index.js | 16 +++-- .../features/ui/components/favourite_modal.js | 84 ++++++++++++++++++++++ .../glitch/features/ui/components/modal_root.js | 4 +- .../flavours/glitch/styles/components/index.scss | 11 ++- .../flavours/glitch/util/initial_state.js | 1 + app/models/user.rb | 2 +- app/views/settings/preferences/show.html.haml | 1 + config/locales/simple_form.en.yml | 1 + config/settings.yml | 1 + 13 files changed, 130 insertions(+), 17 deletions(-) create mode 100644 app/javascript/flavours/glitch/features/ui/components/favourite_modal.js (limited to 'app/controllers') diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index 277f0f657..9177d37da 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -33,6 +33,7 @@ class Settings::PreferencesController < Settings::BaseController :setting_default_sensitive, :setting_unfollow_modal, :setting_boost_modal, + :setting_favourite_modal, :setting_delete_modal, :setting_auto_play_gif, :setting_reduce_motion, diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index b0d9e3757..6cfd05735 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -58,6 +58,7 @@ export default class Status extends ImmutablePureComponent { 'settings', 'prepend', 'boostModal', + 'favouriteModal', 'muted', 'collapse', 'notification', @@ -204,8 +205,8 @@ export default class Status extends ImmutablePureComponent { this.props.onReply(this.props.status, this.context.router.history); } - handleHotkeyFavourite = () => { - this.props.onFavourite(this.props.status); + handleHotkeyFavourite = e => { + this.props.onFavourite(this.props.status, e); } handleHotkeyBoost = e => { diff --git a/app/javascript/flavours/glitch/containers/status_container.js b/app/javascript/flavours/glitch/containers/status_container.js index b753de7b3..8bf33c70f 100644 --- a/app/javascript/flavours/glitch/containers/status_container.js +++ b/app/javascript/flavours/glitch/containers/status_container.js @@ -20,7 +20,7 @@ import { initMuteModal } from 'flavours/glitch/actions/mutes'; import { initReport } from 'flavours/glitch/actions/reports'; import { openModal } from 'flavours/glitch/actions/modal'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; -import { boostModal, deleteModal } from 'flavours/glitch/util/initial_state'; +import { boostModal, favouriteModal, deleteModal } from 'flavours/glitch/util/initial_state'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -78,14 +78,22 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ } }, - onFavourite (status) { + onModalFavourite (status) { + dispatch(favourite(status)); + }, + + onFavourite (status, e) { if (status.get('favourited')) { dispatch(unfavourite(status)); } else { - dispatch(favourite(status)); + if (e.shiftKey || !favouriteModal) { + this.onModalFavourite(status); + } else { + dispatch(openModal('FAVOURITE', { status, onFavourite: this.onModalFavourite })); + } } }, - + onPin (status) { if (status.get('pinned')) { dispatch(unpin(status)); diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.js b/app/javascript/flavours/glitch/features/status/components/action_bar.js index 4d660ee3c..3190fd0be 100644 --- a/app/javascript/flavours/glitch/features/status/components/action_bar.js +++ b/app/javascript/flavours/glitch/features/status/components/action_bar.js @@ -48,8 +48,8 @@ export default class ActionBar extends React.PureComponent { this.props.onReblog(this.props.status, e); } - handleFavouriteClick = () => { - this.props.onFavourite(this.props.status); + handleFavouriteClick = (e) => { + this.props.onFavourite(this.props.status, e); } handleDeleteClick = () => { diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js index 93b0fe9d9..8b81caa9a 100644 --- a/app/javascript/flavours/glitch/features/status/index.js +++ b/app/javascript/flavours/glitch/features/status/index.js @@ -30,7 +30,7 @@ import { openModal } from 'flavours/glitch/actions/modal'; import { defineMessages, injectIntl } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { HotKeys } from 'react-hotkeys'; -import { boostModal, deleteModal } from 'flavours/glitch/util/initial_state'; +import { boostModal, favouriteModal, deleteModal } from 'flavours/glitch/util/initial_state'; import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from 'flavours/glitch/util/fullscreen'; const messages = defineMessages({ @@ -95,11 +95,19 @@ export default class Status extends ImmutablePureComponent { } }; - handleFavouriteClick = (status) => { + handleModalFavourite = (status) => { + this.props.dispatch(favourite(status)); + } + + handleFavouriteClick = (status, e) => { if (status.get('favourited')) { this.props.dispatch(unfavourite(status)); } else { - this.props.dispatch(favourite(status)); + if (e.shiftKey || !favoriteModal) { + this.handleModalFavourite(status); + } else { + this.props.dispatch(openModal('FAVOURITE', { status, onFavourite: this.handleModalFavourite })); + } } } @@ -118,7 +126,7 @@ export default class Status extends ImmutablePureComponent { handleModalReblog = (status) => { this.props.dispatch(reblog(status)); } - + handleReblogClick = (status, e) => { if (status.get('reblogged')) { this.props.dispatch(unreblog(status)); diff --git a/app/javascript/flavours/glitch/features/ui/components/favourite_modal.js b/app/javascript/flavours/glitch/features/ui/components/favourite_modal.js new file mode 100644 index 000000000..a2322d0b8 --- /dev/null +++ b/app/javascript/flavours/glitch/features/ui/components/favourite_modal.js @@ -0,0 +1,84 @@ +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import Button from 'flavours/glitch/components/button'; +import StatusContent from 'flavours/glitch/components/status_content'; +import Avatar from 'flavours/glitch/components/avatar'; +import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp'; +import DisplayName from 'flavours/glitch/components/display_name'; +import ImmutablePureComponent from 'react-immutable-pure-component'; + +const messages = defineMessages({ + reblog: { id: 'status.favourite', defaultMessage: 'Favourite' }, +}); + +@injectIntl +export default class BoostModal extends ImmutablePureComponent { + + static contextTypes = { + router: PropTypes.object, + }; + + static propTypes = { + status: ImmutablePropTypes.map.isRequired, + onReblog: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + }; + + componentDidMount() { + this.button.focus(); + } + + handleFavourite = () => { + this.props.onFavourite(this.props.status); + this.props.onClose(); + } + + handleAccountClick = (e) => { + if (e.button === 0) { + e.preventDefault(); + this.props.onClose(); + this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`); + } + } + + setRef = (c) => { + this.button = c; + } + + render () { + const { status, intl } = this.props; + + return ( +
+
+
+
+
+ +
+ + +
+ +
+ + +
+
+ + +
+
+ +
+
Shift + }} />
+
+
+ ); + } + +} diff --git a/app/javascript/flavours/glitch/features/ui/components/modal_root.js b/app/javascript/flavours/glitch/features/ui/components/modal_root.js index 61b239283..66acae68e 100644 --- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js +++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js @@ -7,6 +7,7 @@ import ActionsModal from './actions_modal'; import MediaModal from './media_modal'; import VideoModal from './video_modal'; import BoostModal from './boost_modal'; +import FavouriteModal from './favourite_modal'; import DoodleModal from './doodle_modal'; import ConfirmationModal from './confirmation_modal'; import { @@ -22,6 +23,7 @@ const MODAL_COMPONENTS = { 'ONBOARDING': OnboardingModal, 'VIDEO': () => Promise.resolve({ default: VideoModal }), 'BOOST': () => Promise.resolve({ default: BoostModal }), + 'FAVOURITE': () => Promise.resolve({ default: FavouriteModal }), 'DOODLE': () => Promise.resolve({ default: DoodleModal }), 'CONFIRM': () => Promise.resolve({ default: ConfirmationModal }), 'MUTE': MuteModal, @@ -90,7 +92,7 @@ export default class ModalRoot extends React.PureComponent { } renderLoading = modalId => () => { - return ['MEDIA', 'VIDEO', 'BOOST', 'DOODLE', 'CONFIRM', 'ACTIONS'].indexOf(modalId) === -1 ? : null; + return ['MEDIA', 'VIDEO', 'BOOST', 'FAVOURITE', 'DOODLE', 'CONFIRM', 'ACTIONS'].indexOf(modalId) === -1 ? : null; } renderError = (props) => { diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss index 7aeab0a6a..eb8d8245f 100644 --- a/app/javascript/flavours/glitch/styles/components/index.scss +++ b/app/javascript/flavours/glitch/styles/components/index.scss @@ -3901,6 +3901,7 @@ button.icon-button.active i.fa-retweet { } .boost-modal, +.favourite-modal, .confirmation-modal, .report-modal, .actions-modal, @@ -3932,7 +3933,8 @@ button.icon-button.active i.fa-retweet { } } -.boost-modal__container { +.boost-modal__container, +.favourite-modal__container{ overflow-x: scroll; padding: 10px; @@ -3943,6 +3945,7 @@ button.icon-button.active i.fa-retweet { } .boost-modal__action-bar, +.favourite-modal__action-bar, .confirmation-modal__action-bar, .mute-modal__action-bar, .report-modal__action-bar { @@ -3964,11 +3967,13 @@ button.icon-button.active i.fa-retweet { } } -.boost-modal__status-header { +.boost-modal__status-header, +.favourite-modal__status-header { font-size: 15px; } -.boost-modal__status-time { +.boost-modal__status-time, +.favourite-modal__status-time { float: right; font-size: 14px; } diff --git a/app/javascript/flavours/glitch/util/initial_state.js b/app/javascript/flavours/glitch/util/initial_state.js index ef5d8b0ef..607d6b9b0 100644 --- a/app/javascript/flavours/glitch/util/initial_state.js +++ b/app/javascript/flavours/glitch/util/initial_state.js @@ -15,6 +15,7 @@ export const reduceMotion = getMeta('reduce_motion'); export const autoPlayGif = getMeta('auto_play_gif'); export const unfollowModal = getMeta('unfollow_modal'); export const boostModal = getMeta('boost_modal'); +export const favouriteModal = getMeta('favourite_modal'); export const deleteModal = getMeta('delete_modal'); export const me = getMeta('me'); diff --git a/app/models/user.rb b/app/models/user.rb index 29bdcbd67..47bf22e17 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -75,7 +75,7 @@ class User < ApplicationRecord has_many :session_activations, dependent: :destroy - delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal, + delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :favourite_modal, :delete_modal, :reduce_motion, :system_font_ui, :noindex, :flavour, :skin, to: :settings, prefix: :setting, allow_nil: false diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index 9564c0399..9e72e5734 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -32,6 +32,7 @@ = f.input :setting_unfollow_modal, as: :boolean, wrapper: :with_label = f.input :setting_boost_modal, as: :boolean, wrapper: :with_label + = f.input :setting_favourite_modal, as: :boolean, wrapper: :with_label = f.input :setting_delete_modal, as: :boolean, wrapper: :with_label .fields-group diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 756f6b119..599df9e28 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -43,6 +43,7 @@ en: password: Password setting_auto_play_gif: Auto-play animated GIFs setting_boost_modal: Show confirmation dialog before boosting + setting_favourite_modal: Show confirmation dialog before favouriting setting_default_privacy: Post privacy setting_default_sensitive: Always mark media as sensitive setting_delete_modal: Show confirmation dialog before deleting a toot diff --git a/config/settings.yml b/config/settings.yml index 5aad45da2..dbc5f6a26 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -22,6 +22,7 @@ defaults: &defaults default_sensitive: false unfollow_modal: false boost_modal: false + favourite_modal: false delete_modal: true auto_play_gif: false reduce_motion: false -- cgit From fcc0795a4013db355c629da071d909ceda1f37b1 Mon Sep 17 00:00:00 2001 From: abcang Date: Sun, 10 Dec 2017 07:37:31 +0900 Subject: Remove unused function (#5950) --- app/controllers/api/base_controller.rb | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index 7cfe8fe71..5983c0fbe 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -72,19 +72,4 @@ class Api::BaseController < ApplicationController def render_empty render json: {}, status: 200 end - - def set_maps(statuses) # rubocop:disable Style/AccessorMethodName - if current_account.nil? - @reblogs_map = {} - @favourites_map = {} - @mutes_map = {} - return - end - - status_ids = statuses.compact.flat_map { |s| [s.id, s.reblog_of_id] }.uniq - conversation_ids = statuses.compact.map(&:conversation_id).compact.uniq - @reblogs_map = Status.reblogs_map(status_ids, current_account) - @favourites_map = Status.favourites_map(status_ids, current_account) - @mutes_map = Status.mutes_map(conversation_ids, current_account) - end end -- cgit From b28cd6769c77190160e4ffefde78b324c7aad269 Mon Sep 17 00:00:00 2001 From: kibigo! Date: Thu, 7 Dec 2017 19:07:47 -0800 Subject: Javascript intl8n flavour support --- app/controllers/application_controller.rb | 2 + app/javascript/flavours/glitch/locales/ar.js | 7 + app/javascript/flavours/glitch/locales/bg.js | 7 + app/javascript/flavours/glitch/locales/ca.js | 7 + app/javascript/flavours/glitch/locales/de.js | 7 + app/javascript/flavours/glitch/locales/en.js | 50 +++++ app/javascript/flavours/glitch/locales/eo.js | 7 + app/javascript/flavours/glitch/locales/es.js | 7 + app/javascript/flavours/glitch/locales/fa.js | 7 + app/javascript/flavours/glitch/locales/fi.js | 7 + app/javascript/flavours/glitch/locales/fr.js | 7 + app/javascript/flavours/glitch/locales/he.js | 7 + app/javascript/flavours/glitch/locales/hr.js | 7 + app/javascript/flavours/glitch/locales/hu.js | 7 + app/javascript/flavours/glitch/locales/id.js | 7 + app/javascript/flavours/glitch/locales/io.js | 7 + app/javascript/flavours/glitch/locales/it.js | 7 + app/javascript/flavours/glitch/locales/ja.js | 7 + app/javascript/flavours/glitch/locales/ko.js | 7 + app/javascript/flavours/glitch/locales/nl.js | 7 + app/javascript/flavours/glitch/locales/no.js | 7 + app/javascript/flavours/glitch/locales/oc.js | 7 + app/javascript/flavours/glitch/locales/pl.js | 48 +++++ app/javascript/flavours/glitch/locales/pt-BR.js | 7 + app/javascript/flavours/glitch/locales/pt.js | 7 + app/javascript/flavours/glitch/locales/ru.js | 7 + app/javascript/flavours/glitch/locales/sv.js | 7 + app/javascript/flavours/glitch/locales/th.js | 7 + app/javascript/flavours/glitch/locales/tr.js | 7 + app/javascript/flavours/glitch/locales/uk.js | 7 + app/javascript/flavours/glitch/locales/zh-CN.js | 7 + app/javascript/flavours/glitch/locales/zh-HK.js | 7 + app/javascript/flavours/glitch/locales/zh-TW.js | 7 + app/javascript/flavours/glitch/theme.yml | 6 + app/javascript/flavours/vanilla/theme.yml | 10 +- app/javascript/glitch/locales/en.json | 46 ----- app/javascript/glitch/locales/pl.json | 44 ---- app/javascript/locales/locale-data/README.md | 221 +++++++++++++++++++++ app/javascript/locales/locale-data/oc.js | 108 ++++++++++ .../mastodon/locales/locale-data/README.md | 221 --------------------- app/javascript/mastodon/locales/locale-data/oc.js | 108 ---------- app/lib/themes.rb | 8 + app/views/layouts/application.html.haml | 5 +- config/webpack/configuration.js | 5 +- config/webpack/generateLocalePacks.js | 108 +++++----- config/webpack/shared.js | 8 +- 46 files changed, 722 insertions(+), 486 deletions(-) create mode 100644 app/javascript/flavours/glitch/locales/ar.js create mode 100644 app/javascript/flavours/glitch/locales/bg.js create mode 100644 app/javascript/flavours/glitch/locales/ca.js create mode 100644 app/javascript/flavours/glitch/locales/de.js create mode 100644 app/javascript/flavours/glitch/locales/en.js create mode 100644 app/javascript/flavours/glitch/locales/eo.js create mode 100644 app/javascript/flavours/glitch/locales/es.js create mode 100644 app/javascript/flavours/glitch/locales/fa.js create mode 100644 app/javascript/flavours/glitch/locales/fi.js create mode 100644 app/javascript/flavours/glitch/locales/fr.js create mode 100644 app/javascript/flavours/glitch/locales/he.js create mode 100644 app/javascript/flavours/glitch/locales/hr.js create mode 100644 app/javascript/flavours/glitch/locales/hu.js create mode 100644 app/javascript/flavours/glitch/locales/id.js create mode 100644 app/javascript/flavours/glitch/locales/io.js create mode 100644 app/javascript/flavours/glitch/locales/it.js create mode 100644 app/javascript/flavours/glitch/locales/ja.js create mode 100644 app/javascript/flavours/glitch/locales/ko.js create mode 100644 app/javascript/flavours/glitch/locales/nl.js create mode 100644 app/javascript/flavours/glitch/locales/no.js create mode 100644 app/javascript/flavours/glitch/locales/oc.js create mode 100644 app/javascript/flavours/glitch/locales/pl.js create mode 100644 app/javascript/flavours/glitch/locales/pt-BR.js create mode 100644 app/javascript/flavours/glitch/locales/pt.js create mode 100644 app/javascript/flavours/glitch/locales/ru.js create mode 100644 app/javascript/flavours/glitch/locales/sv.js create mode 100644 app/javascript/flavours/glitch/locales/th.js create mode 100644 app/javascript/flavours/glitch/locales/tr.js create mode 100644 app/javascript/flavours/glitch/locales/uk.js create mode 100644 app/javascript/flavours/glitch/locales/zh-CN.js create mode 100644 app/javascript/flavours/glitch/locales/zh-HK.js create mode 100644 app/javascript/flavours/glitch/locales/zh-TW.js delete mode 100644 app/javascript/glitch/locales/en.json delete mode 100644 app/javascript/glitch/locales/pl.json create mode 100644 app/javascript/locales/locale-data/README.md create mode 100644 app/javascript/locales/locale-data/oc.js delete mode 100644 app/javascript/mastodon/locales/locale-data/README.md delete mode 100644 app/javascript/mastodon/locales/locale-data/oc.js (limited to 'app/controllers') diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c6d148c8c..3b2070f39 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -62,6 +62,7 @@ class ApplicationController < ActionController::Base pack: pack_name, preload: nil, skin: nil, + supported_locales: data['locales'], } if data['pack'][pack_name].is_a?(Hash) pack_data[:common] = nil if data['pack'][pack_name]['use_common'] == false @@ -93,6 +94,7 @@ class ApplicationController < ActionController::Base pack: nil, preload: nil, skin: nil, + supported_locales: data['locales'], } end diff --git a/app/javascript/flavours/glitch/locales/ar.js b/app/javascript/flavours/glitch/locales/ar.js new file mode 100644 index 000000000..1081147d5 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/ar.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/ar.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/bg.js b/app/javascript/flavours/glitch/locales/bg.js new file mode 100644 index 000000000..979039376 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/bg.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/bg.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/ca.js b/app/javascript/flavours/glitch/locales/ca.js new file mode 100644 index 000000000..baf76bd6f --- /dev/null +++ b/app/javascript/flavours/glitch/locales/ca.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/ca.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/de.js b/app/javascript/flavours/glitch/locales/de.js new file mode 100644 index 000000000..ce6453623 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/de.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/de.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/en.js b/app/javascript/flavours/glitch/locales/en.js new file mode 100644 index 000000000..96182196e --- /dev/null +++ b/app/javascript/flavours/glitch/locales/en.js @@ -0,0 +1,50 @@ +import inherited from 'mastodon/locales/en.json'; + +const messages = { + "getting_started.open_source_notice": "Glitchsoc is free open source software forked from {Mastodon}. You can contribute or report issues on GitHub at {github}.", + "layout.auto": "Auto", + "layout.current_is": "Your current layout is:", + "layout.desktop": "Desktop", + "layout.mobile": "Mobile", + "navigation_bar.app_settings": "App settings", + "getting_started.onboarding": "Show me around", + "onboarding.page_one.federation": "{domain} is an 'instance' of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.", + "onboarding.page_one.welcome": "Welcome to {domain}!", + "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}, and is compatible with any Mastodon instance or app. Glitchsoc is entirely free and open-source. You can report bugs, request features, or contribute to the code on {github}.", + "settings.auto_collapse": "Automatic collapsing", + "settings.auto_collapse_all": "Everything", + "settings.auto_collapse_lengthy": "Lengthy toots", + "settings.auto_collapse_media": "Toots with media", + "settings.auto_collapse_notifications": "Notifications", + "settings.auto_collapse_reblogs": "Boosts", + "settings.auto_collapse_replies": "Replies", + "settings.close": "Close", + "settings.collapsed_statuses": "Collapsed toots", + "settings.enable_collapsed": "Enable collapsed toots", + "settings.general": "General", + "settings.image_backgrounds": "Image backgrounds", + "settings.image_backgrounds_media": "Preview collapsed toot media", + "settings.image_backgrounds_users": "Give collapsed toots an image background", + "settings.media": "Media", + "settings.media_letterbox": "Letterbox media", + "settings.media_fullwidth": "Full-width media previews", + "settings.preferences": "User preferences", + "settings.wide_view": "Wide view (Desktop mode only)", + "settings.navbar_under": "Navbar at the bottom (Mobile only)", + "status.collapse": "Collapse", + "status.uncollapse": "Uncollapse", + + "home.column_settings.show_direct": "Show DMs", + + "notification.markForDeletion": "Mark for deletion", + "notifications.clear": "Clear all my notifications", + "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected notifications?", + "notifications.marked_clear": "Clear selected notifications", + + "notification_purge.btn_all": "Select\nall", + "notification_purge.btn_none": "Select\nnone", + "notification_purge.btn_invert": "Invert\nselection", + "notification_purge.btn_apply": "Clear\nselected", +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/eo.js b/app/javascript/flavours/glitch/locales/eo.js new file mode 100644 index 000000000..04192f506 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/eo.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/eo.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/es.js b/app/javascript/flavours/glitch/locales/es.js new file mode 100644 index 000000000..456df3c47 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/es.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/es.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/fa.js b/app/javascript/flavours/glitch/locales/fa.js new file mode 100644 index 000000000..d82461a1a --- /dev/null +++ b/app/javascript/flavours/glitch/locales/fa.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/fa.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/fi.js b/app/javascript/flavours/glitch/locales/fi.js new file mode 100644 index 000000000..11c3cd082 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/fi.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/fi.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/fr.js b/app/javascript/flavours/glitch/locales/fr.js new file mode 100644 index 000000000..8562f5594 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/fr.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/fr.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/he.js b/app/javascript/flavours/glitch/locales/he.js new file mode 100644 index 000000000..99516ee0c --- /dev/null +++ b/app/javascript/flavours/glitch/locales/he.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/he.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/hr.js b/app/javascript/flavours/glitch/locales/hr.js new file mode 100644 index 000000000..dbf9b4b9f --- /dev/null +++ b/app/javascript/flavours/glitch/locales/hr.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/hr.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/hu.js b/app/javascript/flavours/glitch/locales/hu.js new file mode 100644 index 000000000..1f0849af3 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/hu.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/hu.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/id.js b/app/javascript/flavours/glitch/locales/id.js new file mode 100644 index 000000000..07e5f7e56 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/id.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/id.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/io.js b/app/javascript/flavours/glitch/locales/io.js new file mode 100644 index 000000000..74ea6fae6 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/io.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/io.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/it.js b/app/javascript/flavours/glitch/locales/it.js new file mode 100644 index 000000000..90f543093 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/it.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/it.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/ja.js b/app/javascript/flavours/glitch/locales/ja.js new file mode 100644 index 000000000..cc7143443 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/ja.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/ja.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/ko.js b/app/javascript/flavours/glitch/locales/ko.js new file mode 100644 index 000000000..3b55f89b9 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/ko.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/ko.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/nl.js b/app/javascript/flavours/glitch/locales/nl.js new file mode 100644 index 000000000..17c371c58 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/nl.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/nl.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/no.js b/app/javascript/flavours/glitch/locales/no.js new file mode 100644 index 000000000..794b1da25 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/no.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/no.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/oc.js b/app/javascript/flavours/glitch/locales/oc.js new file mode 100644 index 000000000..8f161fd8c --- /dev/null +++ b/app/javascript/flavours/glitch/locales/oc.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/oc.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/pl.js b/app/javascript/flavours/glitch/locales/pl.js new file mode 100644 index 000000000..ab96dec60 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/pl.js @@ -0,0 +1,48 @@ +import inherited from 'mastodon/locales/pl.json'; + +const messages = { + "getting_started.open_source_notice": "Glitchsoc jest wolnym i otwartoźródłowym forkiem oprogramowania {Mastodon}. Możesz współtworzyć projekt lub zgłaszać błędy na GitHubie pod adresem {github}.", + "layout.auto": "Automatyczny", + "layout.current_is": "Twój obecny układ to:", + "layout.desktop": "Desktopowy", + "layout.mobile": "Mobilny", + "navigation_bar.app_settings": "Ustawienia aplikacji", + "getting_started.onboarding": "Rozejrzyj się", + "onboarding.page_one.federation": "{domain} jest 'instancją' Mastodona. Mastodon to sieć działających niezależnie serwerów tworzących jedną sieć społecznościową. Te serwery nazywane są instancjami.", + "onboarding.page_one.welcome": "Witamy na {domain}!", + "onboarding.page_six.github": "{domain} jest oparty na Glitchsoc. Glitchsoc jest {forkiem} {Mastodon}a kompatybilnym z każdym klientem i aplikacją Mastodona. Glitchsoc jest całkowicie wolnym i otwartoźródłowym oprogramowaniem. Możesz zgłaszać błędy i sugestie funkcji oraz współtworzyć projekt na {github}.", + "settings.auto_collapse": "Automatyczne zwijanie", + "settings.auto_collapse_all": "Wszystko", + "settings.auto_collapse_lengthy": "Długie wpisy", + "settings.auto_collapse_media": "Wpisy z zawartością multimedialną", + "settings.auto_collapse_notifications": "Powiadomienia", + "settings.auto_collapse_reblogs": "Podbicia", + "settings.auto_collapse_replies": "Odpowiedzi", + "settings.close": "Zamknij", + "settings.collapsed_statuses": "Zwijanie wpisów", + "settings.enable_collapsed": "Włącz zwijanie wpisów", + "settings.general": "Ogólne", + "settings.image_backgrounds": "Obrazy w tle", + "settings.image_backgrounds_media": "Wyświetlaj zawartość multimedialną zwiniętych wpisów", + "settings.image_backgrounds_users": "Nadaj tło zwiniętym wpisom", + "settings.media": "Zawartość multimedialna", + "settings.media_letterbox": "Letterbox media", + "settings.media_fullwidth": "Podgląd zawartości multimedialnej o pełnej szerokości", + "settings.preferences": "Preferencje użyytkownika", + "settings.wide_view": "Szeroki widok (tylko w trybie desktopowym)", + "settings.navbar_under": "Pasek nawigacji na dole (tylko w trybie mobilnym)", + "status.collapse": "Zwiń", + "status.uncollapse": "Rozwiń", + + "notification.markForDeletion": "Oznacz do usunięcia", + "notifications.clear": "Wyczyść wszystkie powiadomienia", + "notifications.marked_clear_confirmation": "Czy na pewno chcesz bezpowrtonie usunąć wszystkie powiadomienia?", + "notifications.marked_clear": "Usuń zaznaczone powiadomienia", + + "notification_purge.btn_all": "Zaznacz\nwszystkie", + "notification_purge.btn_none": "Odznacz\nwszystkie", + "notification_purge.btn_invert": "Odwróć\nzaznaczenie", + "notification_purge.btn_apply": "Usuń\nzaznaczone" +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/pt-BR.js b/app/javascript/flavours/glitch/locales/pt-BR.js new file mode 100644 index 000000000..6fed635f8 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/pt-BR.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/pt-BR.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/pt.js b/app/javascript/flavours/glitch/locales/pt.js new file mode 100644 index 000000000..0156f55ff --- /dev/null +++ b/app/javascript/flavours/glitch/locales/pt.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/pt.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/ru.js b/app/javascript/flavours/glitch/locales/ru.js new file mode 100644 index 000000000..0e9f1de71 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/ru.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/ru.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/sv.js b/app/javascript/flavours/glitch/locales/sv.js new file mode 100644 index 000000000..b62c353fe --- /dev/null +++ b/app/javascript/flavours/glitch/locales/sv.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/sv.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/th.js b/app/javascript/flavours/glitch/locales/th.js new file mode 100644 index 000000000..e939f8631 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/th.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/th.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/tr.js b/app/javascript/flavours/glitch/locales/tr.js new file mode 100644 index 000000000..c2b740617 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/tr.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/tr.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/uk.js b/app/javascript/flavours/glitch/locales/uk.js new file mode 100644 index 000000000..ab6d9a7dc --- /dev/null +++ b/app/javascript/flavours/glitch/locales/uk.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/uk.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/zh-CN.js b/app/javascript/flavours/glitch/locales/zh-CN.js new file mode 100644 index 000000000..944588e02 --- /dev/null +++ b/app/javascript/flavours/glitch/locales/zh-CN.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/zh-CN.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/zh-HK.js b/app/javascript/flavours/glitch/locales/zh-HK.js new file mode 100644 index 000000000..b71c81f2b --- /dev/null +++ b/app/javascript/flavours/glitch/locales/zh-HK.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/zh-HK.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/locales/zh-TW.js b/app/javascript/flavours/glitch/locales/zh-TW.js new file mode 100644 index 000000000..de2b7769c --- /dev/null +++ b/app/javascript/flavours/glitch/locales/zh-TW.js @@ -0,0 +1,7 @@ +import inherited from 'mastodon/locales/zh-TW.json'; + +const messages = { + // No translations available. +}; + +export default Object.assign({}, inherited, messages); diff --git a/app/javascript/flavours/glitch/theme.yml b/app/javascript/flavours/glitch/theme.yml index fe09fa105..9437e2c04 100644 --- a/app/javascript/flavours/glitch/theme.yml +++ b/app/javascript/flavours/glitch/theme.yml @@ -20,6 +20,12 @@ pack: settings: share: packs/share.js +# (OPTIONAL) The directory which contains localization files for +# the flavour, relative to this directory. The contents of this +# directory must be `.js` or `.json` files whose names correspond to +# language tags and whose default exports are a messages object. +locales: locales + # (OPTIONAL) The directory which contains the pack files. # Defaults to the theme directory (`app/javascript/themes/[theme]`), # which should be sufficient for like 99% of use-cases lol. diff --git a/app/javascript/flavours/vanilla/theme.yml b/app/javascript/flavours/vanilla/theme.yml index 67fd9723e..491ea173b 100644 --- a/app/javascript/flavours/vanilla/theme.yml +++ b/app/javascript/flavours/vanilla/theme.yml @@ -20,13 +20,17 @@ pack: settings: share: share.js +# (OPTIONAL) The directory which contains localization files for +# the flavour, relative to this directory. +locales: ../../mastodon/locales + # (OPTIONAL) The directory which contains the pack files. -# Defaults to the theme directory (`app/javascript/themes/[theme]`), -# but in the case of the vanilla Mastodon theme the pack files are +# Defaults to this directory (`app/javascript/flavour/[flavour]`), +# but in the case of the vanilla Mastodon flavour the pack files are # somewhere else. pack_directory: app/javascript/packs -# (OPTIONAL) By default the theme will fallback to the default theme +# (OPTIONAL) By default the theme will fallback to the default flavour # if a particular pack is not provided. You can specify different # fallbacks here, or disable fallback behaviours altogether by # specifying a `null` value. diff --git a/app/javascript/glitch/locales/en.json b/app/javascript/glitch/locales/en.json deleted file mode 100644 index 0276cb837..000000000 --- a/app/javascript/glitch/locales/en.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "getting_started.open_source_notice": "Glitchsoc is free open source software forked from {Mastodon}. You can contribute or report issues on GitHub at {github}.", - "layout.auto": "Auto", - "layout.current_is": "Your current layout is:", - "layout.desktop": "Desktop", - "layout.mobile": "Mobile", - "navigation_bar.app_settings": "App settings", - "getting_started.onboarding": "Show me around", - "onboarding.page_one.federation": "{domain} is an 'instance' of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.", - "onboarding.page_one.welcome": "Welcome to {domain}!", - "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}, and is compatible with any Mastodon instance or app. Glitchsoc is entirely free and open-source. You can report bugs, request features, or contribute to the code on {github}.", - "settings.auto_collapse": "Automatic collapsing", - "settings.auto_collapse_all": "Everything", - "settings.auto_collapse_lengthy": "Lengthy toots", - "settings.auto_collapse_media": "Toots with media", - "settings.auto_collapse_notifications": "Notifications", - "settings.auto_collapse_reblogs": "Boosts", - "settings.auto_collapse_replies": "Replies", - "settings.close": "Close", - "settings.collapsed_statuses": "Collapsed toots", - "settings.enable_collapsed": "Enable collapsed toots", - "settings.general": "General", - "settings.image_backgrounds": "Image backgrounds", - "settings.image_backgrounds_media": "Preview collapsed toot media", - "settings.image_backgrounds_users": "Give collapsed toots an image background", - "settings.media": "Media", - "settings.media_letterbox": "Letterbox media", - "settings.media_fullwidth": "Full-width media previews", - "settings.preferences": "User preferences", - "settings.wide_view": "Wide view (Desktop mode only)", - "settings.navbar_under": "Navbar at the bottom (Mobile only)", - "status.collapse": "Collapse", - "status.uncollapse": "Uncollapse", - - "home.column_settings.show_direct": "Show DMs", - - "notification.markForDeletion": "Mark for deletion", - "notifications.clear": "Clear all my notifications", - "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected notifications?", - "notifications.marked_clear": "Clear selected notifications", - - "notification_purge.btn_all": "Select\nall", - "notification_purge.btn_none": "Select\nnone", - "notification_purge.btn_invert": "Invert\nselection", - "notification_purge.btn_apply": "Clear\nselected" -} diff --git a/app/javascript/glitch/locales/pl.json b/app/javascript/glitch/locales/pl.json deleted file mode 100644 index 1481b6a2a..000000000 --- a/app/javascript/glitch/locales/pl.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "getting_started.open_source_notice": "Glitchsoc jest wolnym i otwartoźródłowym forkiem oprogramowania {Mastodon}. Możesz współtworzyć projekt lub zgłaszać błędy na GitHubie pod adresem {github}.", - "layout.auto": "Automatyczny", - "layout.current_is": "Twój obecny układ to:", - "layout.desktop": "Desktopowy", - "layout.mobile": "Mobilny", - "navigation_bar.app_settings": "Ustawienia aplikacji", - "getting_started.onboarding": "Rozejrzyj się", - "onboarding.page_one.federation": "{domain} jest 'instancją' Mastodona. Mastodon to sieć działających niezależnie serwerów tworzących jedną sieć społecznościową. Te serwery nazywane są instancjami.", - "onboarding.page_one.welcome": "Witamy na {domain}!", - "onboarding.page_six.github": "{domain} jest oparty na Glitchsoc. Glitchsoc jest {forkiem} {Mastodon}a kompatybilnym z każdym klientem i aplikacją Mastodona. Glitchsoc jest całkowicie wolnym i otwartoźródłowym oprogramowaniem. Możesz zgłaszać błędy i sugestie funkcji oraz współtworzyć projekt na {github}.", - "settings.auto_collapse": "Automatyczne zwijanie", - "settings.auto_collapse_all": "Wszystko", - "settings.auto_collapse_lengthy": "Długie wpisy", - "settings.auto_collapse_media": "Wpisy z zawartością multimedialną", - "settings.auto_collapse_notifications": "Powiadomienia", - "settings.auto_collapse_reblogs": "Podbicia", - "settings.auto_collapse_replies": "Odpowiedzi", - "settings.close": "Zamknij", - "settings.collapsed_statuses": "Zwijanie wpisów", - "settings.enable_collapsed": "Włącz zwijanie wpisów", - "settings.general": "Ogólne", - "settings.image_backgrounds": "Obrazy w tle", - "settings.image_backgrounds_media": "Wyświetlaj zawartość multimedialną zwiniętych wpisów", - "settings.image_backgrounds_users": "Nadaj tło zwiniętym wpisom", - "settings.media": "Zawartość multimedialna", - "settings.media_letterbox": "Letterbox media", - "settings.media_fullwidth": "Podgląd zawartości multimedialnej o pełnej szerokości", - "settings.preferences": "Preferencje użyytkownika", - "settings.wide_view": "Szeroki widok (tylko w trybie desktopowym)", - "settings.navbar_under": "Pasek nawigacji na dole (tylko w trybie mobilnym)", - "status.collapse": "Zwiń", - "status.uncollapse": "Rozwiń", - - "notification.markForDeletion": "Oznacz do usunięcia", - "notifications.clear": "Wyczyść wszystkie powiadomienia", - "notifications.marked_clear_confirmation": "Czy na pewno chcesz bezpowrtonie usunąć wszystkie powiadomienia?", - "notifications.marked_clear": "Usuń zaznaczone powiadomienia", - - "notification_purge.btn_all": "Zaznacz\nwszystkie", - "notification_purge.btn_none": "Odznacz\nwszystkie", - "notification_purge.btn_invert": "Odwróć\nzaznaczenie", - "notification_purge.btn_apply": "Usuń\nzaznaczone" -} diff --git a/app/javascript/locales/locale-data/README.md b/app/javascript/locales/locale-data/README.md new file mode 100644 index 000000000..83368fae7 --- /dev/null +++ b/app/javascript/locales/locale-data/README.md @@ -0,0 +1,221 @@ +# Custom Locale Data + +This folder is used to store custom locale data. These custom locale data are +not yet provided by [Unicode Common Locale Data Repository](http://cldr.unicode.org/development/new-cldr-developers) +and hence not provided in [react-intl/locale-data/*](https://github.com/yahoo/react-intl). + +The locale data should support [Locale Data APIs](https://github.com/yahoo/react-intl/wiki/API#locale-data-apis) +of the react-intl library. + +It is recommended to start your custom locale data from this sample English +locale data ([*](#plural-rules)): + +```javascript +/*eslint eqeqeq: "off"*/ +/*eslint no-nested-ternary: "off"*/ + +export default [ + { + locale: "en", + pluralRuleFunction: function(e, a) { + var n = String(e).split("."), + l = !n[1], + o = Number(n[0]) == e, + t = o && n[0].slice(-1), + r = o && n[0].slice(-2); + return a ? 1 == t && 11 != r ? "one" : 2 == t && 12 != r ? "two" : 3 == t && 13 != r ? "few" : "other" : 1 == e && l ? "one" : "other" + }, + fields: { + year: { + displayName: "year", + relative: { + 0: "this year", + 1: "next year", + "-1": "last year" + }, + relativeTime: { + future: { + one: "in {0} year", + other: "in {0} years" + }, + past: { + one: "{0} year ago", + other: "{0} years ago" + } + } + }, + month: { + displayName: "month", + relative: { + 0: "this month", + 1: "next month", + "-1": "last month" + }, + relativeTime: { + future: { + one: "in {0} month", + other: "in {0} months" + }, + past: { + one: "{0} month ago", + other: "{0} months ago" + } + } + }, + day: { + displayName: "day", + relative: { + 0: "today", + 1: "tomorrow", + "-1": "yesterday" + }, + relativeTime: { + future: { + one: "in {0} day", + other: "in {0} days" + }, + past: { + one: "{0} day ago", + other: "{0} days ago" + } + } + }, + hour: { + displayName: "hour", + relativeTime: { + future: { + one: "in {0} hour", + other: "in {0} hours" + }, + past: { + one: "{0} hour ago", + other: "{0} hours ago" + } + } + }, + minute: { + displayName: "minute", + relativeTime: { + future: { + one: "in {0} minute", + other: "in {0} minutes" + }, + past: { + one: "{0} minute ago", + other: "{0} minutes ago" + } + } + }, + second: { + displayName: "second", + relative: { + 0: "now" + }, + relativeTime: { + future: { + one: "in {0} second", + other: "in {0} seconds" + }, + past: { + one: "{0} second ago", + other: "{0} seconds ago" + } + } + } + } + } +] + +``` + +## Notes + +### Plural Rules + +The function `pluralRuleFunction()` should return the key to proper string of +a plural form(s). The purpose of the function is to provide key of translate +strings of correct plural form according. The different forms are described in +[CLDR's Plural Rules][cldr-plural-rules], + +[cldr-plural-rules]: http://cldr.unicode.org/index/cldr-spec/plural-rules + +#### Quick Overview on CLDR Rules + +Let's take English as an example. + +When you describe a number, you can be either describe it as: +* Cardinals: 1st, 2nd, 3rd ... 11th, 12th ... 21st, 22nd, 23nd .... +* Ordinals: 1, 2, 3 ... + +In any of these cases, the nouns will reflect the number with singular or plural +form. For example: +* in 0 days +* in 1 day +* in 2 days + +The `pluralRuleFunction` receives 2 parameters: +* `e`: a string representation of the number. Such as, "`1`", "`2`", "`2.1`". +* `a`: `true` if this is "cardinal" type of description. `false` for ordinal and other case. + +#### How you should write `pluralRuleFunction` + +The first rule to write pluralRuleFunction is never translate the output string +into your language. [Plural Rules][cldr-plural-rules] specified you should use +these as the return values: + + * "`zero`" + * "`one`" (singular) + * "`two`" (dual) + * "`few`" (paucal) + * "`many`" (also used for fractions if they have a separate class) + * "`other`" (required—general plural form—also used if the language only has a single form) + +Again, we'll use English as the example here. + +Let's read the `return` statement in the pluralRuleFunction above: +```javascript + return a ? 1 == t && 11 != r ? "one" : 2 == t && 12 != r ? "two" : 3 == t && 13 != r ? "few" : "other" : 1 == e && l ? "one" : "other" +``` + +This nested ternary is hard to read. It basically means: +```javascript +// e: the number variable to examine +// a: "true" if cardinals +// l: "true" if the variable e has nothin after decimal mark (e.g. "1.0" would be false) +// o: "true" if the variable e is an integer +// t: the "ones" of the number. e.g. "3" for number "9123" +// r: the "ones" and "tens" of the number. e.g. "23" for number "9123" +if (a == true) { + if (t == 1 && r != 11) { + return "one"; // i.e. 1st, 21st, 101st, 121st ... + } else if (t == 2 && r != 12) { + return "two"; // i.e. 2nd, 22nd, 102nd, 122nd ... + } else if (t == 3 && r != 13) { + return "few"; // i.e. 3rd, 23rd, 103rd, 123rd ... + } else { + return "other"; // i.e. 4th, 11th, 12th, 24th ... + } +} else { + if (e == 1 && l) { + return "one"; // i.e. 1 day + } else { + return "other"; // i.e. 0 days, 2 days, 3 days + } +} +``` + +If your language, like French, do not have complicated cardinal rules, you may +use the French's version of it: +```javascript +function (e, a) { + return a ? 1 == e ? "one" : "other" : e >= 0 && e < 2 ? "one" : "other"; +} +``` + +If your language, like Chinese, do not have any pluralization rule at all you +may use the Chinese's version of it: +```javascript +function (e, a) { + return "other"; +} +``` diff --git a/app/javascript/locales/locale-data/oc.js b/app/javascript/locales/locale-data/oc.js new file mode 100644 index 000000000..c4b56350b --- /dev/null +++ b/app/javascript/locales/locale-data/oc.js @@ -0,0 +1,108 @@ +/*eslint eqeqeq: "off"*/ +/*eslint no-nested-ternary: "off"*/ +/*eslint quotes: "off"*/ + +export default [{ + locale: "oc", + pluralRuleFunction: function (e, a) { + return a ? 1 == e ? "one" : "other" : e >= 0 && e < 2 ? "one" : "other"; + }, + fields: { + year: { + displayName: "an", + relative: { + 0: "ongan", + 1: "l'an que ven", + "-1": "l'an passat", + }, + relativeTime: { + future: { + one: "dins {0} an", + other: "dins {0} ans", + }, + past: { + one: "fa {0} an", + other: "fa {0} ans", + }, + }, + }, + month: { + displayName: "mes", + relative: { + 0: "aqueste mes", + 1: "lo mes que ven", + "-1": "lo mes passat", + }, + relativeTime: { + future: { + one: "dins {0} mes", + other: "dins {0} meses", + }, + past: { + one: "fa {0} mes", + other: "fa {0} meses", + }, + }, + }, + day: { + displayName: "jorn", + relative: { + 0: "uèi", + 1: "deman", + "-1": "ièr", + }, + relativeTime: { + future: { + one: "dins {0} jorn", + other: "dins {0} jorns", + }, + past: { + one: "fa {0} jorn", + other: "fa {0} jorns", + }, + }, + }, + hour: { + displayName: "ora", + relativeTime: { + future: { + one: "dins {0} ora", + other: "dins {0} oras", + }, + past: { + one: "fa {0} ora", + other: "fa {0} oras", + }, + }, + }, + minute: { + displayName: "minuta", + relativeTime: { + future: { + one: "dins {0} minuta", + other: "dins {0} minutas", + }, + past: { + one: "fa {0} minuta", + other: "fa {0} minutas", + }, + }, + }, + second: { + displayName: "segonda", + relative: { + 0: "ara", + }, + relativeTime: { + future: { + one: "dins {0} segonda", + other: "dins {0} segondas", + }, + past: { + one: "fa {0} segonda", + other: "fa {0} segondas", + }, + }, + }, + }, +}]; diff --git a/app/javascript/mastodon/locales/locale-data/README.md b/app/javascript/mastodon/locales/locale-data/README.md deleted file mode 100644 index 83368fae7..000000000 --- a/app/javascript/mastodon/locales/locale-data/README.md +++ /dev/null @@ -1,221 +0,0 @@ -# Custom Locale Data - -This folder is used to store custom locale data. These custom locale data are -not yet provided by [Unicode Common Locale Data Repository](http://cldr.unicode.org/development/new-cldr-developers) -and hence not provided in [react-intl/locale-data/*](https://github.com/yahoo/react-intl). - -The locale data should support [Locale Data APIs](https://github.com/yahoo/react-intl/wiki/API#locale-data-apis) -of the react-intl library. - -It is recommended to start your custom locale data from this sample English -locale data ([*](#plural-rules)): - -```javascript -/*eslint eqeqeq: "off"*/ -/*eslint no-nested-ternary: "off"*/ - -export default [ - { - locale: "en", - pluralRuleFunction: function(e, a) { - var n = String(e).split("."), - l = !n[1], - o = Number(n[0]) == e, - t = o && n[0].slice(-1), - r = o && n[0].slice(-2); - return a ? 1 == t && 11 != r ? "one" : 2 == t && 12 != r ? "two" : 3 == t && 13 != r ? "few" : "other" : 1 == e && l ? "one" : "other" - }, - fields: { - year: { - displayName: "year", - relative: { - 0: "this year", - 1: "next year", - "-1": "last year" - }, - relativeTime: { - future: { - one: "in {0} year", - other: "in {0} years" - }, - past: { - one: "{0} year ago", - other: "{0} years ago" - } - } - }, - month: { - displayName: "month", - relative: { - 0: "this month", - 1: "next month", - "-1": "last month" - }, - relativeTime: { - future: { - one: "in {0} month", - other: "in {0} months" - }, - past: { - one: "{0} month ago", - other: "{0} months ago" - } - } - }, - day: { - displayName: "day", - relative: { - 0: "today", - 1: "tomorrow", - "-1": "yesterday" - }, - relativeTime: { - future: { - one: "in {0} day", - other: "in {0} days" - }, - past: { - one: "{0} day ago", - other: "{0} days ago" - } - } - }, - hour: { - displayName: "hour", - relativeTime: { - future: { - one: "in {0} hour", - other: "in {0} hours" - }, - past: { - one: "{0} hour ago", - other: "{0} hours ago" - } - } - }, - minute: { - displayName: "minute", - relativeTime: { - future: { - one: "in {0} minute", - other: "in {0} minutes" - }, - past: { - one: "{0} minute ago", - other: "{0} minutes ago" - } - } - }, - second: { - displayName: "second", - relative: { - 0: "now" - }, - relativeTime: { - future: { - one: "in {0} second", - other: "in {0} seconds" - }, - past: { - one: "{0} second ago", - other: "{0} seconds ago" - } - } - } - } - } -] - -``` - -## Notes - -### Plural Rules - -The function `pluralRuleFunction()` should return the key to proper string of -a plural form(s). The purpose of the function is to provide key of translate -strings of correct plural form according. The different forms are described in -[CLDR's Plural Rules][cldr-plural-rules], - -[cldr-plural-rules]: http://cldr.unicode.org/index/cldr-spec/plural-rules - -#### Quick Overview on CLDR Rules - -Let's take English as an example. - -When you describe a number, you can be either describe it as: -* Cardinals: 1st, 2nd, 3rd ... 11th, 12th ... 21st, 22nd, 23nd .... -* Ordinals: 1, 2, 3 ... - -In any of these cases, the nouns will reflect the number with singular or plural -form. For example: -* in 0 days -* in 1 day -* in 2 days - -The `pluralRuleFunction` receives 2 parameters: -* `e`: a string representation of the number. Such as, "`1`", "`2`", "`2.1`". -* `a`: `true` if this is "cardinal" type of description. `false` for ordinal and other case. - -#### How you should write `pluralRuleFunction` - -The first rule to write pluralRuleFunction is never translate the output string -into your language. [Plural Rules][cldr-plural-rules] specified you should use -these as the return values: - - * "`zero`" - * "`one`" (singular) - * "`two`" (dual) - * "`few`" (paucal) - * "`many`" (also used for fractions if they have a separate class) - * "`other`" (required—general plural form—also used if the language only has a single form) - -Again, we'll use English as the example here. - -Let's read the `return` statement in the pluralRuleFunction above: -```javascript - return a ? 1 == t && 11 != r ? "one" : 2 == t && 12 != r ? "two" : 3 == t && 13 != r ? "few" : "other" : 1 == e && l ? "one" : "other" -``` - -This nested ternary is hard to read. It basically means: -```javascript -// e: the number variable to examine -// a: "true" if cardinals -// l: "true" if the variable e has nothin after decimal mark (e.g. "1.0" would be false) -// o: "true" if the variable e is an integer -// t: the "ones" of the number. e.g. "3" for number "9123" -// r: the "ones" and "tens" of the number. e.g. "23" for number "9123" -if (a == true) { - if (t == 1 && r != 11) { - return "one"; // i.e. 1st, 21st, 101st, 121st ... - } else if (t == 2 && r != 12) { - return "two"; // i.e. 2nd, 22nd, 102nd, 122nd ... - } else if (t == 3 && r != 13) { - return "few"; // i.e. 3rd, 23rd, 103rd, 123rd ... - } else { - return "other"; // i.e. 4th, 11th, 12th, 24th ... - } -} else { - if (e == 1 && l) { - return "one"; // i.e. 1 day - } else { - return "other"; // i.e. 0 days, 2 days, 3 days - } -} -``` - -If your language, like French, do not have complicated cardinal rules, you may -use the French's version of it: -```javascript -function (e, a) { - return a ? 1 == e ? "one" : "other" : e >= 0 && e < 2 ? "one" : "other"; -} -``` - -If your language, like Chinese, do not have any pluralization rule at all you -may use the Chinese's version of it: -```javascript -function (e, a) { - return "other"; -} -``` diff --git a/app/javascript/mastodon/locales/locale-data/oc.js b/app/javascript/mastodon/locales/locale-data/oc.js deleted file mode 100644 index c4b56350b..000000000 --- a/app/javascript/mastodon/locales/locale-data/oc.js +++ /dev/null @@ -1,108 +0,0 @@ -/*eslint eqeqeq: "off"*/ -/*eslint no-nested-ternary: "off"*/ -/*eslint quotes: "off"*/ - -export default [{ - locale: "oc", - pluralRuleFunction: function (e, a) { - return a ? 1 == e ? "one" : "other" : e >= 0 && e < 2 ? "one" : "other"; - }, - fields: { - year: { - displayName: "an", - relative: { - 0: "ongan", - 1: "l'an que ven", - "-1": "l'an passat", - }, - relativeTime: { - future: { - one: "dins {0} an", - other: "dins {0} ans", - }, - past: { - one: "fa {0} an", - other: "fa {0} ans", - }, - }, - }, - month: { - displayName: "mes", - relative: { - 0: "aqueste mes", - 1: "lo mes que ven", - "-1": "lo mes passat", - }, - relativeTime: { - future: { - one: "dins {0} mes", - other: "dins {0} meses", - }, - past: { - one: "fa {0} mes", - other: "fa {0} meses", - }, - }, - }, - day: { - displayName: "jorn", - relative: { - 0: "uèi", - 1: "deman", - "-1": "ièr", - }, - relativeTime: { - future: { - one: "dins {0} jorn", - other: "dins {0} jorns", - }, - past: { - one: "fa {0} jorn", - other: "fa {0} jorns", - }, - }, - }, - hour: { - displayName: "ora", - relativeTime: { - future: { - one: "dins {0} ora", - other: "dins {0} oras", - }, - past: { - one: "fa {0} ora", - other: "fa {0} oras", - }, - }, - }, - minute: { - displayName: "minuta", - relativeTime: { - future: { - one: "dins {0} minuta", - other: "dins {0} minutas", - }, - past: { - one: "fa {0} minuta", - other: "fa {0} minutas", - }, - }, - }, - second: { - displayName: "segonda", - relative: { - 0: "ara", - }, - relativeTime: { - future: { - one: "dins {0} segonda", - other: "dins {0} segondas", - }, - past: { - one: "fa {0} segonda", - other: "fa {0} segondas", - }, - }, - }, - }, -}]; diff --git a/app/lib/themes.rb b/app/lib/themes.rb index 863326e2d..49e9ebbc3 100644 --- a/app/lib/themes.rb +++ b/app/lib/themes.rb @@ -15,6 +15,14 @@ class Themes Dir.glob(Rails.root.join('app', 'javascript', 'flavours', '*', 'theme.yml')) do |path| data = YAML.load_file(path) name = File.basename(File.dirname(path)) + if data['locales'] + locales = [] + Dir.glob(File.join(File.dirname(path), data['locales'], '*.{js,json}')) do |locale| + localeName = File.basename(locale, File.extname(locale)) + locales.push(localeName) unless localeName.match(/defaultMessages|whitelist|index/) + end + data['locales'] = locales + end if data['pack'] data['name'] = name data['skin'] = { 'default' => [] } diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 99ae7d90d..4d32c5035 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -19,7 +19,10 @@ = title = javascript_pack_tag "locales", integrity: true, crossorigin: 'anonymous' - = javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous' + - if @theme[:supported_locales].include? I18n.locale.to_s + = javascript_pack_tag "locales/#{@theme[:flavour]}/#{I18n.locale}", integrity: true, crossorigin: 'anonymous' + - elsif @theme[:supported_locales].include? 'en' + = javascript_pack_tag "locales/#{@theme[:flavour]}/en", integrity: true, crossorigin: 'anonymous' = csrf_meta_tags = yield :header_tags diff --git a/config/webpack/configuration.js b/config/webpack/configuration.js index 9cdd6f934..852185eb9 100644 --- a/config/webpack/configuration.js +++ b/config/webpack/configuration.js @@ -30,6 +30,9 @@ for (let i = 0; i < flavourFiles.length; i++) { if (!data.pack_directory) { data.pack_directory = dirname(flavourFile); } + if (data.locales) { + data.locales = join(dirname(flavourFile), data.locales); + } if (data.pack && typeof data.pack === 'object') { flavours[data.name] = data; } @@ -45,7 +48,7 @@ for (let i = 0; i < skinFiles.length; i++) { const data = flavours[name].skin; if (lstatSync(skinFile).isDirectory()) { data[skin] = {}; - const skinPacks = glob.sync(resolve(skinFile, '*.{css,scss}')); + const skinPacks = glob.sync(join(skinFile, '*.{css,scss}')); for (let j = 0; j < skinPacks.length; j++) { const pack = skinPacks[i]; data[skin][basename(pack, extname(pack))] = pack; diff --git a/config/webpack/generateLocalePacks.js b/config/webpack/generateLocalePacks.js index a943589f7..09fba4a18 100644 --- a/config/webpack/generateLocalePacks.js +++ b/config/webpack/generateLocalePacks.js @@ -1,70 +1,66 @@ +// A message from upstream: +// ======================== // To avoid adding a lot of boilerplate, locale packs are // automatically generated here. These are written into the tmp/ // directory and then used to generate locale_en.js, locale_fr.js, etc. -const fs = require('fs'); -const path = require('path'); +// Glitch note: +// ============ +// This code has been entirely rewritten to support glitch flavours. +// However, the underlying process is exactly the same. + +const { existsSync, readdirSync, writeFileSync } = require('fs'); +const { join, resolve } = require('path'); const rimraf = require('rimraf'); const mkdirp = require('mkdirp'); +const { flavours } = require('./configuration.js'); -const localesJsonPath = path.join(__dirname, '../../app/javascript/mastodon/locales'); -const locales = fs.readdirSync(localesJsonPath).filter(filename => { - return /\.json$/.test(filename) && - !/defaultMessages/.test(filename) && - !/whitelist/.test(filename); -}).map(filename => filename.replace(/\.json$/, '')); - -const outPath = path.join(__dirname, '../../tmp/packs'); - -rimraf.sync(outPath); -mkdirp.sync(outPath); - -const outPaths = []; - -locales.forEach(locale => { - const localePath = path.join(outPath, `locale_${locale}.js`); - const baseLocale = locale.split('-')[0]; // e.g. 'zh-TW' -> 'zh' - const localeDataPath = [ - // first try react-intl - `../../node_modules/react-intl/locale-data/${baseLocale}.js`, - // then check locales/locale-data - `../../app/javascript/mastodon/locales/locale-data/${baseLocale}.js`, - // fall back to English (this is what react-intl does anyway) - '../../node_modules/react-intl/locale-data/en.js', - ].filter(filename => fs.existsSync(path.join(outPath, filename))) - .map(filename => filename.replace(/..\/..\/node_modules\//, ''))[0]; - - let glitchInject = ` -const mergedMessages = messages; -`; - - const glitchPath = `../../app/javascript/glitch/locales/${locale}.json`; - if (fs.existsSync(path.join(outPath, glitchPath))) { - glitchInject = ` -import glitchMessages from ${JSON.stringify(glitchPath)}; - -let mergedMessages = messages; -Object.keys(glitchMessages).forEach(function (key) { - mergedMessages[key] = glitchMessages[key]; -}); - -`; +module.exports = Object.keys(flavours).reduce(function (map, entry) { + const flavour = flavours[entry]; + if (!flavour.locales) { + return map; } + const locales = readdirSync(flavour.locales).filter( + filename => /\.js(?:on)?$/.test(filename) && !/defaultMessages|whitelist|index/.test(filename) + ); + const outPath = resolve('tmp', 'locales', entry); + + rimraf.sync(outPath); + mkdirp.sync(outPath); - const localeContent = `// -// locale_${locale}.js + locales.forEach(function (locale) { + const localeName = locale.replace(/\.js(?:on)?$/, ''); + const localePath = join(outPath, `${localeName}.js`); + const baseLocale = localeName.split('-')[0]; // e.g. 'zh-TW' -> 'zh' + const localeDataPath = [ + // first try react-intl + `node_modules/react-intl/locale-data/${baseLocale}.js`, + // then check locales/locale-data + `app/javascript/locales/locale-data/${baseLocale}.js`, + // fall back to English (this is what react-intl does anyway) + 'node_modules/react-intl/locale-data/en.js', + ].filter( + filename => existsSync(filename) + ).map( + filename => filename.replace(/(?:node_modules|app\/javascript)\//, '') + )[0]; + const localeContent = `// +// locales/${entry}/${localeName}.js // automatically generated by generateLocalePacks.js // -import messages from '../../app/javascript/mastodon/locales/${locale}.json'; -import localeData from ${JSON.stringify(localeDataPath)}; -import { setLocale } from 'locales'; -${glitchInject} -setLocale({messages: mergedMessages, localeData: localeData}); -`; - fs.writeFileSync(localePath, localeContent, 'utf8'); - outPaths.push(localePath); -}); -module.exports = outPaths; +import messages from '../../../${flavour.locales}/${locale.replace(/\.js$/, '')}'; +import localeData from '${localeDataPath}'; +import { setLocale } from 'locales'; +setLocale({ + localeData, + messages, +}); +`; + writeFileSync(localePath, localeContent, 'utf8'); + map[`locales/${entry}/${localeName}`] = localePath; + }); + return map; +}, {}); diff --git a/config/webpack/shared.js b/config/webpack/shared.js index e4b057ffb..62d96c3a2 100644 --- a/config/webpack/shared.js +++ b/config/webpack/shared.js @@ -7,7 +7,7 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ManifestPlugin = require('webpack-manifest-plugin'); const extname = require('path-complete-extname'); const { env, settings, core, flavours, output, loadersDir } = require('./configuration.js'); -const localePackPaths = require('./generateLocalePacks'); +const localePacks = require('./generateLocalePacks'); function reducePacks (data, into = {}) { if (!data.pack) { @@ -48,11 +48,7 @@ function reducePacks (data, into = {}) { module.exports = { entry: Object.assign( { locales: resolve('app', 'javascript', 'locales') }, - localePackPaths.reduce((map, entry) => { - const localMap = map; - localMap[basename(entry, extname(entry, extname(entry)))] = resolve(entry); - return localMap; - }, {}), + localePacks, reducePacks(core), Object.keys(flavours).reduce((map, entry) => reducePacks(flavours[entry], map), {}) ), -- cgit From dabf66e676c693e7e26a6035e0c6296e6804e776 Mon Sep 17 00:00:00 2001 From: kibigo! Date: Sun, 10 Dec 2017 17:56:17 -0800 Subject: Moved flavour UI into own prefs tab --- app/controllers/settings/flavours_controller.rb | 35 ++++++++++++++++++++++ app/controllers/settings/preferences_controller.rb | 2 -- app/javascript/core/settings.js | 4 --- app/javascript/flavours/glitch/names.yml | 4 ++- app/javascript/flavours/vanilla/names.yml | 4 ++- app/views/settings/flavours/show.html.haml | 16 ++++++++++ app/views/settings/preferences/show.html.haml | 4 --- config/locales/en.yml | 2 ++ config/locales/simple_form.en.yml | 2 -- config/navigation.rb | 6 ++++ config/routes.rb | 4 ++- 11 files changed, 68 insertions(+), 15 deletions(-) create mode 100644 app/controllers/settings/flavours_controller.rb create mode 100644 app/views/settings/flavours/show.html.haml (limited to 'app/controllers') diff --git a/app/controllers/settings/flavours_controller.rb b/app/controllers/settings/flavours_controller.rb new file mode 100644 index 000000000..865d5a479 --- /dev/null +++ b/app/controllers/settings/flavours_controller.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +class Settings::FlavoursController < Settings::BaseController + + def index + redirect_to action: 'show', flavour: current_flavour + end + + def show + unless Themes.instance.flavours.include?(params[:flavour]) or params[:flavour] == current_flavour + redirect_to action: 'show', flavour: current_flavour + end + + @listing = Themes.instance.flavours + @selected = params[:flavour] + end + + def update + user_settings.update(user_settings_params(params[:flavour]).to_h) + redirect_to action: 'show', flavour: params[:flavour] + end + + private + + def user_settings + UserSettingsDecorator.new(current_user) + end + + def user_settings_params(flavour) + params.require(:user).merge({ setting_flavour: flavour }).permit( + :setting_flavour, + :setting_skin + ) + end +end diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index 9177d37da..7cd1abe0c 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -39,8 +39,6 @@ class Settings::PreferencesController < Settings::BaseController :setting_reduce_motion, :setting_system_font_ui, :setting_noindex, - :setting_flavour, - :setting_skin, notification_emails: %i(follow follow_request reblog favourite mention digest), interactions: %i(must_be_follower must_be_following) ) diff --git a/app/javascript/core/settings.js b/app/javascript/core/settings.js index ada5fba2b..c9edcf197 100644 --- a/app/javascript/core/settings.js +++ b/app/javascript/core/settings.js @@ -37,7 +37,3 @@ delegate(document, '#account_header', 'change', ({ target }) => { header.style.backgroundImage = `url(${url})`; }); - -delegate(document, '#user_setting_flavour, #user_setting_skin', 'change', ({ target }) => { - target.form.submit(); -}); diff --git a/app/javascript/flavours/glitch/names.yml b/app/javascript/flavours/glitch/names.yml index b3d579cb2..ef82abed2 100644 --- a/app/javascript/flavours/glitch/names.yml +++ b/app/javascript/flavours/glitch/names.yml @@ -1,6 +1,8 @@ en: flavours: - glitch: Glitch Edition + glitch: + description: The default flavour for GlitchSoc instances. + name: Glitch Edition skins: glitch: default: Default diff --git a/app/javascript/flavours/vanilla/names.yml b/app/javascript/flavours/vanilla/names.yml index 8816fcb3a..94326f6ee 100644 --- a/app/javascript/flavours/vanilla/names.yml +++ b/app/javascript/flavours/vanilla/names.yml @@ -1,6 +1,8 @@ en: flavours: - vanilla: Vanilla Mastodon + vanilla: + description: The theme used by vanilla Mastodon instances. This theme might not support all of the features of GlitchSoc. + name: Vanilla Mastodon skins: vanilla: default: Default diff --git a/app/views/settings/flavours/show.html.haml b/app/views/settings/flavours/show.html.haml new file mode 100644 index 000000000..488fd2d15 --- /dev/null +++ b/app/views/settings/flavours/show.html.haml @@ -0,0 +1,16 @@ +- content_for :page_title do + = t "flavours.#{@selected}.name", default: @selected + += simple_form_for current_user, url: settings_flavour_path(@selected), html: { method: :put } do |f| + = render 'shared/error_messages', object: current_user + + .flavour-description + = t "flavours.#{@selected}.description", default: '' + + %hr/ + + .fields-group + = f.input :setting_skin, collection: Themes.instance.skins_for(@selected), label_method: lambda { |skin| I18n.t("skins.#{@selected}.#{skin}", default: skin) }, wrapper: :with_label, include_blank: false + + .actions + = f.button :button, t('generic.use_this'), type: :submit diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index 45a3b2eb0..d1459d93c 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -26,10 +26,6 @@ %h4= t 'preferences.web' .fields-group - - if Themes.instance.flavours.size > 1 - = f.input :setting_flavour, collection: Themes.instance.flavours, label_method: lambda { |flavour| I18n.t("flavours.#{flavour}", default: flavour) }, wrapper: :with_label, include_blank: false - = f.input :setting_skin, collection: Themes.instance.skins_for(current_flavour), label_method: lambda { |skin| I18n.t("skins.#{current_flavour}.#{skin}", default: skin) }, wrapper: :with_label, include_blank: false - = f.input :setting_unfollow_modal, as: :boolean, wrapper: :with_label = f.input :setting_boost_modal, as: :boolean, wrapper: :with_label = f.input :setting_favourite_modal, as: :boolean, wrapper: :with_label diff --git a/config/locales/en.yml b/config/locales/en.yml index c8acc237a..804f9b199 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -424,6 +424,7 @@ en: changes_saved_msg: Changes successfully saved! powered_by: powered by %{link} save_changes: Save changes + use_this: Use this validation_errors: one: Something isn't quite right yet! Please review the error below other: Something isn't quite right yet! Please review %{count} errors below @@ -587,6 +588,7 @@ en: development: Development edit_profile: Edit profile export: Data export + flavours: Flavours followers: Authorized followers import: Import keyword_mutes: Muted keywords diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 1722eea7b..aa6940e91 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -13,7 +13,6 @@ en: note: one: 1 character left other: %{count} characters left - setting_flavour: Affects how Mastodon looks when you're logged in from any device setting_noindex: Affects your public profile and status pages setting_skin: Reskins the selected Mastodon flavour imports: @@ -47,7 +46,6 @@ en: setting_default_sensitive: Always mark media as sensitive setting_delete_modal: Show confirmation dialog before deleting a toot setting_favourite_modal: Show confirmation dialog before favouriting - setting_flavour: Flavour setting_noindex: Opt-out of search engine indexing setting_reduce_motion: Reduce motion in animations setting_skin: Skin diff --git a/config/navigation.rb b/config/navigation.rb index 3f4c00dfa..b08b1769d 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -17,6 +17,12 @@ SimpleNavigation::Configuration.run do |navigation| settings.item :follower_domains, safe_join([fa_icon('users fw'), t('settings.followers')]), settings_follower_domains_url end + primary.item :flavours, safe_join([fa_icon('paint-brush fw'), t('settings.flavours')]), settings_flavours_url do |flavours| + Themes.instance.flavours.each do |flavour| + flavours.item flavour.to_sym, safe_join([fa_icon('star fw'), t("flavours.#{flavour}.name", default: flavour)]), settings_flavour_url(flavour) + end + end + primary.item :invites, safe_join([fa_icon('user-plus fw'), t('invites.title')]), invites_path, if: proc { Setting.min_invite_role == 'user' } primary.item :development, safe_join([fa_icon('code fw'), t('settings.development')]), settings_applications_url do |development| diff --git a/config/routes.rb b/config/routes.rb index a41e76c2c..75b9c2d58 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -102,6 +102,8 @@ Rails.application.routes.draw do end end + resources :flavours, only: [:index, :show, :update], param: :flavour + resource :delete, only: [:show, :destroy] resource :migration, only: [:show, :update] @@ -240,7 +242,7 @@ Rails.application.routes.draw do resources :media, only: [:create, :update] resources :blocks, only: [:index] resources :mutes, only: [:index] do - collection do + collection do get 'details' end end -- cgit From 204688e803f76acdfbee833c4d1c0c0a9cc07560 Mon Sep 17 00:00:00 2001 From: David Yip Date: Mon, 11 Dec 2017 00:17:30 -0600 Subject: Add missing set_pack def/filter in OAuth::AuthorizedApplicationsController. --- app/controllers/oauth/authorized_applications_controller.rb | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'app/controllers') diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb index 395fbc51b..f95d672ec 100644 --- a/app/controllers/oauth/authorized_applications_controller.rb +++ b/app/controllers/oauth/authorized_applications_controller.rb @@ -5,6 +5,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio before_action :store_current_location before_action :authenticate_resource_owner! + before_action :set_pack include Localized @@ -13,4 +14,8 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio def store_current_location store_location_for(:user, request.url) end + + def set_pack + use_pack 'settings' + end end -- cgit From feed07227ba9feb8def161dc127033016c749ac5 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 11 Dec 2017 15:32:29 +0100 Subject: Apply a 25x rate limit by IP even to authenticated requests (#5948) --- app/controllers/concerns/rate_limit_headers.rb | 4 ++-- config/initializers/rack_attack.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/concerns/rate_limit_headers.rb b/app/controllers/concerns/rate_limit_headers.rb index ac9b58f5d..b79c558d8 100644 --- a/app/controllers/concerns/rate_limit_headers.rb +++ b/app/controllers/concerns/rate_limit_headers.rb @@ -44,8 +44,8 @@ module RateLimitHeaders end def api_throttle_data - request.env['rack.attack.throttle_data']['throttle_authenticated_api'] || - request.env['rack.attack.throttle_data']['throttle_unauthenticated_api'] + most_limited_type, = request.env['rack.attack.throttle_data'].min_by { |_, v| v[:limit] } + request.env['rack.attack.throttle_data'][most_limited_type] end def request_time diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb index 41db76929..b38fb302b 100644 --- a/config/initializers/rack_attack.rb +++ b/config/initializers/rack_attack.rb @@ -49,8 +49,8 @@ class Rack::Attack req.api_request? && req.authenticated_user_id end - throttle('throttle_unauthenticated_api', limit: 300, period: 5.minutes) do |req| - req.ip if req.api_request? && req.unauthenticated? + throttle('throttle_unauthenticated_api', limit: 7_500, period: 5.minutes) do |req| + req.ip if req.api_request? end throttle('protected_paths', limit: 5, period: 5.minutes) do |req| -- cgit From 44992df257e537afe0b91682934d5eeed5575439 Mon Sep 17 00:00:00 2001 From: cwm Date: Mon, 11 Dec 2017 17:54:40 -0600 Subject: load pack --- app/controllers/oauth/authorizations_controller.rb | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'app/controllers') diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb index e9cdf9fa8..987f20c41 100644 --- a/app/controllers/oauth/authorizations_controller.rb +++ b/app/controllers/oauth/authorizations_controller.rb @@ -5,6 +5,7 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController before_action :store_current_location before_action :authenticate_resource_owner! + before_action :set_pack include Localized @@ -13,4 +14,8 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController def store_current_location store_location_for(:user, request.url) end + + def set_pack + use_pack 'settings' + end end -- cgit From 0a52e376484500279edded7121e3b23953d37b1b Mon Sep 17 00:00:00 2001 From: cwm Date: Mon, 11 Dec 2017 18:14:41 -0600 Subject: change pack to 'auth' --- app/controllers/oauth/authorizations_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/controllers') diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb index 987f20c41..eb977510b 100644 --- a/app/controllers/oauth/authorizations_controller.rb +++ b/app/controllers/oauth/authorizations_controller.rb @@ -16,6 +16,6 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController end def set_pack - use_pack 'settings' + use_pack 'auth' end end -- cgit From 1356ed72cd4f595e93a5fa23fd8d7459fc8f81b3 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 12 Dec 2017 03:55:39 +0100 Subject: Fix #5953 - Add GET /api/v1/accounts/:id/lists (#5983) --- .../api/v1/accounts/lists_controller.rb | 20 +++++++++++++++++++ config/routes.rb | 1 + .../api/v1/accounts/lists_controller_spec.rb | 23 ++++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 app/controllers/api/v1/accounts/lists_controller.rb create mode 100644 spec/controllers/api/v1/accounts/lists_controller_spec.rb (limited to 'app/controllers') diff --git a/app/controllers/api/v1/accounts/lists_controller.rb b/app/controllers/api/v1/accounts/lists_controller.rb new file mode 100644 index 000000000..a7ba89ce2 --- /dev/null +++ b/app/controllers/api/v1/accounts/lists_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class Api::V1::Accounts::ListsController < Api::BaseController + before_action -> { doorkeeper_authorize! :read } + before_action :require_user! + before_action :set_account + + respond_to :json + + def index + @lists = @account.lists.where(account: current_account) + render json: @lists, each_serializer: REST::ListSerializer + end + + private + + def set_account + @account = Account.find(params[:account_id]) + end +end diff --git a/config/routes.rb b/config/routes.rb index 6313a355d..467849c03 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -269,6 +269,7 @@ Rails.application.routes.draw do resources :statuses, only: :index, controller: 'accounts/statuses' resources :followers, only: :index, controller: 'accounts/follower_accounts' resources :following, only: :index, controller: 'accounts/following_accounts' + resources :lists, only: :index, controller: 'accounts/lists' member do post :follow diff --git a/spec/controllers/api/v1/accounts/lists_controller_spec.rb b/spec/controllers/api/v1/accounts/lists_controller_spec.rb new file mode 100644 index 000000000..0a372f65b --- /dev/null +++ b/spec/controllers/api/v1/accounts/lists_controller_spec.rb @@ -0,0 +1,23 @@ +require 'rails_helper' + +describe Api::V1::Accounts::ListsController do + render_views + + let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } + let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') } + let(:account) { Fabricate(:account) } + let(:list) { Fabricate(:list, account: user.account) } + + before do + allow(controller).to receive(:doorkeeper_token) { token } + user.account.follow!(account) + list.accounts << account + end + + describe 'GET #index' do + it 'returns http success' do + get :index, params: { account_id: account.id } + expect(response).to have_http_status(:success) + end + end +end -- cgit From 20a6584d2dd9d5ecaa19a45a0c0c5ffec5a100ff Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 13 Dec 2017 12:15:10 +0100 Subject: Clean up admin UI for accounts (#6004) * Add staff filter to admin UI for accounts, remove obsolete columns * Only display OStatus section in admin UI for accounts when OStatus data --- app/controllers/admin/accounts_controller.rb | 3 ++- app/helpers/admin/filter_helper.rb | 2 +- app/models/account_filter.rb | 2 ++ app/views/admin/accounts/_account.html.haml | 17 +++-------------- app/views/admin/accounts/index.html.haml | 9 ++++++--- app/views/admin/accounts/show.html.haml | 3 ++- config/locales/en.yml | 1 + 7 files changed, 17 insertions(+), 20 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index e9a512e70..7428c3f22 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -89,7 +89,8 @@ module Admin :username, :display_name, :email, - :ip + :ip, + :staff ) end end diff --git a/app/helpers/admin/filter_helper.rb b/app/helpers/admin/filter_helper.rb index 9443934b3..7fe3def98 100644 --- a/app/helpers/admin/filter_helper.rb +++ b/app/helpers/admin/filter_helper.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Admin::FilterHelper - ACCOUNT_FILTERS = %i(local remote by_domain silenced suspended recent username display_name email ip).freeze + ACCOUNT_FILTERS = %i(local remote by_domain silenced suspended recent username display_name email ip staff).freeze REPORT_FILTERS = %i(resolved account_id target_account_id).freeze INVITE_FILTER = %i(available expired).freeze diff --git a/app/models/account_filter.rb b/app/models/account_filter.rb index 189872368..dc7a03039 100644 --- a/app/models/account_filter.rb +++ b/app/models/account_filter.rb @@ -45,6 +45,8 @@ class AccountFilter else Account.default_scoped end + when 'staff' + accounts_with_users.merge User.staff else raise "Unknown filter: #{key}" end diff --git a/app/views/admin/accounts/_account.html.haml b/app/views/admin/accounts/_account.html.haml index 5265d77f6..598f6cddd 100644 --- a/app/views/admin/accounts/_account.html.haml +++ b/app/views/admin/accounts/_account.html.haml @@ -4,22 +4,11 @@ %td.domain - unless account.local? = link_to account.domain, admin_accounts_path(by_domain: account.domain) - %td.protocol - - unless account.local? - = account.protocol.humanize - %td.confirmed - - if account.local? - - if account.user_confirmed? - %i.fa.fa-check - - else - %i.fa.fa-times - %td.subscribed + %td - if account.local? - = t('admin.accounts.location.local') - - elsif account.subscribed? - %i.fa.fa-check + = t("admin.accounts.roles.#{account.user&.role}") - else - %i.fa.fa-times + = account.protocol.humanize %td = table_link_to 'circle', t('admin.accounts.web'), web_path("accounts/#{account.id}") = table_link_to 'globe', t('admin.accounts.public'), TagManager.instance.url_for(account) diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml index 27a0682d8..6aa39a80a 100644 --- a/app/views/admin/accounts/index.html.haml +++ b/app/views/admin/accounts/index.html.haml @@ -30,6 +30,11 @@ = filter_link_to t('admin.accounts.moderation.suspended'), {suspended: nil}, {suspended: '1'} - else = filter_link_to t('admin.accounts.moderation.suspended'), suspended: '1' + .filter-subset + %strong= t('admin.accounts.role') + %ul + %li= filter_link_to t('admin.accounts.moderation.all'), staff: nil + %li= filter_link_to t('admin.accounts.roles.staff'), staff: '1' .filter-subset %strong= t('admin.accounts.order.title') %ul @@ -56,9 +61,7 @@ %tr %th= t('admin.accounts.username') %th= t('admin.accounts.domain') - %th= t('admin.accounts.protocol') - %th= t('admin.accounts.confirmed') - %th= fa_icon 'paper-plane-o' + %th %th %tbody = render @accounts diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index ddb1cf15d..5f5d0995c 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -104,7 +104,7 @@ - else = link_to t('admin.accounts.perform_full_suspension'), admin_account_suspension_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button' if can?(:suspend, @account) -- unless @account.local? +- if !@account.local? && @account.hub_url.present? %hr %h3 OStatus @@ -132,6 +132,7 @@ - if @account.subscribed? = link_to t('admin.accounts.unsubscribe'), unsubscribe_admin_account_path(@account.id), method: :post, class: 'button negative' if can?(:unsubscribe, @account) +- if !@account.local? && @account.inbox_url.present? %hr %h3 ActivityPub diff --git a/config/locales/en.yml b/config/locales/en.yml index 44c021bd5..cc22d02ac 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -116,6 +116,7 @@ en: roles: admin: Administrator moderator: Moderator + staff: Staff user: User salmon_url: Salmon URL search: Search -- cgit From ad75ec8b5b1775edaa485e94ddab69f189affde9 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 13 Dec 2017 13:28:31 +0100 Subject: Add filters to admin UI for custom emojis (#6003) --- app/controllers/admin/custom_emojis_controller.rb | 4 +++- app/helpers/admin/filter_helper.rb | 9 +++++---- app/models/custom_emoji_filter.rb | 2 ++ app/views/admin/custom_emojis/_custom_emoji.html.haml | 2 +- app/views/admin/custom_emojis/index.html.haml | 14 ++++++++++++++ config/locales/en.yml | 1 + 6 files changed, 26 insertions(+), 6 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index 3fa2a0b72..ccab03de4 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -92,7 +92,9 @@ module Admin def filter_params params.permit( :local, - :remote + :remote, + :by_domain, + :shortcode ) end end diff --git a/app/helpers/admin/filter_helper.rb b/app/helpers/admin/filter_helper.rb index 7fe3def98..359c43d0e 100644 --- a/app/helpers/admin/filter_helper.rb +++ b/app/helpers/admin/filter_helper.rb @@ -1,11 +1,12 @@ # frozen_string_literal: true module Admin::FilterHelper - ACCOUNT_FILTERS = %i(local remote by_domain silenced suspended recent username display_name email ip staff).freeze - REPORT_FILTERS = %i(resolved account_id target_account_id).freeze - INVITE_FILTER = %i(available expired).freeze + ACCOUNT_FILTERS = %i(local remote by_domain silenced suspended recent 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 - FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER + FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS + INVITE_FILTER + CUSTOM_EMOJI_FILTERS def filter_link_to(text, link_to_params, link_class_params = link_to_params) new_url = filtered_url_for(link_to_params) diff --git a/app/models/custom_emoji_filter.rb b/app/models/custom_emoji_filter.rb index 2d1394a59..2c09ed65c 100644 --- a/app/models/custom_emoji_filter.rb +++ b/app/models/custom_emoji_filter.rb @@ -27,6 +27,8 @@ class CustomEmojiFilter CustomEmoji.remote when 'by_domain' CustomEmoji.where(domain: value) + when 'shortcode' + CustomEmoji.where(shortcode: value) else raise "Unknown filter: #{key}" end diff --git a/app/views/admin/custom_emojis/_custom_emoji.html.haml b/app/views/admin/custom_emojis/_custom_emoji.html.haml index bab34bc8d..f7fd2538c 100644 --- a/app/views/admin/custom_emojis/_custom_emoji.html.haml +++ b/app/views/admin/custom_emojis/_custom_emoji.html.haml @@ -7,7 +7,7 @@ - if custom_emoji.local? = t('admin.accounts.location.local') - else - = custom_emoji.domain + = link_to custom_emoji.domain, admin_custom_emojis_path(by_domain: custom_emoji.domain) %td - if custom_emoji.local? - if custom_emoji.visible_in_picker diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml index 20ffb8529..89ea3a6fe 100644 --- a/app/views/admin/custom_emojis/index.html.haml +++ b/app/views/admin/custom_emojis/index.html.haml @@ -17,6 +17,20 @@ - else = filter_link_to t('admin.accounts.location.remote'), remote: '1', local: nil += form_tag admin_custom_emojis_url, method: 'GET', class: 'simple_form' do + .fields-group + - Admin::FilterHelper::CUSTOM_EMOJI_FILTERS.each do |key| + - if params[key].present? + = hidden_field_tag key, params[key] + + - %i(shortcode by_domain).each do |key| + .input.string.optional + = text_field_tag key, params[key], class: 'string optional', placeholder: I18n.t("admin.custom_emojis.#{key}") + + .actions + %button= t('admin.accounts.search') + = link_to t('admin.accounts.reset'), admin_accounts_path, class: 'button negative' + .table-wrapper %table.table %thead diff --git a/config/locales/en.yml b/config/locales/en.yml index cc22d02ac..325391cfd 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -161,6 +161,7 @@ en: update_status: "%{name} updated status by %{target}" title: Audit log custom_emojis: + by_domain: Domain copied_msg: Successfully created local copy of the emoji copy: Copy copy_failed_msg: Could not make a local copy of that emoji -- cgit From a3b2ea599df58b1d9944acba8b59f9a465f5adbd Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 14 Dec 2017 21:35:30 +0100 Subject: Fix #6022 - Prevent nested migrated accounts, or migrations to self (#6026) --- app/controllers/settings/migrations_controller.rb | 3 ++- app/serializers/rest/account_serializer.rb | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'app/controllers') diff --git a/app/controllers/settings/migrations_controller.rb b/app/controllers/settings/migrations_controller.rb index b18403a7f..bc6436b87 100644 --- a/app/controllers/settings/migrations_controller.rb +++ b/app/controllers/settings/migrations_controller.rb @@ -28,6 +28,7 @@ class Settings::MigrationsController < ApplicationController end def migration_account_changed? - current_account.moved_to_account_id != @migration.account&.id + current_account.moved_to_account_id != @migration.account&.id && + current_account.id != @migration.account&.id end end diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb index bab944c5a..19b746520 100644 --- a/app/serializers/rest/account_serializer.rb +++ b/app/serializers/rest/account_serializer.rb @@ -7,9 +7,7 @@ class REST::AccountSerializer < ActiveModel::Serializer :note, :url, :avatar, :avatar_static, :header, :header_static, :followers_count, :following_count, :statuses_count - has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved? - - delegate :moved?, to: :object + has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested? def id object.id.to_s @@ -38,4 +36,8 @@ class REST::AccountSerializer < ActiveModel::Serializer def header_static full_asset_url(object.header_static_url) end + + def moved_and_not_nested? + object.moved? && object.moved_to_account.moved_to_account_id.nil? + end end -- cgit