Merge branch 'master' into master

This commit is contained in:
JantsoP 2017-04-04 08:41:46 +02:00 committed by GitHub
commit db4a41cf58
46 changed files with 565 additions and 75 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -43,7 +43,7 @@ const GettingStarted = ({ intl, me }) => {
<div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}> <div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}>
<div className='static-content getting-started'> <div className='static-content getting-started'>
<p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on github at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/mastodon/blob/master/docs/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p> <p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/mastodon/blob/master/docs/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p>
</div> </div>
</div> </div>
</Column> </Column>

View File

@ -25,7 +25,7 @@ const en = {
"getting_started.heading": "Getting started", "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_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.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}.", "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.",
"column.home": "Home", "column.home": "Home",
"column.community": "Local timeline", "column.community": "Local timeline",
"column.public": "Federated timeline", "column.public": "Federated timeline",

View File

@ -9,6 +9,24 @@ class Admin::DomainBlocksController < ApplicationController
@blocks = DomainBlock.paginate(page: params[:page], per_page: 40) @blocks = DomainBlock.paginate(page: params[:page], per_page: 40)
end end
def new
@domain_block = DomainBlock.new
end
def create def create
@domain_block = DomainBlock.new(resource_params)
if @domain_block.save
DomainBlockWorker.perform_async(@domain_block.id)
redirect_to admin_domain_blocks_path, notice: 'Domain block is now being processed'
else
render action: :new
end
end
private
def resource_params
params.require(:domain_block).permit(:domain, :severity)
end end
end end

View File

@ -16,19 +16,19 @@ class Admin::ReportsController < ApplicationController
end end
def resolve def resolve
@report.update(action_taken: true) @report.update(action_taken: true, action_taken_by_account_id: current_account.id)
redirect_to admin_report_path(@report) redirect_to admin_report_path(@report)
end end
def suspend def suspend
Admin::SuspensionWorker.perform_async(@report.target_account.id) Admin::SuspensionWorker.perform_async(@report.target_account.id)
@report.update(action_taken: true) Report.unresolved.where(target_account: @report.target_account).update_all(action_taken: true, action_taken_by_account_id: current_account.id)
redirect_to admin_report_path(@report) redirect_to admin_report_path(@report)
end end
def silence def silence
@report.target_account.update(silenced: true) @report.target_account.update(silenced: true)
@report.update(action_taken: true) Report.unresolved.where(target_account: @report.target_account).update_all(action_taken: true, action_taken_by_account_id: current_account.id)
redirect_to admin_report_path(@report) redirect_to admin_report_path(@report)
end end

View File

@ -4,6 +4,12 @@ class Api::V1::AppsController < ApiController
respond_to :json respond_to :json
def create def create
@app = Doorkeeper::Application.create!(name: params[:client_name], redirect_uri: params[:redirect_uris], scopes: (params[:scopes] || Doorkeeper.configuration.default_scopes), website: params[:website]) @app = Doorkeeper::Application.create!(name: app_params[:client_name], redirect_uri: app_params[:redirect_uris], scopes: (app_params[:scopes] || Doorkeeper.configuration.default_scopes), website: app_params[:website])
end
private
def app_params
params.permit(:client_name, :redirect_uris, :scopes, :website)
end end
end end

View File

@ -7,7 +7,7 @@ class Api::V1::FollowsController < ApiController
respond_to :json respond_to :json
def create def create
raise ActiveRecord::RecordNotFound if params[:uri].blank? raise ActiveRecord::RecordNotFound if follow_params[:uri].blank?
@account = FollowService.new.call(current_user.account, target_uri).try(:target_account) @account = FollowService.new.call(current_user.account, target_uri).try(:target_account)
render action: :show render action: :show
@ -16,6 +16,10 @@ class Api::V1::FollowsController < ApiController
private private
def target_uri def target_uri
params[:uri].strip.gsub(/\A@/, '') follow_params[:uri].strip.gsub(/\A@/, '')
end
def follow_params
params.permit(:uri)
end end
end end

View File

@ -10,10 +10,16 @@ class Api::V1::MediaController < ApiController
respond_to :json respond_to :json
def create def create
@media = MediaAttachment.create!(account: current_user.account, file: params[:file]) @media = MediaAttachment.create!(account: current_user.account, file: media_params[:file])
rescue Paperclip::Errors::NotIdentifiedByImageMagickError rescue Paperclip::Errors::NotIdentifiedByImageMagickError
render json: { error: 'File type of uploaded media could not be verified' }, status: 422 render json: { error: 'File type of uploaded media could not be verified' }, status: 422
rescue Paperclip::Error rescue Paperclip::Error
render json: { error: 'Error processing thumbnail for uploaded media' }, status: 500 render json: { error: 'Error processing thumbnail for uploaded media' }, status: 500
end end
private
def media_params
params.permit(:file)
end
end end

View File

@ -12,13 +12,19 @@ class Api::V1::ReportsController < ApiController
end end
def create def create
status_ids = params[:status_ids].is_a?(Enumerable) ? params[:status_ids] : [params[:status_ids]] status_ids = report_params[:status_ids].is_a?(Enumerable) ? report_params[:status_ids] : [report_params[:status_ids]]
@report = Report.create!(account: current_account, @report = Report.create!(account: current_account,
target_account: Account.find(params[:account_id]), target_account: Account.find(report_params[:account_id]),
status_ids: Status.find(status_ids).pluck(:id), status_ids: Status.find(status_ids).pluck(:id),
comment: params[:comment]) comment: report_params[:comment])
render :show render :show
end end
private
def report_params
params.permit(:account_id, :comment, status_ids: [])
end
end end

View File

@ -62,11 +62,11 @@ class Api::V1::StatusesController < ApiController
end end
def create def create
@status = PostStatusService.new.call(current_user.account, params[:status], params[:in_reply_to_id].blank? ? nil : Status.find(params[:in_reply_to_id]), media_ids: params[:media_ids], @status = PostStatusService.new.call(current_user.account, status_params[:status], status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]), media_ids: status_params[:media_ids],
sensitive: params[:sensitive], sensitive: status_params[:sensitive],
spoiler_text: params[:spoiler_text], spoiler_text: status_params[:spoiler_text],
visibility: params[:visibility], visibility: status_params[:visibility],
application: doorkeeper_token.application) application: doorkeeper_token.application)
render action: :show render action: :show
end end
@ -111,4 +111,8 @@ class Api::V1::StatusesController < ApiController
@status = Status.find(params[:id]) @status = Status.find(params[:id])
raise ActiveRecord::RecordNotFound unless @status.permitted?(current_account) raise ActiveRecord::RecordNotFound unless @status.permitted?(current_account)
end end
def status_params
params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, media_ids: [])
end
end end

