diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a0dc24ee84..c6dcc4d46a 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,20 +1,15 @@ # For details, see https://github.com/devcontainers/images/tree/main/src/ruby FROM mcr.microsoft.com/devcontainers/ruby:1-3.3-bookworm -# Install Rails -# RUN gem install rails webdrivers +# Install node version from .nvmrc +WORKDIR /app +COPY .nvmrc . +RUN /bin/bash --login -i -c "nvm install" -ARG NODE_VERSION="20" -RUN . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1 +# Install additional OS packages +RUN apt-get update && \ + export DEBIAN_FRONTEND=noninteractive && \ + apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libvips42 libpam-dev -# [Optional] Uncomment this section to install additional OS packages. -RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libvips42 libpam-dev - -# [Optional] Uncomment this line to install additional gems. -RUN gem install foreman - -# [Optional] Uncomment this line to install global node packages. -RUN . /usr/local/share/nvm/nvm.sh && corepack enable 2>&1 - -COPY welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt +# Move welcome message to where VS Code expects it +COPY .devcontainer/welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt diff --git a/.devcontainer/codespaces/devcontainer.json b/.devcontainer/codespaces/devcontainer.json index c14d2c529a..d2358657f6 100644 --- a/.devcontainer/codespaces/devcontainer.json +++ b/.devcontainer/codespaces/devcontainer.json @@ -1,6 +1,6 @@ { "name": "Mastodon on GitHub Codespaces", - "dockerComposeFile": "../docker-compose.yml", + "dockerComposeFile": "../compose.yaml", "service": "app", "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/compose.yaml similarity index 96% rename from .devcontainer/docker-compose.yml rename to .devcontainer/compose.yaml index 4d6eb3c487..1e2e1ba7de 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/compose.yaml @@ -2,8 +2,8 @@ services: app: working_dir: /workspaces/mastodon/ build: - context: . - dockerfile: Dockerfile + context: .. + dockerfile: .devcontainer/Dockerfile volumes: - ..:/workspaces/mastodon:cached environment: diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 4a9cf11cc2..fb88f7801f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,6 @@ { "name": "Mastodon on local machine", - "dockerComposeFile": "docker-compose.yml", + "dockerComposeFile": "compose.yaml", "service": "app", "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", diff --git a/.github/codecov.yml b/.github/codecov.yml index 9d6413a106..701ba3af8f 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -3,9 +3,9 @@ coverage: status: project: default: - # Github status check is not blocking + # GitHub status check is not blocking informational: true patch: default: - # Github status check is not blocking + # GitHub status check is not blocking informational: true diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 378d4fc83c..2cf7bec8ee 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -2,6 +2,7 @@ $schema: 'https://docs.renovatebot.com/renovate-schema.json', extends: [ 'config:recommended', + 'customManagers:dockerfileVersions', ':labels(dependencies)', ':prConcurrentLimitNone', // Remove limit for open PRs at any time. ':prHourlyLimit2', // Rate limit PR creation to a maximum of two per hour. @@ -59,7 +60,7 @@ dependencyDashboardApproval: true, }, { - // Update Github Actions and Docker images weekly + // Update GitHub Actions and Docker images weekly matchManagers: ['github-actions', 'dockerfile', 'docker-compose'], extends: ['schedule:weekly'], }, diff --git a/.github/workflows/build-container-image.yml b/.github/workflows/build-container-image.yml index e100e15821..dbb32af9bf 100644 --- a/.github/workflows/build-container-image.yml +++ b/.github/workflows/build-container-image.yml @@ -68,7 +68,7 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Log in to the Github Container registry + - name: Log in to the GitHub Container registry if: contains(inputs.push_to_images, 'ghcr.io') uses: docker/login-action@v3 with: diff --git a/.github/workflows/crowdin-download.yml b/.github/workflows/crowdin-download.yml index 08de2749fb..1212e66296 100644 --- a/.github/workflows/crowdin-download.yml +++ b/.github/workflows/crowdin-download.yml @@ -59,13 +59,13 @@ jobs: title: 'New Crowdin Translations (automated)' author: 'GitHub Actions ' body: | - New Crowdin translations, automated with Github Actions + New Crowdin translations, automated with GitHub Actions See `.github/workflows/crowdin-download.yml` This PR will be updated every day with new translations. - Due to a limitation in Github Actions, checks are not running on this PR without manual action. + Due to a limitation in GitHub Actions, checks are not running on this PR without manual action. If you want to run the checks, then close and re-open it. branch: i18n/crowdin/translations base: main diff --git a/Dockerfile b/Dockerfile index 09aa8f2ddb..cb5b872059 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,8 +11,10 @@ ARG TARGETPLATFORM=${TARGETPLATFORM} ARG BUILDPLATFORM=${BUILDPLATFORM} # Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.3.x"] +# renovate: datasource=docker depName=docker.io/ruby ARG RUBY_VERSION="3.3.2" # # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"] +# renovate: datasource=node-version depName=node ARG NODE_MAJOR_VERSION="20" # Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"] ARG DEBIAN_VERSION="bookworm" diff --git a/Gemfile b/Gemfile index 136d074d33..be02a65626 100644 --- a/Gemfile +++ b/Gemfile @@ -57,7 +57,7 @@ gem 'hiredis', '~> 0.6' gem 'htmlentities', '~> 4.3' gem 'http', '~> 5.2.0' gem 'http_accept_language', '~> 2.1' -gem 'httplog', '~> 1.6.2' +gem 'httplog', '~> 1.7.0' gem 'i18n' gem 'idn-ruby', require: 'idn' gem 'inline_svg' diff --git a/Gemfile.lock b/Gemfile.lock index 8ff990260b..4d16fea47c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -168,7 +168,7 @@ GEM climate_control (1.2.0) cocoon (1.2.15) color_diff (0.1) - concurrent-ruby (1.3.1) + concurrent-ruby (1.3.3) connection_pool (2.4.1) cose (1.3.0) cbor (~> 0.5.9) @@ -321,7 +321,7 @@ GEM http-form_data (2.3.0) http_accept_language (2.1.1) httpclient (2.8.3) - httplog (1.6.3) + httplog (1.7.0) rack (>= 2.0) rainbow (>= 2.0.0) i18n (1.14.5) @@ -453,7 +453,7 @@ GEM concurrent-ruby (~> 1.0, >= 1.0.2) sidekiq (>= 3.5) statsd-ruby (~> 1.4, >= 1.4.0) - oj (3.16.3) + oj (3.16.4) bigdecimal (>= 3.0) omniauth (2.1.2) hashie (>= 3.4.6) @@ -578,7 +578,7 @@ GEM opentelemetry-api (~> 1.0) orm_adapter (0.5.0) ox (2.14.18) - parallel (1.24.0) + parallel (1.25.1) parser (3.3.2.0) ast (~> 2.4.1) racc @@ -739,7 +739,7 @@ GEM unicode-display_width (>= 2.4.0, < 3.0) rubocop-ast (1.31.3) parser (>= 3.3.1.0) - rubocop-capybara (2.20.0) + rubocop-capybara (2.21.0) rubocop (~> 1.41) rubocop-factory_bot (2.25.1) rubocop (~> 1.41) @@ -947,7 +947,7 @@ DEPENDENCIES htmlentities (~> 4.3) http (~> 5.2.0) http_accept_language (~> 2.1) - httplog (~> 1.6.2) + httplog (~> 1.7.0) i18n i18n-tasks (~> 1.0) idn-ruby diff --git a/README.md b/README.md index b423e89eef..c2f80374af 100644 --- a/README.md +++ b/README.md @@ -102,42 +102,51 @@ A **Vagrant** configuration is included for development purposes. To use it, com - Run `vagrant ssh -c "cd /vagrant && bin/dev"` - Open `http://mastodon.local` in your browser -### MacOS +### macOS -To set up **MacOS** for native development, complete the following steps: +To set up **macOS** for native development, complete the following steps: -- Use a Ruby version manager to install the specified version from `.ruby-version` -- Run `bundle` to install required gems -- Run `brew install postgresql@14 redis imagemagick libidn` to install required dependencies -- Navigate to Mastodon's root directory and run `brew install nvm` then `nvm use` to use the version from `.nvmrc` -- Run `yarn` to install required packages -- Run `corepack enable && corepack prepare` -- Run `RAILS_ENV=development bundle exec rails db:setup` -- Finally, run `bin/dev` which will launch the local services via `overmind` (if installed) or `foreman` +- Install [Homebrew] and run `brew install postgresql@14 redis imagemagick +libidn nvm` to install the required project dependencies +- Use a Ruby version manager to activate the ruby in `.ruby-version` and run + `nvm use` to activate the node version from `.nvmrc` +- Run the `bin/setup` script, which will install the required ruby gems and node + packages and prepare the database for local development +- Finally, run the `bin/dev` script which will launch services via `overmind` + (if installed) or `foreman` ### Docker For production hosting and deployment with **Docker**, use the `Dockerfile` and -`docker-compose.yml` in the project root directory. To create a local -development environment with **Docker**, complete the following steps: +`docker-compose.yml` in the project root directory. -- Install Docker Desktop -- Run `docker compose -f .devcontainer/docker-compose.yml up -d` -- Run `docker compose -f .devcontainer/docker-compose.yml exec app bin/setup` -- Finally, run `docker compose -f .devcontainer/docker-compose.yml exec app bin/dev` +For local development, install and launch [Docker], and run: -If you are using an IDE with [support for the Development Container specification](https://containers.dev/supporting), it will run the above `docker compose` commands automatically. For **Visual Studio Code** this requires the [Dev Container extension](https://containers.dev/supporting#dev-containers). +```shell +docker compose -f .devcontainer/compose.yaml up -d +docker compose -f .devcontainer/compose.yaml exec app bin/setup +docker compose -f .devcontainer/compose.yaml exec app bin/dev +``` + +### Dev Containers + +Within IDEs that support the [Development Containers] specification, start the +"Mastodon on local machine" container from the editor. The necessary `docker +compose` commands to build and setup the container should run automatically. For +**Visual Studio Code** this requires installing the [Dev Container extension]. ### GitHub Codespaces -To get you coding in just a few minutes, GitHub Codespaces provides a web-based version of Visual Studio Code and a cloud-hosted development environment fully configured with the software needed for this project.. +[GitHub Codespaces] provides a web-based version of VS Code and a cloud hosted +development environment configured with the software needed for this project. -- Click this button to create a new codespace:
- [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=52281283&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json) -- Wait for the environment to build. This will take a few minutes. -- When the editor is ready, run `bin/dev` in the terminal. -- After a few seconds, a popup will appear with a button labeled _Open in Browser_. This will open Mastodon. -- On the _Ports_ tab, right click on the “stream” row and select _Port visibility_ → _Public_. +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)][codespace] + +- Click the button to create a new codespace, and confirm the options +- Wait for the environment to build (takes a few minutes) +- When the editor is ready, run `bin/dev` in the terminal +- Wait for an _Open in Browser_ prompt. This will open Mastodon +- On the _Ports_ tab "stream" setting change _Port visibility_ → _Public_ ## Contributing @@ -156,3 +165,10 @@ This program is free software: you can redistribute it and/or modify it under th This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . + +[codespace]: https://codespaces.new/mastodon/mastodon?quickstart=1&devcontainer_path=.devcontainer%2Fcodespaces%2Fdevcontainer.json +[Dev Container extension]: https://containers.dev/supporting#dev-containers +[Development Containers]: https://containers.dev/supporting +[Docker]: https://docs.docker.com +[GitHub Codespaces]: https://docs.github.com/en/codespaces +[Homebrew]: https://brew.sh diff --git a/SECURITY.md b/SECURITY.md index 81472b01b4..156954ce02 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,7 +2,7 @@ If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you can either: -- open a [Github security issue on the Mastodon project](https://github.com/mastodon/mastodon/security/advisories/new) +- open a [GitHub security issue on the Mastodon project](https://github.com/mastodon/mastodon/security/advisories/new) - reach us at You should _not_ report such issues on public GitHub issues or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk. diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 6e7885f485..2dbbf78773 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -498,7 +498,14 @@ "poll_button.add_poll": "Aldoni balotenketon", "poll_button.remove_poll": "Forigi balotenketon", "privacy.change": "Agordi mesaĝan privatecon", + "privacy.direct.long": "Ĉiuj menciitaj en la afiŝo", + "privacy.direct.short": "Specifaj homoj", + "privacy.private.long": "Nur viaj sekvantoj", + "privacy.private.short": "Sekvantoj", + "privacy.public.long": "Ĉiujn ajn ĉe kaj ekster Mastodon", "privacy.public.short": "Publika", + "privacy.unlisted.long": "Malpli algoritmaj fanfaroj", + "privacy.unlisted.short": "Diskrete publika", "privacy_policy.last_updated": "Laste ĝisdatigita en {date}", "privacy_policy.title": "Politiko de privateco", "recommended": "Rekomendita", diff --git a/app/javascript/mastodon/locales/fr-CA.json b/app/javascript/mastodon/locales/fr-CA.json index 9e29852904..9c14d05d5c 100644 --- a/app/javascript/mastodon/locales/fr-CA.json +++ b/app/javascript/mastodon/locales/fr-CA.json @@ -156,7 +156,7 @@ "compose_form.poll.duration": "Durée du sondage", "compose_form.poll.multiple": "Choix multiple", "compose_form.poll.option_placeholder": "Option {number}", - "compose_form.poll.single": "Choisissez-en un", + "compose_form.poll.single": "Choix unique", "compose_form.poll.switch_to_multiple": "Changer le sondage pour autoriser plusieurs choix", "compose_form.poll.switch_to_single": "Changer le sondage pour n'autoriser qu'un seul choix", "compose_form.poll.type": "Style", @@ -585,9 +585,9 @@ "privacy.private.short": "Abonnés", "privacy.public.long": "Tout le monde sur et en dehors de Mastodon", "privacy.public.short": "Public", - "privacy.unlisted.additional": "Cette option se comporte exactement comme l'option publique, sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, l'exploration ou la recherche Mastodon, même si vous avez opté pour l'option publique pour l'ensemble de votre compte.", + "privacy.unlisted.additional": "Se comporte exactement comme « public », sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, explorer ou la recherche Mastodon, même si vous les avez activé au niveau de votre compte.", "privacy.unlisted.long": "Moins de fanfares algorithmiques", - "privacy.unlisted.short": "Public calme", + "privacy.unlisted.short": "Public discret", "privacy_policy.last_updated": "Dernière mise à jour {date}", "privacy_policy.title": "Politique de confidentialité", "recommended": "Recommandé", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 1a5803623f..36ec673a47 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -156,7 +156,7 @@ "compose_form.poll.duration": "Durée du sondage", "compose_form.poll.multiple": "Choix multiple", "compose_form.poll.option_placeholder": "Option {number}", - "compose_form.poll.single": "Choisissez-en un", + "compose_form.poll.single": "Choix unique", "compose_form.poll.switch_to_multiple": "Changer le sondage pour autoriser plusieurs choix", "compose_form.poll.switch_to_single": "Modifier le sondage pour autoriser qu'un seul choix", "compose_form.poll.type": "Style", @@ -585,9 +585,9 @@ "privacy.private.short": "Abonnés", "privacy.public.long": "Tout le monde sur et en dehors de Mastodon", "privacy.public.short": "Public", - "privacy.unlisted.additional": "Cette option se comporte exactement comme l'option publique, sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, l'exploration ou la recherche Mastodon, même si vous avez opté pour l'option publique pour l'ensemble de votre compte.", + "privacy.unlisted.additional": "Se comporte exactement comme « public », sauf que le message n'apparaîtra pas dans les flux en direct, les hashtags, explorer ou la recherche Mastodon, même si vous les avez activé au niveau de votre compte.", "privacy.unlisted.long": "Moins de fanfares algorithmiques", - "privacy.unlisted.short": "Public calme", + "privacy.unlisted.short": "Public discret", "privacy_policy.last_updated": "Dernière mise à jour {date}", "privacy_policy.title": "Politique de confidentialité", "recommended": "Recommandé", diff --git a/app/javascript/mastodon/locales/ia.json b/app/javascript/mastodon/locales/ia.json index ce4e89e99a..ed33a45d45 100644 --- a/app/javascript/mastodon/locales/ia.json +++ b/app/javascript/mastodon/locales/ia.json @@ -19,7 +19,7 @@ "account.block_domain": "Blocar dominio {domain}", "account.block_short": "Blocar", "account.blocked": "Blocate", - "account.browse_more_on_origin_server": "Percurrer plus sur le profilo original", + "account.browse_more_on_origin_server": "Explorar plus sur le profilo original", "account.cancel_follow_request": "Cancellar sequimento", "account.copy": "Copiar ligamine a profilo", "account.direct": "Mentionar privatemente @{name}", @@ -111,7 +111,7 @@ "bundle_modal_error.message": "Un error ha occurrite durante le cargamento de iste componente.", "bundle_modal_error.retry": "Tentar novemente", "closed_registrations.other_server_instructions": "Perque Mastodon es decentralisate, tu pote crear un conto sur un altere servitor e totevia interager con iste servitor.", - "closed_registrations_modal.description": "Crear un conto in {domain} actualmente non es possibile, ma considera que non es necessari haber un conto specificamente sur {domain} pro usar Mastodon.", + "closed_registrations_modal.description": "Crear un conto sur {domain} non es actualmente possibile, ma considera que non es necessari haber un conto specificamente sur {domain} pro usar Mastodon.", "closed_registrations_modal.find_another_server": "Cercar un altere servitor", "closed_registrations_modal.preamble": "Mastodon es decentralisate, dunque, non importa ubi tu crea tu conto, tu pote sequer e communicar con omne persona sur iste servitor. Tu pote mesmo hospitar tu proprie servitor!", "closed_registrations_modal.title": "Crear un conto sur Mastodon", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index 33161f8882..79224c57df 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -299,6 +299,11 @@ "follow_suggestions.dismiss": "Jangan tampilkan lagi", "follow_suggestions.hints.featured": "Profil ini telah dipilih sendiri oleh tim {domain}.", "follow_suggestions.hints.friends_of_friends": "Profil ini populer di kalangan orang yang anda ikuti.", + "follow_suggestions.personalized_suggestion": "Saran yang dipersonalisasi", + "follow_suggestions.popular_suggestion": "Saran populer", + "follow_suggestions.popular_suggestion_longer": "Populer di {domain}", + "follow_suggestions.similar_to_recently_followed_longer": "Serupa dengan profil yang baru Anda ikuti", + "follow_suggestions.view_all": "Lihat semua", "followed_tags": "Tagar yang diikuti", "footer.about": "Tentang", "footer.directory": "Direktori profil", @@ -324,6 +329,7 @@ "home.column_settings.show_reblogs": "Tampilkan boost", "home.column_settings.show_replies": "Tampilkan balasan", "home.hide_announcements": "Sembunyikan pengumuman", + "home.pending_critical_update.link": "Lihat pembaruan", "home.show_announcements": "Tampilkan pengumuman", "interaction_modal.description.follow": "Dengan sebuah akun di Mastodon, Anda bisa mengikuti {name} untuk menerima kirimannya di beranda Anda.", "interaction_modal.description.reblog": "Dengan sebuah akun di Mastodon, Anda bisa mem-boost kiriman ini untuk membagikannya ke pengikut Anda sendiri.", @@ -375,6 +381,7 @@ "lightbox.previous": "Sebelumnya", "limited_account_hint.action": "Tetap tampilkan profil", "limited_account_hint.title": "Profil ini telah disembunyikan oleh moderator {domain}.", + "link_preview.author": "Oleh {name}", "lists.account.add": "Tambah ke daftar", "lists.account.remove": "Hapus dari daftar", "lists.delete": "Hapus daftar", @@ -389,8 +396,11 @@ "lists.search": "Cari di antara orang yang Anda ikuti", "lists.subheading": "Daftar Anda", "load_pending": "{count, plural, other {# item baru}}", + "loading_indicator.label": "Memuat…", "media_gallery.toggle_visible": "Tampil/Sembunyikan", "moved_to_account_banner.text": "Akun {disabledAccount} Anda kini dinonaktifkan karena Anda pindah ke {movedToAccount}.", + "mute_modal.hide_options": "Sembunyikan opsi", + "mute_modal.title": "Bisukan pengguna?", "navigation_bar.about": "Tentang", "navigation_bar.blocks": "Pengguna diblokir", "navigation_bar.bookmarks": "Markah", diff --git a/app/javascript/mastodon/locales/la.json b/app/javascript/mastodon/locales/la.json index a49ec94d72..4bee0efed4 100644 --- a/app/javascript/mastodon/locales/la.json +++ b/app/javascript/mastodon/locales/la.json @@ -1,7 +1,9 @@ { "about.contact": "Ratio:", "about.domain_blocks.no_reason_available": "Ratio abdere est", + "about.domain_blocks.silenced.explanation": "Tua profilia atque tuum contentum ab hac serve praecipue non videbis, nisi explōrēs expresse aut subsequeris et optēs.", "account.account_note_header": "Annotatio", + "account.add_or_remove_from_list": "Adde aut ēripe ex tabellīs", "account.badges.bot": "Robotum", "account.badges.group": "Congregatio", "account.block": "Impedire @{name}", @@ -11,11 +13,21 @@ "account.domain_blocked": "Dominium impeditum", "account.edit_profile": "Recolere notionem", "account.featured_tags.last_status_never": "Nulla contributa", + "account.featured_tags.title": "Hashtag notātī {name}", + "account.followers_counter": "{count, plural, one {{counter} Sectator} other {{counter} Sectatores}}", + "account.following_counter": "{count, plural, one {{counter} Sequens} other {{counter} Sequentes}}", + "account.moved_to": "{name} significavit eum suam rationem novam nunc esse:", "account.muted": "Confutatus", + "account.requested_follow": "{name} postulavit ut te sequeretur", + "account.statuses_counter": "{count, plural, one {{counter} Nuntius} other {{counter} Nuntii}}", "account.unblock_short": "Solvere impedimentum", "account_note.placeholder": "Click to add a note", "admin.dashboard.retention.average": "Mediocritas", + "admin.impact_report.instance_accounts": "Rationes perfiles hoc deleret", + "alert.unexpected.message": "Error inopinatus occurrit.", "announcement.announcement": "Proclamatio", + "attachments_list.unprocessed": "(immūtātus)", + "block_modal.you_wont_see_mentions": "Nuntios quibus eos commemorant non videbis.", "bundle_column_error.error.title": "Eheu!", "bundle_column_error.retry": "Retemptare", "bundle_column_error.routing.title": "CCCCIIII", @@ -37,26 +49,55 @@ "compose_form.placeholder": "What is on your mind?", "compose_form.publish_form": "Barrire", "compose_form.spoiler.marked": "Text is hidden behind warning", - "compose_form.spoiler.unmarked": "Text is not hidden", + "compose_form.spoiler.unmarked": "Adde praeconium contentūs", "confirmations.block.confirm": "Impedire", "confirmations.delete.confirm": "Oblitterare", "confirmations.delete.message": "Are you sure you want to delete this status?", "confirmations.delete_list.confirm": "Oblitterare", + "confirmations.discard_edit_media.message": "Habēs mutationēs in descriptionem vel prōspectum medii quae nōn sunt servātae; eas dēmittam?", "confirmations.mute.confirm": "Confutare", "confirmations.reply.confirm": "Respondere", + "disabled_account_banner.account_settings": "Praeferentiae ratiōnis", + "disabled_account_banner.text": "Ratio tua {disabledAccount} debilitata est.", "dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.", "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", + "domain_block_modal.you_will_lose_followers": "Omnes sectatores tuī ex hoc servō removēbuntur.", + "domain_block_modal.you_wont_see_posts": "Nuntios aut notificātiōnēs ab usoribus in hōc servō nōn vidēbis.", + "domain_pill.activitypub_like_language": "ActivityPub est velut lingua quam Mastodon cum aliīs sociālibus rētibus loquitur.", + "domain_pill.your_handle": "Tuus nominulus:", + "domain_pill.your_server": "Tua domus digitalis, ubi omnia tua nuntia habitant. Hanc non amas? Servēs trānsferāre potes quōcumque tempore et sectātōrēs tuōs simul addūcere.", + "domain_pill.your_username": "Tuō singulāre id indicium in hōc servō est. Est possibile invenīre usōrēs cum eōdem nōmine in servīs aliīs.", "embed.instructions": "Embed this status on your website by copying the code below.", + "emoji_button.activity": "Actiō", "emoji_button.food": "Cibus et potus", "emoji_button.people": "Homines", "emoji_button.search": "Quaerere...", + "empty_column.account_suspended": "Rātiō suspēnsa", "empty_column.account_timeline": "Hic nulla contributa!", "empty_column.account_unavailable": "Notio non impetrabilis", - "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}", + "empty_column.blocks": "Nondum quemquam usorem obsēcāvisti.", + "empty_column.direct": "Nōn habēs adhūc ullo mentionēs prīvātās. Cum ūnam mīseris aut accipis, hīc apparēbit.", + "empty_column.followed_tags": "Nōn adhūc aliquem hastāginem secūtus es. Cum id fēceris, hic ostendētur.", + "empty_column.home": "Tua linea temporum domesticus vacua est! Sequere plures personas ut eam compleas.", "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", + "empty_column.lists": "Nōn adhūc habēs ullo tabellās. Cum creās, hīc apparēbunt.", + "empty_column.mutes": "Nondum quemquam usorem tacuisti.", + "empty_column.notification_requests": "Omnia clara sunt! Nihil hic est. Cum novās notificātiōnēs accipīs, hic secundum tua praecepta apparebunt.", + "empty_column.notifications": "Nōn adhūc habēs ullo notificātiōnēs. Cum aliī tē interagunt, hīc videbis.", "explore.trending_statuses": "Contributa", + "filtered_notifications_banner.mentions": "{count, plural, one {mentiō} other {mentiōnēs}}", + "firehose.all": "Omnis", + "footer.about": "De", "generic.saved": "Servavit", + "hashtag.column_settings.tag_mode.all": "Haec omnia", "hashtag.column_settings.tag_toggle": "Include additional tags in this column", + "hashtag.counter_by_accounts": "{count, plural, one {{counter} particeps} other {{counter} participēs}}", + "hashtag.counter_by_uses": "{count, plural, one {{counter} nuntius} other {{counter} nuntii}}", + "hashtag.counter_by_uses_today": "{count, plural, one {{counter} nuntius} other {{counter} nuntii}} hodie", + "hashtags.and_other": "…et {count, plural, other {# plus}}", + "intervals.full.days": "{number, plural, one {# die} other {# dies}}", + "intervals.full.hours": "{number, plural, one {# hora} other {# horae}}", + "intervals.full.minutes": "{number, plural, one {# minutum} other {# minuta}}", "keyboard_shortcuts.back": "Re navigare", "keyboard_shortcuts.blocked": "Aperire listam usorum obstructorum", "keyboard_shortcuts.boost": "Inlustrare publicatio", @@ -90,18 +131,47 @@ "keyboard_shortcuts.up": "to move up in the list", "lightbox.close": "Claudere", "lightbox.next": "Secundum", + "lists.account.add": "Adde ad tabellās", + "lists.new.create": "Addere tabella", + "load_pending": "{count, plural, one {# novum item} other {# nova itema}}", + "media_gallery.toggle_visible": "{number, plural, one {Cēla imaginem} other {Cēla imagines}}", + "moved_to_account_banner.text": "Tua ratione {disabledAccount} interdum reposita est, quod ad {movedToAccount} migrāvisti.", + "mute_modal.you_wont_see_mentions": "Non videbis nuntios quī eōs commemorant.", + "navigation_bar.about": "De", "navigation_bar.domain_blocks": "Hidden domains", - "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.", + "not_signed_in_indicator.not_signed_in": "Ad hunc locum pervenire oportet ut inīre facias.", + "notification.admin.report": "{name} nuntiavit {target}", + "notification.admin.sign_up": "{name} subscripsit", + "notification.favourite": "{name} nuntium tuum favit", + "notification.follow": "{name} te secutus est", + "notification.follow_request": "{name} postulavit ut te sequeretur", + "notification.mention": "{name} memoravi", + "notification.moderation_warning": "Accepistī monitionem moderationis.", + "notification.moderation_warning.action_disable": "Ratio tua debilitata est.", "notification.moderation_warning.action_none": "Tua ratiō monitum moderātiōnis accēpit.", - "notification.reblog": "{name} boosted your status", + "notification.moderation_warning.action_sensitive": "Tua nuntia hinc sensibiliter notabuntur.", + "notification.moderation_warning.action_silence": "Ratio tua est limitata.", + "notification.moderation_warning.action_suspend": "Ratio tua suspensus est.", + "notification.own_poll": "Suffragium tuum terminatum est.", + "notification.poll": "Electione in quam suffragium dedisti finita est.", + "notification.reblog": "{name} tuum nuntium amplificavit.", + "notification.relationships_severance_event.account_suspension": "Admin ab {from} {target} suspendit, quod significat nōn iam posse tē novitātēs ab eīs accipere aut cum eīs interagere.", + "notification.relationships_severance_event.domain_block": "Admin ab {from} {target} obsēcāvit, includēns {followersCount} ex tuīs sectātōribus et {followingCount, plural, one {# ratione} other {# rationibus}} quās sequeris.", + "notification.relationships_severance_event.user_domain_block": "Bloqueāstī {target}, removēns {followersCount} ex sectātōribus tuīs et {followingCount, plural, one {# rationem} other {# rationēs}} quōs sequeris.", + "notification.status": "{name} nuper publicavit", + "notification.update": "{name} nuntium correxit", + "notification_requests.accept": "Accipe", "notifications.filter.all": "Omnia", "notifications.filter.polls": "Eventus electionis", + "notifications.group": "Notificātiōnēs", "onboarding.actions.go_to_explore": "See what's trending", "onboarding.actions.go_to_home": "Go to your home feed", - "onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!", + "onboarding.follows.lead": "Tua domus feed est principalis via Mastodon experīrī. Quō plūrēs persōnas sequeris, eō actīvior et interessantior erit. Ad tē incipiendum, ecce quaedam suāsiones:", "onboarding.follows.title": "Popular on Mastodon", - "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:", + "onboarding.profile.display_name_hint": "Tuum nomen completum aut tuum nomen ludens...", + "onboarding.start.lead": "Nunc pars es Mastodonis, singularis, socialis medii platformae decentralis ubi—non algorismus—tuam ipsius experientiam curas. Incipiāmus in nova hac socialis regione:", "onboarding.start.skip": "Want to skip right ahead?", + "onboarding.start.title": "Perfecisti eam!", "onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.", "onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}", "onboarding.steps.publish_status.body": "Say hello to the world.", @@ -109,31 +179,48 @@ "onboarding.steps.setup_profile.title": "Customize your profile", "onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!", "onboarding.steps.share_profile.title": "Share your profile", - "onboarding.tips.accounts_from_other_servers": "Scisne? Quoniam Mastodon dēcentālis est, nōnnulla profīlia quae invenīs in servīs aliīs quam tuōrum erunt hospitāta. Tamen cum eīs sine impedīmentō interāgere potes! Servus eōrum in alterā parte nōminis eōrum est!", + "onboarding.tips.2fa": "Scisne? Tūam ratiōnem sēcūrāre potes duōrum elementōrum authentīcātiōnem in ratiōnis tuī praeferentiīs statuendō. Cum ūllā app TOTP ex tuā ēlēctiōne operātur, numerus tēlephōnicus necessārius nōn est!", + "onboarding.tips.accounts_from_other_servers": "Scisne? Quoniam Mastodon dēcentrālis est, nōnnulla profīlia quae invenīs in servīs aliīs quam tuōrum erunt hospitāta. Tamen cum eīs sine impedīmentō interāgere potes! Servus eōrum in alterā parte nōminis eōrum est!", "onboarding.tips.migration": "Scisne? Sī sentīs {domain} tibi in futūrō nōn esse optimam servī ēlēctiōnem, ad alium servum Mastodon sine amittendō sectātōribus tuīs migrāre potes. Etiam tuum servum hospitārī potes!", + "onboarding.tips.verification": "Scisne? Tūam ratiōnem verificāre potes iungendō nexum ad prōfīlium Mastodon tuum in propriā pāginā interrētiā et addendō pāginam ad prōfīlium tuum. Nullae pecūniae aut documenta necessāria sunt!", "poll.closed": "Clausum", + "poll.total_people": "{count, plural, one {# persona} other {# personae}}", + "poll.total_votes": "{count, plural, one {# suffragium} other {# suffragia}}", "poll.vote": "Eligere", "poll.voted": "Elegisti hoc responsum", + "poll.votes": "{votes, plural, one {# sufragium} other {# sufragia}}", "poll_button.add_poll": "Addere electionem", "poll_button.remove_poll": "Auferre electionem", "privacy.change": "Adjust status privacy", "privacy.public.short": "Coram publico", + "regeneration_indicator.sublabel": "Tua domus feed praeparātur!", + "relative_time.full.days": "{number, plural, one {# ante die} other {# ante dies}}", + "relative_time.full.hours": "{number, plural, one {# ante horam} other {# ante horas}}", "relative_time.full.just_now": "nunc", + "relative_time.full.minutes": "{number, plural, one {# ante minutum} other {# ante minuta}}", + "relative_time.full.seconds": "{number, plural, one {# ante secundum} other {# ante secunda}}", "relative_time.just_now": "nunc", "relative_time.today": "hodie", + "reply_indicator.attachments": "{count, plural, one {# annexus} other {# annexūs}}", "report.block": "Impedimentum", + "report.block_explanation": "Non videbis eorum nuntios. Non poterunt vidēre tuōs nuntios aut tē sequī. Intelligere poterunt sē obstrūctōs esse.", "report.categories.other": "Altera", "report.category.title_account": "notio", "report.category.title_status": "contributum", "report.close": "Confectum", "report.mute": "Confutare", + "report.mute_explanation": "Non videbis eōrum nuntiōs. Possunt adhuc tē sequī et tuōs nuntiōs vidēre, nec sciēbunt sē tacitōs esse.", "report.next": "Secundum", - "report.placeholder": "Type or paste additional comments", + "report.placeholder": "Commentāriī adiūnctī", "report.submit": "Mittere", "report.target": "Report {target}", - "report_notification.attached_statuses": "{count, plural, one {# post} other {# posts}} attached", + "report_notification.attached_statuses": "{count, plural, one {{count} nuntius} other {{count} nuntii}} attachiatus", "report_notification.categories.other": "Altera", "search.placeholder": "Quaerere", + "search_results.all": "Omnis", + "server_banner.active_users": "Usūrāriī āctīvī", + "server_banner.administered_by": "Administratur:", + "server_banner.introduction": "{domain} pars est de rete sociali decentralizato a {mastodon} propulsato.", "server_banner.learn_more": "Discere plura", "sign_in_banner.sign_in": "Sign in", "status.admin_status": "Open this status in the moderation interface", @@ -143,13 +230,29 @@ "status.delete": "Oblitterare", "status.edit": "Recolere", "status.edited_x_times": "Edited {count, plural, one {# time} other {# times}}", + "status.favourites": "{count, plural, one {favoritum} other {favorita}}", + "status.history.created": "{name} creatum {date}", + "status.history.edited": "{name} correxit {date}", "status.open": "Expand this status", - "status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}", + "status.reblogged_by": "{name} adiuvavit", + "status.reblogs": "{count, plural, one {auctus} other {auctūs}}", + "status.title.with_attachments": "{user} publicavit {attachmentCount, plural, one {unum annexum} other {{attachmentCount} annexa}}", "tabs_bar.home": "Domi", + "time_remaining.days": "{number, plural, one {# die} other {# dies}} restant", + "time_remaining.hours": "{number, plural, one {# hora} other {# horae}} restant", + "time_remaining.minutes": "{number, plural, one {# minutum} other {# minuta}} restant", + "time_remaining.seconds": "{number, plural, one {# secundum} other {# secunda}} restant", + "timeline_hint.remote_resource_not_displayed": "{resource} ab aliīs servīs nōn ostenduntur.", "timeline_hint.resources.statuses": "Contributa pristina", - "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {# days}}", + "trends.counter_by_accounts": "{count, plural, one {{counter} persōna} other {{counter} persōnae}} in {days, plural, one {diē prīdiē} other {diēbus praeteritīs {days}}}", + "ui.beforeunload": "Si Mastodon discesseris, tua epitome peribit.", + "units.short.billion": "{count} millia milionum", + "units.short.million": "{count} milionum", + "units.short.thousand": "{count} millia", + "upload_button.label": "Imaginēs, vīdeō aut fīle audītūs adde", "upload_form.audio_description": "Describe for people who are hard of hearing", "upload_form.edit": "Recolere", + "upload_modal.description_placeholder": "A velox brunneis vulpes salit super piger canis", "upload_progress.label": "Uploading…", "video.mute": "Confutare soni" } diff --git a/app/lib/activitypub/activity/flag.rb b/app/lib/activitypub/activity/flag.rb index 68ee43d0eb..b7a412485c 100644 --- a/app/lib/activitypub/activity/flag.rb +++ b/app/lib/activitypub/activity/flag.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class ActivityPub::Activity::Flag < ActivityPub::Activity + COMMENT_SIZE_LIMIT = 5000 + def perform return if skip_reports? @@ -38,6 +40,6 @@ class ActivityPub::Activity::Flag < ActivityPub::Activity end def report_comment - (@json['content'] || '')[0...5000] + (@json['content'] || '')[0...COMMENT_SIZE_LIMIT] end end diff --git a/app/models/appeal.rb b/app/models/appeal.rb index 395056b76f..fafa75e69d 100644 --- a/app/models/appeal.rb +++ b/app/models/appeal.rb @@ -18,6 +18,8 @@ class Appeal < ApplicationRecord MAX_STRIKE_AGE = 20.days + TEXT_LENGTH_LIMIT = 2_000 + belongs_to :account belongs_to :strike, class_name: 'AccountWarning', foreign_key: 'account_warning_id', inverse_of: :appeal @@ -26,7 +28,7 @@ class Appeal < ApplicationRecord belongs_to :rejected_by_account end - validates :text, presence: true, length: { maximum: 2_000 } + validates :text, presence: true, length: { maximum: TEXT_LENGTH_LIMIT } validates :account_warning_id, uniqueness: true validate :validate_time_frame, on: :create diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 71e44c23a6..f67064c9a1 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -187,7 +187,7 @@ class PostStatusService < BaseService end def scheduled_in_the_past? - @scheduled_at.present? && @scheduled_at <= Time.now.utc + MIN_SCHEDULE_OFFSET + @scheduled_at.present? && @scheduled_at <= Time.now.utc end def bump_potential_friendship! diff --git a/bin/setup b/bin/setup index 93c6981d0b..4ccf4594b4 100755 --- a/bin/setup +++ b/bin/setup @@ -18,7 +18,7 @@ FileUtils.chdir APP_ROOT do system('bundle check') || system!('bundle install') puts "\n== Installing JS dependencies ==" - system! 'corepack prepare' + system! 'corepack enable' system! 'bin/yarn install --immutable' puts "\n== Preparing database ==" diff --git a/config/application.rb b/config/application.rb index 069eb37740..b3a9b99ff5 100644 --- a/config/application.rb +++ b/config/application.rb @@ -60,9 +60,10 @@ Bundler.require(:pam_authentication) if ENV['PAM_ENABLED'] == 'true' module Mastodon class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 7.0 + config.load_defaults 7.1 - config.active_record.marshalling_format_version = 7.1 + # Explicitly set the cache format version to align with Rails version + config.active_support.cache_format_version = 7.1 # Please, add to the `ignore` list any other `lib` subdirectories that do # not contain `.rb` files, or that should not be reloaded or eager loaded. diff --git a/config/initializers/active_record_encryption.rb b/config/initializers/active_record_encryption.rb index 777bafc273..900f3c68f0 100644 --- a/config/initializers/active_record_encryption.rb +++ b/config/initializers/active_record_encryption.rb @@ -32,4 +32,9 @@ Rails.application.configure do config.active_record.encryption.deterministic_key = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY') config.active_record.encryption.key_derivation_salt = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT') config.active_record.encryption.primary_key = ENV.fetch('ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY') + config.active_record.encryption.support_sha1_for_non_deterministic_encryption = true + + # TODO: https://github.com/rails/rails/issues/50604#issuecomment-1880990392 + # Remove after updating to Rails 7.1.4 + ActiveRecord::Encryption.configure(**config.active_record.encryption) end diff --git a/config/initializers/new_framework_defaults_7_1.rb b/config/initializers/new_framework_defaults_7_1.rb deleted file mode 100644 index bcc300c89f..0000000000 --- a/config/initializers/new_framework_defaults_7_1.rb +++ /dev/null @@ -1,214 +0,0 @@ -# frozen_string_literal: true - -# Be sure to restart your server when you modify this file. -# -# This file eases your Rails 7.1 framework defaults upgrade. -# -# Uncomment each configuration one by one to switch to the new default. -# Once your application is ready to run with all new defaults, you can remove -# this file and set the `config.load_defaults` to `7.1`. -# -# Read the Guide for Upgrading Ruby on Rails for more info on each option. -# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html - -# No longer add autoloaded paths into `$LOAD_PATH`. This means that you won't be able -# to manually require files that are managed by the autoloader, which you shouldn't do anyway. -# This will reduce the size of the load path, making `require` faster if you don't use bootsnap, or reduce the size -# of the bootsnap cache if you use it. -Rails.application.config.add_autoload_paths_to_load_path = false - -# Remove the default X-Download-Options headers since it is used only by Internet Explorer. -# If you need to support Internet Explorer, add back `"X-Download-Options" => "noopen"`. -# Rails.application.config.action_dispatch.default_headers = { -# "X-Frame-Options" => "SAMEORIGIN", -# "X-XSS-Protection" => "0", -# "X-Content-Type-Options" => "nosniff", -# "X-Permitted-Cross-Domain-Policies" => "none", -# "Referrer-Policy" => "strict-origin-when-cross-origin" -# } - -# Do not treat an `ActionController::Parameters` instance -# as equal to an equivalent `Hash` by default. -Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality = false - -# Active Record Encryption now uses SHA-256 as its hash digest algorithm. Important: If you have -# data encrypted with previous Rails versions, there are two scenarios to consider: -# -# 1. If you have +config.active_support.key_generator_hash_digest_class+ configured as SHA1 (the default -# before Rails 7.0), you need to configure SHA-1 for Active Record Encryption too: -# Rails.application.config.active_record.encryption.hash_digest_class = OpenSSL::Digest::SHA1 -# 2. If you have +config.active_support.key_generator_hash_digest_class+ configured as SHA256 (the new default -# in 7.0), then you need to configure SHA-256 for Active Record Encryption: -# Rails.application.config.active_record.encryption.hash_digest_class = OpenSSL::Digest::SHA256 -# -# If you don't currently have data encrypted with Active Record encryption, you can disable this setting to -# configure the default behavior starting 7.1+: -# Rails.application.config.active_record.encryption.support_sha1_for_non_deterministic_encryption = false - -# No longer run after_commit callbacks on the first of multiple Active Record -# instances to save changes to the same database row within a transaction. -# Instead, run these callbacks on the instance most likely to have internal -# state which matches what was committed to the database, typically the last -# instance to save. -Rails.application.config.active_record.run_commit_callbacks_on_first_saved_instances_in_transaction = false - -# Configures SQLite with a strict strings mode, which disables double-quoted string literals. -# -# SQLite has some quirks around double-quoted string literals. -# It first tries to consider double-quoted strings as identifier names, but if they don't exist -# it then considers them as string literals. Because of this, typos can silently go unnoticed. -# For example, it is possible to create an index for a non existing column. -# See https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted for more details. -Rails.application.config.active_record.sqlite3_adapter_strict_strings_by_default = true - -# Disable deprecated singular associations names -Rails.application.config.active_record.allow_deprecated_singular_associations_name = false - -# Enable the Active Job `BigDecimal` argument serializer, which guarantees -# roundtripping. Without this serializer, some queue adapters may serialize -# `BigDecimal` arguments as simple (non-roundtrippable) strings. -# -# When deploying an application with multiple replicas, old (pre-Rails 7.1) -# replicas will not be able to deserialize `BigDecimal` arguments from this -# serializer. Therefore, this setting should only be enabled after all replicas -# have been successfully upgraded to Rails 7.1. -# Rails.application.config.active_job.use_big_decimal_serializer = true - -# Specify if an `ArgumentError` should be raised if `Rails.cache` `fetch` or -# `write` are given an invalid `expires_at` or `expires_in` time. -# Options are `true`, and `false`. If `false`, the exception will be reported -# as `handled` and logged instead. -Rails.application.config.active_support.raise_on_invalid_cache_expiration_time = true - -# Specify whether Query Logs will format tags using the SQLCommenter format -# (https://open-telemetry.github.io/opentelemetry-sqlcommenter/), or using the legacy format. -# Options are `:legacy` and `:sqlcommenter`. -Rails.application.config.active_record.query_log_tags_format = :sqlcommenter - -# Specify the default serializer used by `MessageEncryptor` and `MessageVerifier` -# instances. -# -# The legacy default is `:marshal`, which is a potential vector for -# deserialization attacks in cases where a message signing secret has been -# leaked. -# -# In Rails 7.1, the new default is `:json_allow_marshal` which serializes and -# deserializes with `ActiveSupport::JSON`, but can fall back to deserializing -# with `Marshal` so that legacy messages can still be read. -# -# In Rails 7.2, the default will become `:json` which serializes and -# deserializes with `ActiveSupport::JSON` only. -# -# Alternatively, you can choose `:message_pack` or `:message_pack_allow_marshal`, -# which serialize with `ActiveSupport::MessagePack`. `ActiveSupport::MessagePack` -# can roundtrip some Ruby types that are not supported by JSON, and may provide -# improved performance, but it requires the `msgpack` gem. -# -# For more information, see -# https://guides.rubyonrails.org/v7.1/configuring.html#config-active-support-message-serializer -# -# If you are performing a rolling deploy of a Rails 7.1 upgrade, wherein servers -# that have not yet been upgraded must be able to read messages from upgraded -# servers, first deploy without changing the serializer, then set the serializer -# in a subsequent deploy. -# Rails.application.config.active_support.message_serializer = :json_allow_marshal - -# Enable a performance optimization that serializes message data and metadata -# together. This changes the message format, so messages serialized this way -# cannot be read by older versions of Rails. However, messages that use the old -# format can still be read, regardless of whether this optimization is enabled. -# -# To perform a rolling deploy of a Rails 7.1 upgrade, wherein servers that have -# not yet been upgraded must be able to read messages from upgraded servers, -# leave this optimization off on the first deploy, then enable it on a -# subsequent deploy. -# Rails.application.config.active_support.use_message_serializer_for_metadata = true - -# Set the maximum size for Rails log files. -# -# `config.load_defaults 7.1` does not set this value for environments other than -# development and test. -# -Rails.application.config.log_file_size = 100 * 1024 * 1024 if Rails.env.local? - -# Enable raising on assignment to attr_readonly attributes. The previous -# behavior would allow assignment but silently not persist changes to the -# database. -Rails.application.config.active_record.raise_on_assign_to_attr_readonly = true - -# Enable validating only parent-related columns for presence when the parent is mandatory. -# The previous behavior was to validate the presence of the parent record, which performed an extra query -# to get the parent every time the child record was updated, even when parent has not changed. -Rails.application.config.active_record.belongs_to_required_validates_foreign_key = false - -# Enable precompilation of `config.filter_parameters`. Precompilation can -# improve filtering performance, depending on the quantity and types of filters. -Rails.application.config.precompile_filter_parameters = true - -# Enable before_committed! callbacks on all enrolled records in a transaction. -# The previous behavior was to only run the callbacks on the first copy of a record -# if there were multiple copies of the same record enrolled in the transaction. -Rails.application.config.active_record.before_committed_on_all_records = true - -# Disable automatic column serialization into YAML. -# To keep the historic behavior, you can set it to `YAML`, however it is -# recommended to explicitly define the serialization method for each column -# rather than to rely on a global default. -Rails.application.config.active_record.default_column_serializer = nil - -# Run `after_commit` and `after_*_commit` callbacks in the order they are defined in a model. -# This matches the behaviour of all other callbacks. -# In previous versions of Rails, they ran in the inverse order. -Rails.application.config.active_record.run_after_transaction_callbacks_in_order_defined = true - -# Whether a `transaction` block is committed or rolled back when exited via `return`, `break` or `throw`. -# -# Rails.application.config.active_record.commit_transaction_on_non_local_return = true - -# Controls when to generate a value for has_secure_token declarations. -# -Rails.application.config.active_record.generate_secure_token_on = :initialize - -# ** Please read carefully, this must be configured in config/application.rb ** -# Change the format of the cache entry. -# Changing this default means that all new cache entries added to the cache -# will have a different format that is not supported by Rails 7.0 -# applications. -# Only change this value after your application is fully deployed to Rails 7.1 -# and you have no plans to rollback. -# When you're ready to change format, add this to `config/application.rb` (NOT -# this file): -# config.active_support.cache_format_version = 7.1 - -# Configure Action View to use HTML5 standards-compliant sanitizers when they are supported on your -# platform. -# -# `Rails::HTML::Sanitizer.best_supported_vendor` will cause Action View to use HTML5-compliant -# sanitizers if they are supported, else fall back to HTML4 sanitizers. -# -# In previous versions of Rails, Action View always used `Rails::HTML4::Sanitizer` as its vendor. -# -Rails.application.config.action_view.sanitizer_vendor = Rails::HTML::Sanitizer.best_supported_vendor - -# Configure Action Text to use an HTML5 standards-compliant sanitizer when it is supported on your -# platform. -# -# `Rails::HTML::Sanitizer.best_supported_vendor` will cause Action Text to use HTML5-compliant -# sanitizers if they are supported, else fall back to HTML4 sanitizers. -# -# In previous versions of Rails, Action Text always used `Rails::HTML4::Sanitizer` as its vendor. -# -# Rails.application.config.action_text.sanitizer_vendor = Rails::HTML::Sanitizer.best_supported_vendor - -# Configure the log level used by the DebugExceptions middleware when logging -# uncaught exceptions during requests -# Rails.application.config.action_dispatch.debug_exception_log_level = :error - -# Configure the test helpers in Action View, Action Dispatch, and rails-dom-testing to use HTML5 -# parsers. -# -# Nokogiri::HTML5 isn't supported on JRuby, so JRuby applications must set this to :html4. -# -# In previous versions of Rails, these test helpers always used an HTML4 parser. -# -Rails.application.config.dom_testing_default_html_version = :html5 diff --git a/config/locales/devise.la.yml b/config/locales/devise.la.yml index 3a7ba0d445..a6fe5e1e4b 100644 --- a/config/locales/devise.la.yml +++ b/config/locales/devise.la.yml @@ -1 +1,8 @@ +--- la: + devise: + passwords: + send_instructions: Sī adresa tua epistularis in nostra basi datōrum exstat, vinculum ad recuperandam clavem adresa tua epistulari adferētur pauca momenta post. Sī autem hanc epistulam nōn recēpistī, rogāmus ut scrūtināriōnem spurcāriī tuī faciās. + registrations: + destroyed: Vale! Ratio tua succēssu cancellāta est. Spērāmus tē mox iterum vidēre. + signed_up_but_inactive: Te cōnscrīpsistī succēdāneē. At nōn potuimus tē introīre quod ratio* tua nōn adhūc est activāta.* diff --git a/config/locales/doorkeeper.ja.yml b/config/locales/doorkeeper.ja.yml index 62f2a3eb0a..26f7ff5635 100644 --- a/config/locales/doorkeeper.ja.yml +++ b/config/locales/doorkeeper.ja.yml @@ -135,6 +135,7 @@ ja: media: メディアの添付 mutes: ミュート notifications: 通知 + profile: Mastodonのプロフィール push: プッシュ通知 reports: 通報 search: 検索 @@ -165,6 +166,7 @@ ja: admin:write:reports: 通報に対するアクションの実行 crypto: エンドツーエンド暗号化の使用 follow: アカウントのつながりを変更 + profile: アカウントのプロフィール情報の読み取りのみ push: プッシュ通知の受信 read: アカウントのすべてのデータの読み取り read:accounts: アカウント情報の読み取り diff --git a/config/locales/doorkeeper.lad.yml b/config/locales/doorkeeper.lad.yml index b2c140b9c2..c335d67fd6 100644 --- a/config/locales/doorkeeper.lad.yml +++ b/config/locales/doorkeeper.lad.yml @@ -135,6 +135,7 @@ lad: media: Aneksos de multimedia mutes: Silensiasyones notifications: Avizos + profile: Tu profil de Mastodon push: Avizos arrepushados reports: Raportos search: Bushkeda @@ -165,6 +166,7 @@ lad: admin:write:reports: fazer aksyones de moderasyon en raportos crypto: kulanear shifrasyon de lado a lado follow: modifikar relasyones de kuentos + profile: melda solo la informasyon del profil push: risivir tus avizos arrepushados read: meldar todos tus datos de kuento read:accounts: ver enformasyon de kuentos diff --git a/config/locales/doorkeeper.lt.yml b/config/locales/doorkeeper.lt.yml index 5c2e4fd4e0..38bb17ad13 100644 --- a/config/locales/doorkeeper.lt.yml +++ b/config/locales/doorkeeper.lt.yml @@ -135,6 +135,7 @@ lt: media: Medijos priedai mutes: Nutildymai notifications: Pranešimai + profile: Tavo Mastodon profilis push: Tiesioginiai pranešimai reports: Ataskaitos search: Paieška @@ -165,6 +166,7 @@ lt: admin:write:reports: atlikti ataskaitų prižiūrėjimo veiksmus crypto: naudoti visapusį šifravimą follow: modifikuoti paskyros sąryšius + profile: skaityti tik tavo paskyros profilio informaciją push: gauti tiesioginius pranešimus read: skaityti visus paskyros duomenis read:accounts: matyti paskyrų informaciją diff --git a/config/locales/doorkeeper.sv.yml b/config/locales/doorkeeper.sv.yml index f2c8bd34b8..bc4ba6f53e 100644 --- a/config/locales/doorkeeper.sv.yml +++ b/config/locales/doorkeeper.sv.yml @@ -135,6 +135,7 @@ sv: media: Mediabilagor mutes: Tystade användare notifications: Aviseringar + profile: Din Mastodon-profil push: Push-aviseringar reports: Rapporter search: Sök diff --git a/config/locales/doorkeeper.th.yml b/config/locales/doorkeeper.th.yml index 067e065588..b0d0549d1d 100644 --- a/config/locales/doorkeeper.th.yml +++ b/config/locales/doorkeeper.th.yml @@ -135,6 +135,7 @@ th: media: ไฟล์แนบสื่อ mutes: การซ่อน notifications: การแจ้งเตือน + profile: โปรไฟล์ Mastodon ของคุณ push: การแจ้งเตือนแบบผลัก reports: การรายงาน search: ค้นหา @@ -165,6 +166,7 @@ th: admin:write:reports: ทำการกระทำการกลั่นกรองต่อรายงาน crypto: ใช้การเข้ารหัสแบบต้นทางถึงปลายทาง follow: ปรับเปลี่ยนความสัมพันธ์ของบัญชี + profile: อ่านเฉพาะข้อมูลโปรไฟล์ของบัญชีของคุณเท่านั้น push: รับการแจ้งเตือนแบบผลักของคุณ read: อ่านข้อมูลบัญชีทั้งหมดของคุณ read:accounts: ดูข้อมูลบัญชี diff --git a/config/locales/doorkeeper.vi.yml b/config/locales/doorkeeper.vi.yml index 7f1c5430ed..d0bdd2cc7b 100644 --- a/config/locales/doorkeeper.vi.yml +++ b/config/locales/doorkeeper.vi.yml @@ -135,6 +135,7 @@ vi: media: Tập tin đính kèm mutes: Đã ẩn notifications: Thông báo + profile: Hồ sơ Mastodon của bạn push: Thông báo đẩy reports: Báo cáo search: Tìm kiếm @@ -165,6 +166,7 @@ vi: admin:write:reports: áp đặt kiểm duyệt với các báo cáo crypto: dùng mã hóa đầu cuối follow: sửa đổi các mối quan hệ tài khoản + profile: chỉ đọc thông tin tài khoản cơ bản push: nhận thông báo đẩy read: đọc mọi dữ liệu tài khoản read:accounts: xem thông tin tài khoản diff --git a/config/locales/id.yml b/config/locales/id.yml index aae790f481..ac42afdb4f 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -89,6 +89,7 @@ id: moderation: active: Aktif all: Semua + disabled: Nonaktif pending: Tertunda silenced: Terbatas suspended: Disuspen @@ -121,6 +122,8 @@ id: removed_header_msg: Berhasil menghapus gambar header %{username} resend_confirmation: already_confirmed: Pengguna ini sudah dikonfirmasi + send: Kirim ulang email konfirmasi + success: Email konfirmasi berhasil dikirim! reset: Atur ulang reset_password: Reset kata sandi resubscribe: Langganan ulang @@ -128,6 +131,7 @@ id: search: Cari search_same_email_domain: Pengguna lain dengan domain email yang sama search_same_ip: Pengguna lain dengan IP yang sama + security: Keamanan security_measures: only_password: Hanya kata sandi password_and_2fa: Kata sandi dan 2FA @@ -278,6 +282,7 @@ id: update_custom_emoji_html: "%{name} memperbarui emoji %{target}" update_domain_block_html: "%{name} memperbarui blokir domain untuk %{target}" update_ip_block_html: "%{name} mengubah peraturan untuk IP %{target}" + update_report_html: "%{name} memperbarui laporan %{target}" update_status_html: "%{name} memperbarui status %{target}" update_user_role_html: "%{name} mengubah peran %{target}" deleted_account: akun yang dihapus @@ -302,6 +307,7 @@ id: unpublish: Batal terbitkan unpublished_msg: Pengumuman berhasil ditarik! updated_msg: Pengumuman berhasil diperbarui! + critical_update_pending: Pembaruan penting tertunda custom_emojis: assign_category: Beri kategori by_domain: Domain diff --git a/config/locales/la.yml b/config/locales/la.yml index 3a7ba0d445..d3733df934 100644 --- a/config/locales/la.yml +++ b/config/locales/la.yml @@ -1 +1,34 @@ +--- la: + about: + about_mastodon_html: '"Rete sociale futuri: Nullae praebendae, nulla observatio corporativa, ethica designatio, et decentralizatio! Tua data cum Mastodonte posside!"' + contact_missing: Non definitum + contact_unavailable: Nōn Applicābilis + hosted_on: Mastodon in %{domain} hospitātum + title: De + accounts: + follow: Sequere + followers: + one: Sectātor + other: Sectātōrēs + following: Sequendī + instance_actor_flash: Hic ratio est actōr virtuālis ad repraesentandam ipsum servatorem et non ullam individuam usorem. Ad scopōs foederātiōnis ūtor nec suspendendus est. + last_active: Ultimum Actum + link_verified_on: Dominium huius nexūs est comprobatum die %{date}. + nothing_here: Nihil est hic! + pin_errors: + following: Iam debes sequi personam quam vis probare. + posts: + one: Nuntius + other: Nuntii + posts_tab_heading: Nuntii + admin: + account_actions: + action: Agere actionem + title: Actionem moderationis in %{acct} gerere + account_moderation_notes: + create: Relinque nota + created_msg: Nota moderationis feliciter creata est! + destroyed_msg: Nota moderationis feliciter deleta est! + accounts: + are_you_sure: Esne certus? diff --git a/config/locales/nl.yml b/config/locales/nl.yml index a527fdb5a7..328467f483 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -1814,14 +1814,14 @@ nl: subject: Jouw archief staat klaar om te worden gedownload title: Archief ophalen failed_2fa: - details: 'Hier zijn details van de inlogpoging:' + details: 'Hier zijn de details van de inlogpoging:' explanation: Iemand heeft geprobeerd om in te loggen op jouw account maar heeft een ongeldige tweede verificatiefactor opgegeven. further_actions_html: Als jij dit niet was, raden we je aan om onmiddellijk %{action} aangezien het in gevaar kan zijn. subject: Tweestapsverificatiefout title: Tweestapsverificatie mislukt suspicious_sign_in: change_password: je wachtwoord te wijzigen - details: 'Hier zijn de details van inlogpoging:' + details: 'Hier zijn de details van het inloggen:' explanation: We hebben vastgesteld dat iemand vanaf een nieuw IP-adres op jouw account is ingelogd. further_actions_html: Wanneer jij dit niet was, adviseren wij om onmiddellijk %{action} en om tweestapsverificatie in te schakelen, om zo je account veilig te houden. subject: Jouw account is vanaf een nieuw IP-adres benaderd diff --git a/config/locales/simple_form.id.yml b/config/locales/simple_form.id.yml index 856f312eda..1f493435e8 100644 --- a/config/locales/simple_form.id.yml +++ b/config/locales/simple_form.id.yml @@ -2,6 +2,9 @@ id: simple_form: hints: + account: + discoverable: Postingan dan profil publik Anda mungkin ditampilkan atau direkomendasikan di berbagai area Mastodon dan profil Anda mungkin disarankan ke pengguna lain. + display_name: Nama lengkap Anda atau nama lucu Anda. account_alias: acct: Tentukan namapengguna@domain akun yang ingin Anda pindah account_migration: diff --git a/config/locales/th.yml b/config/locales/th.yml index bafcd30dee..d1359e0176 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -1837,11 +1837,13 @@ th: edit_profile_title: ปรับแต่งโปรไฟล์ของคุณ explanation: นี่คือเคล็ดลับบางส่วนที่จะช่วยให้คุณเริ่มต้นใช้งาน feature_action: เรียนรู้เพิ่มเติม - feature_audience: Mastodon มีความพิเศษที่ให้คุณจัดการผู้รับสารของคุณได้โดยไม่มีตัวกลาง นอกจากนี้ การติดตั้ง Mastodon บนโครงสร้างพื้นฐานของคุณจะทำให้คุณสามารถติดตาม (และติดตามโดย) เซิร์ฟเวอร์ Mastodon แห่งไหนก็ได้ที่ทำงานอยู่ โดยไม่มีใครสามารถควบคุมได้นอกจากคุณ + feature_audience: Mastodon ให้ความเป็นไปได้ที่เป็นเอกลักษณ์แก่คุณในการจัดการผู้ชมของคุณโดยไม่มีตัวกลาง Mastodon ที่ปรับใช้ในโครงสร้างพื้นฐานของคุณเองอนุญาตให้คุณติดตามและได้รับการติดตามจากเซิร์ฟเวอร์ Mastodon อื่นใดทางออนไลน์และไม่อยู่ภายใต้การควบคุมของใครนอกจากคุณ feature_audience_title: สร้างผู้ชมของคุณด้วยความมั่นใจ feature_control: คุณทราบดีที่สุดถึงสิ่งที่คุณต้องการเห็นในฟีดหน้าแรกของคุณ ไม่มีอัลกอริทึมหรือโฆษณาให้เสียเวลาของคุณ ติดตามใครก็ตามทั่วทั้งเซิร์ฟเวอร์ Mastodon ใด ๆ จากบัญชีเดียวและรับโพสต์ของเขาตามลำดับเวลา และทำให้มุมอินเทอร์เน็ตของคุณเป็นเหมือนคุณมากขึ้นอีกนิด feature_control_title: การควบคุมเส้นเวลาของคุณเอง + feature_creativity: Mastodon รองรับโพสต์เสียง วิดีโอ และรูปภาพ, คำอธิบายการช่วยการเข้าถึง, การสำรวจความคิดเห็น, คำเตือนเนื้อหา, ภาพประจำตัวแบบเคลื่อนไหว, อีโมจิที่กำหนดเอง, การควบคุมการครอบตัดภาพขนาดย่อ และอื่น ๆ เพื่อช่วยให้คุณแสดงออกตัวคุณเองทางออนไลน์ ไม่ว่าคุณกำลังจะเผยแพร่ศิลปะของคุณ, เพลงของคุณ หรือพอดแคสต์ของคุณ Mastodon อยู่ที่นั่นเพื่อคุณ feature_creativity_title: ความคิดสร้างสรรค์ที่ไม่มีใครเทียบได้ + feature_moderation: Mastodon นำการตัดสินใจกลับมาอยู่ในมือของคุณ แต่ละเซิร์ฟเวอร์สร้างกฎและระเบียบข้อบังคับของตนเอง ซึ่งบังคับใช้ในเซิร์ฟเวอร์และไม่ใช่จากบนลงล่างเหมือนสื่อสังคมขององค์กร ทำให้สื่อสังคมยืดหยุ่นมากที่สุดในการตอบสนองต่อความต้องการของกลุ่มคนที่แตกต่างกัน เข้าร่วมเซิร์ฟเวอร์ที่มีกฎที่คุณเห็นด้วย หรือโฮสต์ของคุณเอง feature_moderation_title: การกลั่นกรองในแบบที่ควรจะเป็น follow_action: ติดตาม follow_step: การติดตามผู้คนที่น่าสนใจคือสิ่งที่ Mastodon ให้ความสำคัญ diff --git a/crowdin.yml b/crowdin.yml index d05b0e69f5..991c5b8252 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,4 +1,4 @@ -# This is needed for the Github Action +# This is needed for the GitHub Action project_id_env: CROWDIN_PROJECT_ID api_token_env: CROWDIN_PERSONAL_TOKEN diff --git a/spec/lib/activitypub/activity/flag_spec.rb b/spec/lib/activitypub/activity/flag_spec.rb index 8593d567f4..426cd97df9 100644 --- a/spec/lib/activitypub/activity/flag_spec.rb +++ b/spec/lib/activitypub/activity/flag_spec.rb @@ -54,7 +54,7 @@ RSpec.describe ActivityPub::Activity::Flag do }.with_indifferent_access, sender) end - let(:long_comment) { Faker::Lorem.characters(number: 6000) } + let(:long_comment) { 'a' * described_class::COMMENT_SIZE_LIMIT * 2 } before do subject.perform @@ -63,10 +63,12 @@ RSpec.describe ActivityPub::Activity::Flag do it 'creates a report but with a truncated comment' do report = Report.find_by(account: sender, target_account: flagged) - expect(report).to_not be_nil - expect(report.comment.length).to eq 5000 - expect(report.comment).to eq long_comment[0...5000] - expect(report.status_ids).to eq [status.id] + expect(report) + .to be_present + .and have_attributes(status_ids: [status.id]) + expect(report.comment) + .to have_attributes(length: described_class::COMMENT_SIZE_LIMIT) + .and eq(long_comment[0...described_class::COMMENT_SIZE_LIMIT]) end end diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 2c5df198d9..225929ae39 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -768,20 +768,20 @@ RSpec.describe Account do expect(account).to model_have_error_on_field(:username) end - it 'is invalid if the username is longer than 30 characters' do - account = Fabricate.build(:account, username: Faker::Lorem.characters(number: 31)) + it 'is invalid if the username is longer than the character limit' do + account = Fabricate.build(:account, username: username_over_limit) account.valid? expect(account).to model_have_error_on_field(:username) end - it 'is invalid if the display name is longer than 30 characters' do - account = Fabricate.build(:account, display_name: Faker::Lorem.characters(number: 31)) + it 'is invalid if the display name is longer than the character limit' do + account = Fabricate.build(:account, display_name: username_over_limit) account.valid? expect(account).to model_have_error_on_field(:display_name) end - it 'is invalid if the note is longer than 500 characters' do - account = Fabricate.build(:account, note: Faker::Lorem.characters(number: 501)) + it 'is invalid if the note is longer than the character limit' do + account = Fabricate.build(:account, note: account_note_over_limit) account.valid? expect(account).to model_have_error_on_field(:note) end @@ -814,24 +814,32 @@ RSpec.describe Account do expect(account).to model_have_error_on_field(:username) end - it 'is valid even if the username is longer than 30 characters' do - account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(number: 31)) + it 'is valid even if the username is longer than the character limit' do + account = Fabricate.build(:account, domain: 'domain', username: username_over_limit) account.valid? expect(account).to_not model_have_error_on_field(:username) end - it 'is valid even if the display name is longer than 30 characters' do - account = Fabricate.build(:account, domain: 'domain', display_name: Faker::Lorem.characters(number: 31)) + it 'is valid even if the display name is longer than the character limit' do + account = Fabricate.build(:account, domain: 'domain', display_name: username_over_limit) account.valid? expect(account).to_not model_have_error_on_field(:display_name) end - it 'is valid even if the note is longer than 500 characters' do - account = Fabricate.build(:account, domain: 'domain', note: Faker::Lorem.characters(number: 501)) + it 'is valid even if the note is longer than the character limit' do + account = Fabricate.build(:account, domain: 'domain', note: account_note_over_limit) account.valid? expect(account).to_not model_have_error_on_field(:note) end end + + def username_over_limit + 'a' * described_class::USERNAME_LENGTH_LIMIT * 2 + end + + def account_note_over_limit + 'a' * described_class::NOTE_LENGTH_LIMIT * 2 + end end describe 'scopes' do diff --git a/spec/models/appeal_spec.rb b/spec/models/appeal_spec.rb index 12373a9494..13ca3a2d90 100644 --- a/spec/models/appeal_spec.rb +++ b/spec/models/appeal_spec.rb @@ -3,6 +3,19 @@ require 'rails_helper' describe Appeal do + describe 'Validations' do + it 'validates text length is under limit' do + appeal = Fabricate.build( + :appeal, + strike: Fabricate(:account_warning), + text: 'a' * described_class::TEXT_LENGTH_LIMIT * 2 + ) + + expect(appeal).to_not be_valid + expect(appeal).to model_have_error_on_field(:text) + end + end + describe 'scopes' do describe 'approved' do let(:approved_appeal) { Fabricate(:appeal, approved_at: 10.days.ago) } diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb index 0168268bcb..d01d37bd8b 100644 --- a/spec/models/report_spec.rb +++ b/spec/models/report_spec.rb @@ -123,14 +123,14 @@ describe Report do describe 'validations' do let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') } - it 'is invalid if comment is longer than 1000 characters only if reporter is local' do - report = Fabricate.build(:report, comment: Faker::Lorem.characters(number: 1001)) + it 'is invalid if comment is longer than character limit and reporter is local' do + report = Fabricate.build(:report, comment: comment_over_limit) expect(report.valid?).to be false expect(report).to model_have_error_on_field(:comment) end - it 'is valid if comment is longer than 1000 characters and reporter is not local' do - report = Fabricate.build(:report, account: remote_account, comment: Faker::Lorem.characters(number: 1001)) + it 'is valid if comment is longer than character limit and reporter is not local' do + report = Fabricate.build(:report, account: remote_account, comment: comment_over_limit) expect(report.valid?).to be true end @@ -146,5 +146,9 @@ describe Report do expect(report.valid?).to be false expect(report).to model_have_error_on_field(:rule_ids) end + + def comment_over_limit + 'a' * described_class::COMMENT_SIZE_LIMIT * 2 + end end end diff --git a/spec/models/scheduled_status_spec.rb b/spec/models/scheduled_status_spec.rb new file mode 100644 index 0000000000..15031a5895 --- /dev/null +++ b/spec/models/scheduled_status_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ScheduledStatus do + let(:account) { Fabricate(:account) } + + describe 'validations' do + context 'when scheduled_at is less than minimum offset' do + subject { Fabricate.build(:scheduled_status, scheduled_at: 4.minutes.from_now, account: account) } + + it 'is not valid', :aggregate_failures do + expect(subject).to_not be_valid + expect(subject.errors[:scheduled_at]).to include(I18n.t('scheduled_statuses.too_soon')) + end + end + + context 'when account has reached total limit' do + subject { Fabricate.build(:scheduled_status, account: account) } + + before do + allow(account.scheduled_statuses).to receive(:count).and_return(described_class::TOTAL_LIMIT) + end + + it 'is not valid', :aggregate_failures do + expect(subject).to_not be_valid + expect(subject.errors[:base]).to include(I18n.t('scheduled_statuses.over_total_limit', limit: ScheduledStatus::TOTAL_LIMIT)) + end + end + + context 'when account has reached daily limit' do + subject { Fabricate.build(:scheduled_status, account: account, scheduled_at: base_time + 10.minutes) } + + let(:base_time) { Time.current.change(hour: 12) } + + before do + stub_const('ScheduledStatus::DAILY_LIMIT', 3) + + travel_to base_time do + Fabricate.times(ScheduledStatus::DAILY_LIMIT, :scheduled_status, account: account, scheduled_at: base_time + 1.hour) + end + end + + it 'is not valid', :aggregate_failures do + expect(subject).to_not be_valid + expect(subject.errors[:base]).to include(I18n.t('scheduled_statuses.over_daily_limit', limit: ScheduledStatus::DAILY_LIMIT)) + end + end + end +end diff --git a/spec/requests/api/v1/statuses_spec.rb b/spec/requests/api/v1/statuses_spec.rb index 694861fb1c..2f99b35e74 100644 --- a/spec/requests/api/v1/statuses_spec.rb +++ b/spec/requests/api/v1/statuses_spec.rb @@ -193,6 +193,32 @@ describe '/api/v1/statuses' do expect(response).to have_http_status(404) end end + + context 'when scheduling a status' do + let(:params) { { status: 'Hello world', scheduled_at: 10.minutes.from_now } } + let(:account) { user.account } + + it 'returns HTTP 200' do + subject + + expect(response).to have_http_status(200) + end + + it 'creates a scheduled status' do + expect { subject }.to change { account.scheduled_statuses.count }.from(0).to(1) + end + + context 'when the scheduling time is less than 5 minutes' do + let(:params) { { status: 'Hello world', scheduled_at: 4.minutes.from_now } } + + it 'does not create a scheduled status', :aggregate_failures do + subject + + expect(response).to have_http_status(422) + expect(account.scheduled_statuses).to be_empty + end + end + end end describe 'DELETE /api/v1/statuses/:id' do diff --git a/spec/services/post_status_service_spec.rb b/spec/services/post_status_service_spec.rb index 11bf4c30ea..f21548b5f2 100644 --- a/spec/services/post_status_service_spec.rb +++ b/spec/services/post_status_service_spec.rb @@ -61,6 +61,16 @@ RSpec.describe PostStatusService do status2 = subject.call(account, text: 'test', idempotency: 'meepmeep', scheduled_at: future) expect(status2.id).to eq status1.id end + + context 'when scheduled_at is less than min offset' do + let(:invalid_scheduled_time) { 4.minutes.from_now } + + it 'raises invalid record error' do + expect do + subject.call(account, text: 'Hi future!', scheduled_at: invalid_scheduled_time) + end.to raise_error(ActiveRecord::RecordInvalid) + end + end end it 'creates response to the original status of boost' do diff --git a/spec/system/filters_spec.rb b/spec/system/filters_spec.rb index 9d18e90460..a0cb965a61 100644 --- a/spec/system/filters_spec.rb +++ b/spec/system/filters_spec.rb @@ -46,7 +46,7 @@ describe 'Filters' do click_on I18n.t('filters.index.delete') end.to change(CustomFilter, :count).by(-1) - expect(page).to_not have_content(filter_title) + expect(page).to have_no_content(filter_title) end end diff --git a/streaming/Dockerfile b/streaming/Dockerfile index 7f373e9cd5..564e717a40 100644 --- a/streaming/Dockerfile +++ b/streaming/Dockerfile @@ -8,6 +8,7 @@ ARG TARGETPLATFORM=${TARGETPLATFORM} ARG BUILDPLATFORM=${BUILDPLATFORM} # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"] +# renovate: datasource=node-version depName=node ARG NODE_MAJOR_VERSION="20" # Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"] ARG DEBIAN_VERSION="bookworm" diff --git a/streaming/package.json b/streaming/package.json index cf1fe4ba69..2e515167c7 100644 --- a/streaming/package.json +++ b/streaming/package.json @@ -27,7 +27,7 @@ "pino": "^9.0.0", "pino-http": "^10.0.0", "prom-client": "^15.0.0", - "uuid": "^9.0.0", + "uuid": "^10.0.0", "ws": "^8.12.1" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index e9f5940d92..1f37dff2f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2945,7 +2945,7 @@ __metadata: prom-client: "npm:^15.0.0" typescript: "npm:^5.0.4" utf-8-validate: "npm:^6.0.3" - uuid: "npm:^9.0.0" + uuid: "npm:^10.0.0" ws: "npm:^8.12.1" dependenciesMeta: bufferutil: @@ -3715,9 +3715,9 @@ __metadata: linkType: hard "@types/lodash@npm:^4.14.195": - version: 4.17.4 - resolution: "@types/lodash@npm:4.17.4" - checksum: 10c0/0124c64cb9fe7a0f78b6777955abd05ef0d97844d49118652eae45f8fa57bfb7f5a7a9bccc0b5a84c0a6dc09631042e4590cb665acb9d58dfd5e6543c75341ec + version: 4.17.5 + resolution: "@types/lodash@npm:4.17.5" + checksum: 10c0/55924803ed853e72261512bd3eaf2c5b16558c3817feb0a3125ef757afe46e54b86f33d1960e40b7606c0ddab91a96f47966bf5e6006b7abfd8994c13b04b19b languageName: node linkType: hard @@ -13086,8 +13086,8 @@ __metadata: linkType: hard "pino-pretty@npm:^11.0.0": - version: 11.1.0 - resolution: "pino-pretty@npm:11.1.0" + version: 11.2.0 + resolution: "pino-pretty@npm:11.2.0" dependencies: colorette: "npm:^2.0.7" dateformat: "npm:^4.6.3" @@ -13105,7 +13105,7 @@ __metadata: strip-json-comments: "npm:^3.1.1" bin: pino-pretty: bin.js - checksum: 10c0/418be6f854b0d62c83c65e75b0969d5311792bfadeefbfe77d8a7f8c5ba26b8bea40f549222b5f500439f440eb4d6c2fa99d712bdd02881ebae7be3a0193b581 + checksum: 10c0/59421522c0e07877614ed8b51eb45fe79aad9865244b95dfaf5e28c83f9e95631941b5b9e37a277d1751ed90903d7593915e8a8857cc856b4af7e6bf20a5f97d languageName: node linkType: hard @@ -14046,11 +14046,11 @@ __metadata: linkType: hard "prettier@npm:^3.0.0": - version: 3.3.0 - resolution: "prettier@npm:3.3.0" + version: 3.3.1 + resolution: "prettier@npm:3.3.1" bin: prettier: bin/prettier.cjs - checksum: 10c0/d033c356320aa2e468bf29c931b094ac730d2f4defd5eb2989d8589313dec901d2fc866e3788f3d161e420b142ea4ec3dda535dbe0169ef4d0026397a68ba9cf + checksum: 10c0/c25a709c9f0be670dc6bcb190b622347e1dbeb6c3e7df8b0711724cb64d8647c60b839937a4df4df18e9cfb556c2b08ca9d24d9645eb5488a7fc032a2c4d5cb3 languageName: node linkType: hard @@ -17513,6 +17513,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^10.0.0": + version: 10.0.0 + resolution: "uuid@npm:10.0.0" + bin: + uuid: dist/bin/uuid + checksum: 10c0/eab18c27fe4ab9fb9709a5d5f40119b45f2ec8314f8d4cf12ce27e4c6f4ffa4a6321dc7db6c515068fa373c075b49691ba969f0010bf37f44c37ca40cd6bf7fe + languageName: node + linkType: hard + "uuid@npm:^3.3.2": version: 3.4.0 resolution: "uuid@npm:3.4.0" @@ -17531,15 +17540,6 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^9.0.0": - version: 9.0.1 - resolution: "uuid@npm:9.0.1" - bin: - uuid: dist/bin/uuid - checksum: 10c0/1607dd32ac7fc22f2d8f77051e6a64845c9bce5cd3dd8aa0070c074ec73e666a1f63c7b4e0f4bf2bc8b9d59dc85a15e17807446d9d2b17c8485fbc2147b27f9b - languageName: node - linkType: hard - "v8-compile-cache@npm:^2.1.1": version: 2.3.0 resolution: "v8-compile-cache@npm:2.3.0"