Merge tag 'v1.6.0rc3' into sync/upstream
commit
514fc908a3
|
@ -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,6 +4503,24 @@ noscript {
|
|||
}
|
||||
}
|
||||
|
||||
.embed-modal {
|
||||
max-width: 80vw;
|
||||
max-height: 80vh;
|
||||
|
||||
h4 {
|
||||
padding: 30px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.embed-modal__container {
|
||||
padding: 10px;
|
||||
|
||||
.hint {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.embed-modal__html {
|
||||
color: $ui-secondary-color;
|
||||
outline: 0;
|
||||
|
@ -4517,26 +4555,11 @@ noscript {
|
|||
}
|
||||
}
|
||||
|
||||
.embed-modal {
|
||||
h4 {
|
||||
padding: 30px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hint {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.embed-modal__container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.embed-modal__iframe {
|
||||
width: 100%;
|
||||
min-width: 400px;
|
||||
width: 400px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -399,29 +399,23 @@
|
|||
|
||||
.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 {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 14px;
|
||||
flex: 0 auto;
|
||||
font-size: 14px;
|
||||
|
||||
svg {
|
||||
|
@ -447,3 +441,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status__content,
|
||||
.detailed-status__meta {
|
||||
flex: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -50,7 +50,7 @@ ar:
|
|||
about_x_months: "%{count} شهر"
|
||||
about_x_years: "%{count} سنة"
|
||||
almost_x_years: "%{count} سنوات"
|
||||
half_a_minute: Just now
|
||||
half_a_minute: الآن
|
||||
less_than_x_minutes: "%{count} د"
|
||||
less_than_x_seconds: الآن
|
||||
over_x_years: "%{count} سنين"
|
||||
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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…
Reference in New Issue