View File

@ -39,7 +39,14 @@ class ApplicationController < ActionController::Base
end end
def set_user_activity def set_user_activity
current_user.touch(:current_sign_in_at) if !current_user.nil? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < 24.hours.ago) return unless !current_user.nil? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < 24.hours.ago)
# Mark user as signed-in today
current_user.update_tracked_fields(request)
# If the sign in is after a two week break, we need to regenerate their feed
RegenerationWorker.perform_async(current_user.account_id) if current_user.last_sign_in_at < 14.days.ago
return
end end
def check_suspension def check_suspension

View File

@ -3,6 +3,7 @@
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
skip_before_action :authenticate_resource_owner! skip_before_action :authenticate_resource_owner!
before_action :set_locale
before_action :store_current_location before_action :store_current_location
before_action :authenticate_resource_owner! before_action :authenticate_resource_owner!
@ -11,4 +12,10 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
def store_current_location def store_current_location
store_location_for(:user, request.url) store_location_for(:user, request.url)
end end
def set_locale
I18n.locale = current_user.try(:locale) || I18n.default_locale
rescue I18n::InvalidLocale
I18n.locale = I18n.default_locale
end
end end

View File

@ -4,4 +4,5 @@ module Mastodon
class Error < StandardError; end class Error < StandardError; end
class NotPermittedError < Error; end class NotPermittedError < Error; end
class ValidationError < Error; end class ValidationError < Error; end
class RaceConditionError < Error; end
end end

View File

@ -10,17 +10,9 @@ class Feed
max_id = '+inf' if max_id.blank? max_id = '+inf' if max_id.blank?
since_id = '-inf' if since_id.blank? since_id = '-inf' if since_id.blank?
unhydrated = redis.zrevrangebyscore(key, "(#{max_id}", "(#{since_id}", limit: [0, limit], with_scores: true).map(&:last).map(&:to_i) unhydrated = redis.zrevrangebyscore(key, "(#{max_id}", "(#{since_id}", limit: [0, limit], with_scores: true).map(&:last).map(&:to_i)
status_map = Status.where(id: unhydrated).cache_ids.map { |s| [s.id, s] }.to_h
# If we're after most recent items and none are there, we need to precompute the feed unhydrated.map { |id| status_map[id] }.compact
if unhydrated.empty? && max_id == '+inf' && since_id == '-inf'
RegenerationWorker.perform_async(@account.id, @type)
@statuses = Status.send("as_#{@type}_timeline", @account).cache_ids.paginate_by_max_id(limit, nil, nil)
else
status_map = Status.where(id: unhydrated).cache_ids.map { |s| [s.id, s] }.to_h
@statuses = unhydrated.map { |id| status_map[id] }.compact
end
@statuses
end end
private private

View File

@ -3,6 +3,7 @@
class Report < ApplicationRecord class Report < ApplicationRecord
belongs_to :account belongs_to :account
belongs_to :target_account, class_name: 'Account' belongs_to :target_account, class_name: 'Account'
belongs_to :action_taken_by_account, class_name: 'Account'
scope :unresolved, -> { where(action_taken: false) } scope :unresolved, -> { where(action_taken: false) }
scope :resolved, -> { where(action_taken: true) } scope :resolved, -> { where(action_taken: true) }

View File

@ -188,7 +188,7 @@ class Status < ApplicationRecord
end end
before_validation do before_validation do
text.strip! text&.strip!
spoiler_text&.strip! spoiler_text&.strip!
self.reply = !(in_reply_to_id.nil? && thread.nil?) unless reply self.reply = !(in_reply_to_id.nil? && thread.nil?) unless reply

View File

