Merge tag 'v1.6.0rc3' into sync/upstream
This commit is contained in:
commit
514fc908a3
87 changed files with 947 additions and 285 deletions
.env.production.sampleGemfileGemfile.lock
app
controllers
helpers
javascript
mastodon
components
features
locales
styles
lib
models
presenters
serializers
services
views
workers
config
db
migrate
20170905044538_add_index_id_account_id_activity_type_on_notifications.rb20170905165803_add_local_to_statuses.rb
schema.rblib
package.jsonspec
controllers
fabricators
lib
models
services
streaming
yarn.lock
|
@ -26,7 +26,7 @@ LOCAL_HTTPS=true
|
|||
# ALTERNATE_DOMAINS=example1.com,example2.com
|
||||
|
||||
# Application secrets
|
||||
# Generate each with the `rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
|
||||
# Generate each with the `RAILS_ENV=production bundle exec rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
|
||||
PAPERCLIP_SECRET=
|
||||
SECRET_KEY_BASE=
|
||||
OTP_SECRET=
|
||||
|
@ -36,7 +36,7 @@ OTP_SECRET=
|
|||
# You should only generate this once per instance. If you later decide to change it, all push subscription will
|
||||
# be invalidated, requiring the users to access the website again to resubscribe.
|
||||
#
|
||||
# Generate with `rake mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web rake mastodon:webpush:generate_vapid_key` if you use docker compose)
|
||||
# Generate with `RAILS_ENV=production bundle exec rake mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web rake mastodon:webpush:generate_vapid_key` if you use docker compose)
|
||||
#
|
||||
# For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html
|
||||
VAPID_PRIVATE_KEY=
|
||||
|
@ -98,6 +98,15 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
|||
# S3_ENDPOINT=
|
||||
# S3_SIGNATURE_VERSION=
|
||||
|
||||
# Swift (optional)
|
||||
# SWIFT_ENABLED=true
|
||||
# SWIFT_USERNAME=
|
||||
# SWIFT_TENANT=
|
||||
# SWIFT_PASSWORD=
|
||||
# SWIFT_AUTH_URL=
|
||||
# SWIFT_CONTAINER=
|
||||
# SWIFT_OBJECT_URL=
|
||||
|
||||
# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
|
||||
# S3_CLOUDFRONT_HOST=
|
||||
|
||||
|
|
1
Gemfile
1
Gemfile
|
@ -15,6 +15,7 @@ gem 'pghero', '~> 1.7'
|
|||
gem 'dotenv-rails', '~> 2.2'
|
||||
|
||||
gem 'aws-sdk', '~> 2.9'
|
||||
gem 'fog-openstack', '~> 0.1'
|
||||
gem 'paperclip', '~> 5.1'
|
||||
gem 'paperclip-av-transcoder', '~> 0.6'
|
||||
|
||||
|
|
15
Gemfile.lock
15
Gemfile.lock
|
@ -154,12 +154,25 @@ GEM
|
|||
erubis (2.7.0)
|
||||
et-orbi (1.0.5)
|
||||
tzinfo
|
||||
excon (0.58.0)
|
||||
execjs (2.7.0)
|
||||
fabrication (2.16.2)
|
||||
faker (1.7.3)
|
||||
i18n (~> 0.5)
|
||||
fast_blank (1.0.0)
|
||||
ffi (1.9.18)
|
||||
fog-core (1.45.0)
|
||||
builder
|
||||
excon (~> 0.58)
|
||||
formatador (~> 0.2)
|
||||
fog-json (1.0.2)
|
||||
fog-core (~> 1.0)
|
||||
multi_json (~> 1.10)
|
||||
fog-openstack (0.1.21)
|
||||
fog-core (>= 1.40)
|
||||
fog-json (>= 1.0)
|
||||
ipaddress (>= 0.8)
|
||||
formatador (0.2.5)
|
||||
fuubar (2.2.0)
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
|
@ -211,6 +224,7 @@ GEM
|
|||
rainbow (~> 2.2)
|
||||
terminal-table (>= 1.5.1)
|
||||
idn-ruby (0.1.0)
|
||||
ipaddress (0.8.3)
|
||||
jmespath (1.3.1)
|
||||
json (2.1.0)
|
||||
json-ld (2.1.5)
|
||||
|
@ -535,6 +549,7 @@ DEPENDENCIES
|
|||
fabrication (~> 2.16)
|
||||
faker (~> 1.7)
|
||||
fast_blank (~> 1.0)
|
||||
fog-openstack (~> 0.1)
|
||||
fuubar (~> 2.2)
|
||||
goldfinger (~> 2.0)
|
||||
hamlit-rails (~> 0.2)
|
||||
|
|
|
@ -14,7 +14,7 @@ class AccountsController < ApplicationController
|
|||
return
|
||||
end
|
||||
|
||||
@pinned_statuses = cache_collection(@account.pinned_statuses, Status) unless media_requested?
|
||||
@pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses?
|
||||
@statuses = filtered_statuses.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
@next_url = next_url unless @statuses.empty?
|
||||
|
@ -22,7 +22,7 @@ class AccountsController < ApplicationController
|
|||
|
||||
format.atom do
|
||||
@entries = @account.stream_entries.where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.feed(@account, @entries.to_a))
|
||||
render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.feed(@account, @entries.reject { |entry| entry.status.nil? }))
|
||||
end
|
||||
|
||||
format.json do
|
||||
|
@ -33,6 +33,10 @@ class AccountsController < ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def show_pinned_statuses?
|
||||
[replies_requested?, media_requested?, params[:max_id].present?, params[:since_id].present?].none?
|
||||
end
|
||||
|
||||
def filtered_statuses
|
||||
default_statuses.tap do |statuses|
|
||||
statuses.merge!(only_media_scope) if media_requested?
|
||||
|
|
|
@ -26,8 +26,12 @@ class ActivityPub::InboxesController < Api::BaseController
|
|||
end
|
||||
|
||||
def upgrade_account
|
||||
return unless signed_request_account.subscribed?
|
||||
Pubsubhubbub::UnsubscribeWorker.perform_async(signed_request_account.id)
|
||||
if signed_request_account.ostatus?
|
||||
signed_request_account.update(last_webfingered_at: nil)
|
||||
ResolveRemoteAccountWorker.perform_async(signed_request_account.acct)
|
||||
end
|
||||
|
||||
Pubsubhubbub::UnsubscribeWorker.perform_async(signed_request_account.id) if signed_request_account.subscribed?
|
||||
end
|
||||
|
||||
def process_payload
|
||||
|
|
|
@ -14,6 +14,16 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
|
||||
def follow
|
||||
FollowService.new.call(current_user.account, @account.acct)
|
||||
|
||||
unless @account.locked?
|
||||
relationships = AccountRelationshipsPresenter.new(
|
||||
[@account.id],
|
||||
current_user.account_id,
|
||||
following_map: { @account.id => true },
|
||||
requested_map: { @account.id => false }
|
||||
)
|
||||
end
|
||||
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
|
|
|
@ -12,8 +12,14 @@ module RoutingHelper
|
|||
end
|
||||
|
||||
def full_asset_url(source, options = {})
|
||||
source = ActionController::Base.helpers.asset_url(source, options) unless Rails.configuration.x.use_s3
|
||||
source = ActionController::Base.helpers.asset_url(source, options) unless use_storage?
|
||||
|
||||
URI.join(root_url, source).to_s
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def use_storage?
|
||||
Rails.configuration.x.use_s3 || Rails.configuration.x.use_swift
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,6 +5,7 @@ import IntersectionObserverArticle from './intersection_observer_article';
|
|||
import LoadMore from './load_more';
|
||||
import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper';
|
||||
import { throttle } from 'lodash';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
|
||||
export default class ScrollableList extends PureComponent {
|
||||
|
||||
|
@ -95,7 +96,12 @@ export default class ScrollableList extends PureComponent {
|
|||
|
||||
getFirstChildKey (props) {
|
||||
const { children } = props;
|
||||
const firstChild = Array.isArray(children) ? children[0] : children;
|
||||
let firstChild = children;
|
||||
if (children instanceof ImmutableList) {
|
||||
firstChild = children.get(0);
|
||||
} else if (Array.isArray(children)) {
|
||||
firstChild = children[0];
|
||||
}
|
||||
return firstChild && firstChild.key;
|
||||
}
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ export default class VideoPlayer extends React.PureComponent {
|
|||
if (!this.state.visible) {
|
||||
if (sensitive) {
|
||||
return (
|
||||
<div role='button' tabIndex='0' style={{ width: `${width}px`, height: `${height}px`, marginTop: '8px' }} className='media-spoiler' onClick={this.handleVisibility}>
|
||||
<div role='button' tabIndex='0' style={{ width: `${width}px`, height: `${height}px`, marginTop: '8px' }} className='media-spoiler__video' onClick={this.handleVisibility}>
|
||||
{spoilerButton}
|
||||
<span className='media-spoiler__warning'><FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /></span>
|
||||
<span className='media-spoiler__trigger'><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
|
||||
|
@ -157,7 +157,7 @@ export default class VideoPlayer extends React.PureComponent {
|
|||
);
|
||||
} else {
|
||||
return (
|
||||
<div role='button' tabIndex='0' style={{ width: `${width}px`, height: `${height}px`, marginTop: '8px' }} className='media-spoiler' onClick={this.handleVisibility}>
|
||||
<div role='button' tabIndex='0' style={{ width: `${width}px`, height: `${height}px`, marginTop: '8px' }} className='media-spoiler__video' onClick={this.handleVisibility}>
|
||||
{spoilerButton}
|
||||
<span className='media-spoiler__warning'><FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' /></span>
|
||||
<span className='media-spoiler__trigger'><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
|
||||
|
|
|
@ -77,6 +77,7 @@ export default class Favourites extends ImmutablePureComponent {
|
|||
onClick={this.handleHeaderClick}
|
||||
pinned={pinned}
|
||||
multiColumn={multiColumn}
|
||||
showBackButton
|
||||
/>
|
||||
|
||||
<StatusList
|
||||
|
|
|
@ -124,6 +124,7 @@ export default class Notifications extends React.PureComponent {
|
|||
const scrollContainer = (
|
||||
<ScrollableList
|
||||
scrollKey={`notifications-${columnId}`}
|
||||
trackScroll={!pinned}
|
||||
isLoading={isLoading}
|
||||
hasMore={hasMore}
|
||||
emptyMessage={emptyMessage}
|
||||
|
|
|
@ -26,12 +26,12 @@
|
|||
"bundle_modal_error.close": "Schließen",
|
||||
"bundle_modal_error.message": "Etwas ist beim Laden schiefgelaufen.",
|
||||
"bundle_modal_error.retry": "Erneut versuchen",
|
||||
"column.blocks": "Blockierte Benutzer",
|
||||
"column.blocks": "Blockierte Profile",
|
||||
"column.community": "Lokale Zeitleiste",
|
||||
"column.favourites": "Favoriten",
|
||||
"column.follow_requests": "Folgeanfragen",
|
||||
"column.home": "Startseite",
|
||||
"column.mutes": "Stummgeschaltete Benutzer",
|
||||
"column.mutes": "Stummgeschaltete Profile",
|
||||
"column.notifications": "Mitteilungen",
|
||||
"column.public": "Gesamtes bekanntes Netz",
|
||||
"column_back_button.label": "Zurück",
|
||||
|
@ -46,7 +46,7 @@
|
|||
"compose_form.lock_disclaimer": "Dein Profil ist nicht {locked}. Jeder kann dir jederzeit folgen, um deine privaten Beiträge einzusehen.",
|
||||
"compose_form.lock_disclaimer.lock": "gesperrt",
|
||||
"compose_form.placeholder": "Worüber möchtest du schreiben?",
|
||||
"compose_form.privacy_disclaimer": "Dein privater Status wird an die genannten Benutzer auf den Domains {domains} zugestellt. Vertraust du {domainsCount, plural, one {diesem Server} other {diesen Servern}}? Private Beiträge funktionieren nur auf Mastodon-Instanzen. Wenn {domains} {domainsCount, plural, one {keine Mastodon-Instanz ist} other {keine Mastodon-Instanzen sind}}, wird es dort kein Anzeichen geben, dass dein Beitrag privat ist und er könnte geteilt oder anderweitig für unerwünschte Empfänger sichtbar gemacht werden.",
|
||||
"compose_form.privacy_disclaimer": "Dein privater Status wird an die genannten Profile auf den Domains {domains} zugestellt. Vertraust du {domainsCount, plural, one {diesem Server} other {diesen Servern}}? Private Beiträge funktionieren nur auf Mastodon-Instanzen. Wenn {domains} {domainsCount, plural, one {keine Mastodon-Instanz ist} other {keine Mastodon-Instanzen sind}}, wird es dort kein Anzeichen geben, dass dein Beitrag privat ist und er könnte geteilt oder anderweitig für unerwünschte Empfänger sichtbar gemacht werden.",
|
||||
"compose_form.publish": "Tröt",
|
||||
"compose_form.publish_loud": "{publish}!",
|
||||
"compose_form.sensitive": "Medien als heikel markieren",
|
||||
|
@ -77,18 +77,18 @@
|
|||
"emoji_button.travel": "Reise und Orte",
|
||||
"empty_column.community": "Die lokale Zeitleiste ist leer. Schreibe etwas öffentlich, um den Ball ins Rollen zu bringen!",
|
||||
"empty_column.hashtag": "Es gibt noch nichts unter diesem Hashtag.",
|
||||
"empty_column.home": "Du folgst noch niemandem. Besuche {public} oder benutze die Suche, um zu starten oder andere Benutzer anzutreffen.",
|
||||
"empty_column.home": "Du folgst noch niemandem. Besuche {public} oder benutze die Suche, um zu starten oder andere Profile zu finden.",
|
||||
"empty_column.home.inactivity": "Deine Zeitleiste ist leer. Falls du eine längere Zeit inaktiv gewesen bist, wird sie für dich so schnell wie möglich wiedererstellt.",
|
||||
"empty_column.home.public_timeline": "die öffentliche Zeitleiste",
|
||||
"empty_column.notifications": "Du hast noch keine Mitteilungen. Interagiere mit anderen, um die Konversation zu starten.",
|
||||
"empty_column.public": "Hier ist nichts zu sehen! Schreibe etwas öffentlich oder folge Benutzern von anderen Instanzen, um es aufzufüllen.",
|
||||
"empty_column.public": "Hier ist nichts zu sehen! Schreibe etwas öffentlich oder folge Profilen von anderen Instanzen, um es aufzufüllen.",
|
||||
"follow_request.authorize": "Erlauben",
|
||||
"follow_request.reject": "Ablehnen",
|
||||
"getting_started.appsshort": "Anwendungen",
|
||||
"getting_started.faq": "Häufig gestellte Fragen",
|
||||
"getting_started.heading": "Erste Schritte",
|
||||
"getting_started.open_source_notice": "Mastodon ist quelloffene Software. Du kannst auf {github} dazu beitragen oder Probleme melden.",
|
||||
"getting_started.userguide": "Nutzeranleitung",
|
||||
"getting_started.userguide": "Bedienungsanleitung",
|
||||
"home.column_settings.advanced": "Fortgeschritten",
|
||||
"home.column_settings.basic": "Einfach",
|
||||
"home.column_settings.filter_regex": "Filter durch reguläre Ausdrücke",
|
||||
|
@ -101,14 +101,14 @@
|
|||
"loading_indicator.label": "Lade…",
|
||||
"media_gallery.toggle_visible": "Sichtbarkeit einstellen",
|
||||
"missing_indicator.label": "Nicht gefunden",
|
||||
"navigation_bar.blocks": "Blockierte Benutzer",
|
||||
"navigation_bar.blocks": "Blockierte Profile",
|
||||
"navigation_bar.community_timeline": "Lokale Zeitleiste",
|
||||
"navigation_bar.edit_profile": "Profil bearbeiten",
|
||||
"navigation_bar.favourites": "Favoriten",
|
||||
"navigation_bar.follow_requests": "Folgeanfragen",
|
||||
"navigation_bar.info": "Erweiterte Informationen",
|
||||
"navigation_bar.logout": "Abmelden",
|
||||
"navigation_bar.mutes": "Stummgeschaltete Benutzer",
|
||||
"navigation_bar.mutes": "Stummgeschaltete Profile",
|
||||
"navigation_bar.preferences": "Einstellungen",
|
||||
"navigation_bar.public_timeline": "Föderierte Zeitleiste",
|
||||
"notification.favourite": "{name} favorisierte deinen Status",
|
||||
|
@ -132,7 +132,7 @@
|
|||
"onboarding.page_four.home": "Die Startseite zeigt dir Beiträge von Leuten, denen du folgst.",
|
||||
"onboarding.page_four.notifications": "Wenn jemand mir dir interagiert, bekommst du eine Mitteilung.",
|
||||
"onboarding.page_one.federation": "Mastodon ist ein soziales Netzwerk, das aus unabhängigen Servern besteht. Diese Server nennen wir auch Instanzen.",
|
||||
"onboarding.page_one.handle": "Du bist auf der Instanz {domain}, also ist dein vollständiger Nutzername im Netzwerk {handle}",
|
||||
"onboarding.page_one.handle": "Du bist auf der Instanz {domain}, also ist dein vollständiger Profilname im Netzwerk {handle}",
|
||||
"onboarding.page_one.welcome": "Willkommen bei Mastodon!",
|
||||
"onboarding.page_six.admin": "Für deine Instanz ist {admin} zuständig.",
|
||||
"onboarding.page_six.almost_done": "Fast fertig…",
|
||||
|
@ -143,11 +143,11 @@
|
|||
"onboarding.page_six.read_guidelines": "Bitte mach dich mit den {guidelines} von {domain} vertraut!",
|
||||
"onboarding.page_six.various_app": "mobile Anwendungen",
|
||||
"onboarding.page_three.profile": "Bearbeite dein Profil, um dein Bild, deinen Namen oder deine Beschreibung anzupassen. Dort findest du auch andere Einstellungen.",
|
||||
"onboarding.page_three.search": "Benutze die Suchfunktion, um Leute oder Themen zu finden. Zum Beispiel, die Hashtags {illustration} oder {introductions}. Um eine Person zu finden, die auf einer anderen Instanz ist, benutze den vollständigen Nutzernamen.",
|
||||
"onboarding.page_three.search": "Benutze die Suchfunktion, um Leute oder Themen zu finden. Zum Beispiel, die Hashtags {illustration} oder {introductions}. Um eine Person zu finden, die auf einer anderen Instanz ist, benutze den vollständigen Profilnamen.",
|
||||
"onboarding.page_two.compose": "Schreibe Beiträge aus der Schreiben-Spalte. Du kannst Bilder und kurze Videos hochladen, Sichtbarkeitseinstellungen ändern und Inhaltswarnungen hinzufügen.",
|
||||
"onboarding.skip": "Überspringen",
|
||||
"privacy.change": "Privatsphäre des Status anpassen",
|
||||
"privacy.direct.long": "Beitrag nur an erwähnte Benutzer",
|
||||
"privacy.direct.long": "Beitrag nur an erwähnte Profile",
|
||||
"privacy.direct.short": "Direkt",
|
||||
"privacy.private.long": "Beitrag nur an Folgende",
|
||||
"privacy.private.short": "Privat",
|
||||
|
|
|
@ -63,8 +63,8 @@
|
|||
"confirmations.mute.message": "آیا واقعاً میخواهید {name} را بیصدا کنید؟",
|
||||
"confirmations.unfollow.confirm": "لغو پیگیری",
|
||||
"confirmations.unfollow.message": "آیا واقعاً میخواهید به پیگیری از {name} پایان دهید؟",
|
||||
"embed.instructions": "Embed this status on your website by copying the code below.",
|
||||
"embed.preview": "Here is what it will look like:",
|
||||
"embed.instructions": "برای جاگذاری این نوشته در سایت خودتان، کد زیر را کپی کنید.",
|
||||
"embed.preview": "نوشتهٔ جاگذاریشده این گونه به نظر خواهد رسید:",
|
||||
"emoji_button.activity": "فعالیت",
|
||||
"emoji_button.flags": "پرچمها",
|
||||
"emoji_button.food": "غذا و نوشیدنی",
|
||||
|
@ -164,14 +164,14 @@
|
|||
"standalone.public_title": "نگاهی به کاربران این سرور...",
|
||||
"status.cannot_reblog": "این نوشته را نمیشود بازبوقید",
|
||||
"status.delete": "پاککردن",
|
||||
"status.embed": "Embed",
|
||||
"status.embed": "جاگذاری",
|
||||
"status.favourite": "پسندیدن",
|
||||
"status.load_more": "بیشتر نشان بده",
|
||||
"status.media_hidden": "تصویر پنهان شده",
|
||||
"status.mention": "نامبردن از @{name}",
|
||||
"status.mute_conversation": "بیصداکردن گفتگو",
|
||||
"status.open": "این نوشته را باز کن",
|
||||
"status.pin": "Pin on profile",
|
||||
"status.pin": "نوشتهٔ ثابت نمایه",
|
||||
"status.reblog": "بازبوقیدن",
|
||||
"status.reblogged_by": "{name} بازبوقید",
|
||||
"status.reply": "پاسخ",
|
||||
|
@ -183,7 +183,7 @@
|
|||
"status.show_less": "نهفتن",
|
||||
"status.show_more": "نمایش",
|
||||
"status.unmute_conversation": "باصداکردن گفتگو",
|
||||
"status.unpin": "Unpin from profile",
|
||||
"status.unpin": "برداشتن نوشتهٔ ثابت نمایه",
|
||||
"tabs_bar.compose": "بنویسید",
|
||||
"tabs_bar.federated_timeline": "همگانی",
|
||||
"tabs_bar.home": "خانه",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"account.block": "Blokiraj @{name}",
|
||||
"account.block_domain": "Sakrij sve sa {domain}",
|
||||
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
|
||||
"account.disclaimer_full": "Ovaj korisnik je sa druge instance. Ovaj broj bi mogao biti veći.",
|
||||
"account.edit_profile": "Uredi profil",
|
||||
"account.follow": "Slijedi",
|
||||
"account.followers": "Sljedbenici",
|
||||
|
@ -15,7 +15,7 @@
|
|||
"account.requested": "Čeka pristanak",
|
||||
"account.share": "Share @{name}'s profile",
|
||||
"account.unblock": "Deblokiraj @{name}",
|
||||
"account.unblock_domain": "Otkrij {domain}",
|
||||
"account.unblock_domain": "Poništi sakrivanje {domain}",
|
||||
"account.unfollow": "Prestani slijediti",
|
||||
"account.unmute": "Poništi utišavanje @{name}",
|
||||
"account.view_full_profile": "View full profile",
|
||||
|
@ -43,7 +43,7 @@
|
|||
"column_header.unpin": "Unpin",
|
||||
"column_subheading.navigation": "Navigacija",
|
||||
"column_subheading.settings": "Postavke",
|
||||
"compose_form.lock_disclaimer": "Tvoj račun nije {locked}. Svatko te može slijediti i vidjeti tvoje postove namijenjene samo sljedbenicima.",
|
||||
"compose_form.lock_disclaimer": "Tvoj račun nije {locked}. Svatko te može slijediti kako bi vidio postove namijenjene samo tvojim sljedbenicima.",
|
||||
"compose_form.lock_disclaimer.lock": "zaključan",
|
||||
"compose_form.placeholder": "Što ti je na umu?",
|
||||
"compose_form.privacy_disclaimer": "Tvoj privatni status će biti dostavljen spomenutim korisnicima na {domains}. Vjeruješ li {domainsCount, plural, one {that server} drugim {those servers}}? Privatnost postova radi samo na Mastodon instancama. Ako {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, neće biti indikacije da je tvoj post privatan, i mogao bi biti podignut ili biti učinjen vidljivim na drugi način neželjenim primateljima.",
|
||||
|
@ -54,13 +54,14 @@
|
|||
"compose_form.spoiler_placeholder": "Upozorenje o sadržaju",
|
||||
"confirmation_modal.cancel": "Otkaži",
|
||||
"confirmations.block.confirm": "Blokiraj",
|
||||
"confirmations.block.message": "Jesi li siguran da želiš blokirati {name}?",
|
||||
"confirmations.block.message": "Želiš li sigurno blokirati {name}?",
|
||||
"confirmations.delete.confirm": "Obriši",
|
||||
"confirmations.delete.message": "Jesi li siguran da želiš obrisati ovaj status?",
|
||||
"confirmations.delete.message": "Želiš li stvarno obrisati ovaj status?",
|
||||
"confirmations.domain_block.confirm": "Sakrij cijelu domenu",
|
||||
"confirmations.domain_block.message": "Jesi li zaista, zaista siguran da želiš blokirati sve sa {domain}? U većini slučajeva nekoliko ciljanih blokiranja ili utišavanja je dostatno i poželjnije.",
|
||||
"confirmations.domain_block.message": "Jesi li zaista, zaista siguran da želiš potpuno blokirati {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.",
|
||||
"confirmations.mute.confirm": "Utišaj",
|
||||
"confirmations.mute.message": "Jesi li siguran da želiš utišati {name}?",
|
||||
"confirmations.mute.message": "Jesi li siguran da želiš utišati {name}?",
|
||||
"confirmations.unfollow.confirm": "Unfollow",
|
||||
"confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
|
||||
"embed.instructions": "Embed this status on your website by copying the code below.",
|
||||
|
@ -69,16 +70,16 @@
|
|||
"emoji_button.flags": "Zastave",
|
||||
"emoji_button.food": "Hrana & Piće",
|
||||
"emoji_button.label": "Umetni smajlije",
|
||||
"emoji_button.nature": "Nature",
|
||||
"emoji_button.nature": "Priroda",
|
||||
"emoji_button.objects": "Objekti",
|
||||
"emoji_button.people": "Ljudi",
|
||||
"emoji_button.search": "Traži...",
|
||||
"emoji_button.symbols": "Simboli",
|
||||
"emoji_button.travel": "Putovanja i Mjesta",
|
||||
"emoji_button.travel": "Putovanja & Mjesta",
|
||||
"empty_column.community": "Lokalni timeline je prazan. Napiši nešto javno kako bi pokrenuo stvari!",
|
||||
"empty_column.hashtag": "Još ne postoji ništa s ovim hashtagom.",
|
||||
"empty_column.home": "Još ne slijediš nikoga. Posjeti {public} ili koristi tražilicu kako bi počeo i upoznao druge korisnike.",
|
||||
"empty_column.home.inactivity": "Tvoj home feed je prazan. Ako si neko vrijeme bio neaktivan, regenerirat će se uskoro.",
|
||||
"empty_column.home.inactivity": "Tvoj home feed je prazan. Ako si neko vrijeme bio neaktivan, uskoro ćese regenerirati.",
|
||||
"empty_column.home.public_timeline": "javni timeline",
|
||||
"empty_column.notifications": "Još nemaš notifikacija. Komuniciraj sa drugima kako bi započeo razgovor.",
|
||||
"empty_column.public": "Ovdje nema ništa! Napiši nešto javno, ili ručno slijedi korisnike sa drugih instanci kako bi popunio",
|
||||
|
@ -88,11 +89,11 @@
|
|||
"getting_started.faq": "FAQ",
|
||||
"getting_started.heading": "Počnimo",
|
||||
"getting_started.open_source_notice": "Mastodon je softver otvorenog koda. Možeš pridonijeti ili prijaviti probleme na GitHubu {github}.",
|
||||
"getting_started.userguide": "Vodič za korisnike",
|
||||
"getting_started.userguide": "Upute za korištenje",
|
||||
"home.column_settings.advanced": "Napredno",
|
||||
"home.column_settings.basic": "Osnovno",
|
||||
"home.column_settings.filter_regex": "Filtriraj s regularnim izrazima",
|
||||
"home.column_settings.show_reblogs": "Pokaži boosts",
|
||||
"home.column_settings.show_reblogs": "Pokaži boostove",
|
||||
"home.column_settings.show_replies": "Pokaži odgovore",
|
||||
"home.settings": "Postavke Stupca",
|
||||
"lightbox.close": "Zatvori",
|
||||
|
@ -113,7 +114,7 @@
|
|||
"navigation_bar.public_timeline": "Federalni timeline",
|
||||
"notification.favourite": "{name} je lajkao tvoj status",
|
||||
"notification.follow": "{name} te sada slijedi",
|
||||
"notification.mention": "{name} mentioned you",
|
||||
"notification.mention": "{name} te je spomenuo",
|
||||
"notification.reblog": "{name} je podigao tvoj status",
|
||||
"notifications.clear": "Očisti notifikacije",
|
||||
"notifications.clear_confirmation": "Želiš li zaista obrisati sve svoje notifikacije?",
|
||||
|
@ -123,28 +124,28 @@
|
|||
"notifications.column_settings.mention": "Spominjanja:",
|
||||
"notifications.column_settings.push": "Push notifications",
|
||||
"notifications.column_settings.push_meta": "This device",
|
||||
"notifications.column_settings.reblog": "Boosts:",
|
||||
"notifications.column_settings.reblog": "Boostovi:",
|
||||
"notifications.column_settings.show": "Prikaži u stupcu",
|
||||
"notifications.column_settings.sound": "Sviraj zvuk",
|
||||
"onboarding.done": "Učinjeno",
|
||||
"onboarding.next": "Sljedeća",
|
||||
"onboarding.page_five.public_timelines": "The local timeline prikazuje javne postove svih na {domain}. Federalni timeline pokazuje javne postove svih sa {domain} domena koje slijediš. To je sjajan način da otkriješ nove ljude.",
|
||||
"onboarding.page_four.home": "The home timeline prikazuje samo postove ljudi koje slijediš.",
|
||||
"onboarding.page_four.notifications": "Stupac notifikacija pokazuje kada je netko u interakciji s tobom.",
|
||||
"onboarding.page_one.federation": "Mastodon je mreža nezavisnih servera udruženih kako bi stvorili veću socijalnu mrežu. Te servere zovemo instance.",
|
||||
"onboarding.page_one.handle": "Ti si na {domain}, tako da je tvoj potpuni opis {handle}",
|
||||
"onboarding.page_one.welcome": "Dobro došli u Mastodon!",
|
||||
"onboarding.next": "Sljedeće",
|
||||
"onboarding.page_five.public_timelines": "Lokalni timeline prikazuje javne postove sviju od svakog na {domain}. Federalni timeline prikazuje javne postove svakog koga ljudi na {domain} slijede. To su Javni Timelineovi, sjajan način za otkriti nove ljude.",
|
||||
"onboarding.page_four.home": "The home timeline prikazuje postove ljudi koje slijediš.",
|
||||
"onboarding.page_four.notifications": "Stupac za notifikacije pokazuje poruke drugih upućene tebi.",
|
||||
"onboarding.page_one.federation": "Mastodon čini mreža neovisnih servera udruženih u jednu veću socialnu mrežu. Te servere nazivamo instancama.",
|
||||
"onboarding.page_one.handle": "Ti si na {domain}, i tvoja puna handle je {handle}",
|
||||
"onboarding.page_one.welcome": "Dobro došli na Mastodon!",
|
||||
"onboarding.page_six.admin": "Administrator tvoje instance je {admin}.",
|
||||
"onboarding.page_six.almost_done": "Još malo pa gotovo...",
|
||||
"onboarding.page_six.appetoot": "Živjeli!",
|
||||
"onboarding.page_six.apps_available": "Postoje {apps} dostupne za iOS, Android i druge platforme.",
|
||||
"onboarding.page_six.github": "Mastodon je besplatan softver otvorenog koda. Možeš prijaviti greške, zahtijevati mogućnosti, ili pridonijeti kodu na {github}.",
|
||||
"onboarding.page_six.github": "Mastodon je besplatan softver otvorenog koda. You can report bugs, request features, or contribute to the code on {github}.",
|
||||
"onboarding.page_six.guidelines": "smjernice zajednice",
|
||||
"onboarding.page_six.read_guidelines": "Molimo, pročitaj {domain}'s {guidelines}!",
|
||||
"onboarding.page_six.read_guidelines": "Molimo pročitaj {domain}'s {guidelines}!",
|
||||
"onboarding.page_six.various_app": "mobilne aplikacije",
|
||||
"onboarding.page_three.profile": "Uredi svoj profil mijenjanjem avatara, biografije i imena koje će biti prikazano. Naći ćeš i druge korisne postavke.",
|
||||
"onboarding.page_three.search": "Koristi tražilicu kako bi pronašao ljude i sadržaj sa određenim hashtagovima, kao što su {illustration} i {introductions}. Da bi našao osobu koja nije na ovoj instanci, upotrijebi njihov puni opis.",
|
||||
"onboarding.page_two.compose": "Piši postove u stupcu za njihovo sastavljanje. Možeš uploadati slike, promijeniti postavke privatnosti, i dodati upozorenja o sadržaju s ikonama ispod.",
|
||||
"onboarding.page_three.profile": "Uredi svoj profil promjenom svog avatara, biografije, i imena. Ovdje ćeš isto tako pronaći i druge postavke.",
|
||||
"onboarding.page_three.search": "Koristi tražilicu kako bi pronašao ljude i tražio hashtags, kao što su {illustration} i {introductions}. Kako bi pronašao osobu koja nije na ovoj instanci, upotrijebi njen pun handle.",
|
||||
"onboarding.page_two.compose": "Piši postove u stupcu za sastavljanje. Možeš uploadati slike, promijeniti postavke privatnosti, i dodati upozorenja o sadržaju s ikonama ispod.",
|
||||
"onboarding.skip": "Preskoči",
|
||||
"privacy.change": "Podesi status privatnosti",
|
||||
"privacy.direct.long": "Prikaži samo spomenutim korisnicima",
|
||||
|
@ -162,7 +163,7 @@
|
|||
"search.placeholder": "Traži",
|
||||
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
|
||||
"standalone.public_title": "A look inside...",
|
||||
"status.cannot_reblog": "Ovaj post ne može biti podignut",
|
||||
"status.cannot_reblog": "Ovaj post ne može biti boostan",
|
||||
"status.delete": "Obriši",
|
||||
"status.embed": "Embed",
|
||||
"status.favourite": "Označi omiljenim",
|
||||
|
@ -196,5 +197,5 @@
|
|||
"video_player.expand": "Proširi video",
|
||||
"video_player.toggle_sound": "Toggle zvuk",
|
||||
"video_player.toggle_visible": "Preklopi vidljivost",
|
||||
"video_player.video_error": "Video nije mogao biti prikazan"
|
||||
"video_player.video_error": "Video ne može biti reproduciran"
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"account.mute": "Rescondre @{name}",
|
||||
"account.posts": "Estatuts",
|
||||
"account.report": "Senhalar @{name}",
|
||||
"account.requested": "Invitacion mandada",
|
||||
"account.requested": "Invitacion mandada. Clicatz per anullar.",
|
||||
"account.share": "Partejar lo perfil a @{name}",
|
||||
"account.unblock": "Desblocar @{name}",
|
||||
"account.unblock_domain": "Desblocar {domain}",
|
||||
|
@ -63,8 +63,8 @@
|
|||
"confirmations.mute.message": "Sètz segur de voler metre en silenci {name} ?",
|
||||
"confirmations.unfollow.confirm": "Quitar de sègre",
|
||||
"confirmations.unfollow.message": "Volètz vertadièrament quitar de sègre {name} ?",
|
||||
"embed.instructions": "Embed this status on your website by copying the code below.",
|
||||
"embed.preview": "Here is what it will look like:",
|
||||
"embed.instructions": "Embarcar aqueste estatut per o far veire sus un site Internet en copiar lo còdi çai-jos.",
|
||||
"embed.preview": "Semblarà aquò : ",
|
||||
"emoji_button.activity": "Activitats",
|
||||
"emoji_button.flags": "Drapèus",
|
||||
"emoji_button.food": "Beure e manjar",
|
||||
|
@ -164,7 +164,7 @@
|
|||
"standalone.public_title": "Una ulhada dedins…",
|
||||
"status.cannot_reblog": "Aqueste estatut pòt pas èsser partejat",
|
||||
"status.delete": "Escafar",
|
||||
"status.embed": "Embed",
|
||||
"status.embed": "Embarcar",
|
||||
"status.favourite": "Apondre als favorits",
|
||||
"status.load_more": "Cargar mai",
|
||||
"status.media_hidden": "Mèdia rescondut",
|
||||
|
|
|
@ -1885,6 +1885,10 @@
|
|||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding: 0 15px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.column-back-button__icon {
|
||||
|
@ -2712,6 +2716,22 @@ button.icon-button.active i.fa-retweet {
|
|||
}
|
||||
}
|
||||
|
||||
.media-spoiler__video {
|
||||
align-items: center;
|
||||
background: $base-overlay-background;
|
||||
color: $primary-text-color;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.media-spoiler__warning {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
|
@ -4483,41 +4503,10 @@ noscript {
|
|||
}
|
||||
}
|
||||
|
||||
.embed-modal__html {
|
||||
color: $ui-secondary-color;
|
||||
outline: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
font-family: 'mastodon-font-monospace', monospace;
|
||||
background: $ui-base-color;
|
||||
color: $ui-primary-color;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&::-moz-focus-inner,
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.embed-modal {
|
||||
max-width: 80vw;
|
||||
max-height: 80vh;
|
||||
|
||||
h4 {
|
||||
padding: 30px;
|
||||
font-weight: 500;
|
||||
|
@ -4525,18 +4514,52 @@ noscript {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.hint {
|
||||
margin-bottom: 15px;
|
||||
.embed-modal__container {
|
||||
padding: 10px;
|
||||
|
||||
.hint {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.embed-modal__html {
|
||||
color: $ui-secondary-color;
|
||||
outline: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
font-family: 'mastodon-font-monospace', monospace;
|
||||
background: $ui-base-color;
|
||||
color: $ui-primary-color;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&::-moz-focus-inner,
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.embed-modal__iframe {
|
||||
width: 400px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.embed-modal__container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.embed-modal__iframe {
|
||||
width: 100%;
|
||||
min-width: 400px;
|
||||
overflow: hidden;
|
||||
border: 0;
|
||||
}
|
||||
|
|
|
@ -399,51 +399,54 @@
|
|||
|
||||
.embed {
|
||||
.activity-stream {
|
||||
border-radius: 4px;
|
||||
box-shadow: none;
|
||||
|
||||
.entry {
|
||||
&:last-child {
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
|
||||
&:first-child {
|
||||
border-radius: 4px 4px 0 0;
|
||||
.detailed-status.light {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
|
||||
&:last-child {
|
||||
border-radius: 4px;
|
||||
.detailed-status__display-name {
|
||||
flex: 1;
|
||||
margin: 0 5px 15px 0;
|
||||
}
|
||||
|
||||
.button.button-secondary.logo-button {
|
||||
flex: 0 auto;
|
||||
font-size: 14px;
|
||||
|
||||
svg {
|
||||
width: 20px;
|
||||
height: auto;
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
|
||||
path:first-child {
|
||||
fill: $ui-primary-color;
|
||||
}
|
||||
|
||||
path:last-child {
|
||||
fill: $simple-background-color;
|
||||
}
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus,
|
||||
&:hover {
|
||||
svg path:first-child {
|
||||
fill: lighten($ui-primary-color, 4%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status__content,
|
||||
.detailed-status__meta {
|
||||
flex: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button.button-secondary.logo-button {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 14px;
|
||||
font-size: 14px;
|
||||
|
||||
svg {
|
||||
width: 20px;
|
||||
height: auto;
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
|
||||
path:first-child {
|
||||
fill: $ui-primary-color;
|
||||
}
|
||||
|
||||
path:last-child {
|
||||
fill: $simple-background-color;
|
||||
}
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus,
|
||||
&:hover {
|
||||
svg path:first-child {
|
||||
fill: lighten($ui-primary-color, 4%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
|||
def status_params
|
||||
{
|
||||
uri: @object['id'],
|
||||
url: @object['url'] || @object['id'],
|
||||
url: object_url || @object['id'],
|
||||
account: @account,
|
||||
text: text_from_content || '',
|
||||
language: language_from_content,
|
||||
|
@ -147,6 +147,16 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
|||
@object['contentMap'].keys.first
|
||||
end
|
||||
|
||||
def object_url
|
||||
return if @object['url'].blank?
|
||||
|
||||
value = first_of_value(@object['url'])
|
||||
|
||||
return value if value.is_a?(String)
|
||||
|
||||
value['href']
|
||||
end
|
||||
|
||||
def language_map?
|
||||
@object['contentMap'].is_a?(Hash) && !@object['contentMap'].empty?
|
||||
end
|
||||
|
|
|
@ -65,7 +65,7 @@ class OStatus::AtomSerializer
|
|||
|
||||
add_namespaces(entry) if root
|
||||
|
||||
append_element(entry, 'id', TagManager.instance.unique_tag(stream_entry.created_at, stream_entry.activity_id, stream_entry.activity_type))
|
||||
append_element(entry, 'id', TagManager.instance.uri_for(stream_entry.status))
|
||||
append_element(entry, 'published', stream_entry.created_at.iso8601)
|
||||
append_element(entry, 'updated', stream_entry.updated_at.iso8601)
|
||||
append_element(entry, 'title', stream_entry&.status&.title || "#{stream_entry.account.acct} deleted status")
|
||||
|
@ -86,7 +86,7 @@ class OStatus::AtomSerializer
|
|||
serialize_status_attributes(entry, stream_entry.status)
|
||||
end
|
||||
|
||||
append_element(entry, 'link', nil, rel: :alternate, type: 'text/html', href: account_stream_entry_url(stream_entry.account, stream_entry))
|
||||
append_element(entry, 'link', nil, rel: :alternate, type: 'text/html', href: TagManager.instance.url_for(stream_entry.status))
|
||||
append_element(entry, 'link', nil, rel: :self, type: 'application/atom+xml', href: account_stream_entry_url(stream_entry.account, stream_entry, format: 'atom'))
|
||||
append_element(entry, 'thr:in-reply-to', nil, ref: TagManager.instance.uri_for(stream_entry.thread), href: TagManager.instance.url_for(stream_entry.thread)) if stream_entry.threaded?
|
||||
append_element(entry, 'ostatus:conversation', nil, ref: conversation_uri(stream_entry.status.conversation)) unless stream_entry&.status&.conversation_id.nil?
|
||||
|
|
|
@ -49,12 +49,17 @@ class TagManager
|
|||
|
||||
def unique_tag_to_local_id(tag, expected_type)
|
||||
return nil unless local_id?(tag)
|
||||
matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag)
|
||||
return matches[1] unless matches.nil?
|
||||
|
||||
if ActivityPub::TagManager.instance.local_uri?(tag)
|
||||
ActivityPub::TagManager.instance.uri_to_local_id(tag)
|
||||
else
|
||||
matches = Regexp.new("objectId=([\\d]+):objectType=#{expected_type}").match(tag)
|
||||
return matches[1] unless matches.nil?
|
||||
end
|
||||
end
|
||||
|
||||
def local_id?(id)
|
||||
id.start_with?("tag:#{Rails.configuration.x.local_domain}")
|
||||
id.start_with?("tag:#{Rails.configuration.x.local_domain}") || ActivityPub::TagManager.instance.local_uri?(id)
|
||||
end
|
||||
|
||||
def web_domain?(domain)
|
||||
|
@ -92,7 +97,7 @@ class TagManager
|
|||
when :person
|
||||
account_url(target)
|
||||
when :note, :comment, :activity
|
||||
unique_tag(target.created_at, target.id, 'Status')
|
||||
target.uri || unique_tag(target.created_at, target.id, 'Status')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
# reblogs_count :integer default(0), not null
|
||||
# language :string
|
||||
# conversation_id :integer
|
||||
# local :boolean
|
||||
#
|
||||
|
||||
class Status < ApplicationRecord
|
||||
|
@ -84,7 +85,7 @@ class Status < ApplicationRecord
|
|||
end
|
||||
|
||||
def local?
|
||||
uri.nil?
|
||||
attributes['local'] || uri.nil?
|
||||
end
|
||||
|
||||
def reblog?
|
||||
|
@ -131,11 +132,14 @@ class Status < ApplicationRecord
|
|||
!sensitive? && media_attachments.any?
|
||||
end
|
||||
|
||||
after_create :store_uri, if: :local?
|
||||
|
||||
before_validation :prepare_contents, if: :local?
|
||||
before_validation :set_reblog
|
||||
before_validation :set_visibility
|
||||
before_validation :set_conversation
|
||||
before_validation :set_sensitivity
|
||||
before_validation :set_local
|
||||
|
||||
class << self
|
||||
def not_in_filtered_languages(account)
|
||||
|
@ -253,6 +257,10 @@ class Status < ApplicationRecord
|
|||
|
||||
private
|
||||
|
||||
def store_uri
|
||||
update_attribute(:uri, ActivityPub::TagManager.instance.uri_for(self)) if uri.nil?
|
||||
end
|
||||
|
||||
def prepare_contents
|
||||
text&.strip!
|
||||
spoiler_text&.strip!
|
||||
|
@ -292,4 +300,8 @@ class Status < ApplicationRecord
|
|||
thread.account_id
|
||||
end
|
||||
end
|
||||
|
||||
def set_local
|
||||
self.local = account.local?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,12 +4,12 @@ class AccountRelationshipsPresenter
|
|||
attr_reader :following, :followed_by, :blocking,
|
||||
:muting, :requested, :domain_blocking
|
||||
|
||||
def initialize(account_ids, current_account_id)
|
||||
@following = Account.following_map(account_ids, current_account_id)
|
||||
@followed_by = Account.followed_by_map(account_ids, current_account_id)
|
||||
@blocking = Account.blocking_map(account_ids, current_account_id)
|
||||
@muting = Account.muting_map(account_ids, current_account_id)
|
||||
@requested = Account.requested_map(account_ids, current_account_id)
|
||||
@domain_blocking = Account.domain_blocking_map(account_ids, current_account_id)
|
||||
def initialize(account_ids, current_account_id, options = {})
|
||||
@following = Account.following_map(account_ids, current_account_id).merge(options[:following_map] || {})
|
||||
@followed_by = Account.followed_by_map(account_ids, current_account_id).merge(options[:followed_by_map] || {})
|
||||
@blocking = Account.blocking_map(account_ids, current_account_id).merge(options[:blocking_map] || {})
|
||||
@muting = Account.muting_map(account_ids, current_account_id).merge(options[:muting_map] || {})
|
||||
@requested = Account.requested_map(account_ids, current_account_id).merge(options[:requested_map] || {})
|
||||
@domain_blocking = Account.domain_blocking_map(account_ids, current_account_id).merge(options[:domain_blocking_map] || {})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
|
|||
include RoutingHelper
|
||||
|
||||
attributes :id, :type, :following, :followers,
|
||||
:inbox, :outbox, :shared_inbox,
|
||||
:inbox, :outbox,
|
||||
:preferred_username, :name, :summary,
|
||||
:url, :manually_approves_followers
|
||||
|
||||
|
@ -24,6 +24,18 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
|
|||
end
|
||||
end
|
||||
|
||||
class EndpointsSerializer < ActiveModel::Serializer
|
||||
include RoutingHelper
|
||||
|
||||
attributes :shared_inbox
|
||||
|
||||
def shared_inbox
|
||||
inbox_url
|
||||
end
|
||||
end
|
||||
|
||||
has_one :endpoints, serializer: EndpointsSerializer
|
||||
|
||||
has_one :icon, serializer: ImageSerializer, if: :avatar_exists?
|
||||
has_one :image, serializer: ImageSerializer, if: :header_exists?
|
||||
|
||||
|
@ -51,8 +63,8 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer
|
|||
account_outbox_url(object)
|
||||
end
|
||||
|
||||
def shared_inbox
|
||||
inbox_url
|
||||
def endpoints
|
||||
object
|
||||
end
|
||||
|
||||
def preferred_username
|
||||
|
|
|
@ -40,8 +40,7 @@ class OEmbedSerializer < ActiveModel::Serializer
|
|||
attributes = {
|
||||
src: embed_short_account_status_url(object.account, object),
|
||||
class: 'mastodon-embed',
|
||||
frameborder: '0',
|
||||
scrolling: 'no',
|
||||
style: 'max-width: 100%; border: none;',
|
||||
width: width,
|
||||
height: height,
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ class ActivityPub::ProcessAccountService < BaseService
|
|||
# Should be called with confirmed valid JSON
|
||||
# and WebFinger-resolved username and domain
|
||||
def call(username, domain, json)
|
||||
return unless json['inbox'].present?
|
||||
return if json['inbox'].blank?
|
||||
|
||||
@json = json
|
||||
@uri = @json['id']
|
||||
|
@ -42,9 +42,9 @@ class ActivityPub::ProcessAccountService < BaseService
|
|||
@account.protocol = :activitypub
|
||||
@account.inbox_url = @json['inbox'] || ''
|
||||
@account.outbox_url = @json['outbox'] || ''
|
||||
@account.shared_inbox_url = @json['sharedInbox'] || ''
|
||||
@account.shared_inbox_url = (@json['endpoints'].is_a?(Hash) ? @json['endpoints']['sharedInbox'] : @json['sharedInbox']) || ''
|
||||
@account.followers_url = @json['followers'] || ''
|
||||
@account.url = @json['url'] || @uri
|
||||
@account.url = url || @uri
|
||||
@account.display_name = @json['name'] || ''
|
||||
@account.note = @json['summary'] || ''
|
||||
@account.avatar_remote_url = image_url('icon')
|
||||
|
@ -62,7 +62,7 @@ class ActivityPub::ProcessAccountService < BaseService
|
|||
value = first_of_value(@json[key])
|
||||
|
||||
return if value.nil?
|
||||
return @json[key]['url'] if @json[key].is_a?(Hash)
|
||||
return value['url'] if value.is_a?(Hash)
|
||||
|
||||
image = fetch_resource(value)
|
||||
image['url'] if image
|
||||
|
@ -78,6 +78,16 @@ class ActivityPub::ProcessAccountService < BaseService
|
|||
key['publicKeyPem'] if key
|
||||
end
|
||||
|
||||
def url
|
||||
return if @json['url'].blank?
|
||||
|
||||
value = first_of_value(@json['url'])
|
||||
|
||||
return value if value.is_a?(String)
|
||||
|
||||
value['href']
|
||||
end
|
||||
|
||||
def auto_suspend?
|
||||
domain_block && domain_block.suspend?
|
||||
end
|
||||
|
|
|
@ -41,7 +41,7 @@ class ProcessMentionsService < BaseService
|
|||
NotifyService.new.call(mentioned_account, mention)
|
||||
elsif mentioned_account.ostatus? && (Rails.configuration.x.use_ostatus_privacy || !status.stream_entry.hidden?)
|
||||
NotificationWorker.perform_async(stream_entry_to_xml(status.stream_entry), status.account_id, mentioned_account.id)
|
||||
elsif mentioned_account.activitypub? && !mentioned_account.following?(status.account)
|
||||
elsif mentioned_account.activitypub?
|
||||
ActivityPub::DeliveryWorker.perform_async(build_json(mention.status), mention.status.account_id, mentioned_account.inbox_url)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,13 +21,13 @@
|
|||
= stylesheet_pack_tag 'common', media: 'all'
|
||||
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
|
||||
|
||||
= javascript_pack_tag 'features/getting_started', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script'
|
||||
= javascript_pack_tag 'features/compose', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script'
|
||||
= javascript_pack_tag 'features/home_timeline', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script'
|
||||
= javascript_pack_tag 'features/notifications', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script'
|
||||
= javascript_pack_tag 'features/community_timeline', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script'
|
||||
= javascript_pack_tag 'features/public_timeline', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script'
|
||||
= javascript_pack_tag 'emojione_picker', integrity: true, crossorigin: 'anonymous', rel: 'preload', as: 'script'
|
||||
%link{ href: asset_pack_path('features/getting_started.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/
|
||||
%link{ href: asset_pack_path('features/compose.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/
|
||||
%link{ href: asset_pack_path('features/home_timeline.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/
|
||||
%link{ href: asset_pack_path('features/notifications.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/
|
||||
%link{ href: asset_pack_path('features/community_timeline.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/
|
||||
%link{ href: asset_pack_path('features/public_timeline.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/
|
||||
%link{ href: asset_pack_path('emojione_picker.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/
|
||||
|
||||
= javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous'
|
||||
= csrf_meta_tags
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
.detailed-status.light
|
||||
- if embedded_view?
|
||||
= link_to "web+mastodon://follow?uri=#{status.account.local_username_and_domain}", class: 'button button-secondary logo-button', target: '_new' do
|
||||
= render file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')
|
||||
= t('accounts.follow')
|
||||
|
||||
= link_to TagManager.instance.url_for(status.account), class: 'detailed-status__display-name p-author h-card', target: stream_link_target, rel: 'noopener' do
|
||||
%div
|
||||
.avatar
|
||||
|
@ -12,6 +7,11 @@
|
|||
%strong.p-name.emojify= display_name(status.account)
|
||||
%span= acct(status.account)
|
||||
|
||||
- if embedded_view?
|
||||
= link_to "web+mastodon://follow?uri=#{status.account.local_username_and_domain}", class: 'button button-secondary logo-button', target: '_new' do
|
||||
= render file: Rails.root.join('app', 'javascript', 'images', 'logo.svg')
|
||||
= t('accounts.follow')
|
||||
|
||||
.status__content.p-name.emojify<
|
||||
- if status.spoiler_text?
|
||||
%p{ style: 'margin-bottom: 0' }<
|
||||
|
|
|
@ -6,7 +6,7 @@ class Pubsubhubbub::DistributionWorker
|
|||
sidekiq_options queue: 'push'
|
||||
|
||||
def perform(stream_entry_ids)
|
||||
stream_entries = StreamEntry.where(id: stream_entry_ids).includes(:status).reject { |e| e.status&.direct_visibility? }
|
||||
stream_entries = StreamEntry.where(id: stream_entry_ids).includes(:status).reject { |e| e.status.nil? || e.status.direct_visibility? }
|
||||
|
||||
return if stream_entries.empty?
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
class Pubsubhubbub::SubscribeWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'push', retry: 10, unique: :until_executed, dead: false
|
||||
sidekiq_options queue: 'push', retry: 10, unique: :until_executed, dead: false, unique_retry: true
|
||||
|
||||
sidekiq_retry_in do |count|
|
||||
case count
|
||||
|
|
11
app/workers/resolve_remote_account_worker.rb
Normal file
11
app/workers/resolve_remote_account_worker.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ResolveRemoteAccountWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: 'pull', unique: :until_executed
|
||||
|
||||
def perform(uri)
|
||||
ResolveRemoteAccountService.new.call(uri)
|
||||
end
|
||||
end
|
|
@ -10,6 +10,7 @@ require_relative '../app/lib/exceptions'
|
|||
require_relative '../lib/paperclip/gif_transcoder'
|
||||
require_relative '../lib/paperclip/video_transcoder'
|
||||
require_relative '../lib/mastodon/version'
|
||||
require_relative '../lib/mastodon/unique_retry_job_middleware'
|
||||
|
||||
Dotenv::Railtie.load
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ Rails.application.configure do
|
|||
config.x.web_domain = web_host
|
||||
config.x.use_https = https
|
||||
config.x.use_s3 = ENV['S3_ENABLED'] == 'true'
|
||||
config.x.use_swift = ENV['SWIFT_ENABLED'] == 'true'
|
||||
|
||||
config.x.alternate_domains = alternate_domains.split(/\s*,\s*/)
|
||||
|
||||
|
|
|
@ -40,6 +40,21 @@ if ENV['S3_ENABLED'] == 'true'
|
|||
Paperclip::Attachment.default_options[:url] = ':s3_alias_url'
|
||||
Paperclip::Attachment.default_options[:s3_host_alias] = ENV['S3_CLOUDFRONT_HOST']
|
||||
end
|
||||
elsif ENV['SWIFT_ENABLED'] == 'true'
|
||||
Paperclip::Attachment.default_options.merge!(
|
||||
path: ':class/:attachment/:id_partition/:style/:filename',
|
||||
storage: :fog,
|
||||
fog_credentials: {
|
||||
provider: 'OpenStack',
|
||||
openstack_username: ENV.fetch('SWIFT_USERNAME'),
|
||||
openstack_tenant: ENV.fetch('SWIFT_TENANT'),
|
||||
openstack_api_key: ENV.fetch('SWIFT_PASSWORD'),
|
||||
openstack_auth_url: ENV.fetch('SWIFT_AUTH_URL'),
|
||||
},
|
||||
fog_directory: ENV.fetch('SWIFT_CONTAINER'),
|
||||
fog_host: ENV.fetch('SWIFT_OBJECT_URL'),
|
||||
fog_public: true
|
||||
)
|
||||
else
|
||||
Paperclip::Attachment.default_options[:path] = (ENV['PAPERCLIP_ROOT_PATH'] || ':rails_root/public/system') + '/:class/:attachment/:id_partition/:style/:filename'
|
||||
Paperclip::Attachment.default_options[:url] = (ENV['PAPERCLIP_ROOT_URL'] || '/system') + '/:class/:attachment/:id_partition/:style/:filename'
|
||||
|
|
|
@ -13,4 +13,7 @@ end
|
|||
|
||||
Sidekiq.configure_client do |config|
|
||||
config.redis = redis_params
|
||||
config.client_middleware do |chain|
|
||||
chain.add Mastodon::UniqueRetryJobMiddleware
|
||||
end
|
||||
end
|
||||
|
|
|
@ -47,16 +47,16 @@ ar:
|
|||
datetime:
|
||||
distance_in_words:
|
||||
about_x_hours: "%{count}سا"
|
||||
about_x_months: "%{count}شهر"
|
||||
about_x_years: "%{count}سنة"
|
||||
almost_x_years: "%{count}سنوات"
|
||||
half_a_minute: Just now
|
||||
less_than_x_minutes: "%{count}د"
|
||||
about_x_months: "%{count} شهر"
|
||||
about_x_years: "%{count} سنة"
|
||||
almost_x_years: "%{count} سنوات"
|
||||
half_a_minute: الآن
|
||||
less_than_x_minutes: "%{count} د"
|
||||
less_than_x_seconds: الآن
|
||||
over_x_years: "%{count}سنين"
|
||||
x_days: "%{count}أيام"
|
||||
over_x_years: "%{count} سنين"
|
||||
x_days: "%{count} أيام"
|
||||
x_minutes: "%{count}د"
|
||||
x_months: "%{count}شه"
|
||||
x_months: "%{count} شه"
|
||||
x_seconds: "%{count}ث"
|
||||
exports:
|
||||
blocks: قمت بحظر
|
||||
|
@ -94,7 +94,7 @@ ar:
|
|||
one: "إشعار واحد منذ زيارتك الأخيرة \U0001F418"
|
||||
other: "%{count} إشعارات جديدة منذ زيارتك الأخيرة \U0001F418"
|
||||
favourite:
|
||||
body: 'Your status was favourited by %{name}:'
|
||||
body: 'أُعجب %{name} بمنشورك'
|
||||
subject: "%{name} favourited your status"
|
||||
follow:
|
||||
body: "%{name} من متتبعيك الآن !"
|
||||
|
@ -108,6 +108,17 @@ ar:
|
|||
reblog:
|
||||
body: 'Your status was boosted by %{name}:'
|
||||
subject: "%{name} boosted your status"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: التالي
|
||||
prev: السابق
|
||||
|
@ -148,7 +159,7 @@ ar:
|
|||
enabled_success: تم تفعيل إثبات الهوية المزدوج بنجاح
|
||||
instructions_html: "<strong>Scan this QR code into Google Authenticator or a similiar TOTP app on your phone</strong>. From now on, that app will generate tokens that you will have to enter when logging in."
|
||||
manual_instructions: 'If you can''t scan the QR code and need to enter it manually, here is the plain-text secret:'
|
||||
setup: Set up
|
||||
setup: تنشيط
|
||||
wrong_code: الرمز الذي أدخلته غير صالح. تحقق من صحة الوقت على الخادم و الجهاز.
|
||||
users:
|
||||
invalid_email: عنوان البريد الإلكتروني غير صالح
|
||||
|
|
|
@ -108,6 +108,17 @@ bg:
|
|||
reblog:
|
||||
body: 'Твоята публикация беше споделена от %{name}:'
|
||||
subject: "%{name} сподели публикацията ти"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Напред
|
||||
prev: Назад
|
||||
|
|
|
@ -340,6 +340,17 @@ ca:
|
|||
reblog:
|
||||
body: "%{name} ha retootejat el teu estat"
|
||||
subject: "%{name} ha retootejat el teu estat"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Pròxim
|
||||
prev: Anterior
|
||||
|
|
|
@ -12,15 +12,15 @@ de:
|
|||
source_code: Quellcode
|
||||
status_count_after: Beiträge verfassten
|
||||
status_count_before: die
|
||||
user_count_after: Benutzer
|
||||
user_count_after: Profile
|
||||
user_count_before: Heimat für
|
||||
accounts:
|
||||
follow: Folgen
|
||||
followers: Folgende
|
||||
following: Folgt
|
||||
nothing_here: Hier gibt es nichts!
|
||||
people_followed_by: Nutzer, denen %{name} folgt
|
||||
people_who_follow: Nutzer, die %{name} folgen
|
||||
people_followed_by: Profile, denen %{name} folgt
|
||||
people_who_follow: Profile, die %{name} folgen
|
||||
posts: Beiträge
|
||||
remote_follow: Folgen
|
||||
unfollow: Entfolgen
|
||||
|
@ -67,7 +67,7 @@ de:
|
|||
title: Konten
|
||||
undo_silenced: Stummschaltung zurücknehmen
|
||||
undo_suspension: Sperre zurücknehmen
|
||||
username: Benutzername
|
||||
username: Profilname
|
||||
web: Web
|
||||
domain_blocks:
|
||||
add_new: Neu hinzufügen
|
||||
|
@ -124,7 +124,7 @@ de:
|
|||
settings:
|
||||
contact_information:
|
||||
email: Eine öffentliche E-Mail-Adresse angeben
|
||||
username: Einen Benutzernamen angeben
|
||||
username: Einen Profilnamen angeben
|
||||
registrations:
|
||||
closed_message:
|
||||
desc_html: Wird auf der Frontseite angezeigt, wenn die Registrierung geschlossen ist<br>Du kannst HTML-Tags benutzen
|
||||
|
@ -208,7 +208,7 @@ de:
|
|||
following: Folgeliste
|
||||
muting: Stummschaltungsliste
|
||||
upload: Hochladen
|
||||
landing_strip_html: "<strong>%{name}</strong> ist ein Benutzer auf %{link_to_root_path}. Du kannst ihm folgen oder mit ihm interagieren, sofern du ein Konto irgendwo in der Fediverse hast."
|
||||
landing_strip_html: "<strong>%{name}</strong> hat ein Profil auf %{link_to_root_path}. Du kannst folgen oder interagieren, sofern du ein Konto irgendwo im Fediversum hast."
|
||||
landing_strip_signup_html: Wenn nicht, kannst du dich <a href="%{sign_up_path}">hier anmelden</a>.
|
||||
media_attachments:
|
||||
validations:
|
||||
|
@ -239,12 +239,23 @@ de:
|
|||
reblog:
|
||||
body: 'Dein Beitrag wurde von %{name} geteilt:'
|
||||
subject: "%{name} teilte deinen Beitrag."
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Vorwärts
|
||||
prev: Zurück
|
||||
truncate: "…"
|
||||
remote_follow:
|
||||
acct: Dein Nutzername@Domain, von dem aus du dieser Person folgen möchtest.
|
||||
acct: Dein Profilname@Domain, von dem aus du dieser Person folgen möchtest.
|
||||
missing_resource: Die erforderliche Weiterleitungs-URL konnte leider in deinem Profil nicht gefunden werden.
|
||||
proceed: Weiter
|
||||
prompt: 'Du wirst dieser Person folgen:'
|
||||
|
|
|
@ -77,7 +77,7 @@ de:
|
|||
invalid_grant: Die bereitgestellte Autorisierung ist inkorrekt, abgelaufen, widerrufen, ist mit einem anderen Client verknüpft oder der Redirection URI stimmt nicht mit der Autorisierungs-Anfrage überein.
|
||||
invalid_redirect_uri: Der Redirect-URI in der Anfrage ist ungültig.
|
||||
invalid_request: Die Anfrage enthält einen nicht-unterstützten Parameter, ein Parameter fehlt oder sie ist anderweitig fehlerhaft.
|
||||
invalid_resource_owner: Die angegebenen Zugangsdaten für den "Resource Owner" sind inkorrekt oder dieser Benutzer existiert nicht.
|
||||
invalid_resource_owner: Die angegebenen Zugangsdaten für den "Resource Owner" sind inkorrekt oder dieses Profil existiert nicht.
|
||||
invalid_scope: Der angeforderte Scope ist inkorrekt, unbekannt oder fehlerhaft.
|
||||
invalid_token:
|
||||
expired: Der Zugriffstoken ist abgelaufen
|
||||
|
@ -108,6 +108,6 @@ de:
|
|||
application:
|
||||
title: OAuth-Autorisierung nötig
|
||||
scopes:
|
||||
follow: Nutzer folgen, blocken, entblocken und entfolgen
|
||||
follow: Profil folgen, blocken, entblocken und entfolgen
|
||||
read: deine Daten lesen
|
||||
write: Beiträge von deinem Konto aus veröffentlichen
|
||||
|
|
|
@ -3,8 +3,10 @@ fa:
|
|||
activerecord:
|
||||
attributes:
|
||||
doorkeeper/application:
|
||||
name: Name
|
||||
name: Application name
|
||||
redirect_uri: Redirect URI
|
||||
scopes: Scopes
|
||||
website: Application website
|
||||
errors:
|
||||
models:
|
||||
doorkeeper/application:
|
||||
|
@ -33,18 +35,22 @@ fa:
|
|||
redirect_uri: Use one line per URI
|
||||
scopes: Separate scopes with spaces. Leave blank to use the default scopes.
|
||||
index:
|
||||
application: Application
|
||||
callback_url: Callback URL
|
||||
delete: Delete
|
||||
name: Name
|
||||
new: New Application
|
||||
new: New application
|
||||
scopes: Scopes
|
||||
show: Show
|
||||
title: Your applications
|
||||
new:
|
||||
title: New Application
|
||||
title: New application
|
||||
show:
|
||||
actions: Actions
|
||||
application_id: Application Id
|
||||
callback_urls: Callback urls
|
||||
application_id: Client key
|
||||
callback_urls: Callback URLs
|
||||
scopes: Scopes
|
||||
secret: Secret
|
||||
secret: Client secret
|
||||
title: 'Application: %{name}'
|
||||
authorizations:
|
||||
buttons:
|
||||
|
|
|
@ -5,6 +5,8 @@ oc:
|
|||
doorkeeper/application:
|
||||
name: Nom
|
||||
redirect_uri: URL de redireccion
|
||||
scopes: Encastres
|
||||
website: Aplicacion web
|
||||
errors:
|
||||
models:
|
||||
doorkeeper/application:
|
||||
|
@ -33,9 +35,13 @@ oc:
|
|||
redirect_uri: Utilizatz una linha per URI
|
||||
scopes: Separatz los encastres amb d’espacis. Daissatz void per utilizar l’encastre per defaut.
|
||||
index:
|
||||
application: Aplicacion
|
||||
callback_url: URL de rapèl
|
||||
delete: Suprimir
|
||||
name: Nom
|
||||
new: Nòva aplicacion
|
||||
scopes: Encastres
|
||||
show: Veire
|
||||
title: Vòstras aplicacions
|
||||
new:
|
||||
title: Nòva aplicacion
|
||||
|
|
|
@ -103,6 +103,17 @@ eo:
|
|||
reblog:
|
||||
body: "%{name} diskonigis vian mesaĝon:"
|
||||
subject: "%{name} diskonigis vian mesaĝon"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Sekva
|
||||
prev: Malsekva
|
||||
|
|
|
@ -108,6 +108,17 @@ es:
|
|||
reblog:
|
||||
body: "%{name} ha retooteado tu estado"
|
||||
subject: "%{name} ha retooteado tu estado"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Próximo
|
||||
prev: Anterior
|
||||
|
|
|
@ -339,6 +339,17 @@ fa:
|
|||
reblog:
|
||||
body: "%{name} نوشتهٔ شما را بازبوقید:"
|
||||
subject: "%{name} نوشتهٔ شما را بازبوقید"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: بعدی
|
||||
prev: قبلی
|
||||
|
|
|
@ -103,6 +103,17 @@ fi:
|
|||
reblog:
|
||||
body: 'Sinun statustasi boostasi %{name}:'
|
||||
subject: "%{name} boostasi statustasi"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Seuraava
|
||||
prev: Edellinen
|
||||
|
|
|
@ -358,6 +358,17 @@ fr:
|
|||
reblog:
|
||||
body: "%{name} a partagé votre statut :"
|
||||
subject: "%{name} a partagé votre statut"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Suivant
|
||||
prev: Précédent
|
||||
|
|
|
@ -264,6 +264,17 @@ he:
|
|||
reblog:
|
||||
body: 'חצרוצך הודהד על ידי %{name}:'
|
||||
subject: חצרוצך הודהד על ידי%{name}
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: הבא
|
||||
prev: הקודם
|
||||
|
|
|
@ -105,6 +105,17 @@ hr:
|
|||
reblog:
|
||||
body: 'Tvoj status je potaknut od %{name}:'
|
||||
subject: "%{name} je potakao tvoj status"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Sljedeći
|
||||
prev: Prošli
|
||||
|
|
|
@ -45,6 +45,17 @@ hu:
|
|||
reblog:
|
||||
body: 'Az állapotod reblogolta %{name}:'
|
||||
subject: "%{name} reblogolta az állapotod"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Következő
|
||||
prev: Előző
|
||||
|
|
|
@ -254,6 +254,17 @@ id:
|
|||
reblog:
|
||||
body: 'Status anda di-boost oleh %{name}:'
|
||||
subject: "%{name} mem-boost status anda"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Selanjutnya
|
||||
prev: Sebelumnya
|
||||
|
|
|
@ -239,6 +239,17 @@ io:
|
|||
reblog:
|
||||
body: "%{name} diskonocigis tua mesajo:"
|
||||
subject: "%{name} diskonocigis tua mesajo"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Sequanta
|
||||
prev: Preiranta
|
||||
|
|
|
@ -108,6 +108,17 @@ it:
|
|||
reblog:
|
||||
body: 'Il tuo status è stato condiviso da %{name}:'
|
||||
subject: "%{name} ha condiviso il tuo status"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Avanti
|
||||
prev: Indietro
|
||||
|
|
|
@ -1,29 +1,52 @@
|
|||
---
|
||||
ko:
|
||||
about:
|
||||
about_mastodon_html: Mastodon 은<em>자유로운 오픈 소스</em>소셜 네트워크입니다. 상용 플랫폼의 대체로써 <em>분산형 구조</em>를 채택해, 여러분의 대화가 한 회사에 독점되는 것을 방지합니다. 신뢰할 수 있는 인스턴스를 선택하세요 — 어떤 인스턴스를 고르더라도, 누구와도 대화할 수 있습니다. 누구나 자신만의 Mastodon 인스턴스를 만들 수 있으며, Seamless하게 <em>소셜 네트워크</em>에 참가할 수 있습니다.
|
||||
about_mastodon_html: Mastodon은 <em>오픈 소스 기반의</em> 소셜 네트워크 서비스 입니다. 상용 플랫폼의 대체로서 <em>분산형 구조</em>를 채택해, 여러분의 대화가 한 회사에 독점되는 것을 방지합니다. 신뢰할 수 있는 인스턴스를 선택하세요 — 어떤 인스턴스를 고르더라도, 누구와도 대화할 수 있습니다. 누구나 자신만의 Mastodon 인스턴스를 만들 수 있으며, Seamless하게 <em>소셜 네트워크</em>에 참가할 수 있습니다.
|
||||
about_this: 이 인스턴스에 대해서
|
||||
closed_registrations: 현재 이 인스턴스에서는 신규 등록을 받고 있지 않습니다.
|
||||
contact: 연락처
|
||||
description_headline: "%{domain} 는 무엇인가요?"
|
||||
contact_missing: 미설정
|
||||
contact_unavailable: N/A
|
||||
description_headline: "%{domain} (은)는 무엇인가요?"
|
||||
domain_count_after: 개의 인스턴스
|
||||
domain_count_before: 연결됨
|
||||
domain_count_before: 연결된
|
||||
extended_description_html: |
|
||||
<h3>룰을 작성하는 장소</h3>
|
||||
<p>아직 설명이 작성되지 않았습니다.</p>
|
||||
features:
|
||||
humane_approach_body: 다른 SNS의 실패를 교훈삼아, Mastodon은 소셜미디어가 잘못 사용되는 것을 막기 위하여 윤리적인 설계를 추구합니다.
|
||||
humane_approach_title: 보다 배려를 의식한 설계를 추구
|
||||
not_a_product_body: Mastodon은 이익을 추구하는 SNS가 아닙니다. 그러므로 광고와 데이터의 수집 및 분석이 존재하지 않고, 유저를 구속하지도 않습니다.
|
||||
not_a_product_title: 여러분은 사람이며, 상품이 아닙니다.
|
||||
real_conversation_body: 자유롭게 사용할 수 있는 500문자의 메세지와 미디어 경고 내용을 바탕으로, 자기자신을 자유롭게 표현할 수 있습니다.
|
||||
real_conversation_title: 진정한 커뮤니케이션을 위하여
|
||||
within_reach_body: 개발자 친화적인 API에 의해서 실현된 iOS나 Android, 그 외의 여러 Platform들 덕분에 어디서든 친구들과 자유롭게 메세지를 주고 받을 수 있습니다.
|
||||
within_reach_title: 언제나 유저의 곁에서
|
||||
find_another_instance: 다른 인스턴스 찾기
|
||||
generic_description: "%{domain} 은 Mastodon의 인스턴스 입니다."
|
||||
hosted_on: Mastodon hosted on %{domain}
|
||||
learn_more: 자세히
|
||||
other_instances: 다른 인스턴스
|
||||
source_code: 소스 코드
|
||||
status_count_after: Toot
|
||||
status_count_before: Toot 수
|
||||
user_count_after: 명
|
||||
user_count_before: 사용자 수
|
||||
what_is_mastodon: Mastodon이란?
|
||||
accounts:
|
||||
follow: 팔로우
|
||||
followers: 팔로워
|
||||
following: 팔로잉
|
||||
media: 미디어
|
||||
nothing_here: 아무 것도 없습니다.
|
||||
people_followed_by: "%{name} 님이 팔로우 중인 계정"
|
||||
people_who_follow: "%{name} 님을 팔로우 중인 계정"
|
||||
posts: 포스트
|
||||
posts: Toot
|
||||
posts_with_replies: Toot와 답장
|
||||
remote_follow: 리모트 팔로우
|
||||
reserved_username: 이 아이디는 예약되어 있습니다.
|
||||
roles:
|
||||
admin: Admin
|
||||
unfollow: 팔로우 해제
|
||||
admin:
|
||||
accounts:
|
||||
|
@ -38,6 +61,7 @@ ko:
|
|||
feed_url: 피드 URL
|
||||
followers: 팔로워 수
|
||||
follows: 팔로잉 수
|
||||
inbox_url: Inbox URL
|
||||
ip: IP
|
||||
location:
|
||||
all: 전체
|
||||
|
@ -57,8 +81,10 @@ ko:
|
|||
alphabetic: 알파벳 순
|
||||
most_recent: 최근 활동 순
|
||||
title: 순서
|
||||
outbox_url: Outbox URL
|
||||
perform_full_suspension: 완전히 정지시키기
|
||||
profile_url: 프로필 URL
|
||||
protocol: Protocol
|
||||
public: 전체 공개
|
||||
push_subscription_expires: PuSH 구독 기간 만료
|
||||
redownload: 아바타 업데이트
|
||||
|
@ -90,12 +116,14 @@ ko:
|
|||
hint: 도메인 차단은 내부 데이터베이스에 계정이 생성되는 것까지는 막을 수 없지만, 그 도메인에서 생성된 계정에 자동적으로 특정한 모더레이션을 적용하게 할 수 있습니다.
|
||||
severity:
|
||||
desc_html: "<strong>침묵</strong>은 계정을 팔로우 하지 않고 있는 사람들에겐 계정의 Toot을 보이지 않게 합니다. <strong>정지</strong>는 계정의 컨텐츠, 미디어, 프로필 데이터를 삭제합니다."
|
||||
noop: 없음
|
||||
silence: 침묵
|
||||
suspend: 정지
|
||||
title: 새로운 도메인 차단
|
||||
reject_media: 미디어 파일 거부하기
|
||||
reject_media_hint: 로컬에 저장된 미디어 파일을 삭제하고, 이후로도 다운로드를 거부합니다. 정지하고는 관계 없습니다.
|
||||
severities:
|
||||
noop: 없음
|
||||
silence: 침묵
|
||||
suspend: 정지
|
||||
severity: 심각도
|
||||
|
@ -146,16 +174,41 @@ ko:
|
|||
closed_message:
|
||||
desc_html: 신규 등록을 받지 않을 때 프론트 페이지에 표시됩니다. <br>HTML 태그를 사용할 수 있습니다.
|
||||
title: 신규 등록 정지 시 메시지
|
||||
deletion:
|
||||
desc_html: 유저가 자신의 계정을 삭제할 수 있도록 설정합니다.
|
||||
title: 계정 삭제를 허가함
|
||||
open:
|
||||
title: 신규 등록을 받음
|
||||
desc_html: 유저가 자신의 계정을 생성할 수 있도록 설정합니다.
|
||||
title: 신규 계정 등록을 받음
|
||||
site_description:
|
||||
desc_html: 탑 페이지와 meta 태그에 사용됩니다.<br>HTML 태그, 예를 들어<code><a></code> 태그와 <code><em></code> 태그를 사용할 수 있습니다.
|
||||
title: 사이트 설명
|
||||
site_description_extended:
|
||||
desc_html: 인스턴스 정보 페이지에 표시됩니다.<br>HTML 태그를 사용할 수 있습니다.
|
||||
title: 사이트 상세 설명
|
||||
site_terms:
|
||||
desc_html: 당신은 독자적인 개인정보 취급 방침이나 이용약관, 그 외의 법적 근거를 작성할 수 있습니다. 또한 HTML태그를 사용할 수 있습니다.
|
||||
title: 커스텀 서비스 이용 약관
|
||||
site_title: 사이트 이름
|
||||
timeline_preview:
|
||||
desc_html: Landing page에 공개 타임라인을 표시합니다.
|
||||
title: 타임라인 프리뷰
|
||||
title: 사이트 설정
|
||||
statuses:
|
||||
back_to_account: 계정으로 돌아가기
|
||||
batch:
|
||||
delete: 삭제
|
||||
nsfw_off: NSFW 끄기
|
||||
nsfw_on: NSFW 켜기
|
||||
execute: 실행
|
||||
failed_to_execute: 실행이 실패하였습니다.
|
||||
media:
|
||||
hide: 미디어 숨기기
|
||||
show: 미디어 보여주기
|
||||
title: 미디어
|
||||
no_media: 미디어 없음
|
||||
title: 계정 Toot
|
||||
with_media: 미디어 있음
|
||||
subscriptions:
|
||||
callback_url: 콜백 URL
|
||||
confirmed: 확인됨
|
||||
|
@ -173,13 +226,21 @@ ko:
|
|||
signature: Mastodon %{instance} 인스턴스로에서 알림
|
||||
view: 'View:'
|
||||
applications:
|
||||
created: 어플리케이션이 작성되었습니다.
|
||||
destroyed: 어플리케이션이 삭제되었습니다.
|
||||
invalid_url: 올바르지 않은 URL입니다
|
||||
regenerate_token: 토큰 재생성
|
||||
token_regenerated: 액세스 토큰이 재생성되었습니다.
|
||||
warning: 이 데이터는 다른 사람들과 절대로 공유하지 마세요.
|
||||
your_token: 액세스 토큰
|
||||
auth:
|
||||
agreement_html: 이 등록으로 <a href="%{rules_path}">이용규약</a> 과 <a href="%{terms_path}">개인정보 취급 방침</a>에 동의하는 것으로 간주됩니다.
|
||||
change_password: 보안
|
||||
delete_account: 계정 삭제
|
||||
delete_account_html: 계정을 삭제하고 싶은 경우, <a href="%{path}">여기서</a> 삭제할 수 있습니다. 삭제 전 확인 화면이 표시됩니다.
|
||||
didnt_get_confirmation: 확인 메일을 받지 못하셨습니까?
|
||||
forgot_password: 비밀번호를 잊어버리셨습니까?
|
||||
invalid_reset_password_token: 비밀번호 리셋 토큰이 올바르지 못하거나 기간이 만료되었습니다. 다시 요청해주세요.
|
||||
login: 로그인
|
||||
logout: 로그아웃
|
||||
register: 등록하기
|
||||
|
@ -189,6 +250,12 @@ ko:
|
|||
authorize_follow:
|
||||
error: 리모트 팔로우 도중 오류가 발생했습니다.
|
||||
follow: 팔로우
|
||||
follow_request: '당신은 다음 계정에 팔로우 신청을 했습니다:'
|
||||
following: '성공! 당신은 다음 계정을 팔로우 하고 있습니다:'
|
||||
post_follow:
|
||||
close: 혹은, 당신은 이 윈도우를 닫을 수 있습니다
|
||||
return: 유저 프로필로 돌아가기
|
||||
web: 웹으로 가기
|
||||
title: "%{acct} 를 팔로우"
|
||||
datetime:
|
||||
distance_in_words:
|
||||
|
@ -271,8 +338,8 @@ ko:
|
|||
one: "1건의 새로운 알림 \U0001F418"
|
||||
other: "%{count}건의 새로운 알림 \U0001F418"
|
||||
favourite:
|
||||
body: "%{name} 님이 내 Toot을 즐겨찾기에 등록했습니다."
|
||||
subject: "%{name} 님이 내 Toot을 즐겨찾기에 등록했습니다"
|
||||
body: "%{name} 님이 내 Toot를 즐겨찾기에 등록했습니다."
|
||||
subject: "%{name} 님이 내 Toot를 즐겨찾기에 등록했습니다"
|
||||
follow:
|
||||
body: "%{name} 님이 나를 팔로우 했습니다"
|
||||
subject: "%{name} 님이 나를 팔로우 했습니다"
|
||||
|
@ -285,10 +352,35 @@ ko:
|
|||
reblog:
|
||||
body: "%{name} 님이 내 Toot을 부스트 했습니다:"
|
||||
subject: "%{name} 님이 내 Toot을 부스트 했습니다"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: 다음
|
||||
prev: 이전
|
||||
truncate: "…"
|
||||
push_notifications:
|
||||
favourite:
|
||||
title: "%{name} 님이 당신의 Toot를 즐겨찾기에 등록했습니다."
|
||||
follow:
|
||||
title: "%{name} 님이 나를 팔로우 하고 있습니다."
|
||||
group:
|
||||
title: "%{count} 건의 알림"
|
||||
mention:
|
||||
action_boost: 부스트
|
||||
action_expand: 더보기
|
||||
action_favourite: 즐겨찾기
|
||||
title: "%{name} 님이 답장을 보냈습니다"
|
||||
reblog:
|
||||
title: "%{name} 님이 당신의 Toot를 부스트 했습니다."
|
||||
remote_follow:
|
||||
acct: 아이디@도메인을 입력해 주십시오
|
||||
missing_resource: 리디렉션 대상을 찾을 수 없습니다
|
||||
|
@ -330,11 +422,14 @@ ko:
|
|||
windows: Windows
|
||||
windows_mobile: Windows Mobile
|
||||
windows_phone: Windows Phone
|
||||
revoke: 삭제
|
||||
revoke_success: 세션이 삭제되었습니다.
|
||||
title: 세션
|
||||
settings:
|
||||
authorized_apps: 인증된 어플리케이션
|
||||
back: 돌아가기
|
||||
delete: 계정 삭제
|
||||
development: 개발
|
||||
edit_profile: 프로필 편집
|
||||
export: 데이터 내보내기
|
||||
followers: 신뢰 중인 인스턴스
|
||||
|
@ -342,9 +437,14 @@ ko:
|
|||
preferences: 사용자 설정
|
||||
settings: 설정
|
||||
two_factor_authentication: 2단계 인증
|
||||
your_apps: 애플리케이션
|
||||
statuses:
|
||||
open_in_web: Web으로 열기
|
||||
over_character_limit: 최대 %{max}자까지 입력할 수 있습니다
|
||||
pin_errors:
|
||||
ownership: 다른 사람의 Toot는 고정될 수 없습니다.
|
||||
private: 비공개 Toot는 고정될 수 없습니다.
|
||||
reblog: 부스트는 고정될 수 없습니다.
|
||||
show_more: 더 보기
|
||||
visibilities:
|
||||
private: 비공개
|
||||
|
@ -355,8 +455,11 @@ ko:
|
|||
unlisted_long: 누구나 볼 수 있지만, 공개 타임라인에는 표시되지 않습니다
|
||||
stream_entries:
|
||||
click_to_show: 클릭해서 표시
|
||||
pinned: 고정된 Toot
|
||||
reblogged: 님이 부스트 했습니다
|
||||
sensitive_content: 민감한 컨텐츠
|
||||
terms:
|
||||
title: "%{instance} 이용약관과 개인정보 취급 방침"
|
||||
time:
|
||||
formats:
|
||||
default: "%Y년 %m월 %d일 %H:%M"
|
||||
|
@ -379,3 +482,4 @@ ko:
|
|||
users:
|
||||
invalid_email: 메일 주소가 올바르지 않습니다
|
||||
invalid_otp_token: 2단계 인증 코드가 올바르지 않습니다
|
||||
signed_in_as: '다음과 같이 로그인 중:'
|
||||
|
|
|
@ -337,6 +337,17 @@ nl:
|
|||
reblog:
|
||||
body: 'Jouw toot werd door %{name} geboost:'
|
||||
subject: "%{name} boostte jouw toot"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Volgende
|
||||
prev: Vorige
|
||||
|
|
|
@ -257,6 +257,17 @@
|
|||
reblog:
|
||||
body: 'Din status ble fremhevd av %{name}:'
|
||||
subject: "%{name} fremhevde din status"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Neste
|
||||
prev: Forrige
|
||||
|
|
|
@ -103,6 +103,7 @@ oc:
|
|||
title: Comptes
|
||||
undo_silenced: Levar lo silenci
|
||||
undo_suspension: Levar la suspension
|
||||
unsubscribe: Se desabonar
|
||||
username: Nom d’utilizaire
|
||||
web: Web
|
||||
domain_blocks:
|
||||
|
@ -430,6 +431,17 @@ oc:
|
|||
reblog:
|
||||
body: "%{name} a tornat partejar vòstre estatut :"
|
||||
subject: "%{name} a tornat partejar vòstre estatut"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Seguent
|
||||
prev: Precedent
|
||||
|
|
|
@ -355,6 +355,17 @@ pl:
|
|||
reblog:
|
||||
body: 'Twój wpis został podbity przez %{name}:'
|
||||
subject: Twój wpis został podbity przez %{name}
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Następna
|
||||
prev: Poprzednia
|
||||
|
|
|
@ -255,6 +255,17 @@ pt-BR:
|
|||
reblog:
|
||||
body: 'O seu post foi reblogado por %{name}:'
|
||||
subject: "%{name} reblogou o seu post"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Next
|
||||
prev: Prev
|
||||
|
|
|
@ -182,6 +182,17 @@ pt:
|
|||
reblog:
|
||||
body: 'O teu post foi partilhado por %{name}:'
|
||||
subject: "%{name} partilhou o teu post"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Seguinte
|
||||
prev: Anterior
|
||||
|
|
|
@ -262,6 +262,17 @@ ru:
|
|||
reblog:
|
||||
body: 'Ваш статус был продвинут %{name}:'
|
||||
subject: "%{name} продвинул(а) Ваш статус"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: След
|
||||
prev: Пред
|
||||
|
|
|
@ -6,7 +6,7 @@ de:
|
|||
avatar: PNG, GIF oder JPG. Maximal 2MB. Wird auf 120x120px herunterskaliert
|
||||
display_name: '<span class="name-counter">%{count}</span> Zeichen verbleiben'
|
||||
header: PNG, GIF oder JPG. Maximal 2MB. Wird auf 700x335px herunterskaliert
|
||||
locked: Erlaubt dir, Nutzer zu überprüfen, bevor sie dir folgen können
|
||||
locked: Erlaubt dir, Profile zu überprüfen, bevor sie dir folgen können
|
||||
note: '<span class="note-counter">%{count}</span> Zeichen verbleiben'
|
||||
imports:
|
||||
data: CSV-Datei, die von einer anderen Mastodon-Instanz exportiert wurde
|
||||
|
@ -33,10 +33,10 @@ de:
|
|||
setting_default_privacy: Beitragsprivatspäre
|
||||
severity: Gewichtung
|
||||
type: Importtyp
|
||||
username: Nutzername
|
||||
username: Profilname
|
||||
interactions:
|
||||
must_be_follower: Benachrichtigungen von Nicht-Folgern blockieren
|
||||
must_be_following: Benachrichtigungen von Nutzern blockieren, denen ich nicht folge
|
||||
must_be_following: Benachrichtigungen von Profilen blockieren, denen ich nicht folge
|
||||
notification_emails:
|
||||
digest: Schicke Übersichts-E-Mails
|
||||
favourite: E-Mail senden, wenn jemand meinen Beitrag favorisiert
|
||||
|
|
|
@ -257,6 +257,17 @@ th:
|
|||
reblog:
|
||||
body: 'Your status was boosted by %{name}:'
|
||||
subject: "%{name} boosted your status"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: ต่อไป
|
||||
prev: ย้อนกลับ
|
||||
|
|
|
@ -255,6 +255,17 @@ tr:
|
|||
reblog:
|
||||
body: "%{name} durumunuzu boost etti:"
|
||||
subject: "%{name} durumunuzu boost etti"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Sonraki
|
||||
prev: Önceki
|
||||
|
|
|
@ -250,6 +250,17 @@ uk:
|
|||
reblog:
|
||||
body: 'Ваш статус було передмухнуто %{name}:'
|
||||
subject: "%{name} передмухнув ваш статус"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: Далі
|
||||
prev: Назад
|
||||
|
|
|
@ -261,6 +261,17 @@ zh-CN:
|
|||
reblog:
|
||||
body: 你的嘟文得到 %{name} 的转嘟
|
||||
subject: "%{name} 转嘟(嘟嘟滴)了你的嘟文"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: 下一页
|
||||
prev: 上一页
|
||||
|
|
|
@ -256,6 +256,17 @@ zh-HK:
|
|||
reblog:
|
||||
body: 你的文章得到 %{name} 的轉推
|
||||
subject: "%{name} 轉推了你的文章"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: 下一頁
|
||||
prev: 上一頁
|
||||
|
|
|
@ -211,6 +211,17 @@ zh-TW:
|
|||
reblog:
|
||||
body: 您的文章被 %{name} 轉推
|
||||
subject: "%{name} 轉推了您的文章"
|
||||
number:
|
||||
human:
|
||||
decimal_units:
|
||||
format: "%n%u"
|
||||
units:
|
||||
billion: B
|
||||
million: M
|
||||
quadrillion: Q
|
||||
thousand: K
|
||||
trillion: T
|
||||
unit: ''
|
||||
pagination:
|
||||
next: 下一頁
|
||||
prev: 上一頁
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class AddIndexIdAccountIdActivityTypeOnNotifications < ActiveRecord::Migration[5.1]
|
||||
def change
|
||||
add_index :notifications, [:id, :account_id, :activity_type], order: { id: :desc }
|
||||
end
|
||||
end
|
5
db/migrate/20170905165803_add_local_to_statuses.rb
Normal file
5
db/migrate/20170905165803_add_local_to_statuses.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
class AddLocalToStatuses < ActiveRecord::Migration[5.1]
|
||||
def change
|
||||
add_column :statuses, :local, :boolean, null: true, default: nil
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20170901142658) do
|
||||
ActiveRecord::Schema.define(version: 20170905165803) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -180,6 +180,7 @@ ActiveRecord::Schema.define(version: 20170901142658) do
|
|||
t.integer "from_account_id"
|
||||
t.index ["account_id", "activity_id", "activity_type"], name: "account_activity", unique: true
|
||||
t.index ["activity_id", "activity_type"], name: "index_notifications_on_activity_id_and_activity_type"
|
||||
t.index ["id", "account_id", "activity_type"], name: "index_notifications_on_id_and_account_id_and_activity_type", order: { id: :desc }
|
||||
end
|
||||
|
||||
create_table "oauth_access_grants", id: :serial, force: :cascade do |t|
|
||||
|
@ -314,6 +315,7 @@ ActiveRecord::Schema.define(version: 20170901142658) do
|
|||
t.integer "reblogs_count", default: 0, null: false
|
||||
t.string "language"
|
||||
t.bigint "conversation_id"
|
||||
t.boolean "local"
|
||||
t.index ["account_id", "id"], name: "index_statuses_on_account_id_id"
|
||||
t.index ["conversation_id"], name: "index_statuses_on_conversation_id"
|
||||
t.index ["in_reply_to_id"], name: "index_statuses_on_in_reply_to_id"
|
||||
|
|
20
lib/mastodon/unique_retry_job_middleware.rb
Normal file
20
lib/mastodon/unique_retry_job_middleware.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Mastodon::UniqueRetryJobMiddleware
|
||||
def call(_worker_class, item, _queue, _redis_pool)
|
||||
return if item['unique_retry'] && retried?(item)
|
||||
yield
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def retried?(item)
|
||||
# Use unique digest key of SidekiqUniqueJobs
|
||||
unique_key = SidekiqUniqueJobs::UNIQUE_DIGEST_KEY
|
||||
unique_digest = item[unique_key]
|
||||
class_name = item['class']
|
||||
retries = Sidekiq::RetrySet.new
|
||||
|
||||
retries.any? { |job| job.item['class'] == class_name && job.item[unique_key] == unique_digest }
|
||||
end
|
||||
end
|
|
@ -9,11 +9,11 @@ module Mastodon
|
|||
end
|
||||
|
||||
def minor
|
||||
5
|
||||
6
|
||||
end
|
||||
|
||||
def patch
|
||||
1
|
||||
0
|
||||
end
|
||||
|
||||
def pre
|
||||
|
@ -21,7 +21,7 @@ module Mastodon
|
|||
end
|
||||
|
||||
def flags
|
||||
''
|
||||
'rc2'
|
||||
end
|
||||
|
||||
def to_a
|
||||
|
|
|
@ -273,10 +273,17 @@ namespace :mastodon do
|
|||
|
||||
desc 'Remove deprecated preview cards'
|
||||
task remove_deprecated_preview_cards: :environment do
|
||||
return unless ActiveRecord::Base.connection.table_exists? 'deprecated_preview_cards'
|
||||
next unless ActiveRecord::Base.connection.table_exists? 'deprecated_preview_cards'
|
||||
|
||||
class DeprecatedPreviewCard < PreviewCard
|
||||
self.table_name = 'deprecated_preview_cards'
|
||||
class DeprecatedPreviewCard < ActiveRecord::Base
|
||||
self.inheritance_column = false
|
||||
|
||||
path = '/preview_cards/:attachment/:id_partition/:style/:filename'
|
||||
if ENV['S3_ENABLED'] != 'true'
|
||||
path = (ENV['PAPERCLIP_ROOT_PATH'] || ':rails_root/public/system') + path
|
||||
end
|
||||
|
||||
has_attached_file :image, styles: { original: '280x120>' }, convert_options: { all: '-quality 80 -strip' }, path: path
|
||||
end
|
||||
|
||||
puts 'Delete records and associated files from deprecated preview cards? [y/N]: '
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
"react-dom": "^15.6.1",
|
||||
"react-immutable-proptypes": "^2.1.0",
|
||||
"react-immutable-pure-component": "^1.0.0",
|
||||
"react-intl": "^2.3.0",
|
||||
"react-intl": "^2.4.0",
|
||||
"react-motion": "^0.5.0",
|
||||
"react-notification": "^6.7.1",
|
||||
"react-redux": "^5.0.4",
|
||||
|
|
|
@ -61,7 +61,29 @@ RSpec.describe AccountsController, type: :controller do
|
|||
end
|
||||
end
|
||||
|
||||
context 'html' do
|
||||
context 'html without since_id nor max_id' do
|
||||
before do
|
||||
get :show, params: { username: alice.username }
|
||||
end
|
||||
|
||||
it 'assigns @account' do
|
||||
expect(assigns(:account)).to eq alice
|
||||
end
|
||||
|
||||
it 'assigns @pinned_statuses' do
|
||||
pinned_statuses = assigns(:pinned_statuses).to_a
|
||||
expect(pinned_statuses.size).to eq 3
|
||||
expect(pinned_statuses[0]).to eq status7
|
||||
expect(pinned_statuses[1]).to eq status5
|
||||
expect(pinned_statuses[2]).to eq status6
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
end
|
||||
|
||||
context 'html with since_id and max_id' do
|
||||
before do
|
||||
get :show, params: { username: alice.username, max_id: status4.id, since_id: status1.id }
|
||||
end
|
||||
|
@ -77,12 +99,9 @@ RSpec.describe AccountsController, type: :controller do
|
|||
expect(statuses[1]).to eq status2
|
||||
end
|
||||
|
||||
it 'assigns @pinned_statuses' do
|
||||
it 'assigns an empty array to @pinned_statuses' do
|
||||
pinned_statuses = assigns(:pinned_statuses).to_a
|
||||
expect(pinned_statuses.size).to eq 3
|
||||
expect(pinned_statuses[0]).to eq status7
|
||||
expect(pinned_statuses[1]).to eq status5
|
||||
expect(pinned_statuses[2]).to eq status6
|
||||
expect(pinned_statuses.size).to eq 0
|
||||
end
|
||||
|
||||
it 'returns http success' do
|
||||
|
|
|
@ -28,6 +28,13 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
|
|||
expect(response).to have_http_status(:success)
|
||||
end
|
||||
|
||||
it 'returns JSON with following=true and requested=false' do
|
||||
json = body_as_json
|
||||
|
||||
expect(json[:following]).to be true
|
||||
expect(json[:requested]).to be false
|
||||
end
|
||||
|
||||
it 'creates a following relation between user and target user' do
|
||||
expect(user.account.following?(other_account)).to be true
|
||||
end
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
Fabricator(:status) do
|
||||
account
|
||||
text "Lorem ipsum dolor sit amet"
|
||||
|
||||
after_build do |status|
|
||||
status.uri = Faker::Internet.device_token if !status.account.local? && status.uri.nil?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::Activity::Delete do
|
||||
let(:sender) { Fabricate(:account) }
|
||||
let(:sender) { Fabricate(:account, domain: 'example.com') }
|
||||
let(:status) { Fabricate(:status, account: sender, uri: 'foobar') }
|
||||
|
||||
let(:json) do
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe ActivityPub::Activity::Undo do
|
||||
let(:sender) { Fabricate(:account) }
|
||||
let(:sender) { Fabricate(:account, domain: 'example.com') }
|
||||
|
||||
let(:json) do
|
||||
{
|
||||
|
|
|
@ -178,7 +178,7 @@ RSpec.describe Formatter do
|
|||
end
|
||||
|
||||
context 'with remote status' do
|
||||
let(:status) { Fabricate(:status, text: 'Beep boop', uri: 'beepboop') }
|
||||
let(:status) { Fabricate(:status, account: remote_account, text: 'Beep boop') }
|
||||
|
||||
it 'reformats' do
|
||||
is_expected.to eq 'Beep boop'
|
||||
|
@ -226,7 +226,7 @@ RSpec.describe Formatter do
|
|||
end
|
||||
|
||||
context 'with remote status' do
|
||||
let(:status) { Fabricate(:status, text: '<script>alert("Hello")</script>', uri: 'beep boop') }
|
||||
let(:status) { Fabricate(:status, account: remote_account, text: '<script>alert("Hello")</script>') }
|
||||
|
||||
it 'returns tag-stripped text' do
|
||||
is_expected.to eq ''
|
||||
|
|
|
@ -403,8 +403,7 @@ RSpec.describe OStatus::AtomSerializer do
|
|||
|
||||
it 'returns element whose rendered view triggers creation when processed' do
|
||||
remote_account = Account.create!(username: 'username')
|
||||
remote_status = Fabricate(:status, account: remote_account)
|
||||
remote_status.stream_entry.update!(created_at: '2000-01-01T00:00:00Z')
|
||||
remote_status = Fabricate(:status, account: remote_account, created_at: '2000-01-01T00:00:00Z')
|
||||
|
||||
entry = OStatus::AtomSerializer.new.entry(remote_status.stream_entry, true)
|
||||
entry.nodes.delete_if { |node| node[:type] == 'application/activity+json' } # Remove ActivityPub link to simplify test
|
||||
|
@ -421,7 +420,7 @@ RSpec.describe OStatus::AtomSerializer do
|
|||
|
||||
ProcessFeedService.new.call(xml, account)
|
||||
|
||||
expect(Status.find_by(uri: "tag:remote,2000-01-01:objectId=#{remote_status.id}:objectType=Status")).to be_instance_of Status
|
||||
expect(Status.find_by(uri: "https://remote/users/#{remote_status.account.to_param}/statuses/#{remote_status.id}")).to be_instance_of Status
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -465,12 +464,11 @@ RSpec.describe OStatus::AtomSerializer do
|
|||
end
|
||||
|
||||
it 'appends id element with unique tag' do
|
||||
status = Fabricate(:status, reblog_of_id: nil)
|
||||
status.stream_entry.update!(created_at: '2000-01-01T00:00:00Z')
|
||||
status = Fabricate(:status, reblog_of_id: nil, created_at: '2000-01-01T00:00:00Z')
|
||||
|
||||
entry = OStatus::AtomSerializer.new.entry(status.stream_entry)
|
||||
|
||||
expect(entry.id.text).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{status.id}:objectType=Status"
|
||||
expect(entry.id.text).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}"
|
||||
end
|
||||
|
||||
it 'appends published element with created date' do
|
||||
|
@ -515,7 +513,7 @@ RSpec.describe OStatus::AtomSerializer do
|
|||
entry = OStatus::AtomSerializer.new.entry(reblog.stream_entry)
|
||||
|
||||
object = entry.nodes.find { |node| node.name == 'activity:object' }
|
||||
expect(object.id.text).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{reblogged.id}:objectType=Status"
|
||||
expect(object.id.text).to eq "https://cb6e6126.ngrok.io/users/#{reblogged.account.to_param}/statuses/#{reblogged.id}"
|
||||
end
|
||||
|
||||
it 'does not append activity:object element if target is not present' do
|
||||
|
@ -532,7 +530,7 @@ RSpec.describe OStatus::AtomSerializer do
|
|||
|
||||
link = entry.nodes.find { |node| node.name == 'link' && node[:rel] == 'alternate' && node[:type] == 'text/html' }
|
||||
expect(link[:type]).to eq 'text/html'
|
||||
expect(link[:href]).to eq "https://cb6e6126.ngrok.io/users/username/updates/#{status.stream_entry.id}"
|
||||
expect(link[:href]).to eq "https://cb6e6126.ngrok.io/@username/#{status.id}"
|
||||
end
|
||||
|
||||
it 'appends link element for itself' do
|
||||
|
@ -553,7 +551,7 @@ RSpec.describe OStatus::AtomSerializer do
|
|||
entry = OStatus::AtomSerializer.new.entry(reply_status.stream_entry)
|
||||
|
||||
in_reply_to = entry.nodes.find { |node| node.name == 'thr:in-reply-to' }
|
||||
expect(in_reply_to[:ref]).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{in_reply_to_status.id}:objectType=Status"
|
||||
expect(in_reply_to[:ref]).to eq "https://cb6e6126.ngrok.io/users/#{in_reply_to_status.account.to_param}/statuses/#{in_reply_to_status.id}"
|
||||
end
|
||||
|
||||
it 'does not append thr:in-reply-to element if not threaded' do
|
||||
|
@ -934,7 +932,7 @@ RSpec.describe OStatus::AtomSerializer do
|
|||
favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite)
|
||||
|
||||
object = favourite_salmon.nodes.find { |node| node.name == 'activity:object' }
|
||||
expect(object.id.text).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{status.id}:objectType=Status"
|
||||
expect(object.id.text).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}"
|
||||
end
|
||||
|
||||
it 'appends thr:in-reply-to element for status' do
|
||||
|
@ -945,7 +943,7 @@ RSpec.describe OStatus::AtomSerializer do
|
|||
favourite_salmon = OStatus::AtomSerializer.new.favourite_salmon(favourite)
|
||||
|
||||
in_reply_to = favourite_salmon.nodes.find { |node| node.name == 'thr:in-reply-to' }
|
||||
expect(in_reply_to.ref).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{status.id}:objectType=Status"
|
||||
expect(in_reply_to.ref).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}"
|
||||
expect(in_reply_to.href).to eq "https://cb6e6126.ngrok.io/@username/#{status.id}"
|
||||
end
|
||||
|
||||
|
@ -1034,7 +1032,7 @@ RSpec.describe OStatus::AtomSerializer do
|
|||
unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite)
|
||||
|
||||
object = unfavourite_salmon.nodes.find { |node| node.name == 'activity:object' }
|
||||
expect(object.id.text).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{status.id}:objectType=Status"
|
||||
expect(object.id.text).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}"
|
||||
end
|
||||
|
||||
it 'appends thr:in-reply-to element for status' do
|
||||
|
@ -1045,7 +1043,7 @@ RSpec.describe OStatus::AtomSerializer do
|
|||
unfavourite_salmon = OStatus::AtomSerializer.new.unfavourite_salmon(favourite)
|
||||
|
||||
in_reply_to = unfavourite_salmon.nodes.find { |node| node.name == 'thr:in-reply-to' }
|
||||
expect(in_reply_to.ref).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{status.id}:objectType=Status"
|
||||
expect(in_reply_to.ref).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}"
|
||||
expect(in_reply_to.href).to eq "https://cb6e6126.ngrok.io/@username/#{status.id}"
|
||||
end
|
||||
|
||||
|
@ -1453,7 +1451,7 @@ RSpec.describe OStatus::AtomSerializer do
|
|||
it 'appends id element with URL for status' do
|
||||
status = Fabricate(:status, created_at: '2000-01-01T00:00:00Z')
|
||||
object = OStatus::AtomSerializer.new.object(status)
|
||||
expect(object.id.text).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{status.id}:objectType=Status"
|
||||
expect(object.id.text).to eq "https://cb6e6126.ngrok.io/users/#{status.account.to_param}/statuses/#{status.id}"
|
||||
end
|
||||
|
||||
it 'appends published element with created date' do
|
||||
|
@ -1463,7 +1461,8 @@ RSpec.describe OStatus::AtomSerializer do
|
|||
end
|
||||
|
||||
it 'appends updated element with updated date' do
|
||||
status = Fabricate(:status, updated_at: '2000-01-01T00:00:00Z')
|
||||
status = Fabricate(:status)
|
||||
status.updated_at = '2000-01-01T00:00:00Z'
|
||||
object = OStatus::AtomSerializer.new.object(status)
|
||||
expect(object.updated.text).to eq '2000-01-01T00:00:00Z'
|
||||
end
|
||||
|
@ -1523,7 +1522,7 @@ RSpec.describe OStatus::AtomSerializer do
|
|||
entry = OStatus::AtomSerializer.new.object(reply)
|
||||
|
||||
in_reply_to = entry.nodes.find { |node| node.name == 'thr:in-reply-to' }
|
||||
expect(in_reply_to.ref).to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{thread.id}:objectType=Status"
|
||||
expect(in_reply_to.ref).to eq "https://cb6e6126.ngrok.io/users/#{thread.account.to_param}/statuses/#{thread.id}"
|
||||
expect(in_reply_to.href).to eq "https://cb6e6126.ngrok.io/@username/#{thread.id}"
|
||||
end
|
||||
|
||||
|
|
|
@ -157,23 +157,12 @@ RSpec.describe TagManager do
|
|||
describe '#uri_for' do
|
||||
subject { TagManager.instance.uri_for(target) }
|
||||
|
||||
context 'activity object' do
|
||||
let(:target) { Fabricate(:status, reblog: Fabricate(:status)).stream_entry }
|
||||
|
||||
before { target.update!(created_at: '2000-01-01T00:00:00Z') }
|
||||
|
||||
it 'returns the unique tag for status' do
|
||||
expect(target.object_type).to eq :activity
|
||||
is_expected.to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{target.id}:objectType=Status"
|
||||
end
|
||||
end
|
||||
|
||||
context 'comment object' do
|
||||
let(:target) { Fabricate(:status, created_at: '2000-01-01T00:00:00Z', reply: true) }
|
||||
|
||||
it 'returns the unique tag for status' do
|
||||
expect(target.object_type).to eq :comment
|
||||
is_expected.to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{target.id}:objectType=Status"
|
||||
is_expected.to eq target.uri
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -182,7 +171,7 @@ RSpec.describe TagManager do
|
|||
|
||||
it 'returns the unique tag for status' do
|
||||
expect(target.object_type).to eq :note
|
||||
is_expected.to eq "tag:cb6e6126.ngrok.io,2000-01-01:objectId=#{target.id}:objectType=Status"
|
||||
is_expected.to eq target.uri
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -13,9 +13,15 @@ RSpec.describe Status, type: :model do
|
|||
end
|
||||
|
||||
it 'returns false if a remote URI is set' do
|
||||
subject.uri = 'a'
|
||||
alice.update(domain: 'example.com')
|
||||
subject.save
|
||||
expect(subject.local?).to be false
|
||||
end
|
||||
|
||||
it 'returns true if a URI is set and `local` is true' do
|
||||
subject.update(uri: 'example.com', local: true)
|
||||
expect(subject.local?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#reblog?' do
|
||||
|
@ -495,7 +501,7 @@ RSpec.describe Status, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'before_create' do
|
||||
describe 'before_validation' do
|
||||
it 'sets account being replied to correctly over intermediary nodes' do
|
||||
first_status = Fabricate(:status, account: bob)
|
||||
intermediary = Fabricate(:status, thread: first_status, account: alice)
|
||||
|
@ -512,5 +518,22 @@ RSpec.describe Status, type: :model do
|
|||
parent = Fabricate(:status, text: 'First')
|
||||
expect(Status.create(account: alice, thread: parent, text: 'Response').conversation_id).to eq parent.conversation_id
|
||||
end
|
||||
|
||||
it 'sets `local` to true for status by local account' do
|
||||
expect(Status.create(account: alice, text: 'foo').local).to be true
|
||||
end
|
||||
|
||||
it 'sets `local` to false for status by remote account' do
|
||||
alice.update(domain: 'example.com')
|
||||
expect(Status.create(account: alice, text: 'foo').local).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe 'after_create' do
|
||||
it 'saves ActivityPub uri as uri for local status' do
|
||||
status = Status.create(account: alice, text: 'foo')
|
||||
status.reload
|
||||
expect(status.uri).to start_with('https://')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -55,7 +55,7 @@ RSpec.describe FetchLinkCardService do
|
|||
end
|
||||
|
||||
context 'in a remote status' do
|
||||
let(:status) { Fabricate(:status, uri: 'abc', text: 'Habt ihr ein paar gute Links zu #<span class="tag"><a href="https://quitter.se/tag/wannacry" target="_blank" rel="tag noopener" title="https://quitter.se/tag/wannacry">Wannacry</a></span> herumfliegen? Ich will mal unter <br> <a href="https://github.com/qbi/WannaCry" target="_blank" rel="noopener" title="https://github.com/qbi/WannaCry">https://github.com/qbi/WannaCry</a> was sammeln. !<a href="http://sn.jonkman.ca/group/416/id" target="_blank" rel="noopener" title="http://sn.jonkman.ca/group/416/id">security</a> ') }
|
||||
let(:status) { Fabricate(:status, account: Fabricate(:account, domain: 'example.com'), text: 'Habt ihr ein paar gute Links zu #<span class="tag"><a href="https://quitter.se/tag/wannacry" target="_blank" rel="tag noopener" title="https://quitter.se/tag/wannacry">Wannacry</a></span> herumfliegen? Ich will mal unter <br> <a href="https://github.com/qbi/WannaCry" target="_blank" rel="noopener" title="https://github.com/qbi/WannaCry">https://github.com/qbi/WannaCry</a> was sammeln. !<a href="http://sn.jonkman.ca/group/416/id" target="_blank" rel="noopener" title="http://sn.jonkman.ca/group/416/id">security</a> ') }
|
||||
|
||||
it 'parses out URLs' do
|
||||
expect(a_request(:head, 'https://github.com/qbi/WannaCry')).to have_been_made.at_least_once
|
||||
|
|
|
@ -403,11 +403,11 @@ const startWorker = (workerId) => {
|
|||
});
|
||||
|
||||
app.get('/api/v1/streaming/hashtag', (req, res) => {
|
||||
streamFrom(`timeline:hashtag:${req.query.tag}`, req, streamToHttp(req, res), streamHttpEnd(req), true);
|
||||
streamFrom(`timeline:hashtag:${req.query.tag.toLowerCase()}`, req, streamToHttp(req, res), streamHttpEnd(req), true);
|
||||
});
|
||||
|
||||
app.get('/api/v1/streaming/hashtag/local', (req, res) => {
|
||||
streamFrom(`timeline:hashtag:${req.query.tag}:local`, req, streamToHttp(req, res), streamHttpEnd(req), true);
|
||||
streamFrom(`timeline:hashtag:${req.query.tag.toLowerCase()}:local`, req, streamToHttp(req, res), streamHttpEnd(req), true);
|
||||
});
|
||||
|
||||
const wss = new WebSocket.Server({ server, verifyClient: wsVerifyClient });
|
||||
|
@ -438,10 +438,10 @@ const startWorker = (workerId) => {
|
|||
streamFrom('timeline:public:local', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
|
||||
break;
|
||||
case 'hashtag':
|
||||
streamFrom(`timeline:hashtag:${location.query.tag}`, req, streamToWs(req, ws), streamWsEnd(req, ws), true);
|
||||
streamFrom(`timeline:hashtag:${location.query.tag.toLowerCase()}`, req, streamToWs(req, ws), streamWsEnd(req, ws), true);
|
||||
break;
|
||||
case 'hashtag:local':
|
||||
streamFrom(`timeline:hashtag:${location.query.tag}:local`, req, streamToWs(req, ws), streamWsEnd(req, ws), true);
|
||||
streamFrom(`timeline:hashtag:${location.query.tag.toLowerCase()}:local`, req, streamToWs(req, ws), streamWsEnd(req, ws), true);
|
||||
break;
|
||||
default:
|
||||
ws.close();
|
||||
|
|
24
yarn.lock
24
yarn.lock
|
@ -3130,23 +3130,17 @@ intl-messageformat-parser@^1.2.0:
|
|||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/intl-messageformat-parser/-/intl-messageformat-parser-1.3.0.tgz#c5d26ffb894c7d9c2b9fa444c67f417ab2594268"
|
||||
|
||||
intl-messageformat@1.3.0, intl-messageformat@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-1.3.0.tgz#f7d926aded7a3ab19b2dc601efd54e99a4bd4eae"
|
||||
dependencies:
|
||||
intl-messageformat-parser "1.2.0"
|
||||
|
||||
intl-messageformat@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-2.0.0.tgz#3d56982583425aee23b76c8b985fb9b0aae5be3c"
|
||||
dependencies:
|
||||
intl-messageformat-parser "1.2.0"
|
||||
|
||||
intl-relativeformat@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/intl-relativeformat/-/intl-relativeformat-1.3.0.tgz#893dc7076fccd380cf091a2300c380fa57ace45b"
|
||||
intl-messageformat@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-2.1.0.tgz#1c51da76f02a3f7b360654cdc51bbc4d3fa6c72c"
|
||||
dependencies:
|
||||
intl-messageformat "1.3.0"
|
||||
intl-messageformat-parser "1.2.0"
|
||||
|
||||
intl-relativeformat@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
@ -5312,13 +5306,13 @@ react-intl-translations-manager@^5.0.0:
|
|||
json-stable-stringify "^1.0.1"
|
||||
mkdirp "^0.5.1"
|
||||
|
||||
react-intl@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-2.3.0.tgz#e1df6af5667fdf01cbe4aab20e137251e2ae5142"
|
||||
react-intl@^2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react-intl/-/react-intl-2.4.0.tgz#66c14dc9df9a73b2fbbfbd6021726e80a613eb15"
|
||||
dependencies:
|
||||
intl-format-cache "^2.0.5"
|
||||
intl-messageformat "^1.3.0"
|
||||
intl-relativeformat "^1.3.0"
|
||||
intl-messageformat "^2.1.0"
|
||||
intl-relativeformat "^2.0.0"
|
||||
invariant "^2.1.1"
|
||||
|
||||
react-motion@^0.5.0:
|
||||
|
|
Loading…
Add table
Reference in a new issue