Merge commit 'e5655a5f65eb8116640ae434125553e0fe77f35e' into glitch-soc/merge-upstream

This commit is contained in:
Claire 2025-03-06 20:10:00 +01:00
commit b6f3cfb302
40 changed files with 284 additions and 179 deletions

View File

@ -43,4 +43,4 @@ jobs:
- name: Run haml-lint - name: Run haml-lint
run: | run: |
echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json" echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json"
bin/haml-lint --parallel --reporter github bin/haml-lint --reporter github

View File

@ -1,6 +1,6 @@
# This configuration was generated by # This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp` # `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp`
# using RuboCop version 1.72.2. # using RuboCop version 1.73.1.
# The point is for the user to remove these configuration records # The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base. # one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new # Note that changes in the inspected code, or installation of new

View File

@ -719,7 +719,7 @@ GEM
rspec-mocks (~> 3.0) rspec-mocks (~> 3.0)
sidekiq (>= 5, < 8) sidekiq (>= 5, < 8)
rspec-support (3.13.2) rspec-support (3.13.2)
rubocop (1.72.2) rubocop (1.73.1)
json (~> 2.3) json (~> 2.3)
language_server-protocol (~> 3.17.0.2) language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0) lint_roller (~> 1.1.0)
@ -730,7 +730,7 @@ GEM
rubocop-ast (>= 1.38.0, < 2.0) rubocop-ast (>= 1.38.0, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0) unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.38.0) rubocop-ast (1.38.1)
parser (>= 3.3.1.0) parser (>= 3.3.1.0)
rubocop-capybara (2.21.0) rubocop-capybara (2.21.0)
rubocop (~> 1.41) rubocop (~> 1.41)
@ -738,7 +738,7 @@ GEM
lint_roller (~> 1.1) lint_roller (~> 1.1)
rubocop (>= 1.72.1, < 2.0) rubocop (>= 1.72.1, < 2.0)
rubocop-ast (>= 1.38.0, < 2.0) rubocop-ast (>= 1.38.0, < 2.0)
rubocop-rails (2.30.2) rubocop-rails (2.30.3)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
lint_roller (~> 1.1) lint_roller (~> 1.1)
rack (>= 1.1) rack (>= 1.1)
@ -854,7 +854,7 @@ GEM
unicode-display_width (3.1.4) unicode-display_width (3.1.4)
unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4) unicode-emoji (4.0.4)
uri (1.0.2) uri (1.0.3)
useragent (0.16.11) useragent (0.16.11)
validate_email (0.1.6) validate_email (0.1.6)
activemodel (>= 3.0) activemodel (>= 3.0)

View File

@ -3,8 +3,8 @@
class Api::V1::MediaController < Api::BaseController class Api::V1::MediaController < Api::BaseController
before_action -> { doorkeeper_authorize! :write, :'write:media' } before_action -> { doorkeeper_authorize! :write, :'write:media' }
before_action :require_user! before_action :require_user!
before_action :set_media_attachment, except: [:create] before_action :set_media_attachment, except: [:create, :destroy]
before_action :check_processing, except: [:create] before_action :check_processing, except: [:create, :destroy]
def show def show
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment
@ -25,6 +25,15 @@ class Api::V1::MediaController < Api::BaseController
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment
end end
def destroy
@media_attachment = current_account.media_attachments.find(params[:id])
return render json: in_usage_error, status: 422 unless @media_attachment.status_id.nil?
@media_attachment.destroy
render_empty
end
private private
def status_code_for_media_attachment def status_code_for_media_attachment
@ -54,4 +63,8 @@ class Api::V1::MediaController < Api::BaseController
def processing_error def processing_error
{ error: 'Error processing thumbnail for uploaded media' } { error: 'Error processing thumbnail for uploaded media' }
end end
def in_usage_error
{ error: 'Media attachment is currently used by a status' }
end
end end

View File

@ -113,7 +113,7 @@ class Api::V1::StatusesController < Api::BaseController
@status.account.statuses_count = @status.account.statuses_count - 1 @status.account.statuses_count = @status.account.statuses_count - 1
json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true
RemovalWorker.perform_async(@status.id, { 'redraft' => true }) RemovalWorker.perform_async(@status.id, { 'redraft' => !truthy_param?(:delete_media) })
render json: json render json: json
end end

View File