@ -1,13 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
class BlockDomainService < BaseService class BlockDomainService < BaseService
def call(domain, severity) def call(domain_block)
DomainBlock.where(domain: domain).first_or_create!(domain: domain, severity: severity) if domain_block.silence?
Account.where(domain: domain_block.domain).update_all(silenced: true)
if severity == :silence
Account.where(domain: domain).update_all(silenced: true)
else else
Account.where(domain: domain).find_each do |account| Account.where(domain: domain_block.domain).find_each do |account|
account.subscription(api_subscription_url(account.id)).unsubscribe if account.subscribed? account.subscription(api_subscription_url(account.id)).unsubscribe if account.subscribed?
SuspendAccountService.new.call(account) SuspendAccountService.new.call(account)
end end

View File

@ -4,6 +4,8 @@ class FanOutOnWriteService < BaseService
# Push a status into home and mentions feeds # Push a status into home and mentions feeds
# @param [Status] status # @param [Status] status
def call(status) def call(status)
raise Mastodon::RaceConditionError if status.visibility.nil?
deliver_to_self(status) if status.account.local? deliver_to_self(status) if status.account.local?
if status.direct_visibility? if status.direct_visibility?

View File

@ -14,3 +14,4 @@
%td= block.severity %td= block.severity
= will_paginate @blocks, pagination_options = will_paginate @blocks, pagination_options
= link_to 'Add new', new_admin_domain_block_path, class: 'button'

View File

@ -0,0 +1,18 @@
- content_for :page_title do
New domain block
= simple_form_for @domain_block, url: admin_domain_blocks_path do |f|
= render 'shared/error_messages', object: @domain_block
%p.hint The domain block will not prevent creation of account entries in the database, but will retroactively and automatically apply specific moderation methods on those accounts.
= f.input :domain, placeholder: 'Domain'
= f.input :severity, collection: DomainBlock.severities.keys, wrapper: :with_label, include_blank: false
%p.hint
%strong Silence
will make the account's posts invisible to anyone who isn't following them.
%strong Suspend
will remove all of the account's content, media, and profile data.
.actions
= f.button :button, 'Create block', type: :submit

View File

@ -8,20 +8,25 @@
%li= filter_link_to 'Unresolved', action_taken: nil %li= filter_link_to 'Unresolved', action_taken: nil
%li= filter_link_to 'Resolved', action_taken: '1' %li= filter_link_to 'Resolved', action_taken: '1'
%table.table = form_tag do
%thead
%tr %table.table
%th ID %thead
%th Target
%th Reported by
%th Comment
%th
%tbody
- @reports.each do |report|
%tr %tr
%td= "##{report.id}" %th
%td= link_to report.target_account.acct, admin_account_path(report.target_account.id) %th ID
%td= link_to report.account.acct, admin_account_path(report.account.id) %th Target
%td= truncate(report.comment, length: 30, separator: ' ') %th Reported by
%td= table_link_to 'circle', 'View', admin_report_path(report) %th Comment
%th
%tbody
- @reports.each do |report|
%tr
%td= check_box_tag 'select', report.id
%td= "##{report.id}"
%td= link_to report.target_account.acct, admin_account_path(report.target_account.id)
%td= link_to report.account.acct, admin_account_path(report.account.id)
%td= truncate(report.comment, length: 30, separator: ' ')
%td= table_link_to 'circle', 'View', admin_report_path(report)
= will_paginate @reports, pagination_options = will_paginate @reports, pagination_options

View File

@ -27,7 +27,7 @@
= link_to remove_admin_report_path(@report, status_id: status.id), method: :post, class: 'icon-button', style: 'font-size: 24px; width: 24px; height: 24px', title: 'Delete' do = link_to remove_admin_report_path(@report, status_id: status.id), method: :post, class: 'icon-button', style: 'font-size: 24px; width: 24px; height: 24px', title: 'Delete' do
= fa_icon 'trash' = fa_icon 'trash'
- unless @report.action_taken? - if !@report.action_taken?
%hr/ %hr/
%div{ style: 'overflow: hidden' } %div{ style: 'overflow: hidden' }
@ -36,3 +36,9 @@
= link_to 'Suspend account', suspend_admin_report_path(@report), method: :post, class: 'button' = link_to 'Suspend account', suspend_admin_report_path(@report), method: :post, class: 'button'
%div{ style: 'float: left' } %div{ style: 'float: left' }
= link_to 'Mark as resolved', resolve_admin_report_path(@report), method: :post, class: 'button' = link_to 'Mark as resolved', resolve_admin_report_path(@report), method: :post, class: 'button'
- elsif !@report.action_taken_by_account.nil?
%hr/
%p
%strong Action taken by:
= @report.action_taken_by_account.acct

View File

@ -3,7 +3,7 @@
class AfterRemoteFollowRequestWorker class AfterRemoteFollowRequestWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options retry: 5 sidekiq_options queue: 'pull', retry: 5
def perform(follow_request_id) def perform(follow_request_id)
follow_request = FollowRequest.find(follow_request_id) follow_request = FollowRequest.find(follow_request_id)

View File

@ -3,7 +3,7 @@
class AfterRemoteFollowWorker class AfterRemoteFollowWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options retry: 5 sidekiq_options queue: 'pull', retry: 5
def perform(follow_id) def perform(follow_id)
follow = Follow.find(follow_id) follow = Follow.find(follow_id)

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
class DomainBlockWorker
include Sidekiq::Worker
def perform(domain_block_id)
BlockDomainService.new.call(DomainBlock.find(domain_block_id))
rescue ActiveRecord::RecordNotFound
true
end
end

