Merge pull request #2768 from ClearlyClaire/glitch-soc/merge-upstream

Merge upstream changes up to 63ba69810e
main
Claire 2024-07-07 15:22:37 +02:00 committed by GitHub
commit 69766370fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 156 additions and 62 deletions

View File

@ -1,5 +1,5 @@
# This is a sample configuration file. You can generate your configuration # This is a sample configuration file. You can generate your configuration
# with the `rake mastodon:setup` interactive setup wizard, but to customize # with the `bundle exec rails mastodon:setup` interactive setup wizard, but to customize
# your setup even further, you'll need to edit it manually. This sample does # your setup even further, you'll need to edit it manually. This sample does
# not demonstrate all available configuration options. Please look at # not demonstrate all available configuration options. Please look at
# https://docs.joinmastodon.org/admin/config/ for the full documentation. # https://docs.joinmastodon.org/admin/config/ for the full documentation.
@ -68,7 +68,7 @@ DB_PORT=5432
# Secrets # Secrets
# ------- # -------
# Generate each with the `RAILS_ENV=production bundle exec rake secret` task (`docker-compose run --rm web bundle exec rake secret` if you use docker compose) # Generate each with the `RAILS_ENV=production bundle exec rails secret` task (`docker-compose run --rm web bundle exec rails secret` if you use docker compose)
# ------- # -------
SECRET_KEY_BASE= SECRET_KEY_BASE=
OTP_SECRET= OTP_SECRET=
@ -76,7 +76,7 @@ OTP_SECRET=
# Web Push # Web Push
# -------- # --------
# Generate with `rake mastodon:webpush:generate_vapid_key` (first is the private key, second is the public one) # Generate with `bundle exec rails mastodon:webpush:generate_vapid_key` (first is the private key, second is the public one)
# You should only generate this once per instance. If you later decide to change it, all push subscription will # 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. # be invalidated, requiring the users to access the website again to resubscribe.
# -------- # --------

View File

@ -793,10 +793,10 @@ GEM
redis (>= 4.5.0, < 5) redis (>= 4.5.0, < 5)
sidekiq-bulk (0.2.0) sidekiq-bulk (0.2.0)
sidekiq sidekiq
sidekiq-scheduler (5.0.3) sidekiq-scheduler (5.0.5)
rufus-scheduler (~> 3.2) rufus-scheduler (~> 3.2)
sidekiq (>= 6, < 8) sidekiq (>= 6, < 8)
tilt (>= 1.4.0) tilt (>= 1.4.0, < 3)
sidekiq-unique-jobs (7.1.33) sidekiq-unique-jobs (7.1.33)
brpoplpush-redis_script (> 0.1.1, <= 2.0.0) brpoplpush-redis_script (> 0.1.1, <= 2.0.0)
concurrent-ruby (~> 1.0, >= 1.0.5) concurrent-ruby (~> 1.0, >= 1.0.5)

View File

