commit
1a12fd14d4
@ -1,72 +1,129 @@
|
||||
/**
|
||||
* Note for Contributors:
|
||||
* This file (en.jsx) serve as a template for other languages.
|
||||
* To make other contributors' life easier, please REMEMBER:
|
||||
* 1. to add your new string here; and
|
||||
* 2. to remove old strings that are no longer needed; and
|
||||
* 3. to sort the strings by the key.
|
||||
* Thanks!
|
||||
*/
|
||||
const en = {
|
||||
"column_back_button.label": "Back",
|
||||
"lightbox.close": "Close",
|
||||
"loading_indicator.label": "Loading...",
|
||||
"status.mention": "Mention @{name}",
|
||||
"status.delete": "Delete",
|
||||
"status.reply": "Reply",
|
||||
"status.reblog": "Boost",
|
||||
"status.favourite": "Favourite",
|
||||
"status.reblogged_by": "{name} boosted",
|
||||
"status.sensitive_warning": "Sensitive content",
|
||||
"status.sensitive_toggle": "Click to view",
|
||||
"status.show_more": "Show more",
|
||||
"status.show_less": "Show less",
|
||||
"status.open": "Expand this status",
|
||||
"status.report": "Report @{name}",
|
||||
"video_player.toggle_sound": "Toggle sound",
|
||||
"account.mention": "Mention @{name}",
|
||||
"account.edit_profile": "Edit profile",
|
||||
"account.unblock": "Unblock @{name}",
|
||||
"account.unfollow": "Unfollow",
|
||||
"account.block": "Block @{name}",
|
||||
"account.disclaimer": "This user is from another instance. This number may be larger.",
|
||||
"account.edit_profile": "Edit profile",
|
||||
"account.follow": "Follow",
|
||||
"account.posts": "Posts",
|
||||
"account.follows": "Follows",
|
||||
"account.followers": "Followers",
|
||||
"account.follows_you": "Follows you",
|
||||
"account.follows": "Follows",
|
||||
"account.mention": "Mention @{name}",
|
||||
"account.mute": "Mute @{name}",
|
||||
"account.posts": "Posts",
|
||||
"account.report": "Report @{name}",
|
||||
"account.requested": "Awaiting approval",
|
||||
"getting_started.heading": "Getting started",
|
||||
"getting_started.about_addressing": "You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the search form.",
|
||||
"getting_started.about_shortcuts": "If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.",
|
||||
"getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.",
|
||||
"column.home": "Home",
|
||||
"account.unblock": "Unblock @{name}",
|
||||
"account.unfollow": "Unfollow",
|
||||
"account.unmute": "Unmute @{name}",
|
||||
"boost_modal.combo": "You can press {combo} to skip this next time",
|
||||
"column_back_button.label": "Back",
|
||||
"column.blocks": "Blocked users",
|
||||
"column.community": "Local timeline",
|
||||
"column.public": "Federated timeline",
|
||||
"column.favourites": "Favourites",
|
||||
"column.follow_requests": "Follow requests",
|
||||
"column.home": "Home",
|
||||
"column.notifications": "Notifications",
|
||||
"tabs_bar.compose": "Compose",
|
||||
"tabs_bar.home": "Home",
|
||||
"tabs_bar.mentions": "Mentions",
|
||||
"tabs_bar.public": "Federated timeline",
|
||||
"tabs_bar.notifications": "Notifications",
|
||||
"column.public": "Federated timeline",
|
||||
"compose_form.placeholder": "What is on your mind?",
|
||||
"compose_form.privacy_disclaimer": "Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.",
|
||||
"compose_form.publish": "Toot",
|
||||
"compose_form.sensitive": "Mark media as sensitive",
|
||||
"compose_form.spoiler_placeholder": "Content warning",
|
||||
"compose_form.spoiler": "Hide text behind warning",
|
||||
"compose_form.private": "Mark as private",
|
||||
"compose_form.privacy_disclaimer": "Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.",
|
||||
"compose_form.unlisted": "Do not display on public timelines",
|
||||
"emoji_button.label": "Insert emoji",
|
||||
"empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
|
||||
"empty_column.hashtag": "There is nothing in this hashtag yet.",
|
||||
"empty_column.home.public_timeline": "the public timeline",
|
||||
"empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.",
|
||||
"empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
|
||||
"empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
|
||||
"follow_request.authorize": "Authorize",
|
||||
"follow_request.reject": "Rejec",
|
||||
"getting_started.apps": "Various apps are available",
|
||||
"getting_started.heading": "Getting started",
|
||||
"getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.",
|
||||
"home.column_settings.advanced": "Advanced",
|
||||
"home.column_settings.basic": "Basic",
|
||||
"home.column_settings.filter_regex": "Filter out by regular expressions",
|
||||
"home.column_settings.show_reblogs": "Show boosts",
|
||||
"home.column_settings.show_replies": "Show replies",
|
||||
"home.settings": "Column settings",
|
||||
"lightbox.close": "Close",
|
||||
"loading_indicator.label": "Loading...",
|
||||
"media_gallery.toggle_visible": "Toggle visibility",
|
||||
"missing_indicator.label": "Not found",
|
||||
"navigation_bar.blocks": "Blocked users",
|
||||
"navigation_bar.community_timeline": "Local timeline",
|
||||
"navigation_bar.edit_profile": "Edit profile",
|
||||
"navigation_bar.favourites": "Favourites",
|
||||
"navigation_bar.follow_requests": "Follow requests",
|
||||
"navigation_bar.info": "Extended information",
|
||||
"navigation_bar.logout": "Logout",
|
||||
"navigation_bar.preferences": "Preferences",
|
||||
"navigation_bar.community_timeline": "Local timeline",
|
||||
"navigation_bar.public_timeline": "Federated timeline",
|
||||
"navigation_bar.logout": "Logout",
|
||||
"reply_indicator.cancel": "Cancel",
|
||||
"search.placeholder": "Search",
|
||||
"search.account": "Account",
|
||||
"search.hashtag": "Hashtag",
|
||||
"upload_button.label": "Add media",
|
||||
"upload_form.undo": "Undo",
|
||||
"notification.follow": "{name} followed you",
|
||||
"notification.favourite": "{name} favourited your status",
|
||||
"notification.follow": "{name} followed you",
|
||||
"notification.reblog": "{name} boosted your status",
|
||||
"notification.mention": "{name} mentioned you",
|
||||
"notifications.clear_confirmation": "Are you sure you want to clear all your notifications?",
|
||||
"notifications.clear": "Clear notifications",
|
||||
"notifications.column_settings.alert": "Desktop notifications",
|
||||
"notifications.column_settings.show": "Show in column",
|
||||
"notifications.column_settings.follow": "New followers:",
|
||||
"notifications.column_settings.favourite": "Favourites:",
|
||||
"notifications.column_settings.follow": "New followers:",
|
||||
"notifications.column_settings.mention": "Mentions:",
|
||||
"notifications.column_settings.reblog": "Boosts:",
|
||||
"notifications.column_settings.show": "Show in column",
|
||||
"notifications.column_settings.sound": "Play sound",
|
||||
"notifications.settings": "Column settings",
|
||||
"privacy.change": "Adjust status privacy",
|
||||
"privacy.direct.long": "Post to mentioned users only",
|
||||
"privacy.direct.short": "Direct",
|
||||
"privacy.private.long": "Post to followers only",
|
||||
"privacy.private.short": "Private",
|
||||
"privacy.public.long": "Post to public timelines",
|
||||
"privacy.public.short": "Public",
|
||||
"privacy.unlisted.long": "Do not show in public timelines",
|
||||
"privacy.unlisted.short": "Unlisted",
|
||||
"reply_indicator.cancel": "Cancel",
|
||||
"report.heading": "New report",
|
||||
"report.placeholder": "Additional comments",
|
||||
"report.submit": "Submit",
|
||||
"report.target": "Reporting",
|
||||
"search_results.total": "{count} {count, plural, one {result} other {results}}",
|
||||
"search.placeholder": "Search",
|
||||
"search.status_by": "Status by {name}",
|
||||
"status.delete": "Delete",
|
||||
"status.favourite": "Favourite",
|
||||
"status.load_more": "Load more",
|
||||
"status.media_hidden": "Media hidden",
|
||||
"status.mention": "Mention @{name}",
|
||||
"status.open": "Expand this status",
|
||||
"status.reblog": "Boost",
|
||||
"status.reblogged_by": "{name} boosted",
|
||||
"status.reply": "Reply",
|
||||
"status.report": "Report @{name}",
|
||||
"status.sensitive_toggle": "Click to view",
|
||||
"status.sensitive_warning": "Sensitive content",
|
||||
"status.show_less": "Show less",
|
||||
"status.show_more": "Show more",
|
||||
"tabs_bar.compose": "Compose",
|
||||
"tabs_bar.federated_timeline": "Federated",
|
||||
"tabs_bar.home": "Home",
|
||||
"tabs_bar.local_timeline": "Local",
|
||||
"tabs_bar.notifications": "Notifications",
|
||||
"upload_area.title": "Drag & drop to upload",
|
||||
"upload_button.label": "Add media",
|
||||
"upload_form.undo": "Undo",
|
||||
"upload_progress.label": "Uploading...",
|
||||
"video_player.toggle_sound": "Toggle sound",
|
||||
"video_player.toggle_visible": "Toggle visibility",
|
||||
};
|
||||
|
||||
export default en;
|
||||
|
@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class SilencesController < BaseController
|
||||
before_action :set_account
|
||||
|
||||
def create
|
||||
@account.update(silenced: true)
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
def destroy
|
||||
@account.update(silenced: false)
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class SuspensionsController < BaseController
|
||||
before_action :set_account
|
||||
|
||||
def create
|
||||
Admin::SuspensionWorker.perform_async(@account.id)
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
def destroy
|
||||
@account.update(suspended: false)
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module WellKnown
|
||||
class HostMetaController < ApplicationController
|
||||
def show
|
||||
@webfinger_template = "#{webfinger_url}?resource={uri}"
|
||||
|
||||
respond_to do |format|
|
||||
format.xml { render content_type: 'application/xrd+xml' }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,43 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module WellKnown
|
||||
class WebfingerController < ApplicationController
|
||||
def show
|
||||
@account = Account.find_local!(username_from_resource)
|
||||
@canonical_account_uri = @account.to_webfinger_s
|
||||
@magic_key = pem_to_magic_key(@account.keypair.public_key)
|
||||
|
||||
respond_to do |format|
|
||||
format.xml { render content_type: 'application/xrd+xml' }
|
||||
format.json { render content_type: 'application/jrd+json' }
|
||||
end
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
head 404
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def username_from_resource
|
||||
WebfingerResource.new(resource_param).username
|
||||
end
|
||||
|
||||
def pem_to_magic_key(public_key)
|
||||
modulus, exponent = [public_key.n, public_key.e].map do |component|
|
||||
result = []
|
||||
|
||||
until component.zero?
|
||||
result << [component % 256].pack('C')
|
||||
component >>= 8
|
||||
end
|
||||
|
||||
result.reverse.join
|
||||
end
|
||||
|
||||
(['RSA'] + [modulus, exponent].map { |n| Base64.urlsafe_encode64(n) }).join('.')
|
||||
end
|
||||
|
||||
def resource_param
|
||||
params.require(:resource)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,55 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class XrdController < ApplicationController
|
||||
before_action :set_default_format_xml, only: :host_meta
|
||||
|
||||
def host_meta
|
||||
@webfinger_template = "#{webfinger_url}?resource={uri}"
|
||||
|
||||
respond_to do |format|
|
||||
format.xml { render content_type: 'application/xrd+xml' }
|
||||
end
|
||||
end
|
||||
|
||||
def webfinger
|
||||
@account = Account.find_local!(username_from_resource)
|
||||
@canonical_account_uri = @account.to_webfinger_s
|
||||
@magic_key = pem_to_magic_key(@account.keypair.public_key)
|
||||
|
||||
respond_to do |format|
|
||||
format.xml { render content_type: 'application/xrd+xml' }
|
||||
format.json { render content_type: 'application/jrd+json' }
|
||||
end
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
head 404
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_default_format_xml
|
||||
request.format = 'xml' if request.headers['HTTP_ACCEPT'].nil? && params[:format].nil?
|
||||
end
|
||||
|
||||
def username_from_resource
|
||||
WebfingerResource.new(resource_param).username
|
||||
end
|
||||
|
||||
def pem_to_magic_key(public_key)
|
||||
modulus, exponent = [public_key.n, public_key.e].map do |component|
|
||||
result = []
|
||||
|
||||
until component.zero?
|
||||
result << [component % 256].pack('C')
|
||||
component >>= 8
|
||||
end
|
||||
|
||||
result.reverse.join
|
||||
end
|
||||
|
||||
(['RSA'] + [modulus, exponent].map { |n| Base64.urlsafe_encode64(n) }).join('.')
|
||||
end
|
||||
|
||||
def resource_param
|
||||
params.require(:resource)
|
||||
end
|
||||
end
|
@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AccountFilter
|
||||
attr_reader :params
|
||||
|
||||
def initialize(params)
|
||||
@params = params
|
||||
end
|
||||
|
||||
def results
|
||||
scope = Account.alphabetic
|
||||
params.each do |key, value|
|
||||
scope = scope.merge scope_for(key, value)
|
||||
end
|
||||
scope
|
||||
end
|
||||
|
||||
def scope_for(key, value)
|
||||
case key
|
||||
when /local/
|
||||
Account.local
|
||||
when /remote/
|
||||
Account.remote
|
||||
when /by_domain/
|
||||
Account.where(domain: value)
|
||||
when /silenced/
|
||||
Account.silenced
|
||||
when /recent/
|
||||
Account.recent
|
||||
when /suspended/
|
||||
Account.suspended
|
||||
else
|
||||
raise "Unknown filter: #{key}"
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,24 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::SilencesController do
|
||||
let(:account) { Fabricate(:account) }
|
||||
before do
|
||||
sign_in Fabricate(:user, admin: true), scope: :user
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
it 'redirects to admin accounts page' do
|
||||
post :create, params: { account_id: account.id }
|
||||
|
||||
expect(response).to redirect_to(admin_accounts_path)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
it 'redirects to admin accounts page' do
|
||||
delete :destroy, params: { account_id: account.id }
|
||||
|
||||
expect(response).to redirect_to(admin_accounts_path)
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,24 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::SuspensionsController do
|
||||
let(:account) { Fabricate(:account) }
|
||||
before do
|
||||
sign_in Fabricate(:user, admin: true), scope: :user
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
it 'redirects to admin accounts page' do
|
||||
post :create, params: { account_id: account.id }
|
||||
|
||||
expect(response).to redirect_to(admin_accounts_path)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
it 'redirects to admin accounts page' do
|
||||
delete :destroy, params: { account_id: account.id }
|
||||
|
||||
expect(response).to redirect_to(admin_accounts_path)
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,13 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe WellKnown::HostMetaController, type: :controller do
|
||||
render_views
|
||||
|
||||
describe 'GET #show' do
|
||||
it 'returns http success' do
|
||||
get :show, format: :xml
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,21 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe WellKnown::WebfingerController, type: :controller do
|
||||
render_views
|
||||
|
||||
describe 'GET #show' do
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
|
||||
it 'returns http success when account can be found' do
|
||||
get :show, params: { resource: alice.to_webfinger_s }, format: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
|
||||
it 'returns http not found when account cannot be found' do
|
||||
get :show, params: { resource: 'acct:not@existing.com' }, format: :json
|
||||
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,26 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe XrdController, type: :controller do
|
||||
render_views
|
||||
|
||||
describe 'GET #host_meta' do
|
||||
it 'returns http success' do
|
||||
get :host_meta
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #webfinger' do
|
||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||
|
||||
it 'returns http success when account can be found' do
|
||||
get :webfinger, params: { resource: alice.to_webfinger_s }, format: :json
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
|
||||
it 'returns http not found when account cannot be found' do
|
||||
get :webfinger, params: { resource: 'acct:not@existing.com' }, format: :json
|
||||
expect(response).to have_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,31 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe AccountFilter do
|
||||
describe 'with empty params' do
|
||||
it 'defaults to alphabetic account list' do
|
||||
filter = AccountFilter.new({})
|
||||
|
||||
expect(filter.results).to eq Account.alphabetic
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with invalid params' do
|
||||
it 'raises with key error' do
|
||||
filter = AccountFilter.new(wrong: true)
|
||||
|
||||
expect { filter.results }.to raise_error(/wrong/)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with valid params' do
|
||||
it 'combines filters on Account' do
|
||||
filter = AccountFilter.new(by_domain: 'test.com', silenced: true)
|
||||
|
||||
allow(Account).to receive(:where).and_return(Account.none)
|
||||
allow(Account).to receive(:silenced).and_return(Account.none)
|
||||
filter.results
|
||||
expect(Account).to have_received(:where).with(domain: 'test.com')
|
||||
expect(Account).to have_received(:silenced)
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,12 @@
|
||||
require "rails_helper"
|
||||
|
||||
describe "The host_meta route" do
|
||||
describe "requested without accepts headers" do
|
||||
it "returns an xml response" do
|
||||
get host_meta_url
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.content_type).to eq "application/xrd+xml"
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,15 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'the host-meta route' do
|
||||
it 'routes to correct place with xml format' do
|
||||
expect(get('/.well-known/host-meta')).
|
||||
to route_to('well_known/host_meta#show', format: 'xml')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'the webfinger route' do
|
||||
it 'routes to correct place with json format' do
|
||||
expect(get('/.well-known/webfinger')).
|
||||
to route_to('well_known/webfinger#show', format: 'json')
|
||||
end
|
||||
end
|
Loading…
Reference in new issue