View File

@ -5,7 +5,7 @@ require 'csv'
class ImportWorker class ImportWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options retry: false sidekiq_options queue: 'pull', retry: false
def perform(import_id) def perform(import_id)
import = Import.find(import_id) import = Import.find(import_id)

View File

@ -3,7 +3,7 @@
class LinkCrawlWorker class LinkCrawlWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options retry: false sidekiq_options queue: 'pull', retry: false
def perform(status_id) def perform(status_id)
FetchLinkCardService.new.call(Status.find(status_id)) FetchLinkCardService.new.call(Status.find(status_id))

View File

@ -3,6 +3,8 @@
class MergeWorker class MergeWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options queue: 'pull'
def perform(from_account_id, into_account_id) def perform(from_account_id, into_account_id)
FeedManager.instance.merge_into_timeline(Account.find(from_account_id), Account.find(into_account_id)) FeedManager.instance.merge_into_timeline(Account.find(from_account_id), Account.find(into_account_id))
end end

View File

@ -3,7 +3,7 @@
class NotificationWorker class NotificationWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options retry: 5 sidekiq_options queue: 'push', retry: 5
def perform(xml, source_account_id, target_account_id) def perform(xml, source_account_id, target_account_id)
SendInteractionService.new.call(xml, Account.find(source_account_id), Account.find(target_account_id)) SendInteractionService.new.call(xml, Account.find(source_account_id), Account.find(target_account_id))

View File

@ -3,7 +3,7 @@
class ProcessingWorker class ProcessingWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options backtrace: true sidekiq_options queue: 'pull', backtrace: true
def perform(account_id, body) def perform(account_id, body)
ProcessFeedService.new.call(body, Account.find(account_id)) ProcessFeedService.new.call(body, Account.find(account_id))

View File

@ -3,7 +3,9 @@
class RegenerationWorker class RegenerationWorker
include Sidekiq::Worker include Sidekiq::Worker
def perform(account_id, timeline_type) sidekiq_options queue: 'pull', backtrace: true
PrecomputeFeedService.new.call(timeline_type, Account.find(account_id))
def perform(account_id, _ = :home)
PrecomputeFeedService.new.call(:home, Account.find(account_id))
end end
end end

View File

@ -3,7 +3,7 @@
class SalmonWorker class SalmonWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options backtrace: true sidekiq_options queue: 'pull', backtrace: true
def perform(account_id, body) def perform(account_id, body)
ProcessInteractionService.new.call(body, Account.find(account_id)) ProcessInteractionService.new.call(body, Account.find(account_id))

View File

@ -3,7 +3,7 @@
class ThreadResolveWorker class ThreadResolveWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options retry: false sidekiq_options queue: 'pull', retry: false
def perform(child_status_id, parent_url) def perform(child_status_id, parent_url)
child_status = Status.find(child_status_id) child_status = Status.find(child_status_id)

View File

@ -3,6 +3,8 @@
class UnmergeWorker class UnmergeWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options queue: 'pull'
def perform(from_account_id, into_account_id) def perform(from_account_id, into_account_id)
FeedManager.instance.unmerge_from_timeline(Account.find(from_account_id), Account.find(into_account_id)) FeedManager.instance.unmerge_from_timeline(Account.find(from_account_id), Account.find(into_account_id))
end end

View File