@ -130,7 +130,7 @@ export default class Card extends PureComponent {
const showAuthor = !!card.getIn(['authors', 0, 'accountId']); const showAuthor = !!card.getIn(['authors', 0, 'accountId']);
const description = ( const description = (
<div className='status-card__content'> <div className='status-card__content' dir='auto'>
<span className='status-card__host'> <span className='status-card__host'>
<span lang={language}>{provider}</span> <span lang={language}>{provider}</span>
{card.get('published_at') && <> · <RelativeTimestamp timestamp={card.get('published_at')} /></>} {card.get('published_at') && <> · <RelativeTimestamp timestamp={card.get('published_at')} /></>}

View File

@ -11007,7 +11007,7 @@ noscript {
gap: 4px; gap: 4px;
dt { dt {
flex: 0 0 auto; flex: 0 1 auto;
color: $dark-text-color; color: $dark-text-color;
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;

View File

@ -141,7 +141,7 @@ export default class Card extends PureComponent {
const showAuthor = !!card.getIn(['authors', 0, 'accountId']); const showAuthor = !!card.getIn(['authors', 0, 'accountId']);
const description = ( const description = (
<div className='status-card__content'> <div className='status-card__content' dir='auto'>
<span className='status-card__host'> <span className='status-card__host'>
<span lang={language}>{provider}</span> <span lang={language}>{provider}</span>
{card.get('published_at') && <> · <RelativeTimestamp timestamp={card.get('published_at')} /></>} {card.get('published_at') && <> · <RelativeTimestamp timestamp={card.get('published_at')} /></>}

View File

@ -411,6 +411,7 @@
"limited_account_hint.action": "構わず表示する", "limited_account_hint.action": "構わず表示する",
"limited_account_hint.title": "このプロフィールは{domain}のモデレーターによって非表示にされています。", "limited_account_hint.title": "このプロフィールは{domain}のモデレーターによって非表示にされています。",
"link_preview.author": "{name}", "link_preview.author": "{name}",
"link_preview.more_from_author": "{name}さんの投稿をもっと読む",
"lists.account.add": "リストに追加", "lists.account.add": "リストに追加",
"lists.account.remove": "リストから外す", "lists.account.remove": "リストから外す",
"lists.delete": "リストを削除", "lists.delete": "リストを削除",
@ -693,6 +694,7 @@
"server_banner.administered_by": "管理者", "server_banner.administered_by": "管理者",
"server_banner.server_stats": "サーバーの情報", "server_banner.server_stats": "サーバーの情報",
"sign_in_banner.create_account": "アカウント作成", "sign_in_banner.create_account": "アカウント作成",
"sign_in_banner.follow_anyone": "連合内の誰でもフォローして投稿を時系列で見ることができます。アルゴリズム、広告、クリックベイトはありません。",
"sign_in_banner.sign_in": "ログイン", "sign_in_banner.sign_in": "ログイン",
"sign_in_banner.sso_redirect": "ログインまたは登録", "sign_in_banner.sso_redirect": "ログインまたは登録",
"status.admin_account": "@{name}さんのモデレーション画面を開く", "status.admin_account": "@{name}さんのモデレーション画面を開く",

View File

@ -32,7 +32,7 @@
"account.featured_tags.last_status_never": "Немає дописів", "account.featured_tags.last_status_never": "Немає дописів",
"account.featured_tags.title": "{name} виділяє хештеґи", "account.featured_tags.title": "{name} виділяє хештеґи",
"account.follow": "Підписатися", "account.follow": "Підписатися",
"account.follow_back": "Підписатися взаємно", "account.follow_back": "Стежити також",
"account.followers": "Підписники", "account.followers": "Підписники",
"account.followers.empty": "Ніхто ще не підписаний на цього користувача.", "account.followers.empty": "Ніхто ще не підписаний на цього користувача.",
"account.followers_counter": "{count, plural, one {{counter} підписник} few {{counter} підписники} many {{counter} підписників} other {{counter} підписники}}", "account.followers_counter": "{count, plural, one {{counter} підписник} few {{counter} підписники} many {{counter} підписників} other {{counter} підписники}}",

View File

@ -10453,7 +10453,7 @@ noscript {
gap: 4px; gap: 4px;
dt { dt {
flex: 0 0 auto; flex: 0 1 auto;
color: $dark-text-color; color: $dark-text-color;
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;

View File

@ -156,11 +156,11 @@ class LinkDetailsExtractor
end end
def title def title
html_entities.decode(structured_data&.headline || opengraph_tag('og:title') || document.xpath('//title').map(&:content).first).strip html_entities_decode(structured_data&.headline || opengraph_tag('og:title') || document.xpath('//title').map(&:content).first)&.strip
end end
def description def description
html_entities.decode(structured_data&.description || opengraph_tag('og:description') || meta_tag('description')) html_entities_decode(structured_data&.description || opengraph_tag('og:description') || meta_tag('description'))
end end
def published_at def published_at
@ -180,7 +180,7 @@ class LinkDetailsExtractor
end end
def provider_name def provider_name
html_entities.decode(structured_data&.publisher_name || opengraph_tag('og:site_name')) html_entities_decode(structured_data&.publisher_name || opengraph_tag('og:site_name'))
end end
def provider_url def provider_url
@ -188,7 +188,7 @@ class LinkDetailsExtractor
end end
def author_name def author_name
html_entities.decode(structured_data&.author_name || opengraph_tag('og:author') || opengraph_tag('og:author:username')) html_entities_decode(structured_data&.author_name || opengraph_tag('og:author') || opengraph_tag('og:author:username'))
end end
def author_url def author_url
@ -257,7 +257,7 @@ class LinkDetailsExtractor
next if json_ld.blank? next if json_ld.blank?
structured_data = StructuredData.new(html_entities.decode(json_ld)) structured_data = StructuredData.new(html_entities_decode(json_ld))
next unless structured_data.valid? next unless structured_data.valid?
@ -273,10 +273,11 @@ class LinkDetailsExtractor
end end
def detect_encoding_and_parse_document def detect_encoding_and_parse_document
[detect_encoding, nil, @html_charset, 'UTF-8'].uniq.each do |encoding| [detect_encoding, nil, @html_charset].uniq.each do |encoding|
document = Nokogiri::HTML(@html, nil, encoding) document = Nokogiri::HTML(@html, nil, encoding)
return document if document.to_s.valid_encoding? return document if document.to_s.valid_encoding?
end end
Nokogiri::HTML(@html, nil, 'UTF-8')
end end
def detect_encoding def detect_encoding
@ -290,6 +291,15 @@ class LinkDetailsExtractor
end end
end end
def html_entities_decode(string)
return if string.nil?
unicode_string = string.encode('UTF-8')
raise EncodingError, 'cannot convert string to valid UTF-8' unless unicode_string.valid_encoding?
html_entities.decode(unicode_string)
end
def html_entities def html_entities
@html_entities ||= HTMLEntities.new(:expanded) @html_entities ||= HTMLEntities.new(:expanded)
end end

View File

@ -32,7 +32,7 @@ class FetchLinkCardService < BaseService
end end
attach_card if @card&.persisted? attach_card if @card&.persisted?
rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, Encoding::UndefinedConversionError => e rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, EncodingError => e
Rails.logger.debug { "Error fetching link #{@original_url}: #{e}" } Rails.logger.debug { "Error fetching link #{@original_url}: #{e}" }
nil nil
end end

View File

@ -5,7 +5,7 @@ Rails.application.configure do
# You should only generate this once per instance. If you later decide to change it, all push subscription will # 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. # 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 `bundle exec rails mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web bundle exec rails 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 # For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html

View File

@ -31,18 +31,18 @@ gl:
created_msg: Nota de moderación creada correctamente! created_msg: Nota de moderación creada correctamente!
destroyed_msg: Nota de moderación eliminada de xeito correcto! destroyed_msg: Nota de moderación eliminada de xeito correcto!
accounts: accounts:
add_email_domain_block: Bloquear o dominio do email add_email_domain_block: Bloquear o dominio do enderezo
approve: Aprobar approve: Aprobar
approved_msg: Aprobada a solicitude de rexistro de %{username} approved_msg: Aprobada a solicitude de rexistro de %{username}
are_you_sure: Está segura? are_you_sure: Está segura?
avatar: Imaxe de perfil avatar: Imaxe de perfil
by_domain: Dominio by_domain: Dominio
change_email: change_email:
changed_msg: Email mudado de xeito correcto! changed_msg: Correo cambiado de xeito correcto!
current_email: Email actual current_email: Enderezo actual
label: Mudar email label: Cambiar de enderezo
new_email: Novo email new_email: Novo enderezo
submit: Mudar email submit: Cambiar de enderezo
title: Mudar email de %{username} title: Mudar email de %{username}
change_role: change_role:
changed_msg: Rol mudado correctamente! changed_msg: Rol mudado correctamente!
@ -64,10 +64,10 @@ gl:
display_name: Nome a amosar display_name: Nome a amosar
domain: Dominio domain: Dominio
edit: Editar edit: Editar
email: Email email: Enderezo de correo
email_status: Estado do email email_status: Estado do correo
enable: Activar enable: Activar
enable_sign_in_token_auth: Activar autenticación cun token no email enable_sign_in_token_auth: Activar autenticación cun token no correo
enabled: Activado enabled: Activado
enabled_msg: Desbloqueada a conta de %{username} enabled_msg: Desbloqueada a conta de %{username}
followers: Seguidoras followers: Seguidoras
@ -132,7 +132,7 @@ gl:
resubscribe: Resubscribir resubscribe: Resubscribir
role: Rol role: Rol
search: Procurar search: Procurar
search_same_email_domain: Outras usuarias co mesmo dominio de email search_same_email_domain: Outras usuarias co mesmo dominio de correo
search_same_ip: Outras usuarias co mesmo IP search_same_ip: Outras usuarias co mesmo IP
security: Seguridade security: Seguridade
security_measures: security_measures:
@ -154,9 +154,9 @@ gl:
suspension_irreversible: Elimináronse de xeito irreversible os datos desta conta. Podes reactivar a conta para facela usable novamente pero non recuperará os datos eliminados. suspension_irreversible: Elimináronse de xeito irreversible os datos desta conta. Podes reactivar a conta para facela usable novamente pero non recuperará os datos eliminados.
suspension_reversible_hint_html: Esta conta foi suspendida, e os datos serán totalmente eliminados o %{date}. Ata entón, a conta pode ser restaurada sen danos. Se desexas eliminar agora mesmo todos os datos da conta, podes facelo aquí embaixo. suspension_reversible_hint_html: Esta conta foi suspendida, e os datos serán totalmente eliminados o %{date}. Ata entón, a conta pode ser restaurada sen danos. Se desexas eliminar agora mesmo todos os datos da conta, podes facelo aquí embaixo.
title: Contas title: Contas
unblock_email: Desbloquear enderezo de email unblock_email: Desbloquear enderezo de correo
unblocked_email_msg: Enderezo de email de %{username} desbloqueado unblocked_email_msg: Enderezo de correo de %{username} desbloqueado
unconfirmed_email: Email non confirmado unconfirmed_email: Enderezo de correo sen confirmar
undo_sensitized: Desmarcar como sensible undo_sensitized: Desmarcar como sensible
undo_silenced: Desfacer acalar undo_silenced: Desfacer acalar
undo_suspension: Desfacer suspensión undo_suspension: Desfacer suspensión
@ -173,12 +173,12 @@ gl:
approve_appeal: Aprobar apelación approve_appeal: Aprobar apelación
approve_user: Aprobar Usuaria approve_user: Aprobar Usuaria
assigned_to_self_report: Asignar denuncia assigned_to_self_report: Asignar denuncia
change_email_user: Editar email da usuaria change_email_user: Editar correo electrónico da usuaria
change_role_user: Cambiar Rol da Usuaria change_role_user: Cambiar Rol da Usuaria
confirm_user: Confirmar usuaria confirm_user: Confirmar usuaria
create_account_warning: Crear aviso create_account_warning: Crear aviso
create_announcement: Crear anuncio create_announcement: Crear anuncio
create_canonical_email_block: Crear Bloqueo de email create_canonical_email_block: Crear Bloqueo de correo electrónico
create_custom_emoji: Crear emoticonas personalizadas create_custom_emoji: Crear emoticonas personalizadas
create_domain_allow: Crear Dominio Permitido create_domain_allow: Crear Dominio Permitido
create_domain_block: Crear bloquedo do Dominio create_domain_block: Crear bloquedo do Dominio
@ -188,7 +188,7 @@ gl:
create_user_role: Crear Rol create_user_role: Crear Rol
demote_user: Degradar usuaria demote_user: Degradar usuaria
destroy_announcement: Eliminar anuncio destroy_announcement: Eliminar anuncio
destroy_canonical_email_block: Eliminar Bloqueo de email destroy_canonical_email_block: Eliminar Bloqueo de correo electrónico
destroy_custom_emoji: Eliminar emoticona personalizada destroy_custom_emoji: Eliminar emoticona personalizada
destroy_domain_allow: Eliminar Dominio permitido destroy_domain_allow: Eliminar Dominio permitido
destroy_domain_block: Eliminar bloqueo do Dominio destroy_domain_block: Eliminar bloqueo do Dominio
@ -200,7 +200,7 @@ gl:
destroy_user_role: Eliminar Rol destroy_user_role: Eliminar Rol
disable_2fa_user: Desactivar 2FA disable_2fa_user: Desactivar 2FA
disable_custom_emoji: Desactivar emoticona personalizada disable_custom_emoji: Desactivar emoticona personalizada
disable_sign_in_token_auth_user: Desactivar Autenticación por token no email para Usuaria disable_sign_in_token_auth_user: Desactivar Autenticación con token no correo para Usuaria
disable_user: Desactivar usuaria disable_user: Desactivar usuaria
enable_custom_emoji: Activar emoticona personalizada enable_custom_emoji: Activar emoticona personalizada
enable_sign_in_token_auth_user: Activar Autenticación con token no email para Usuaria enable_sign_in_token_auth_user: Activar Autenticación con token no email para Usuaria
@ -211,14 +211,14 @@ gl:
reject_user: Rexeitar Usuaria reject_user: Rexeitar Usuaria
remove_avatar_user: Eliminar avatar remove_avatar_user: Eliminar avatar
reopen_report: Reabrir denuncia reopen_report: Reabrir denuncia
resend_user: Reenviar o email de confirmación resend_user: Reenviar o correo de confirmación
reset_password_user: Restabelecer contrasinal reset_password_user: Restabelecer contrasinal
resolve_report: Resolver denuncia resolve_report: Resolver denuncia
sensitive_account: Marca o multimedia da túa conta como sensible sensitive_account: Marca o multimedia da túa conta como sensible
silence_account: Silenciar conta silence_account: Silenciar conta
suspend_account: Suspender conta suspend_account: Suspender conta
unassigned_report: Desasignar denuncia unassigned_report: Desasignar denuncia
unblock_email_account: Desbloquear enderezo de email unblock_email_account: Desbloquear enderezo de correo
unsensitive_account: Retira a marca de sensible do multimedia da conta unsensitive_account: Retira a marca de sensible do multimedia da conta
unsilence_account: Deixar de silenciar conta unsilence_account: Deixar de silenciar conta
unsuspend_account: Retirar suspensión de conta unsuspend_account: Retirar suspensión de conta
@ -660,7 +660,7 @@ gl:
delete_data_html: Eliminar o perfil e contidos de <strong>@%{acct}</strong> para os próximos 30 días a non ser que sexa suspendida nese período delete_data_html: Eliminar o perfil e contidos de <strong>@%{acct}</strong> para os próximos 30 días a non ser que sexa suspendida nese período
preview_preamble_html: "<strong>@%{acct}</strong> vai recibir un aviso co seguinte contido:" preview_preamble_html: "<strong>@%{acct}</strong> vai recibir un aviso co seguinte contido:"
record_strike_html: Anotar un aviso contra <strong>@%{acct}</strong> para axudarche a xestionar futuros problemas con esta conta record_strike_html: Anotar un aviso contra <strong>@%{acct}</strong> para axudarche a xestionar futuros problemas con esta conta
send_email_html: Enviar un email de aviso a <strong>@%{acct}</strong> send_email_html: Enviar un correo de aviso a <strong>@%{acct}</strong>
warning_placeholder: Razóns adicionais optativas para a acción de moderación. warning_placeholder: Razóns adicionais optativas para a acción de moderación.
target_origin: Orixe da conta denunciada target_origin: Orixe da conta denunciada
title: Denuncias title: Denuncias
@ -1060,7 +1060,7 @@ gl:
redirect_to_app_html: Ímoste redirixir á app <strong>%{app_name}</strong>. Se iso non acontece, proba %{clicking_this_link} ou volve ti manualmente á app. redirect_to_app_html: Ímoste redirixir á app <strong>%{app_name}</strong>. Se iso non acontece, proba %{clicking_this_link} ou volve ti manualmente á app.
registration_complete: Completouse a creación da conta en %{domain}! registration_complete: Completouse a creación da conta en %{domain}!
welcome_title: Benvida, %{name}! welcome_title: Benvida, %{name}!
wrong_email_hint: Se o enderezo de email non é correcto, podes cambialo nos axustes da conta. wrong_email_hint: Se o enderezo de correo non é correcto, podes cambialo nos axustes da conta.
delete_account: Eliminar conta delete_account: Eliminar conta
delete_account_html: Se queres eliminar a túa conta, podes <a href="%{path}">facelo aquí</a>. Deberás confirmar a acción. delete_account_html: Se queres eliminar a túa conta, podes <a href="%{path}">facelo aquí</a>. Deberás confirmar a acción.
description: description:

View File

@ -255,7 +255,7 @@ gl:
require_invite_text: Pedir unha razón para unirse require_invite_text: Pedir unha razón para unirse
show_domain_blocks: Amosar dominios bloqueados show_domain_blocks: Amosar dominios bloqueados
show_domain_blocks_rationale: Explicar porque están bloqueados os dominios show_domain_blocks_rationale: Explicar porque están bloqueados os dominios
site_contact_email: Email de contacto site_contact_email: Correo de contacto
site_contact_username: Nome do contacto site_contact_username: Nome do contacto
site_extended_description: Descrición ampla site_extended_description: Descrición ampla
site_short_description: Descrición do servidor site_short_description: Descrición do servidor

View File

@ -37,13 +37,13 @@ namespace :settings do
end end
end end
resource :otp_authentication, only: [:show, :create], controller: 'two_factor_authentication/otp_authentication' scope module: :two_factor_authentication do
resource :otp_authentication, only: [:show, :create], controller: :otp_authentication
resources :webauthn_credentials, only: [:index, :new, :create, :destroy], resources :webauthn_credentials, only: [:index, :new, :create, :destroy], path: 'security_keys' do
path: 'security_keys', collection do
controller: 'two_factor_authentication/webauthn_credentials' do get :options
collection do end
get :options
end end
end end

View File

@ -0,0 +1,17 @@
HTTP/1.1 200 OK
server: nginx
date: Thu, 13 Jun 2024 14:33:13 GMT
content-type: text/html; charset=utf-8
content-length: 158
accept-ranges: bytes
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tofu á l'orange</title>
</head>
<body>
<h2>Tofu á l'orange</h2>
</body>
</html>

View File

@ -0,0 +1,17 @@
HTTP/1.1 200 OK
server: nginx
date: Thu, 13 Jun 2024 14:33:13 GMT
content-type: text/html; charset=utf-8
content-length: 158
accept-ranges: bytes
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tofu with orange sauce</title>
</head>
<body>
<h2>Tofu á l'orange</h2>
</body>
</html>

View File

@ -0,0 +1,17 @@
HTTP/1.1 200 OK
server: nginx
date: Thu, 13 Jun 2024 14:33:13 GMT
content-type: text/html; charset=utf-8
content-length: 171
accept-ranges: bytes
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<h2>I am not a valid page</h2>
<p>Thankfully, browsers do not care</p>
</body>
</html>

View File

@ -27,7 +27,10 @@ RSpec.describe FetchLinkCardService do
stub_request(:get, 'http://example.com/koi8-r').to_return(request_fixture('koi8-r.txt')) stub_request(:get, 'http://example.com/koi8-r').to_return(request_fixture('koi8-r.txt'))
stub_request(:get, 'http://example.com/windows-1251').to_return(request_fixture('windows-1251.txt')) stub_request(:get, 'http://example.com/windows-1251').to_return(request_fixture('windows-1251.txt'))
stub_request(:get, 'http://example.com/low_confidence_latin1').to_return(request_fixture('low_confidence_latin1.txt')) stub_request(:get, 'http://example.com/low_confidence_latin1').to_return(request_fixture('low_confidence_latin1.txt'))
stub_request(:get, 'http://example.com/latin1_posing_as_utf8_broken').to_return(request_fixture('latin1_posing_as_utf8_broken.txt'))
stub_request(:get, 'http://example.com/latin1_posing_as_utf8_recoverable').to_return(request_fixture('latin1_posing_as_utf8_recoverable.txt'))
stub_request(:get, 'http://example.com/aergerliche-umlaute').to_return(request_fixture('redirect_with_utf8_url.txt')) stub_request(:get, 'http://example.com/aergerliche-umlaute').to_return(request_fixture('redirect_with_utf8_url.txt'))
stub_request(:get, 'http://example.com/page_without_title').to_return(request_fixture('page_without_title.txt'))
Rails.cache.write('oembed_endpoint:example.com', oembed_cache) if oembed_cache Rails.cache.write('oembed_endpoint:example.com', oembed_cache) if oembed_cache
@ -110,6 +113,14 @@ RSpec.describe FetchLinkCardService do
end end
end end
context 'with a page that has no title' do
let(:status) { Fabricate(:status, text: 'http://example.com/page_without_title') }
it 'does not create a preview card' do
expect(status.preview_card).to be_nil
end
end
context 'with a 404 URL' do context 'with a 404 URL' do
let(:status) { Fabricate(:status, text: 'http://example.com/not-found') } let(:status) { Fabricate(:status, text: 'http://example.com/not-found') }
@ -159,10 +170,30 @@ RSpec.describe FetchLinkCardService do
end end
context 'with a URL of a page in ISO-8859-1 encoding, that charlock_holmes cannot detect' do context 'with a URL of a page in ISO-8859-1 encoding, that charlock_holmes cannot detect' do
let(:status) { Fabricate(:status, text: 'Check out http://example.com/low_confidence_latin1') } context 'when encoding in http header is correct' do
let(:status) { Fabricate(:status, text: 'Check out http://example.com/low_confidence_latin1') }
it 'decodes the HTML' do it 'decodes the HTML' do
expect(status.preview_card.title).to eq("Tofu á l'orange") expect(status.preview_card.title).to eq("Tofu á l'orange")
end
end
context 'when encoding in http header is incorrect' do
context 'when encoding problems appear in unrelated tags' do
let(:status) { Fabricate(:status, text: 'Check out http://example.com/latin1_posing_as_utf8_recoverable') }
it 'decodes the HTML' do
expect(status.preview_card.title).to eq('Tofu with orange sauce')
end
end
context 'when encoding problems appear in title tag' do
let(:status) { Fabricate(:status, text: 'Check out http://example.com/latin1_posing_as_utf8_broken') }
it 'does not create a preview card' do
expect(status.preview_card).to be_nil
end
end
end end
end end

View File

@ -6752,16 +6752,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"cssnano-preset-default@npm:^7.0.3": "cssnano-preset-default@npm:^7.0.4":
version: 7.0.3 version: 7.0.4
resolution: "cssnano-preset-default@npm:7.0.3" resolution: "cssnano-preset-default@npm:7.0.4"
dependencies: dependencies:
browserslist: "npm:^4.23.1" browserslist: "npm:^4.23.1"
css-declaration-sorter: "npm:^7.2.0" css-declaration-sorter: "npm:^7.2.0"
cssnano-utils: "npm:^5.0.0" cssnano-utils: "npm:^5.0.0"
postcss-calc: "npm:^10.0.0" postcss-calc: "npm:^10.0.0"
postcss-colormin: "npm:^7.0.1" postcss-colormin: "npm:^7.0.1"
postcss-convert-values: "npm:^7.0.1" postcss-convert-values: "npm:^7.0.2"
postcss-discard-comments: "npm:^7.0.1" postcss-discard-comments: "npm:^7.0.1"
postcss-discard-duplicates: "npm:^7.0.0" postcss-discard-duplicates: "npm:^7.0.0"
postcss-discard-empty: "npm:^7.0.0" postcss-discard-empty: "npm:^7.0.0"
@ -6788,7 +6788,7 @@ __metadata:
postcss-unique-selectors: "npm:^7.0.1" postcss-unique-selectors: "npm:^7.0.1"
peerDependencies: peerDependencies:
postcss: ^8.4.31 postcss: ^8.4.31
checksum: 10c0/ab3e51003efed6542a12d43c10ca693ab26138a1d035697b9be8f07e084e37a78617cbb8028b0a7e7841302ec151f4ecf35cbd763efe291846b62c35ea4c0bb4 checksum: 10c0/0083821e778bdf7b8aa9589408a01a717be730f73584e7b81756a6fcf87af05b8f17342025e666572a8d573cc30783f2d817b0f7ad63670398bc3135b017ccad
languageName: node languageName: node
linkType: hard linkType: hard
@ -6802,14 +6802,14 @@ __metadata:
linkType: hard linkType: hard
"cssnano@npm:^7.0.0": "cssnano@npm:^7.0.0":
version: 7.0.3 version: 7.0.4
resolution: "cssnano@npm:7.0.3" resolution: "cssnano@npm:7.0.4"
dependencies: dependencies:
cssnano-preset-default: "npm:^7.0.3" cssnano-preset-default: "npm:^7.0.4"
lilconfig: "npm:^3.1.2" lilconfig: "npm:^3.1.2"
peerDependencies: peerDependencies:
postcss: ^8.4.31 postcss: ^8.4.31
checksum: 10c0/4cbcd1e0ebe0bd83196cc5b16b3a60d3ebc98326c79b2f71df597bb73c8e3ee1f42b89159d7a038acc398251184d648d9dd516f4194e46746f3af6fa74b4aec7 checksum: 10c0/3939a0b37b11cb4bae92f7916517c7ba21257551f92517b49a640d5df32e855fb7e73321f4be44d2c2de578309c05d711cdcb1976e95607b1b7f92bd4cbd1350
languageName: node languageName: node
linkType: hard linkType: hard
@ -13363,15 +13363,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"postcss-convert-values@npm:^7.0.1": "postcss-convert-values@npm:^7.0.2":
version: 7.0.1 version: 7.0.2
resolution: "postcss-convert-values@npm:7.0.1" resolution: "postcss-convert-values@npm:7.0.2"
dependencies: dependencies:
browserslist: "npm:^4.23.1" browserslist: "npm:^4.23.1"
postcss-value-parser: "npm:^4.2.0" postcss-value-parser: "npm:^4.2.0"
peerDependencies: peerDependencies:
postcss: ^8.4.31 postcss: ^8.4.31
checksum: 10c0/612f025f179f0f2ad7365db8c0b423614dcb8e1e4061875a4691a39dede0bca758d1a8f9f5c8b08e12af053e9e884f65ca5626ccc723d5b3f420650d67fe3046 checksum: 10c0/beb59faf6aae97e6d3c233c5e6ed06cc60d65c49eec576036e3d0da1a831a1e827e3d41f5e81d016440b4f0bdf1406268ae069c4d5b38a6667b310c3da079d22
languageName: node languageName: node
linkType: hard linkType: hard