@ -138,7 +138,7 @@ export function deleteStatus(id, withRedraft = false) {
dispatch(deleteStatusRequest(id)); dispatch(deleteStatusRequest(id));
api().delete(`/api/v1/statuses/${id}`).then(response => { api().delete(`/api/v1/statuses/${id}`, { params: { delete_media: !withRedraft } }).then(response => {
dispatch(deleteStatusSuccess(id)); dispatch(deleteStatusSuccess(id));
dispatch(deleteFromTimelines(id)); dispatch(deleteFromTimelines(id));
dispatch(importFetchedAccount(response.data.account)); dispatch(importFetchedAccount(response.data.account));

View File

@ -58,8 +58,8 @@ import {
import { getAccountHidden } from 'mastodon/selectors/accounts'; import { getAccountHidden } from 'mastodon/selectors/accounts';
import { useAppSelector, useAppDispatch } from 'mastodon/store'; import { useAppSelector, useAppDispatch } from 'mastodon/store';
import MemorialNote from './memorial_note'; import { MemorialNote } from './memorial_note';
import MovedNote from './moved_note'; import { MovedNote } from './moved_note';
const messages = defineMessages({ const messages = defineMessages({
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
@ -833,7 +833,7 @@ export const AccountHeader: React.FC<{
<div className='account-timeline__header'> <div className='account-timeline__header'>
{!hidden && account.memorial && <MemorialNote />} {!hidden && account.memorial && <MemorialNote />}
{!hidden && account.moved && ( {!hidden && account.moved && (
<MovedNote from={account} to={account.moved} /> <MovedNote accountId={account.id} targetAccountId={account.moved} />
)} )}
<div <div

View File

@ -1,11 +1,12 @@
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
const MemorialNote = () => ( export const MemorialNote: React.FC = () => (
<div className='account-memorial-banner'> <div className='account-memorial-banner'>
<div className='account-memorial-banner__message'> <div className='account-memorial-banner__message'>
<FormattedMessage id='account.in_memoriam' defaultMessage='In Memoriam.' /> <FormattedMessage
id='account.in_memoriam'
defaultMessage='In Memoriam.'
/>
</div> </div>
</div> </div>
); );
export default MemorialNote;

View File

@ -1,39 +0,0 @@
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { AvatarOverlay } from '../../../components/avatar_overlay';
import { DisplayName } from '../../../components/display_name';
export default class MovedNote extends ImmutablePureComponent {
static propTypes = {
from: ImmutablePropTypes.map.isRequired,
to: ImmutablePropTypes.map.isRequired,
};
render () {
const { from, to } = this.props;
return (
<div className='moved-account-banner'>
<div className='moved-account-banner__message'>
<FormattedMessage id='account.moved_to' defaultMessage='{name} has indicated that their new account is now:' values={{ name: <bdi><strong dangerouslySetInnerHTML={{ __html: from.get('display_name_html') }} /></bdi> }} />
</div>
<div className='moved-account-banner__action'>
<Link to={`/@${to.get('acct')}`} className='detailed-status__display-name'>
<div className='detailed-status__display-avatar'><AvatarOverlay account={to} friend={from} /></div>
<DisplayName account={to} />
</Link>
<Link to={`/@${to.get('acct')}`} className='button'><FormattedMessage id='account.go_to_profile' defaultMessage='Go to profile' /></Link>
</div>
</div>
);
}
}

View File

@ -0,0 +1,53 @@
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import { AvatarOverlay } from 'mastodon/components/avatar_overlay';
import { DisplayName } from 'mastodon/components/display_name';
import { useAppSelector } from 'mastodon/store';
export const MovedNote: React.FC<{
accountId: string;
targetAccountId: string;
}> = ({ accountId, targetAccountId }) => {
const from = useAppSelector((state) => state.accounts.get(accountId));
const to = useAppSelector((state) => state.accounts.get(targetAccountId));
return (
<div className='moved-account-banner'>
<div className='moved-account-banner__message'>
<FormattedMessage
id='account.moved_to'
defaultMessage='{name} has indicated that their new account is now:'
values={{
name: (
<bdi>
<strong
dangerouslySetInnerHTML={{
__html: from?.display_name_html ?? '',
}}
/>
</bdi>
),
}}
/>
</div>
<div className='moved-account-banner__action'>
<Link to={`/@${to?.acct}`} className='detailed-status__display-name'>
<div className='detailed-status__display-avatar'>
<AvatarOverlay account={to} friend={from} />
</div>
<DisplayName account={to} />
</Link>
<Link to={`/@${to?.acct}`} className='button'>
<FormattedMessage
id='account.go_to_profile'
defaultMessage='Go to profile'
/>
</Link>
</div>
</div>
);
};

View File

@ -120,7 +120,7 @@ class ComposeForm extends ImmutablePureComponent {
return; return;
} }
this.props.onSubmit(missingAltTextModal && this.props.missingAltText); this.props.onSubmit(missingAltTextModal && this.props.missingAltText && this.props.privacy !== 'direct');
if (e) { if (e) {
e.preventDefault(); e.preventDefault();

View File

@ -697,6 +697,7 @@
"poll_button.remove_poll": "Премахване на анкета", "poll_button.remove_poll": "Премахване на анкета",
"privacy.change": "Промяна на поверителността на публикация", "privacy.change": "Промяна на поверителността на публикация",
"privacy.direct.long": "Споменатите в публикацията", "privacy.direct.long": "Споменатите в публикацията",
"privacy.direct.short": "Частно споменаване",
"privacy.private.long": "Само последователите ви", "privacy.private.long": "Само последователите ви",
"privacy.private.short": "Последователи", "privacy.private.short": "Последователи",
"privacy.public.long": "Всеки във и извън Mastodon", "privacy.public.long": "Всеки във и извън Mastodon",

View File

@ -195,7 +195,7 @@
"compose_form.publish": "Publicar", "compose_form.publish": "Publicar",
"compose_form.publish_form": "Nueva publicación", "compose_form.publish_form": "Nueva publicación",
"compose_form.reply": "Respuesta", "compose_form.reply": "Respuesta",
"compose_form.save_changes": "Actualización", "compose_form.save_changes": "Actualizar",
"compose_form.spoiler.marked": "Quitar advertencia de contenido", "compose_form.spoiler.marked": "Quitar advertencia de contenido",
"compose_form.spoiler.unmarked": "Añadir advertencia de contenido", "compose_form.spoiler.unmarked": "Añadir advertencia de contenido",
"compose_form.spoiler_placeholder": "Advertencia de contenido (opcional)", "compose_form.spoiler_placeholder": "Advertencia de contenido (opcional)",

View File

@ -218,6 +218,10 @@
"confirmations.logout.confirm": "خروج از حساب", "confirmations.logout.confirm": "خروج از حساب",
"confirmations.logout.message": "مطمئنید می‌خواهید خارج شوید؟", "confirmations.logout.message": "مطمئنید می‌خواهید خارج شوید؟",
"confirmations.logout.title": "خروج؟", "confirmations.logout.title": "خروج؟",
"confirmations.missing_alt_text.confirm": "متن جایگزین را اضافه کنید",
"confirmations.missing_alt_text.message": "پست شما حاوی رسانه بدون متن جایگزین است. افزودن توضیحات کمک می کند تا محتوای شما برای افراد بیشتری قابل دسترسی باشد.",
"confirmations.missing_alt_text.secondary": "به هر حال پست کن",
"confirmations.missing_alt_text.title": "متن جایگزین اضافه شود؟",
"confirmations.mute.confirm": "خموش", "confirmations.mute.confirm": "خموش",
"confirmations.redraft.confirm": "حذف و بازنویسی", "confirmations.redraft.confirm": "حذف و بازنویسی",
"confirmations.redraft.message": "مطمئنید که می‌خواهید این فرسته را حذف کنید و از نو بنویسید؟ با این کار تقویت‌ها و پسندهایش از دست رفته و پاسخ‌ها به آن بی‌مرجع می‌شود.", "confirmations.redraft.message": "مطمئنید که می‌خواهید این فرسته را حذف کنید و از نو بنویسید؟ با این کار تقویت‌ها و پسندهایش از دست رفته و پاسخ‌ها به آن بی‌مرجع می‌شود.",
@ -693,6 +697,7 @@
"poll_button.remove_poll": "برداشتن نظرسنجی", "poll_button.remove_poll": "برداشتن نظرسنجی",
"privacy.change": "تغییر محرمانگی فرسته", "privacy.change": "تغییر محرمانگی فرسته",
"privacy.direct.long": "هرکسی که در فرسته نام برده شده", "privacy.direct.long": "هرکسی که در فرسته نام برده شده",
"privacy.direct.short": "ذکر خصوصی",
"privacy.private.long": "تنها پی‌گیرندگانتان", "privacy.private.long": "تنها پی‌گیرندگانتان",
"privacy.private.short": "پی‌گیرندگان", "privacy.private.short": "پی‌گیرندگان",
"privacy.public.long": "هرکسی در و بیرون از ماستودون", "privacy.public.long": "هرکسی در و بیرون از ماستودون",

View File

@ -598,11 +598,11 @@
"notification_requests.confirm_accept_multiple.button": "{count, plural, one {Samþykkja beiðni} other {Samþykkja beiðnir}}", "notification_requests.confirm_accept_multiple.button": "{count, plural, one {Samþykkja beiðni} other {Samþykkja beiðnir}}",
"notification_requests.confirm_accept_multiple.message": "Þú ert að fara að samþykkja {count, plural, one {eina beiðni um tilkynningar} other {# beiðnir um tilkynningar}}. Ertu viss um að þú viljir halda áfram?", "notification_requests.confirm_accept_multiple.message": "Þú ert að fara að samþykkja {count, plural, one {eina beiðni um tilkynningar} other {# beiðnir um tilkynningar}}. Ertu viss um að þú viljir halda áfram?",
"notification_requests.confirm_accept_multiple.title": "Samþykkja beiðnir um tilkynningar?", "notification_requests.confirm_accept_multiple.title": "Samþykkja beiðnir um tilkynningar?",
"notification_requests.confirm_dismiss_multiple.button": "{count, plural, one {Afgreiða beiðni} other {Afgreiða beiðnir}}", "notification_requests.confirm_dismiss_multiple.button": "{count, plural, one {Hafna beiðni} other {Hafna beiðnum}}",
"notification_requests.confirm_dismiss_multiple.message": "Þú ert að fara að hunsa {count, plural, one {eina beiðni um tilkynningar} other {# beiðnir um tilkynningar}}. Þú munt ekki eiga auðvelt með að skoða {count, plural, one {hana} other {þær}} aftur síðar. Ertu viss um að þú viljir halda áfram?", "notification_requests.confirm_dismiss_multiple.message": "Þú ert að fara að hunsa {count, plural, one {eina beiðni um tilkynningar} other {# beiðnir um tilkynningar}}. Þú munt ekki eiga auðvelt með að skoða {count, plural, one {hana} other {þær}} aftur síðar. Ertu viss um að þú viljir halda áfram?",
"notification_requests.confirm_dismiss_multiple.title": "Hunsa beiðnir um tilkynningar?", "notification_requests.confirm_dismiss_multiple.title": "Hunsa beiðnir um tilkynningar?",
"notification_requests.dismiss": "Afgreiða", "notification_requests.dismiss": "Hafna",
"notification_requests.dismiss_multiple": "{count, plural, one {Afgreiða # beiðni…} other {Afgreiða # beiðnir…}}", "notification_requests.dismiss_multiple": "{count, plural, one {Hafna # beiðni…} other {Hafna # beiðnum…}}",
"notification_requests.edit_selection": "Breyta", "notification_requests.edit_selection": "Breyta",
"notification_requests.exit_selection": "Lokið", "notification_requests.exit_selection": "Lokið",
"notification_requests.explainer_for_limited_account": "Tilkynningar frá þessum notanda hafa verið síaðar þar sem aðgangur hans hefur verið takmarkaður af umsjónarmanni.", "notification_requests.explainer_for_limited_account": "Tilkynningar frá þessum notanda hafa verið síaðar þar sem aðgangur hans hefur verið takmarkaður af umsjónarmanni.",

View File

@ -311,6 +311,7 @@
"filter_modal.select_filter.subtitle": "Izmanto esošu kategoriju vai izveido jaunu", "filter_modal.select_filter.subtitle": "Izmanto esošu kategoriju vai izveido jaunu",
"filter_modal.select_filter.title": "Filtrēt šo ziņu", "filter_modal.select_filter.title": "Filtrēt šo ziņu",
"filter_modal.title.status": "Filtrēt ziņu", "filter_modal.title.status": "Filtrēt ziņu",
"filtered_notifications_banner.title": "Filtrētie paziņojumi",
"firehose.all": "Visi", "firehose.all": "Visi",
"firehose.local": "Šis serveris", "firehose.local": "Šis serveris",
"firehose.remote": "Citi serveri", "firehose.remote": "Citi serveri",
@ -321,6 +322,8 @@
"follow_suggestions.dismiss": "Vairs nerādīt", "follow_suggestions.dismiss": "Vairs nerādīt",
"follow_suggestions.friends_of_friends_longer": "Populārs to cilvēku vidū, kuriem tu seko", "follow_suggestions.friends_of_friends_longer": "Populārs to cilvēku vidū, kuriem tu seko",
"follow_suggestions.personalized_suggestion": "Pielāgots ieteikums", "follow_suggestions.personalized_suggestion": "Pielāgots ieteikums",
"follow_suggestions.popular_suggestion": "Populārs ieteikums",
"follow_suggestions.popular_suggestion_longer": "Populārs {domain}",
"follow_suggestions.similar_to_recently_followed_longer": "Līdzīgi profieliem, kuriem nesen sāki sekot", "follow_suggestions.similar_to_recently_followed_longer": "Līdzīgi profieliem, kuriem nesen sāki sekot",
"follow_suggestions.view_all": "Skatīt visu", "follow_suggestions.view_all": "Skatīt visu",
"follow_suggestions.who_to_follow": "Kam sekot", "follow_suggestions.who_to_follow": "Kam sekot",
@ -496,6 +499,7 @@
"notifications.column_settings.filter_bar.category": "Atrās atlasīšanas josla", "notifications.column_settings.filter_bar.category": "Atrās atlasīšanas josla",
"notifications.column_settings.follow": "Jauni sekotāji:", "notifications.column_settings.follow": "Jauni sekotāji:",
"notifications.column_settings.follow_request": "Jauni sekošanas pieprasījumi:", "notifications.column_settings.follow_request": "Jauni sekošanas pieprasījumi:",
"notifications.column_settings.group": "Grupēt",
"notifications.column_settings.mention": "Pieminēšanas:", "notifications.column_settings.mention": "Pieminēšanas:",
"notifications.column_settings.poll": "Aptaujas rezultāti:", "notifications.column_settings.poll": "Aptaujas rezultāti:",
"notifications.column_settings.push": "Uznirstošie paziņojumi", "notifications.column_settings.push": "Uznirstošie paziņojumi",
@ -520,6 +524,7 @@
"notifications.permission_denied_alert": "Darbvirsmas paziņojumus nevar iespējot, jo pārlūkprogrammai atļauja tika iepriekš atteikta", "notifications.permission_denied_alert": "Darbvirsmas paziņojumus nevar iespējot, jo pārlūkprogrammai atļauja tika iepriekš atteikta",
"notifications.permission_required": "Darbvirsmas paziņojumi nav pieejami, jo nav piešķirta nepieciešamā atļauja.", "notifications.permission_required": "Darbvirsmas paziņojumi nav pieejami, jo nav piešķirta nepieciešamā atļauja.",
"notifications.policy.accept": "Pieņemt", "notifications.policy.accept": "Pieņemt",
"notifications.policy.drop": "Ignorēt",
"notifications.policy.filter_new_accounts_title": "Jauni konti", "notifications.policy.filter_new_accounts_title": "Jauni konti",
"notifications.policy.filter_not_followers_title": "Cilvēki, kuri Tev neseko", "notifications.policy.filter_not_followers_title": "Cilvēki, kuri Tev neseko",
"notifications.policy.filter_not_following_hint": "Līdz tos pašrocīgi apstiprināsi", "notifications.policy.filter_not_following_hint": "Līdz tos pašrocīgi apstiprināsi",
@ -527,6 +532,7 @@
"notifications_permission_banner.enable": "Iespējot darbvirsmas paziņojumus", "notifications_permission_banner.enable": "Iespējot darbvirsmas paziņojumus",
"notifications_permission_banner.how_to_control": "Lai saņemtu paziņojumus, kad Mastodon nav atvērts, iespējo darbvirsmas paziņojumus. Vari precīzi kontrolēt, kāda veida mijiedarbības rada darbvirsmas paziņojumus, izmantojot augstāk redzamo pogu {icon}, kad tie būs iespējoti.", "notifications_permission_banner.how_to_control": "Lai saņemtu paziņojumus, kad Mastodon nav atvērts, iespējo darbvirsmas paziņojumus. Vari precīzi kontrolēt, kāda veida mijiedarbības rada darbvirsmas paziņojumus, izmantojot augstāk redzamo pogu {icon}, kad tie būs iespējoti.",
"notifications_permission_banner.title": "Nekad nepalaid neko garām", "notifications_permission_banner.title": "Nekad nepalaid neko garām",
"onboarding.follows.back": "Atpakaļ",
"onboarding.follows.empty": "Diemžēl pašlaik nevar parādīt rezultātus. Vari mēģināt izmantot meklēšanu vai pārlūkot izpētes lapu, lai atrastu cilvēkus, kuriem sekot, vai vēlāk mēģināt vēlreiz.", "onboarding.follows.empty": "Diemžēl pašlaik nevar parādīt rezultātus. Vari mēģināt izmantot meklēšanu vai pārlūkot izpētes lapu, lai atrastu cilvēkus, kuriem sekot, vai vēlāk mēģināt vēlreiz.",
"onboarding.profile.discoverable": "Padarīt manu profilu atklājamu", "onboarding.profile.discoverable": "Padarīt manu profilu atklājamu",
"onboarding.profile.display_name": "Attēlojamais vārds", "onboarding.profile.display_name": "Attēlojamais vārds",

View File

@ -115,7 +115,7 @@ module Account::Interactions
end end
def follow!(other_account, reblogs: nil, notify: nil, languages: nil, uri: nil, rate_limit: false, bypass_limit: false) def follow!(other_account, reblogs: nil, notify: nil, languages: nil, uri: nil, rate_limit: false, bypass_limit: false)
rel = active_relationships.create_with(show_reblogs: reblogs.nil? ? true : reblogs, notify: notify.nil? ? false : notify, languages: languages, uri: uri, rate_limit: rate_limit, bypass_follow_limit: bypass_limit) rel = active_relationships.create_with(show_reblogs: reblogs.nil? || reblogs, notify: notify.nil? ? false : notify, languages: languages, uri: uri, rate_limit: rate_limit, bypass_follow_limit: bypass_limit)
.find_or_create_by!(target_account: other_account) .find_or_create_by!(target_account: other_account)
rel.show_reblogs = reblogs unless reblogs.nil? rel.show_reblogs = reblogs unless reblogs.nil?
@ -128,7 +128,7 @@ module Account::Interactions
end end
def request_follow!(other_account, reblogs: nil, notify: nil, languages: nil, uri: nil, rate_limit: false, bypass_limit: false) def request_follow!(other_account, reblogs: nil, notify: nil, languages: nil, uri: nil, rate_limit: false, bypass_limit: false)
rel = follow_requests.create_with(show_reblogs: reblogs.nil? ? true : reblogs, notify: notify.nil? ? false : notify, uri: uri, languages: languages, rate_limit: rate_limit, bypass_follow_limit: bypass_limit) rel = follow_requests.create_with(show_reblogs: reblogs.nil? || reblogs, notify: notify.nil? ? false : notify, uri: uri, languages: languages, rate_limit: rate_limit, bypass_follow_limit: bypass_limit)
.find_or_create_by!(target_account: other_account) .find_or_create_by!(target_account: other_account)
rel.show_reblogs = reblogs unless reblogs.nil? rel.show_reblogs = reblogs unless reblogs.nil?

View File

@ -13,7 +13,7 @@
- if tag.trendable? - if tag.trendable?
· ·
%abbr{ title: t('admin.trends.tags.current_score', score: tag.trend.score) }= t('admin.trends.tags.trending_rank', rank: tag.trend.rank + 1) %abbr{ title: t('admin.trends.tags.current_score', score: tag.trend.score) }= t('admin.trends.tags.trending_rank', rank: tag.trend.rank)
- if tag.decaying? - if tag.decaying?
· ·

View File

@ -12,6 +12,8 @@
= opengraph 'og:title', "#{display_name(@account)} (#{acct(@account)})" = opengraph 'og:title', "#{display_name(@account)} (#{acct(@account)})"
= opengraph 'og:url', short_account_status_url(@account, @status) = opengraph 'og:url', short_account_status_url(@account, @status)
= opengraph 'og:published_time', @status.created_at.iso8601 = opengraph 'og:published_time', @status.created_at.iso8601
- if @status.language.present?
= opengraph 'og:locale', @status.language
= opengraph 'profile:username', acct(@account)[1..] = opengraph 'profile:username', acct(@account)[1..]
= render 'og_description', activity: @status = render 'og_description', activity: @status

View File

@ -194,7 +194,7 @@ ko:
destroy_domain_allow: 도메인 허용 삭제 destroy_domain_allow: 도메인 허용 삭제
destroy_domain_block: 도메인 차단 삭제 destroy_domain_block: 도메인 차단 삭제
destroy_email_domain_block: 이메일 도메인 차단 삭제 destroy_email_domain_block: 이메일 도메인 차단 삭제
destroy_instance: 도메인 제거 destroy_instance: 도메인 퍼지
destroy_ip_block: IP 규칙 삭제 destroy_ip_block: IP 규칙 삭제
destroy_relay: 릴레이 삭제 destroy_relay: 릴레이 삭제
destroy_status: 게시물 삭제 destroy_status: 게시물 삭제
@ -536,7 +536,7 @@ ko:
title: 중재 title: 중재
private_comment: 비공개 주석 private_comment: 비공개 주석
public_comment: 공개 주석 public_comment: 공개 주석
purge: 제거 purge: 퍼지
purge_description_html: 이 도메인이 영구적으로 오프라인 상태라고 생각되면, 스토리지에서 이 도메인의 모든 계정 레코드와 관련 데이터를 삭제할 수 있습니다. 이 작업은 시간이 좀 걸릴 수 있습니다. purge_description_html: 이 도메인이 영구적으로 오프라인 상태라고 생각되면, 스토리지에서 이 도메인의 모든 계정 레코드와 관련 데이터를 삭제할 수 있습니다. 이 작업은 시간이 좀 걸릴 수 있습니다.
title: 연합 title: 연합
total_blocked_by_us: 우리에게 차단 됨 total_blocked_by_us: 우리에게 차단 됨

View File

@ -137,6 +137,7 @@ fa:
admin_email: اخطارهای حقوقی شامل اخطارهای متقابل، دستورها دادگاه، درخواست‌های حذف و درخواست‌های اجرای قانون است. admin_email: اخطارهای حقوقی شامل اخطارهای متقابل، دستورها دادگاه، درخواست‌های حذف و درخواست‌های اجرای قانون است.
arbitration_address: می‌تواند مانند آدرس فیزیکی بالا باشد، یا در صورت استفاده از ایمیل، «N/A» باشد arbitration_address: می‌تواند مانند آدرس فیزیکی بالا باشد، یا در صورت استفاده از ایمیل، «N/A» باشد
arbitration_website: اگر از ایمیل استفاده می کنید، می تواند یک فرم وب یا "N/A" باشد arbitration_website: اگر از ایمیل استفاده می کنید، می تواند یک فرم وب یا "N/A" باشد
choice_of_law: شهر، منطقه، قلمرو یا ایالتی که قوانین ماهوی داخلی آن بر هر یک از دعاوی حاکم است.
dmca_address: برای اپراتورهای ایالات متحده، از آدرس ثبت شده در دایرکتوری نماینده تعیین شده DMCA استفاده کنید. یک P.O. فهرست جعبه در صورت درخواست مستقیم در دسترس است، از درخواست چشم پوشی از صندوق پست دفتر پست DMCA برای ایمیل به دفتر حق نسخه برداری استفاده کنید و توضیح دهید که شما یک ناظر محتوای خانگی هستید که از انتقام یا تلافی برای اعمال خود می ترسید و باید از P.O استفاده کنید. کادری برای حذف آدرس خانه شما از نمای عمومی. dmca_address: برای اپراتورهای ایالات متحده، از آدرس ثبت شده در دایرکتوری نماینده تعیین شده DMCA استفاده کنید. یک P.O. فهرست جعبه در صورت درخواست مستقیم در دسترس است، از درخواست چشم پوشی از صندوق پست دفتر پست DMCA برای ایمیل به دفتر حق نسخه برداری استفاده کنید و توضیح دهید که شما یک ناظر محتوای خانگی هستید که از انتقام یا تلافی برای اعمال خود می ترسید و باید از P.O استفاده کنید. کادری برای حذف آدرس خانه شما از نمای عمومی.
dmca_email: می‌تواند همان ایمیلی باشد که برای “آدرس ایمیل برای اطلاعیه‌های قانونی“ در بالا استفاده شده است dmca_email: می‌تواند همان ایمیلی باشد که برای “آدرس ایمیل برای اطلاعیه‌های قانونی“ در بالا استفاده شده است
domain: شناسایی منحصر به فرد سرویس آنلاینی که ارائه می کنید. domain: شناسایی منحصر به فرد سرویس آنلاینی که ارائه می کنید.
@ -233,6 +234,7 @@ fa:
setting_display_media_show_all: نمایش همه setting_display_media_show_all: نمایش همه
setting_expand_spoilers: همیشه فرسته‌هایی را که هشدار محتوا دارند کامل نشان بده setting_expand_spoilers: همیشه فرسته‌هایی را که هشدار محتوا دارند کامل نشان بده
setting_hide_network: نهفتن شبکهٔ ارتباطی setting_hide_network: نهفتن شبکهٔ ارتباطی
setting_missing_alt_text_modal: نمایش گفتگوی تایید قبل از ارسال رسانه بدون متن جایگزین
setting_reduce_motion: کاستن از حرکت در پویانمایی‌ها setting_reduce_motion: کاستن از حرکت در پویانمایی‌ها
setting_system_font_ui: به‌کاربردن قلم پیش‌فرض سیستم setting_system_font_ui: به‌کاربردن قلم پیش‌فرض سیستم
setting_system_scrollbars_ui: از نوار اسکرول پیش فرض سیستم استفاده کنید setting_system_scrollbars_ui: از نوار اسکرول پیش فرض سیستم استفاده کنید
@ -337,6 +339,7 @@ fa:
admin_email: آدرس ایمیل برای اطلاعیه های حقوقی admin_email: آدرس ایمیل برای اطلاعیه های حقوقی
arbitration_address: آدرس فیزیکی اعلامیه های داوری arbitration_address: آدرس فیزیکی اعلامیه های داوری
arbitration_website: وب سایت ارسال اخطارهای داوری arbitration_website: وب سایت ارسال اخطارهای داوری
choice_of_law: انتخاب قانون
dmca_address: آدرس فیزیکی اعلامیه‌های DMCA/حق نسخه‌برداری dmca_address: آدرس فیزیکی اعلامیه‌های DMCA/حق نسخه‌برداری
dmca_email: آدرس ایمیل برای اعلامیه‌های DMCA/حق نسخه‌برداری dmca_email: آدرس ایمیل برای اعلامیه‌های DMCA/حق نسخه‌برداری
domain: دامنه domain: دامنه

View File

@ -337,6 +337,7 @@ ko:
admin_email: 법적 조치를 위한 이메일 주소 admin_email: 법적 조치를 위한 이메일 주소
arbitration_address: 중재 통지를 위한 실제 주소 arbitration_address: 중재 통지를 위한 실제 주소
arbitration_website: 중재 통지를 제출하기 위한 웹사이트 arbitration_website: 중재 통지를 제출하기 위한 웹사이트
choice_of_law: 준거법 지정
dmca_address: DMCA/저작권 통지를 위한 실제 주소 dmca_address: DMCA/저작권 통지를 위한 실제 주소
dmca_email: DMCA/저작권 통지를 위한 이메일 주소 dmca_email: DMCA/저작권 통지를 위한 이메일 주소
domain: 도메인 domain: 도메인

View File

@ -137,6 +137,7 @@ pl:
admin_email: Zawiadomienia prawne obejmują środki zapobiegawcze, nakazy sądowe, wnioski o popełnienie sprawy oraz wnioski organów ścigania. admin_email: Zawiadomienia prawne obejmują środki zapobiegawcze, nakazy sądowe, wnioski o popełnienie sprawy oraz wnioski organów ścigania.
arbitration_address: Może być taki sam jak adres fizyczny powyżej lub „N/A” jeśli używasz adresu e-mail arbitration_address: Może być taki sam jak adres fizyczny powyżej lub „N/A” jeśli używasz adresu e-mail
arbitration_website: Może być formularzem internetowym lub „N/A”, jeśli używasz adresu e-mail arbitration_website: Może być formularzem internetowym lub „N/A”, jeśli używasz adresu e-mail
choice_of_law: Miasto, region, terytorium lub stan, którego wewnętrzne prawo będzie regulowało wszelkie roszczenia.
dmca_address: W przypadku operatorów z USA należy użyć adresu zarejestrowanego w DMCA Designated Agent Directory. Lista skrytek pocztowych dostępna jest na bezpośrednią prośbę użytkownika. Użyj DMCA Agent Post Office Box Waiver Request, aby wysłać email do Copyright Office z informacją, że jesteś domowym administratorm treści i z powodu obawy o zemstę lub odwetu za swoje działania, musisz użyć skrytki pocztowej, żeby usunąć swój adres domowy z dostępu publicznego. dmca_address: W przypadku operatorów z USA należy użyć adresu zarejestrowanego w DMCA Designated Agent Directory. Lista skrytek pocztowych dostępna jest na bezpośrednią prośbę użytkownika. Użyj DMCA Agent Post Office Box Waiver Request, aby wysłać email do Copyright Office z informacją, że jesteś domowym administratorm treści i z powodu obawy o zemstę lub odwetu za swoje działania, musisz użyć skrytki pocztowej, żeby usunąć swój adres domowy z dostępu publicznego.
dmca_email: Adres email może być taki sam jak wcześniejszy "adres e-mail przeznaczony do celów prawnych" dmca_email: Adres email może być taki sam jak wcześniejszy "adres e-mail przeznaczony do celów prawnych"
domain: Unikalny numer identyfikacji świadczonej przez Ciebie usługi online. domain: Unikalny numer identyfikacji świadczonej przez Ciebie usługi online.
@ -338,6 +339,7 @@ pl:
admin_email: Adres e-mail przeznaczony do celów prawnych admin_email: Adres e-mail przeznaczony do celów prawnych
arbitration_address: Adres fizyczny powiadomień arbitrażowych arbitration_address: Adres fizyczny powiadomień arbitrażowych
arbitration_website: Strona internetowa do składania zgłoszeń arbitrażowych arbitration_website: Strona internetowa do składania zgłoszeń arbitrażowych
choice_of_law: Wybór prawa
dmca_address: Adres fizyczny dla zgłoszeń naruszenia DMCA/praw autorskich dmca_address: Adres fizyczny dla zgłoszeń naruszenia DMCA/praw autorskich
dmca_email: Adres e-mail dla zgłoszeń naruszenia DMCA/praw autorskich dmca_email: Adres e-mail dla zgłoszeń naruszenia DMCA/praw autorskich
domain: Domena domain: Domena

View File

@ -25,10 +25,12 @@ sk:
one: Príspevok one: Príspevok
other: Príspevkov other: Príspevkov
posts_tab_heading: Príspevky posts_tab_heading: Príspevky
self_follow_error: Nieje povolené nasledovať svoj vlastný účet
admin: admin:
account_actions: account_actions:
action: Vykonaj action: Vykonaj
already_silenced: Tento účet už bol obmedzený. already_silenced: Tento účet už bol obmedzený.
already_suspended: Tento účet už bol vylúčený.
title: Vykonaj moderovací úkon voči %{acct} title: Vykonaj moderovací úkon voči %{acct}
account_moderation_notes: account_moderation_notes:
create: Zanechaj poznámku create: Zanechaj poznámku

View File

@ -78,7 +78,7 @@ namespace :api, format: false do
end end
end end
resources :media, only: [:create, :update, :show] resources :media, only: [:create, :update, :show, :destroy]
resources :blocks, only: [:index] resources :blocks, only: [:index]
resources :mutes, only: [:index] resources :mutes, only: [:index]
resources :favourites, only: [:index] resources :favourites, only: [:index]

View File

@ -3,7 +3,7 @@ const config = {
'Gemfile|*.{rb,ruby,ru,rake}': 'bin/rubocop --force-exclusion -a', 'Gemfile|*.{rb,ruby,ru,rake}': 'bin/rubocop --force-exclusion -a',
'*.{js,jsx,ts,tsx}': 'eslint --fix', '*.{js,jsx,ts,tsx}': 'eslint --fix',
'*.{css,scss}': 'stylelint --fix', '*.{css,scss}': 'stylelint --fix',
'*.haml': 'bin/haml-lint -a --parallel', '*.haml': 'bin/haml-lint -a',
'**/*.ts?(x)': () => 'tsc -p tsconfig.json --noEmit', '**/*.ts?(x)': () => 'tsc -p tsconfig.json --noEmit',
}; };

View File

@ -1,23 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::RelationshipsController do
render_views
let(:user) { Fabricate(:admin_user) }
before do
sign_in user, scope: :user
end
describe 'GET #index' do
let(:account) { Fabricate(:account) }
it 'returns http success' do
get :index, params: { account_id: account.id }
expect(response).to have_http_status(:success)
end
end
end

View File

@ -1,21 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::Trends::Links::PreviewCardProvidersController do
render_views
let(:user) { Fabricate(:admin_user) }
before do
sign_in user, scope: :user
end
describe 'GET #index' do
it 'returns http success' do
get :index
expect(response).to have_http_status(:success)
end
end
end

View File

@ -1,21 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::Trends::LinksController do
render_views
let(:user) { Fabricate(:admin_user) }
before do
sign_in user, scope: :user
end
describe 'GET #index' do
it 'returns http success' do
get :index
expect(response).to have_http_status(:success)
end
end
end

View File

@ -1,21 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::Trends::StatusesController do
render_views
let(:user) { Fabricate(:admin_user) }
before do
sign_in user, scope: :user
end
describe 'GET #index' do
it 'returns http success' do
get :index
expect(response).to have_http_status(:success)
end
end
end

View File

@ -1,21 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Admin::Trends::TagsController do
render_views
let(:user) { Fabricate(:admin_user) }
before do
sign_in user, scope: :user
end
describe 'GET #index' do
it 'returns http success' do
get :index
expect(response).to have_http_status(:success)
end
end
end

View File

@ -193,4 +193,57 @@ RSpec.describe 'Media' do
end end
end end
end end
describe 'DELETE /api/v1/media/:id' do
subject do
delete "/api/v1/media/#{media.id}", headers: headers
end
context 'when media is not attached to a status' do
let(:media) { Fabricate(:media_attachment, account: user.account, status: nil) }
it 'returns http empty response' do
subject
expect(response).to have_http_status(200)
expect(response.content_type)
.to start_with('application/json')
expect(MediaAttachment.where(id: media.id)).to_not exist
end
end
context 'when media is attached to a status' do
let(:media) { Fabricate(:media_attachment, account: user.account, status: Fabricate.build(:status)) }
it 'returns http unprocessable entity' do
subject
expect(response).to have_http_status(422)
expect(response.content_type)
.to start_with('application/json')
expect(response.parsed_body).to match(
a_hash_including(
error: 'Media attachment is currently used by a status'
)
)
expect(MediaAttachment.where(id: media.id)).to exist
end
end
context 'when the media belongs to somebody else' do
let(:media) { Fabricate(:media_attachment, status: nil) }
it 'returns http not found' do
subject
expect(response).to have_http_status(404)
expect(response.content_type)
.to start_with('application/json')
expect(MediaAttachment.where(id: media.id)).to exist
end
end
end
end end

View File

@ -257,13 +257,30 @@ RSpec.describe '/api/v1/statuses' do
it_behaves_like 'forbidden for wrong scope', 'read read:statuses' it_behaves_like 'forbidden for wrong scope', 'read read:statuses'
it 'removes the status', :aggregate_failures do it 'discards the status and schedules removal as a redraft', :aggregate_failures do
subject subject
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(response.content_type) expect(response.content_type)
.to start_with('application/json') .to start_with('application/json')
expect(Status.find_by(id: status.id)).to be_nil expect(Status.find_by(id: status.id)).to be_nil
expect(RemovalWorker).to have_enqueued_sidekiq_job(status.id, { 'redraft' => true })
end
context 'when called with truthy delete_media' do
subject do
delete "/api/v1/statuses/#{status.id}?delete_media=true", headers: headers
end
it 'discards the status and schedules removal without the redraft flag', :aggregate_failures do
subject
expect(response).to have_http_status(200)
expect(response.content_type)
.to start_with('application/json')
expect(Status.find_by(id: status.id)).to be_nil
expect(RemovalWorker).to have_enqueued_sidekiq_job(status.id, { 'redraft' => false })
end
end end
end end

View File

@ -0,0 +1,66 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Statuses' do
describe 'GET /@:account_username/:id' do
include AccountsHelper
def site_hostname
Rails.configuration.x.web_domain || Rails.configuration.x.local_domain
end
it 'has valid opengraph tags' do
account = Fabricate(:account, username: 'alice', display_name: 'Alice')
status = Fabricate(:status, account: account, text: 'Hello World')
get "/@#{account.username}/#{status.id}"
expect(head_link_icons.size).to eq(3) # Three favicons with sizes
expect(head_meta_content('og:title')).to match "#{display_name(account)} (#{acct(account)})"
expect(head_meta_content('og:type')).to eq 'article'
expect(head_meta_content('og:published_time')).to eq status.created_at.iso8601
expect(head_meta_content('og:url')).to eq short_account_status_url(account_username: account.username, id: status.id)
expect(head_meta_exists('og:locale')).to be false
end
it 'has og:locale opengraph tag if the status has is written in a given language' do
status_text = "Una prova d'estatus català"
account = Fabricate(:account, username: 'alice', display_name: 'Alice')
status = Fabricate(:status, account: account, text: status_text, language: 'ca')
get "/@#{account.username}/#{status.id}"
expect(head_meta_content('og:title')).to match "#{display_name(account)} (#{acct(account)})"
expect(head_meta_content('og:type')).to eq 'article'
expect(head_meta_content('og:published_time')).to eq status.created_at.iso8601
expect(head_meta_content('og:url')).to eq short_account_status_url(account_username: account.username, id: status.id)
expect(head_meta_exists('og:locale')).to be true
expect(head_meta_content('og:locale')).to eq 'ca'
expect(head_meta_content('og:description')).to eq status_text
end
def head_link_icons
response
.parsed_body
.search('html head link[rel=icon]')
end
def head_meta_content(property)
response
.parsed_body
.search("html head meta[property='#{property}']")
.attr('content')
.text
end
def head_meta_exists(property)
!response
.parsed_body
.search("html head meta[property='#{property}']")
.empty?
end
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Admin Relationships' do
before { sign_in(admin_user) }
describe 'Viewing account relationships page' do
let(:account) { Fabricate(:account) }
it 'shows page with relationships for account' do
visit admin_account_relationships_path(account.id)
expect(page)
.to have_title(I18n.t('admin.relationships.title', acct: account.pretty_acct))
end
end
end

View File

@ -11,6 +11,8 @@ RSpec.describe 'Admin::Trends::Links::PreviewCardProviders' do
context 'without selecting any records' do context 'without selecting any records' do
it 'displays a notice about selection' do it 'displays a notice about selection' do
visit admin_trends_links_preview_card_providers_path visit admin_trends_links_preview_card_providers_path
expect(page)
.to have_title(I18n.t('admin.trends.preview_card_providers.title'))
click_on button_for_allow click_on button_for_allow

View File

@ -11,6 +11,8 @@ RSpec.describe 'Admin::Trends::Links' do
context 'without selecting any records' do context 'without selecting any records' do
it 'displays a notice about selection' do it 'displays a notice about selection' do
visit admin_trends_links_path visit admin_trends_links_path
expect(page)
.to have_title(I18n.t('admin.trends.links.title'))
click_on button_for_allow click_on button_for_allow

View File

@ -11,6 +11,8 @@ RSpec.describe 'Admin::Trends::Statuses' do
context 'without selecting any records' do context 'without selecting any records' do
it 'displays a notice about selection' do it 'displays a notice about selection' do
visit admin_trends_statuses_path visit admin_trends_statuses_path
expect(page)
.to have_title(I18n.t('admin.trends.statuses.title'))
click_on button_for_allow click_on button_for_allow

View File

@ -11,6 +11,8 @@ RSpec.describe 'Admin::Trends::Tags' do
context 'without selecting any records' do context 'without selecting any records' do
it 'displays a notice about selection' do it 'displays a notice about selection' do
visit admin_trends_tags_path visit admin_trends_tags_path
expect(page)
.to have_title(I18n.t('admin.trends.tags.title'))
click_on button_for_allow click_on button_for_allow

View File

@ -3910,9 +3910,9 @@ __metadata:
linkType: hard linkType: hard
"@types/lodash@npm:^4.14.195": "@types/lodash@npm:^4.14.195":
version: 4.17.15 version: 4.17.16
resolution: "@types/lodash@npm:4.17.15" resolution: "@types/lodash@npm:4.17.16"
checksum: 10c0/2eb2dc6d231f5fb4603d176c08c8d7af688f574d09af47466a179cd7812d9f64144ba74bb32ca014570ffdc544eedc51b7a5657212bad083b6eecbd72223f9bb checksum: 10c0/cf017901b8ab1d7aabc86d5189d9288f4f99f19a75caf020c0e2c77b8d4cead4db0d0b842d009b029339f92399f49f34377dd7c2721053388f251778b4c23534
languageName: node languageName: node
linkType: hard linkType: hard