@ -0,0 +1,61 @@
---
'no':
devise:
confirmations:
confirmed: Epostaddressen din er blitt bekreftet.
send_instructions: Du vil motta en epost med instruksjoner for hvordan bekrefte din epostaddresse om noen få minutter.
send_paranoid_instructions: Hvis din epostaddresse finnes i vår database vil du motta en epost med instruksjoner for hvordan bekrefte din epost om noen få minutter.
failure:
already_authenticated: Du er allerede innlogget.
inactive: Din konto er ikke blitt aktivert ennå.
invalid: Ugyldig %{authentication_keys} eller passord.
last_attempt: Du har ett forsøk igjen før kontoen din bli låst.
locked: Din konto er låst.
not_found_in_database: Ugyldig %{authentication_keys} eller passord.
timeout: Sesjonen din løp ut på tid. Logg inn på nytt for å fortsette.
unauthenticated: Du må logge inn eller registrere deg før du kan fortsette.
unconfirmed: Du må bekrefte epostadressen din før du kan fortsette.
mailer:
confirmation_instructions:
subject: 'Mastodon: Instruksjoner for å bekrefte epostadresse'
password_change:
subject: 'Mastodon: Passord endret'
reset_password_instructions:
subject: 'Mastodon: Hvordan nullstille passord?'
unlock_instructions:
subject: 'Mastodon: Instruksjoner for å gjenåpne konto'
omniauth_callbacks:
failure: Kunne ikke autentisere deg fra %{kind} fordi "%{reason}".
success: Vellykket autentisering fra %{kind}.
passwords:
no_token: Du har ingen tilgang til denne siden så lenge du ikke kommer fra en epost om nullstilling av passord. Hvis du kommer fra en passordnullstilling epost, dobbelsjekk at du brukte hele URLen.
send_instructions: Du vil motta en epost med instruksjoner for å nullstille passordet ditt om noen få minutter.
send_paranoid_instructions: Hvis epostadressen din finnes i databasen vår vil du motta en instruksjonsmail om passord nullstilling om noen få minutter.
updated: Passordet ditt har blitt endret. Du er nå logget inn.
updated_not_active: Passordet ditt har blitt endret.
registrations:
destroyed: Adjø! Kontoen din har blitt avsluttet. Vi håper at vi ser deg igjen snart.
signed_up: Velkommen! Registrasjonen var vellykket.
signed_up_but_inactive: Registrasjonen var vellykket. Vi kunne dessverre ikke logge deg inn fordi kontoen din ennå ikke har blitt aktivert.
signed_up_but_locked: Registrasjonen var vellykket. Vi kunne dessverre ikke logge deg inn fordi kontoen din har blitt låst.
signed_up_but_unconfirmed: En epostmelding med en bekreftelseslink har blitt sendt til din adresse. Klikk på linken i eposten for å aktivere kontoen din.
update_needs_confirmation: Du har oppdatert kontoen din, men vi må bekrefte din nye epostadresse. Sjekk eposten din og følg bekreftelseslinken for å bekrefte din nye epostadresse.
updated: Kontoen din ble oppdatert.
sessions:
already_signed_out: Logget ut.
signed_in: Logget inn.
signed_out: Logget ut.
unlocks:
send_instructions: Du vil motta en epost med instruksjoner for å åpne kontoen din om noen få minutter.
send_paranoid_instructions: Hvis kontoen din eksisterer vil du motta en epost med instruksjoner for å åpne kontoen din om noen få minutter.
unlocked: Kontoen din ble åpnet uten problemer. Logg på for å fortsette.
errors:
messages:
already_confirmed: har allerede blitt bekreftet, prøv å logg på istedet.
confirmation_period_expired: må bekreftes innen %{period}. Spør om en ny bekreftelsesmail istedet.
expired: har utløpt, spør om en ny en istedet
not_found: ikke funnet
not_locked: var ikke låst
not_saved:
one: '1 feil hindret denne %{resource} fra å bli lagret:'
other: "%{count} feil hindret denne %{resource} fra å bli lagret:"

View File

@ -0,0 +1,113 @@
---
'no':
activerecord:
attributes:
doorkeeper/application:
name: Navn
redirect_uri: Omdirigerings-URI
errors:
models:
doorkeeper/application:
attributes:
redirect_uri:
fragment_present: kan ikke inneholde ett fragment.
invalid_uri: må være en gyldig URI.
relative_uri: må være en absolutt URI.
secured_uri: må være en HTTPS/SSL URI.
doorkeeper:
applications:
buttons:
authorize: Autoriser
cancel: Avbryt
destroy: Ødelegg
edit: Endre
submit: Send inn
confirmations:
destroy: Er du sikker?
edit:
title: Endre applikasjon
form:
error: Whoops! Sjekk skjemaet ditt for mulige feil
help:
native_redirect_uri: Bruk %{native_redirect_uri} for lokale tester
redirect_uri: Bruk en linje per URI
scopes: Adskill omfang med mellomrom. La det være blankt for å bruke standard omfang.
index:
callback_url: Callback URL
name: Navn
new: Ny Applikasjon
title: Dine applikasjoner
new:
title: Ny Applikasjoner
show:
actions: Operasjoner
application_id: Applikasjon Id
callback_urls: Callback urls
scopes: Omfang
secret: Hemmelighet
title: 'Applikasjon: %{name}'
authorizations:
buttons:
authorize: Autoriser
deny: Avvis
error:
title: En feil oppsto
new:
able_to: Den vil ha mulighet til
prompt: Applikasjon %{client_name} spør om tilgang til din konto
title: Autorisasjon påkrevd
show:
title: Autoriserings kode
authorized_applications:
buttons:
revoke: Opphev
confirmations:
revoke: Opphev?
index:
application: Applikasjon
created_at: Autorisert
date_format: "%Y-%m-%d %H:%M:%S"
scopes: Omfang
title: Dine autoriserte applikasjoner
errors:
messages:
access_denied: Ressurseieren eller autoriserings tjeneren avviste forespørslen.
credential_flow_not_configured: Ressurseiers passord flyt feilet på grunn av at Doorkeeper.configure.resource_owner_from_credentials ikke var konfigurert.
invalid_client: Klient autentisering feilet på grunn av ukjent klient, ingen autentisering inkludert eller autentiserings metode som ikke er støttet.
invalid_grant: Autoriseringen er ugyldig, utløpt, opphevet, stemmer ikke overens med omdirigerings-URIen eller var utstedt til en annen klient.
invalid_redirect_uri: redirect urien som var inkludert er ikke gyldig.
invalid_request: Forespørslen mangler ett eller flere parametere, inkluderte ett parameter som ikke støttes eller har feil struktur.
invalid_resource_owner: Ressurseierens detaljer er ikke gyldig, eller så kan ikke eieren finnes.
invalid_scope: Det etterspurte omfanget er ugyldig, ukjent eller har feil struktur.
invalid_token:
expired: Tilgangsbeviset har utløpt
revoked: Tilgangsbeviset har blitt opphevet
unknown: Tilgangsbeviset er ugyldig
resource_owner_authenticator_not_configured: Ressurseier kunne ikke finnes fordi Doorkeeper.configure.resource_owner_authenticator ikke er konfigurert.
server_error: Autoriserings tjeneren støtte på en uventet hendelse som hindret den i å svare på forespørslen.
temporarily_unavailable: Autoriserings tjeneren kan ikke håndtere forespørslen grunnet en midlertidig overbelastning eller tjenervedlikehold.
unauthorized_client: Klienten har ikke autorisasjon for å utføre denne forespørslen med denne metoden.
unsupported_grant_type: Autorisasjons tildelings typen er ikke støttet av denne autoriserings tjeneren.
unsupported_response_type: Autorisasjons serveren støtter ikke denne typen av forespørsler.
flash:
applications:
create:
notice: Applikasjon opprettet.
destroy:
notice: Applikasjon slettet.
update:
notice: Applikasjon oppdatert.
authorized_applications:
destroy:
notice: Applikasjon opphevet.
layouts:
admin:
nav:
applications: Applikasjoner
oauth2_provider: OAuth2 tilbyder
application:
title: OAuth autorisering påkrevet
scopes:
follow: følg, blokker, avblokker, avfølg kontoer
read: lese dine data
write: poste på dine vegne

164
config/locales/no.yml Normal file
View File

@ -0,0 +1,164 @@
---
'no':
about:
about_mastodon: Mastodon er et <em>gratis, åpen kildekode</em> sosialt nettverk. Et <em>desentralisert</em> alternativ til kommersielle plattformer. Slik kan det unngå risikoene ved å ha et enkelt selskap med monopol på din kommunikasjon. Velg en tjener du stoler på &mdash; uansett hvilken du velger så kan du interagere med alle andre. Alle kan kjøre sin egen Mastodon og delta sømløst i det sosiale nettverket.
about_this: Om denne instansen
apps: Applikasjoner
business_email: 'Bedriftsepost:'
contact: Kontakt
description_headline: Hva er %{domain}?
domain_count_after: andre instanser
domain_count_before: Koblet til
features:
api: Åpent api for applikasjoner og tjenester
blocks: Rikholdige blokkerings verktøy
characters: 500 tegn per post
chronology: Tidslinjer er kronologiske
ethics: 'Etisk design: Ingen reklame, ingen sporing'
gifv: GIFV sett og korte videoer
privacy: Finmaskete personvernsinnstillinger
public: Offentlige tidslinjer
features_headline: Hva skiller Mastodon fra andre sosiale nettverk
get_started: Kom i gang
links: Lenker
other_instances: Andre instanser
source_code: Kildekode
status_count_after: statuser
status_count_before: Hvem skrev
terms: Betingelser
user_count_after: brukere
user_count_before: Hjem til
accounts:
follow: Følg
followers: Følgere
following: Følger
nothing_here: Det er ingenting her!
people_followed_by: Folk som %{name} følger
people_who_follow: Folk som følger %{name}
posts: Poster
remote_follow: Følg fra andre instanser
unfollow: Avfølg
application_mailer:
settings: 'Endre foretrukne epost innstillinger: %{link}'
signature: Mastodon notiser fra %{instance}
view: 'Se:'
applications:
invalid_url: Den oppgitte URLen er ugyldig
auth:
change_password: Brukerdetaljer
didnt_get_confirmation: Fikk du ikke bekreftelsesmailen din?
forgot_password: Har du glemt passordet ditt?
login: Innlogging
logout: Logg ut
register: Bli med
resend_confirmation: Send bekreftelsesinstruksjoner på nytt
reset_password: Nullstill passord
set_new_password: Sett nytt passord
authorize_follow:
error: Uheldigvis så skjedde det en feil når vi prøvde å få tak i en konto fra en annen instans.
follow: Følg
prompt_html: 'Du (<strong>%{self}</strong>) har spurt om å følge:'
title: Følg %{acct}
datetime:
distance_in_words:
about_x_hours: "%{count}t"
about_x_months: "%{count}m"
about_x_years: "%{count}å"
almost_x_years: "%{count}å"
half_a_minute: Nylig
less_than_x_minutes: "%{count}min"
less_than_x_seconds: Nylig
over_x_years: "%{count}å"
x_days: "%{count}d"
x_minutes: "%{count}min"
x_months: "%{count}mo"
x_seconds: "%{count}s"
exports:
blocks: Du blokkerer
csv: CSV
follows: Du følger
storage: Media lagring
generic:
changes_saved_msg: Vellykket lagring av endringer!
powered_by: drevet av %{link}
save_changes: Lagre endringer
validation_errors:
one: Noe er ikke helt riktig ennå. Vær snill å se etter en gang til
other: Noe er ikke helt riktig ennå. Det er ennå %{count} feil å rette på
imports:
preface: Du kan importere data om mennesker du følger eller blokkerer inn til kontoen din på denne instansen, fra filer opprettet av eksporter fra andre instanser.
success: Din data ble mottatt og vil bli prosessert så fort som mulig.
types:
blocking: Blokkeringsliste
following: Følgeliste
upload: Opplastning
landing_strip_html: <strong>%{name}</strong> er en bruker på <strong>%{domain}</strong>. Du kan følge dem eller interagere med dem hvis du har en konto hvor som helst i fediverset. Hvis du ikke har en konto så kan du <a href="%{sign_up_path}">registrere deg her</a>.
notification_mailer:
digest:
body: 'Her er en kort oppsummering av hva du har gått glipp av på %{instance} siden du logget deg inn sist den %{since}:'
mention: "%{name} nevnte deg i:"
new_followers_summary:
one: Du har fått en ny følger. Jippi!
other: Du har fått %{count} nye følgere! Imponerende!
subject:
one: "1 ny hendelse siden ditt siste besøk \U0001F418"
other: "%{count} nye hendelser siden ditt siste besøk \U0001F418"
favourite:
body: 'Din status ble satt som favoritt av %{name}'
subject: "%{name} satte din status som favoritt."
follow:
body: "%{name} følger deg!"
subject: "%{name} følger deg"
follow_request:
body: "%{name} har spurt om å få lov til å følge deg"
subject: 'Ventende følger: %{name}'
mention:
body: 'Du ble nevnt av %{name} i:'
subject: Du ble nevnt av %{name}
reblog:
body: 'Din status fikk en boost av %{name}:'
subject: "%{name} ga din status en boost"
pagination:
next: Neste
prev: Forrige
remote_follow:
acct: Tast inn brukernavn@domene som du vil følge fra
missing_resource: Kunne ikke finne URLen for din konto
proceed: Fortsett med følging
prompt: 'Du kommer til å følge:'
settings:
authorized_apps: Autoriserte applikasjoner
back: Tilbake til Mastodon
edit_profile: Endre profil
export: Data eksport
import: Importer
preferences: Foretrukne valg
settings: Innstillinger
two_factor_auth: To-faktor autentisering
statuses:
open_in_web: Åpne i nettleser
over_character_limit: tegngrense på %{max} overskredet
show_more: Vis mer
visibilities:
private: Vis kun til følgere
public: Offentlig
unlisted: Offentlig, men vis ikke på offentlig tidslinje
stream_entries:
click_to_show: Klikk for å vise
reblogged: boostet
sensitive_content: Sensitivt innhold
time:
formats:
default: "%d, %b %Y, %H:%M"
two_factor_auth:
description_html: Hvis du skru på <strong>tofaktor autentisering</strong> vil innlogging kreve at du har telefonen din, som vil generere koder som du må taste inn.
disable: Skru av
enable: Skru på
instructions_html: "<strong>Scan denne QR-koden i Google Authenticator eller en lignende app på telefonen din</strong>. Fra nå av så vil denne applikasjonen generere koder for deg som skal brukes under innlogging"
plaintext_secret_html: 'Plain-text secret: <samp>%{secret}</samp>'
warning: Hvis du ikke kan konfigurere en autentikatorapp nå, så bør du trykke "Skru av"; ellers vil du ikke kunne logge inn.
users:
invalid_email: E-post addressen er ugyldig
invalid_otp_token: Ugyldig two-faktor kode
will_paginate:
page_gap: "&hellip;"

View File

@ -0,0 +1,46 @@
---
'no':
simple_form:
hints:
defaults:
avatar: PNG, GIF eller JPG. Maksimalt 2MB. Vil bli nedskalert til 120x120px
display_name: Maksimalt 30 tegn
header: PNG, GIF eller JPG. Maksimalt 2MB. Vil bli nedskalert til 700x335px
locked: Krever at du manuelt godkjenner følgere og setter standard beskyttelse av poster til kun-følgere
note: Maksimalt 160 tegn
imports:
data: CSV fil eksportert fra en annen Mastodon instans
labels:
defaults:
avatar: Avatar
confirm_new_password: Bekreft nytt passord
confirm_password: Bekreft passord
current_password: Nåværende passord
data: Data
display_name: Visningsnavn
email: E-post adresse
header: Header
locale: Språk
locked: Endre konto til privat
new_password: Nytt passord
note: Biografi
otp_attempt: To-faktor kode
password: Passord
setting_default_privacy: Leserettigheter for poster
type: Importeringstype
username: Brukernavn
interactions:
must_be_follower: Blokker meldinger fra ikke-følgere
must_be_following: Blokker meldinger fra folk du ikke følger
notification_emails:
digest: Send oppsummerings eposter
favourite: Send e-post når noen setter din status som favoritt
follow: Send e-post når noen følger deg
follow_request: Send e-post når noen spør om å få følge deg
mention: Send e-post når noen nevner deg
reblog: Send e-post når noen reblogger din status
'no': 'Nei'
required:
mark: "*"
text: påkrevd
'yes': 'Ja'

View File

@ -14,11 +14,11 @@ SimpleNavigation::Configuration.run do |navigation|
settings.item :authorized_apps, safe_join([fa_icon('list fw'), t('settings.authorized_apps')]), oauth_authorized_applications_url settings.item :authorized_apps, safe_join([fa_icon('list fw'), t('settings.authorized_apps')]), oauth_authorized_applications_url
end end
primary.item :admin, safe_join([fa_icon('cogs fw'), 'Administration']), admin_accounts_url, if: proc { current_user.admin? } do |admin| primary.item :admin, safe_join([fa_icon('cogs fw'), 'Administration']), admin_reports_url, if: proc { current_user.admin? } do |admin|
admin.item :reports, safe_join([fa_icon('flag fw'), 'Reports']), admin_reports_url, highlights_on: %r{/admin/reports} admin.item :reports, safe_join([fa_icon('flag fw'), 'Reports']), admin_reports_url, highlights_on: %r{/admin/reports}
admin.item :accounts, safe_join([fa_icon('users fw'), 'Accounts']), admin_accounts_url, highlights_on: %r{/admin/accounts} admin.item :accounts, safe_join([fa_icon('users fw'), 'Accounts']), admin_accounts_url, highlights_on: %r{/admin/accounts}
admin.item :pubsubhubbubs, safe_join([fa_icon('paper-plane-o fw'), 'PubSubHubbub']), admin_pubsubhubbub_index_url admin.item :pubsubhubbubs, safe_join([fa_icon('paper-plane-o fw'), 'PubSubHubbub']), admin_pubsubhubbub_index_url
admin.item :domain_blocks, safe_join([fa_icon('lock fw'), 'Domain Blocks']), admin_domain_blocks_url admin.item :domain_blocks, safe_join([fa_icon('lock fw'), 'Domain Blocks']), admin_domain_blocks_url, highlights_on: %r{/admin/domain_blocks}
admin.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url admin.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url
admin.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url admin.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url
admin.item :settings, safe_join([fa_icon('cogs fw'), 'Site Settings']), admin_settings_url admin.item :settings, safe_join([fa_icon('cogs fw'), 'Site Settings']), admin_settings_url

View File

@ -77,7 +77,7 @@ Rails.application.routes.draw do
namespace :admin do namespace :admin do
resources :pubsubhubbub, only: [:index] resources :pubsubhubbub, only: [:index]
resources :domain_blocks, only: [:index, :create] resources :domain_blocks, only: [:index, :new, :create]
resources :settings, only: [:index, :update] resources :settings, only: [:index, :update]
resources :reports, only: [:index, :show] do resources :reports, only: [:index, :show] do

View File

@ -0,0 +1,5 @@
class AddActionTakenByAccountIdToReports < ActiveRecord::Migration[5.0]
def change
add_column :reports, :action_taken_by_account_id, :integer
end
end

View File

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170330164118) do ActiveRecord::Schema.define(version: 20170403172249) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -201,13 +201,14 @@ ActiveRecord::Schema.define(version: 20170330164118) do
end end
create_table "reports", force: :cascade do |t| create_table "reports", force: :cascade do |t|
t.integer "account_id", null: false t.integer "account_id", null: false
t.integer "target_account_id", null: false t.integer "target_account_id", null: false
t.bigint "status_ids", default: [], null: false, array: true t.bigint "status_ids", default: [], null: false, array: true
t.text "comment", default: "", null: false t.text "comment", default: "", null: false
t.boolean "action_taken", default: false, null: false t.boolean "action_taken", default: false, null: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.integer "action_taken_by_account_id"
end end
create_table "settings", force: :cascade do |t| create_table "settings", force: :cascade do |t|

View File

@ -33,7 +33,7 @@ services:
restart: always restart: always
build: . build: .
env_file: .env.production env_file: .env.production
command: bundle exec sidekiq -q default -q mailers -q push command: bundle exec sidekiq -q default -q mailers -q pull -q push
depends_on: depends_on:
- db - db
- redis - redis

View File

@ -180,7 +180,7 @@ User=mastodon
WorkingDirectory=/home/mastodon/live WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production" Environment="RAILS_ENV=production"
Environment="DB_POOL=5" Environment="DB_POOL=5"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 5 -q default -q mailers -q push ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push
TimeoutSec=15 TimeoutSec=15
Restart=always Restart=always

View File

@ -21,5 +21,6 @@ There is also a list at [instances.mastodon.xyz](https://instances.mastodon.xyz)
| [social.mashek.net](https://social.mashek.net) |Themed and customised for Mashekstein Labs community. Selectively federates.|Yes|No| | [social.mashek.net](https://social.mashek.net) |Themed and customised for Mashekstein Labs community. Selectively federates.|Yes|No|
| [masto.themimitoof.fr](https://masto.themimitoof.fr) |N/A|Yes|Yes| | [masto.themimitoof.fr](https://masto.themimitoof.fr) |N/A|Yes|Yes|
| [social.imirhil.fr](https://social.imirhil.fr) |N/A|No|Yes| | [social.imirhil.fr](https://social.imirhil.fr) |N/A|No|Yes|
| [social.wxcafe.net](https://social.wxcafe.net) |Open registrations, federates everywhere, no moderation yet|Yes|Yes|
Let me know if you start running one so I can add it to the list! (Alternatively, add it yourself as a pull request). Let me know if you start running one so I can add it to the list! (Alternatively, add it yourself as a pull request).

View File

@ -14,7 +14,7 @@ RSpec.describe BlockDomainService do
bad_status2 bad_status2
bad_attachment bad_attachment
subject.call('evil.org', :suspend) subject.call(DomainBlock.create!(domain: 'evil.org', severity: :suspend))
end end
it 'creates a domain block' do it 'creates a domain block' do