From a80530d1df39f549b3fa2e4f84669c4851bea443 Mon Sep 17 00:00:00 2001 From: Michael Stanclift Date: Tue, 28 Nov 2023 04:04:40 -0600 Subject: [PATCH 01/95] Dockerfile rewrite based on Ruby image with performance optimizations and size reduction, dedicated Streaming image (#26850) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: “Michael <“mx@vmstan.com> Co-authored-by: Emelia Smith --- .github/workflows/build-container-image.yml | 3 + .github/workflows/build-nightly.yml | 23 ++ .github/workflows/build-push-pr.yml | 17 ++ .github/workflows/build-releases.yml | 22 ++ .github/workflows/test-image-build.yml | 14 + Dockerfile | 295 +++++++++++++++----- streaming/.dockerignore | 7 + streaming/Dockerfile | 102 +++++++ 8 files changed, 419 insertions(+), 64 deletions(-) create mode 100644 streaming/.dockerignore create mode 100644 streaming/Dockerfile diff --git a/.github/workflows/build-container-image.yml b/.github/workflows/build-container-image.yml index 29868c72f..e100e1582 100644 --- a/.github/workflows/build-container-image.yml +++ b/.github/workflows/build-container-image.yml @@ -21,6 +21,8 @@ on: type: string labels: type: string + file_to_build: + type: string jobs: build-image: @@ -86,6 +88,7 @@ jobs: - uses: docker/build-push-action@v5 with: context: . + file: ${{ inputs.file_to_build }} build-args: | MASTODON_VERSION_PRERELEASE=${{ inputs.version_prerelease }} MASTODON_VERSION_METADATA=${{ inputs.version_metadata }} diff --git a/.github/workflows/build-nightly.yml b/.github/workflows/build-nightly.yml index 1790d5c84..7c6f74b45 100644 --- a/.github/workflows/build-nightly.yml +++ b/.github/workflows/build-nightly.yml @@ -25,6 +25,7 @@ jobs: needs: compute-suffix uses: ./.github/workflows/build-container-image.yml with: + file_to_build: Dockerfile platforms: linux/amd64,linux/arm64 use_native_arm64_builder: true cache: false @@ -41,3 +42,25 @@ jobs: type=raw,value=nightly type=schedule,pattern=${{ needs.compute-suffix.outputs.prerelease }} secrets: inherit + + build-image-streaming: + needs: compute-suffix + uses: ./.github/workflows/build-container-image.yml + with: + file_to_build: streaming/Dockerfile + platforms: linux/amd64,linux/arm64 + use_native_arm64_builder: true + cache: false + push_to_images: | + tootsuite/mastodon-streaming + ghcr.io/mastodon/mastodon-streaming + version_prerelease: ${{ needs.compute-suffix.outputs.prerelease }} + labels: | + org.opencontainers.image.description=Nightly build image used for testing purposes + flavor: | + latest=auto + tags: | + type=raw,value=edge + type=raw,value=nightly + type=schedule,pattern=${{ needs.compute-suffix.outputs.prerelease }} + secrets: inherit diff --git a/.github/workflows/build-push-pr.yml b/.github/workflows/build-push-pr.yml index 1f647e2a1..72baed512 100644 --- a/.github/workflows/build-push-pr.yml +++ b/.github/workflows/build-push-pr.yml @@ -29,6 +29,7 @@ jobs: needs: compute-suffix uses: ./.github/workflows/build-container-image.yml with: + file_to_build: Dockerfile platforms: linux/amd64,linux/arm64 use_native_arm64_builder: true push_to_images: | @@ -39,3 +40,19 @@ jobs: tags: | type=ref,event=pr secrets: inherit + + build-image-streaming: + needs: compute-suffix + uses: ./.github/workflows/build-container-image.yml + with: + file_to_build: streaming/Dockerfile + platforms: linux/amd64,linux/arm64 + use_native_arm64_builder: true + push_to_images: | + ghcr.io/mastodon/mastodon-streaming + version_metadata: ${{ needs.compute-suffix.outputs.metadata }} + flavor: | + latest=auto + tags: | + type=ref,event=pr + secrets: inherit diff --git a/.github/workflows/build-releases.yml b/.github/workflows/build-releases.yml index 3b82eef9d..3f0bef32a 100644 --- a/.github/workflows/build-releases.yml +++ b/.github/workflows/build-releases.yml @@ -12,6 +12,7 @@ jobs: build-image: uses: ./.github/workflows/build-container-image.yml with: + file_to_build: Dockerfile platforms: linux/amd64,linux/arm64 use_native_arm64_builder: true push_to_images: | @@ -27,3 +28,24 @@ jobs: type=pep440,pattern={{raw}} type=pep440,pattern=v{{major}}.{{minor}} secrets: inherit + + build-image-streaming: + if: startsWith(github.ref, 'refs/tags/v4.3.') + uses: ./.github/workflows/build-container-image.yml + with: + file_to_build: streaming/Dockerfile + platforms: linux/amd64,linux/arm64 + use_native_arm64_builder: true + push_to_images: | + tootsuite/mastodon-streaming + ghcr.io/mastodon/mastodon-streaming + # Do not use cache when building releases, so apt update is always ran and the release always contain the latest packages + cache: false + # Only tag with latest when ran against the latest stable branch + # This needs to be updated after each minor version release + flavor: | + latest=${{ startsWith(github.ref, 'refs/tags/v4.3.') }} + tags: | + type=pep440,pattern={{raw}} + type=pep440,pattern=v{{major}}.{{minor}} + secrets: inherit diff --git a/.github/workflows/test-image-build.yml b/.github/workflows/test-image-build.yml index 778e34177..980e07189 100644 --- a/.github/workflows/test-image-build.yml +++ b/.github/workflows/test-image-build.yml @@ -7,6 +7,7 @@ on: - .github/workflows/build-releases.yml - .github/workflows/test-image-build.yml - Dockerfile + - streaming/Dockerfile permissions: contents: read @@ -18,4 +19,17 @@ jobs: uses: ./.github/workflows/build-container-image.yml with: + file_to_build: Dockerfile platforms: linux/amd64 # Testing only on native platform so it is performant + cache: true + + build-image-streaming: + concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-streaming + cancel-in-progress: true + + uses: ./.github/workflows/build-container-image.yml + with: + file_to_build: streaming/Dockerfile + platforms: linux/amd64 # Testing only on native platform so it is performant + cache: true diff --git a/Dockerfile b/Dockerfile index 7e032073b..623a4d4ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,182 @@ # syntax=docker/dockerfile:1.4 -# This needs to be bookworm-slim because the Ruby image is built on bookworm-slim -ARG NODE_VERSION="20.9-bookworm-slim" -FROM ghcr.io/moritzheiber/ruby-jemalloc:3.2.2-slim as ruby -FROM node:${NODE_VERSION} as build +# Please see https://docs.docker.com/engine/reference/builder for information about +# the extended buildx capabilities used in this file. +# Make sure multiarch TARGETPLATFORM is available for interpolation +# See: https://docs.docker.com/build/building/multi-platform/ +ARG TARGETPLATFORM=${TARGETPLATFORM} +ARG BUILDPLATFORM=${BUILDPLATFORM} -COPY --link --from=ruby /opt/ruby /opt/ruby +# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.2.2"] +ARG RUBY_VERSION="3.2.2" +# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"] +ARG NODE_MAJOR_VERSION="20" +# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"] +ARG DEBIAN_VERSION="bookworm" +# Node image to use for base image based on combined variables (ex: 20-bookworm-slim) +FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim as node +# Ruby image to use for base image based on combined variables (ex: 3.2.2-slim-bookworm) +FROM docker.io/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} as ruby -ENV DEBIAN_FRONTEND="noninteractive" \ - PATH="${PATH}:/opt/ruby/bin" +# Resulting version string is vX.X.X-MASTODON_VERSION_PRERELEASE+MASTODON_VERSION_METADATA +# Example: v4.2.0-nightly.2023.11.09+something +# Overwrite existance of 'alpha.0' in version.rb [--build-arg MASTODON_VERSION_PRERELEASE="nightly.2023.11.09"] +ARG MASTODON_VERSION_PRERELEASE="" +# Append build metadata or fork information to version.rb [--build-arg MASTODON_VERSION_METADATA="something"] +ARG MASTODON_VERSION_METADATA="" -SHELL ["/bin/bash", "-o", "pipefail", "-c"] +# Allow Ruby on Rails to serve static files +# See: https://docs.joinmastodon.org/admin/config/#rails_serve_static_files +ARG RAILS_SERVE_STATIC_FILES="true" +# Allow to use YJIT compiler +# See: https://github.com/ruby/ruby/blob/master/doc/yjit/yjit.md +ARG RUBY_YJIT_ENABLE="1" +# Timezone used by the Docker container and runtime, change with [--build-arg TZ=Europe/Berlin] +ARG TZ="Etc/UTC" +# Linux UID (user id) for the mastodon user, change with [--build-arg UID=1234] +ARG UID="991" +# Linux GID (group id) for the mastodon user, change with [--build-arg GID=1234] +ARG GID="991" +# Apply Mastodon build options based on options above +ENV \ +# Apply Mastodon version information + MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \ + MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \ +# Apply Mastodon static files and YJIT options + RAILS_SERVE_STATIC_FILES=${RAILS_SERVE_STATIC_FILES} \ + RUBY_YJIT_ENABLE=${RUBY_YJIT_ENABLE} \ +# Apply timezone + TZ=${TZ} + +ENV \ +# Configure the IP to bind Mastodon to when serving traffic + BIND="0.0.0.0" \ +# Use production settings for Yarn, Node and related nodejs based tools + NODE_ENV="production" \ +# Use production settings for Ruby on Rails + RAILS_ENV="production" \ +# Add Ruby and Mastodon installation to the PATH + DEBIAN_FRONTEND="noninteractive" \ + PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" \ +# Optimize jemalloc 5.x performance + MALLOC_CONF="narenas:2,background_thread:true,thp:never,dirty_decay_ms:1000,muzzy_decay_ms:0" + +# Set default shell used for running commands +SHELL ["/bin/bash", "-o", "pipefail", "-o", "errexit", "-c"] + +ARG TARGETPLATFORM + +RUN echo "Target platform is $TARGETPLATFORM" + +RUN \ +# Sets timezone + echo "${TZ}" > /etc/localtime; \ +# Creates mastodon user/group and sets home directory + groupadd -g "${GID}" mastodon; \ + useradd -l -u "${UID}" -g "${GID}" -m -d /opt/mastodon mastodon; \ +# Creates /mastodon symlink to /opt/mastodon + ln -s /opt/mastodon /mastodon; + +# Set /opt/mastodon as working directory WORKDIR /opt/mastodon +# hadolint ignore=DL3008,DL3005 +RUN \ +# Mount Apt cache and lib directories from Docker buildx caches +--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \ +--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \ +# Apt update & upgrade to check for security updates to Debian image + apt-get update; \ + apt-get dist-upgrade -yq; \ +# Install jemalloc, curl and other necessary components + apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + ffmpeg \ + file \ + imagemagick \ + libjemalloc2 \ + patchelf \ + procps \ + tini \ + tzdata \ + ; \ +# Patch Ruby to use jemalloc + patchelf --add-needed libjemalloc.so.2 /usr/local/bin/ruby; \ +# Discard patchelf after use + apt-get purge -y \ + patchelf \ + ; + +# Create temporary build layer from base image +FROM ruby as build + +# Copy Node package configuration files into working directory +COPY package.json yarn.lock .yarnrc.yml /opt/mastodon/ +COPY .yarn /opt/mastodon/.yarn + +COPY --from=node /usr/local/bin /usr/local/bin +COPY --from=node /usr/local/lib /usr/local/lib + +ARG TARGETPLATFORM + # hadolint ignore=DL3008 +RUN \ +# Mount Apt cache and lib directories from Docker buildx caches +--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \ +--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \ +# Install build tools and bundler dependencies from APT + apt-get update; \ + apt-get install -y --no-install-recommends \ + g++ \ + gcc \ + git \ + libgdbm-dev \ + libgmp-dev \ + libicu-dev \ + libidn-dev \ + libpq-dev \ + libssl-dev \ + make \ + shared-mime-info \ + zlib1g-dev \ + ; + +RUN \ +# Configure Corepack + rm /usr/local/bin/yarn*; \ + corepack enable; \ + corepack prepare --activate; + +# Create temporary bundler specific build layer from build layer +FROM build as bundler + +ARG TARGETPLATFORM + +# Copy Gemfile config into working directory +COPY Gemfile* /opt/mastodon/ + +RUN \ +# Mount Ruby Gem caches +--mount=type=cache,id=gem-cache-${TARGETPLATFORM},target=/usr/local/bundle/cache/,sharing=locked \ +# Configure bundle to prevent changes to Gemfile and Gemfile.lock + bundle config set --global frozen "true"; \ +# Configure bundle to not cache downloaded Gems + bundle config set --global cache_all "false"; \ +# Configure bundle to only process production Gems + bundle config set --local without "development test"; \ +# Configure bundle to not warn about root user + bundle config set silence_root_warning "true"; \ +# Download and install required Gems + bundle install -j"$(nproc)"; + +# Create temporary node specific build layer from build layer +FROM build as yarn + +ARG TARGETPLATFORM + +# Copy Node package configuration files into working directory RUN apt-get update && \ apt-get -yq dist-upgrade && \ apt-get install -y --no-install-recommends build-essential \ @@ -41,72 +203,77 @@ COPY Gemfile* package.json yarn.lock .yarnrc.yml /opt/mastodon/ COPY streaming/package.json /opt/mastodon/streaming/ COPY .yarn /opt/mastodon/.yarn -RUN bundle install -j"$(nproc)" +# hadolint ignore=DL3008 +RUN \ +--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \ +--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \ +# Install Node packages + yarn workspaces focus --production @mastodon/mastodon; -RUN yarn workspaces focus --all --production && \ - yarn cache clean +# Create temporary assets build layer from build layer +FROM build as precompiler -FROM node:${NODE_VERSION} +# Copy Mastodon sources into precompiler layer +COPY . /opt/mastodon/ -# Use those args to specify your own version flags & suffixes -ARG MASTODON_VERSION_PRERELEASE="" -ARG MASTODON_VERSION_METADATA="" +# Copy bundler and node packages from build layer to container +COPY --from=yarn /opt/mastodon /opt/mastodon/ +COPY --from=bundler /opt/mastodon /opt/mastodon/ +COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/ -ARG UID="991" -ARG GID="991" +ARG TARGETPLATFORM -COPY --link --from=ruby /opt/ruby /opt/ruby +RUN \ +# Use Ruby on Rails to create Mastodon assets + OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder bundle exec rails assets:precompile; \ +# Cleanup temporary files + rm -fr /opt/mastodon/tmp; -SHELL ["/bin/bash", "-o", "pipefail", "-c"] +# Prep final Mastodon Ruby layer +FROM ruby as mastodon -ENV DEBIAN_FRONTEND="noninteractive" \ - PATH="${PATH}:/opt/ruby/bin:/opt/mastodon/bin" +ARG TARGETPLATFORM -# Ignoring these here since we don't want to pin any versions and the Debian image removes apt-get content after use -# hadolint ignore=DL3008,DL3009 -RUN apt-get update && \ - echo "Etc/UTC" > /etc/localtime && \ - groupadd -g "${GID}" mastodon && \ - useradd -l -u "$UID" -g "${GID}" -m -d /opt/mastodon mastodon && \ - apt-get -y --no-install-recommends install whois \ - wget \ - procps \ - libssl3 \ - libpq5 \ - imagemagick \ - ffmpeg \ - libjemalloc2 \ - libicu72 \ - libidn12 \ - libyaml-0-2 \ - file \ - ca-certificates \ - tzdata \ - libreadline8 \ - tini && \ - ln -s /opt/mastodon /mastodon && \ - corepack enable +# hadolint ignore=DL3008 +RUN \ +# Mount Apt cache and lib directories from Docker buildx caches +--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \ +--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \ +# Mount Corepack and Yarn caches from Docker buildx caches +--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \ +--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \ +# Apt update install non-dev versions of necessary components + apt-get update; \ + apt-get install -y --no-install-recommends \ + libssl3 \ + libpq5 \ + libicu72 \ + libidn12 \ + libreadline8 \ + libyaml-0-2 \ + ; -# Note: no, cleaning here since Debian does this automatically -# See the file /etc/apt/apt.conf.d/docker-clean within the Docker image's filesystem +# Copy Mastodon sources into final layer +COPY . /opt/mastodon/ -COPY --chown=mastodon:mastodon . /opt/mastodon -COPY --chown=mastodon:mastodon --from=build /opt/mastodon /opt/mastodon +# Copy compiled assets to layer +COPY --from=precompiler /opt/mastodon/public/packs /opt/mastodon/public/packs +COPY --from=precompiler /opt/mastodon/public/assets /opt/mastodon/public/assets +# Copy bundler components to layer +COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/ -ENV RAILS_ENV="production" \ - NODE_ENV="production" \ - RAILS_SERVE_STATIC_FILES="true" \ - BIND="0.0.0.0" \ - MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \ - MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" +RUN \ +# Precompile bootsnap code for faster Rails startup + bundle exec bootsnap precompile --gemfile app/ lib/; -# Set the run user +RUN \ +# Pre-create and chown system volume to Mastodon user + mkdir -p /opt/mastodon/public/system; \ + chown mastodon:mastodon /opt/mastodon/public/system; + +# Set the running user for resulting container USER mastodon -WORKDIR /opt/mastodon - -# Precompile assets -RUN OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder rails assets:precompile - -# Set the work dir and the container entry point -ENTRYPOINT ["/usr/bin/tini", "--"] -EXPOSE 3000 4000 +# Expose default Puma ports +EXPOSE 3000 +# Set container tini as default entry point +ENTRYPOINT ["/usr/bin/tini", "--"] \ No newline at end of file diff --git a/streaming/.dockerignore b/streaming/.dockerignore new file mode 100644 index 000000000..ea611a7b8 --- /dev/null +++ b/streaming/.dockerignore @@ -0,0 +1,7 @@ +.env +.env.* +.gitignore +node_modules +.DS_Store +*.swp +*~ diff --git a/streaming/Dockerfile b/streaming/Dockerfile new file mode 100644 index 000000000..ee62e9ec7 --- /dev/null +++ b/streaming/Dockerfile @@ -0,0 +1,102 @@ +# syntax=docker/dockerfile:1.4 + +# Please see https://docs.docker.com/engine/reference/builder for information about +# the extended buildx capabilities used in this file. +# Make sure multiarch TARGETPLATFORM is available for interpolation +# See: https://docs.docker.com/build/building/multi-platform/ +ARG TARGETPLATFORM=${TARGETPLATFORM} +ARG BUILDPLATFORM=${BUILDPLATFORM} + +# Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"] +ARG NODE_MAJOR_VERSION="20" +# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"] +ARG DEBIAN_VERSION="bookworm" +# Node image to use for base image based on combined variables (ex: 20-bookworm-slim) +FROM docker.io/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim as streaming + +# Timezone used by the Docker container and runtime, change with [--build-arg TZ=Europe/Berlin] +ARG TZ="Etc/UTC" +# Linux UID (user id) for the mastodon user, change with [--build-arg UID=1234] +ARG UID="991" +# Linux GID (group id) for the mastodon user, change with [--build-arg GID=1234] +ARG GID="991" + +# Apply Mastodon build options based on options above +ENV \ +# Apply Mastodon version information + MASTODON_VERSION_PRERELEASE="${MASTODON_VERSION_PRERELEASE}" \ + MASTODON_VERSION_METADATA="${MASTODON_VERSION_METADATA}" \ +# Apply timezone + TZ=${TZ} + +ENV \ +# Configure the IP to bind Mastodon to when serving traffic + BIND="0.0.0.0" \ +# Explicitly set PORT to match the exposed port + PORT=4000 \ +# Use production settings for Yarn, Node and related nodejs based tools + NODE_ENV="production" \ +# Add Ruby and Mastodon installation to the PATH + DEBIAN_FRONTEND="noninteractive" + +# Set default shell used for running commands +SHELL ["/bin/bash", "-o", "pipefail", "-o", "errexit", "-c"] + +ARG TARGETPLATFORM + +RUN echo "Target platform is ${TARGETPLATFORM}" + +RUN \ +# Sets timezone + echo "${TZ}" > /etc/localtime; \ +# Creates mastodon user/group and sets home directory + groupadd -g "${GID}" mastodon; \ + useradd -l -u "${UID}" -g "${GID}" -m -d /opt/mastodon mastodon; \ +# Creates symlink for /mastodon folder + ln -s /opt/mastodon /mastodon; + +# hadolint ignore=DL3008,DL3005 +RUN \ +# Mount Apt cache and lib directories from Docker buildx caches +--mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \ +--mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \ +# upgrade to check for security updates to Debian image + apt-get update; \ + apt-get dist-upgrade -yq; \ + apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + tzdata \ + ; + +# Set /opt/mastodon as working directory +WORKDIR /opt/mastodon + +# Copy Node package configuration files from build system to container +COPY package.json yarn.lock .yarnrc.yml /opt/mastodon/ +COPY .yarn /opt/mastodon/.yarn +# Copy Streaming source code from build system to container +COPY ./streaming /opt/mastodon/streaming + +RUN \ +# Mount local Corepack and Yarn caches from Docker buildx caches +--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \ +--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \ + # Configure Corepack + rm /usr/local/bin/yarn*; \ + corepack enable; \ + corepack prepare --activate; + +RUN \ +# Mount Corepack and Yarn caches from Docker buildx caches +--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \ +--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \ +# Install Node packages + yarn workspaces focus --production @mastodon/streaming; + +# Set the running user for resulting container +USER mastodon +# Expose default Streaming ports +EXPOSE 4000 +# Run streaming when started +CMD [ node ./streaming/index.js ] \ No newline at end of file From 4949b6da585335fc277a28efee1d7a99c1c697ab Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Tue, 28 Nov 2023 15:09:21 +0100 Subject: [PATCH 02/95] Fix streaming eslint configuration (#28055) --- .eslintrc.js | 19 ++++++++----------- package.json | 1 + streaming/.eslintrc.js | 32 ++++++++++++++++++++++++++++++++ streaming/package.json | 8 ++++++-- streaming/tsconfig.json | 11 +++++++++++ yarn.lock | 20 ++++++++++++++++++++ 6 files changed, 78 insertions(+), 13 deletions(-) create mode 100644 streaming/.eslintrc.js create mode 100644 streaming/tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js index 70506f60c..a9aa86605 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,4 +1,7 @@ -module.exports = { +// @ts-check +const { defineConfig } = require('eslint-define-config'); + +module.exports = defineConfig({ root: true, extends: [ @@ -193,6 +196,7 @@ module.exports = { 'error', { devDependencies: [ + '.eslintrc.js', 'config/webpack/**', 'app/javascript/mastodon/performance.js', 'app/javascript/mastodon/test_setup.js', @@ -299,6 +303,7 @@ module.exports = { overrides: [ { files: [ + '.eslintrc.js', '*.config.js', '.*rc.js', 'ide-helper.js', @@ -372,14 +377,6 @@ module.exports = { env: { jest: true, }, - }, - { - files: [ - 'streaming/**/*', - ], - rules: { - 'import/no-commonjs': 'off', - }, - }, + } ], -}; +}); diff --git a/package.json b/package.json index 5fe190c69..2f324f77a 100644 --- a/package.json +++ b/package.json @@ -184,6 +184,7 @@ "babel-jest": "^29.5.0", "eslint": "^8.41.0", "eslint-config-prettier": "^9.0.0", + "eslint-define-config": "^2.0.0", "eslint-import-resolver-typescript": "^3.5.5", "eslint-plugin-formatjs": "^4.10.1", "eslint-plugin-import": "~2.29.0", diff --git a/streaming/.eslintrc.js b/streaming/.eslintrc.js new file mode 100644 index 000000000..5e2d233c6 --- /dev/null +++ b/streaming/.eslintrc.js @@ -0,0 +1,32 @@ +// @ts-check +const { defineConfig } = require('eslint-define-config'); + +module.exports = defineConfig({ + extends: ['../.eslintrc.js'], + env: { + browser: false, + }, + parserOptions: { + project: true, + tsconfigRootDir: __dirname, + ecmaFeatures: { + jsx: false, + }, + ecmaVersion: 2021, + }, + rules: { + 'import/no-commonjs': 'off', + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: [ + 'streaming/.eslintrc.js', + ], + optionalDependencies: false, + peerDependencies: false, + includeTypes: true, + packageDir: __dirname, + }, + ], + }, +}); diff --git a/streaming/package.json b/streaming/package.json index 52245aeeb..0cfc5b327 100644 --- a/streaming/package.json +++ b/streaming/package.json @@ -12,7 +12,8 @@ "url": "https://github.com/mastodon/mastodon.git" }, "scripts": { - "start": "node ./index.js" + "start": "node ./index.js", + "check:types": "tsc --noEmit" }, "dependencies": { "dotenv": "^16.0.3", @@ -30,7 +31,10 @@ "@types/express": "^4.17.17", "@types/npmlog": "^7.0.0", "@types/pg": "^8.6.6", - "@types/uuid": "^9.0.0" + "@types/uuid": "^9.0.0", + "@types/ws": "^8.5.9", + "eslint-define-config": "^2.0.0", + "typescript": "^5.0.4" }, "optionalDependencies": { "bufferutil": "^4.0.7", diff --git a/streaming/tsconfig.json b/streaming/tsconfig.json new file mode 100644 index 000000000..032274d85 --- /dev/null +++ b/streaming/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "target": "esnext", + "module": "CommonJS", + "moduleResolution": "node", + "noUnusedParameters": false, + "paths": {} + }, + "include": ["./*.js", "./.eslintrc.js"] +} diff --git a/yarn.lock b/yarn.lock index 98dbfbb68..0dd9a627c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2362,6 +2362,7 @@ __metadata: escape-html: "npm:^1.0.3" eslint: "npm:^8.41.0" eslint-config-prettier: "npm:^9.0.0" + eslint-define-config: "npm:^2.0.0" eslint-import-resolver-typescript: "npm:^3.5.5" eslint-plugin-formatjs: "npm:^4.10.1" eslint-plugin-import: "npm:~2.29.0" @@ -2469,8 +2470,10 @@ __metadata: "@types/npmlog": "npm:^7.0.0" "@types/pg": "npm:^8.6.6" "@types/uuid": "npm:^9.0.0" + "@types/ws": "npm:^8.5.9" bufferutil: "npm:^4.0.7" dotenv: "npm:^16.0.3" + eslint-define-config: "npm:^2.0.0" express: "npm:^4.18.2" ioredis: "npm:^5.3.2" jsdom: "npm:^23.0.0" @@ -2478,6 +2481,7 @@ __metadata: pg: "npm:^8.5.0" pg-connection-string: "npm:^2.6.0" prom-client: "npm:^15.0.0" + typescript: "npm:^5.0.4" utf-8-validate: "npm:^6.0.3" uuid: "npm:^9.0.0" ws: "npm:^8.12.1" @@ -3644,6 +3648,15 @@ __metadata: languageName: node linkType: hard +"@types/ws@npm:^8.5.9": + version: 8.5.9 + resolution: "@types/ws@npm:8.5.9" + dependencies: + "@types/node": "npm:*" + checksum: 678bdd6461c4653f2975c537fb673cb1918c331558e2d2422b69761c9ced67200bb07c664e2593f3864077a891cb7c13ef2a40d303b4aacb06173d095d8aa3ce + languageName: node + linkType: hard + "@types/yargs-parser@npm:*": version: 21.0.2 resolution: "@types/yargs-parser@npm:21.0.2" @@ -7302,6 +7315,13 @@ __metadata: languageName: node linkType: hard +"eslint-define-config@npm:^2.0.0": + version: 2.0.0 + resolution: "eslint-define-config@npm:2.0.0" + checksum: 617c3143bc1ed8df0b20ae632d428d5f241dbb04483631e1410c58fe65ba3e503cf94631c5973115482b58ba464d052422a718c0f4d49182f8d13ffbb36bf1d6 + languageName: node + linkType: hard + "eslint-import-resolver-node@npm:^0.3.9": version: 0.3.9 resolution: "eslint-import-resolver-node@npm:0.3.9" From a4de0e364b9f73299ab121340c0f5ab27125be9e Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Tue, 28 Nov 2023 15:24:41 +0100 Subject: [PATCH 03/95] Refactor streaming to simplify for logging change (#28056) --- streaming/index.js | 104 +++++------------------------------------- streaming/metrics.js | 105 +++++++++++++++++++++++++++++++++++++++++++ streaming/utils.js | 22 +++++++++ 3 files changed, 139 insertions(+), 92 deletions(-) create mode 100644 streaming/metrics.js create mode 100644 streaming/utils.js diff --git a/streaming/index.js b/streaming/index.js index e3b63b53a..fb3e3fb2b 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -12,10 +12,12 @@ const { JSDOM } = require('jsdom'); const log = require('npmlog'); const pg = require('pg'); const dbUrlToConfig = require('pg-connection-string').parse; -const metrics = require('prom-client'); const uuid = require('uuid'); const WebSocket = require('ws'); +const { setupMetrics } = require('./metrics'); +const { isTruthy } = require("./utils"); + const environment = process.env.NODE_ENV || 'development'; // Correctly detect and load .env or .env.production file based on environment: @@ -196,78 +198,15 @@ const startServer = async () => { const redisClient = await createRedisClient(redisConfig); const { redisPrefix } = redisConfig; - // Collect metrics from Node.js - metrics.collectDefaultMetrics(); - - new metrics.Gauge({ - name: 'pg_pool_total_connections', - help: 'The total number of clients existing within the pool', - collect() { - this.set(pgPool.totalCount); - }, - }); - - new metrics.Gauge({ - name: 'pg_pool_idle_connections', - help: 'The number of clients which are not checked out but are currently idle in the pool', - collect() { - this.set(pgPool.idleCount); - }, - }); - - new metrics.Gauge({ - name: 'pg_pool_waiting_queries', - help: 'The number of queued requests waiting on a client when all clients are checked out', - collect() { - this.set(pgPool.waitingCount); - }, - }); - - const connectedClients = new metrics.Gauge({ - name: 'connected_clients', - help: 'The number of clients connected to the streaming server', - labelNames: ['type'], - }); - - const connectedChannels = new metrics.Gauge({ - name: 'connected_channels', - help: 'The number of channels the streaming server is streaming to', - labelNames: [ 'type', 'channel' ] - }); - - const redisSubscriptions = new metrics.Gauge({ - name: 'redis_subscriptions', - help: 'The number of Redis channels the streaming server is subscribed to', - }); - - const redisMessagesReceived = new metrics.Counter({ - name: 'redis_messages_received_total', - help: 'The total number of messages the streaming server has received from redis subscriptions' - }); - - const messagesSent = new metrics.Counter({ - name: 'messages_sent_total', - help: 'The total number of messages the streaming server sent to clients per connection type', - labelNames: [ 'type' ] - }); - - // Prime the gauges so we don't loose metrics between restarts: - redisSubscriptions.set(0); - connectedClients.set({ type: 'websocket' }, 0); - connectedClients.set({ type: 'eventsource' }, 0); - - // For each channel, initialize the gauges at zero; There's only a finite set of channels available - CHANNEL_NAMES.forEach(( channel ) => { - connectedChannels.set({ type: 'websocket', channel }, 0); - connectedChannels.set({ type: 'eventsource', channel }, 0); - }); - - // Prime the counters so that we don't loose metrics between restarts. - // Unfortunately counters don't support the set() API, so instead I'm using - // inc(0) to achieve the same result. - redisMessagesReceived.inc(0); - messagesSent.inc({ type: 'websocket' }, 0); - messagesSent.inc({ type: 'eventsource' }, 0); + const metrics = setupMetrics(CHANNEL_NAMES, pgPool); + // TODO: migrate all metrics to metrics.X.method() instead of just X.method() + const { + connectedClients, + connectedChannels, + redisSubscriptions, + redisMessagesReceived, + messagesSent, + } = metrics; // When checking metrics in the browser, the favicon is requested this // prevents the request from falling through to the API Router, which would @@ -388,25 +327,6 @@ const startServer = async () => { } }; - const FALSE_VALUES = [ - false, - 0, - '0', - 'f', - 'F', - 'false', - 'FALSE', - 'off', - 'OFF', - ]; - - /** - * @param {any} value - * @returns {boolean} - */ - const isTruthy = value => - value && !FALSE_VALUES.includes(value); - /** * @param {any} req * @param {any} res diff --git a/streaming/metrics.js b/streaming/metrics.js new file mode 100644 index 000000000..d05b4c9b1 --- /dev/null +++ b/streaming/metrics.js @@ -0,0 +1,105 @@ +// @ts-check + +const metrics = require('prom-client'); + +/** + * @typedef StreamingMetrics + * @property {metrics.Registry} register + * @property {metrics.Gauge<"type">} connectedClients + * @property {metrics.Gauge<"type" | "channel">} connectedChannels + * @property {metrics.Gauge} redisSubscriptions + * @property {metrics.Counter} redisMessagesReceived + * @property {metrics.Counter<"type">} messagesSent + */ + +/** + * + * @param {string[]} channels + * @param {import('pg').Pool} pgPool + * @returns {StreamingMetrics} + */ +function setupMetrics(channels, pgPool) { + // Collect metrics from Node.js + metrics.collectDefaultMetrics(); + + new metrics.Gauge({ + name: 'pg_pool_total_connections', + help: 'The total number of clients existing within the pool', + collect() { + this.set(pgPool.totalCount); + }, + }); + + new metrics.Gauge({ + name: 'pg_pool_idle_connections', + help: 'The number of clients which are not checked out but are currently idle in the pool', + collect() { + this.set(pgPool.idleCount); + }, + }); + + new metrics.Gauge({ + name: 'pg_pool_waiting_queries', + help: 'The number of queued requests waiting on a client when all clients are checked out', + collect() { + this.set(pgPool.waitingCount); + }, + }); + + const connectedClients = new metrics.Gauge({ + name: 'connected_clients', + help: 'The number of clients connected to the streaming server', + labelNames: ['type'], + }); + + const connectedChannels = new metrics.Gauge({ + name: 'connected_channels', + help: 'The number of channels the streaming server is streaming to', + labelNames: [ 'type', 'channel' ] + }); + + const redisSubscriptions = new metrics.Gauge({ + name: 'redis_subscriptions', + help: 'The number of Redis channels the streaming server is subscribed to', + }); + + const redisMessagesReceived = new metrics.Counter({ + name: 'redis_messages_received_total', + help: 'The total number of messages the streaming server has received from redis subscriptions' + }); + + const messagesSent = new metrics.Counter({ + name: 'messages_sent_total', + help: 'The total number of messages the streaming server sent to clients per connection type', + labelNames: [ 'type' ] + }); + + // Prime the gauges so we don't loose metrics between restarts: + redisSubscriptions.set(0); + connectedClients.set({ type: 'websocket' }, 0); + connectedClients.set({ type: 'eventsource' }, 0); + + // For each channel, initialize the gauges at zero; There's only a finite set of channels available + channels.forEach(( channel ) => { + connectedChannels.set({ type: 'websocket', channel }, 0); + connectedChannels.set({ type: 'eventsource', channel }, 0); + }); + + // Prime the counters so that we don't loose metrics between restarts. + // Unfortunately counters don't support the set() API, so instead I'm using + // inc(0) to achieve the same result. + redisMessagesReceived.inc(0); + messagesSent.inc({ type: 'websocket' }, 0); + messagesSent.inc({ type: 'eventsource' }, 0); + + return { + register: metrics.register, + connectedClients, + connectedChannels, + redisSubscriptions, + redisMessagesReceived, + messagesSent, + }; +} + +exports.setupMetrics = setupMetrics; diff --git a/streaming/utils.js b/streaming/utils.js new file mode 100644 index 000000000..ad8dd4889 --- /dev/null +++ b/streaming/utils.js @@ -0,0 +1,22 @@ +// @ts-check + +const FALSE_VALUES = [ + false, + 0, + '0', + 'f', + 'F', + 'false', + 'FALSE', + 'off', + 'OFF', +]; + +/** + * @param {any} value + * @returns {boolean} + */ +const isTruthy = value => + value && !FALSE_VALUES.includes(value); + +exports.isTruthy = isTruthy; From cdc789424355617c800dc3b589b36fdfdc01b796 Mon Sep 17 00:00:00 2001 From: Renaud Chaput Date: Tue, 28 Nov 2023 15:47:32 +0100 Subject: [PATCH 04/95] Fix devcontainer by not forcing `NODE_ENV` (#28099) --- .devcontainer/post-create.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index 20f3437f4..82a2ccbb6 100755 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -24,4 +24,4 @@ RAILS_ENV=development ./bin/rails db:setup RAILS_ENV=development ./bin/rails assets:precompile # Precompile assets for test -RAILS_ENV=test NODE_ENV=tests ./bin/rails assets:precompile +RAILS_ENV=test ./bin/rails assets:precompile From 1142f4c79e3eaf4450ed727de0f480e300e8b9a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Tue, 28 Nov 2023 18:47:55 +0100 Subject: [PATCH 05/95] Converted app/javascript/mastodon/utils/ folder to TypeScript (#27895) --- .../{base64-test.js => base64-test.ts} | 0 .../__tests__/{html-test.js => html-test.s} | 0 app/javascript/mastodon/utils/config.js | 10 ----- app/javascript/mastodon/utils/config.ts | 13 ++++++ app/javascript/mastodon/utils/html.js | 6 --- app/javascript/mastodon/utils/html.ts | 9 +++++ .../mastodon/utils/{icons.jsx => icons.tsx} | 14 ++++++- .../mastodon/utils/{log_out.js => log_out.ts} | 0 .../mastodon/utils/notifications.js | 30 -------------- .../mastodon/utils/notifications.ts | 13 ++++++ .../{react_router.jsx => react_router.tsx} | 40 +++++++++++-------- .../utils/{scrollbar.js => scrollbar.ts} | 15 +++---- package.json | 1 + yarn.lock | 8 ++++ 14 files changed, 84 insertions(+), 75 deletions(-) rename app/javascript/mastodon/utils/__tests__/{base64-test.js => base64-test.ts} (100%) rename app/javascript/mastodon/utils/__tests__/{html-test.js => html-test.s} (100%) delete mode 100644 app/javascript/mastodon/utils/config.js create mode 100644 app/javascript/mastodon/utils/config.ts delete mode 100644 app/javascript/mastodon/utils/html.js create mode 100644 app/javascript/mastodon/utils/html.ts rename app/javascript/mastodon/utils/{icons.jsx => icons.tsx} (69%) rename app/javascript/mastodon/utils/{log_out.js => log_out.ts} (100%) delete mode 100644 app/javascript/mastodon/utils/notifications.js create mode 100644 app/javascript/mastodon/utils/notifications.ts rename app/javascript/mastodon/utils/{react_router.jsx => react_router.tsx} (53%) rename app/javascript/mastodon/utils/{scrollbar.js => scrollbar.ts} (70%) diff --git a/app/javascript/mastodon/utils/__tests__/base64-test.js b/app/javascript/mastodon/utils/__tests__/base64-test.ts similarity index 100% rename from app/javascript/mastodon/utils/__tests__/base64-test.js rename to app/javascript/mastodon/utils/__tests__/base64-test.ts diff --git a/app/javascript/mastodon/utils/__tests__/html-test.js b/app/javascript/mastodon/utils/__tests__/html-test.s similarity index 100% rename from app/javascript/mastodon/utils/__tests__/html-test.js rename to app/javascript/mastodon/utils/__tests__/html-test.s diff --git a/app/javascript/mastodon/utils/config.js b/app/javascript/mastodon/utils/config.js deleted file mode 100644 index 932cd0cbf..000000000 --- a/app/javascript/mastodon/utils/config.js +++ /dev/null @@ -1,10 +0,0 @@ -import ready from '../ready'; - -export let assetHost = ''; - -ready(() => { - const cdnHost = document.querySelector('meta[name=cdn-host]'); - if (cdnHost) { - assetHost = cdnHost.content || ''; - } -}); diff --git a/app/javascript/mastodon/utils/config.ts b/app/javascript/mastodon/utils/config.ts new file mode 100644 index 000000000..9222c89d1 --- /dev/null +++ b/app/javascript/mastodon/utils/config.ts @@ -0,0 +1,13 @@ +import ready from '../ready'; + +export let assetHost = ''; + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +ready(() => { + const cdnHost = document.querySelector( + 'meta[name=cdn-host]', + ); + if (cdnHost) { + assetHost = cdnHost.content || ''; + } +}); diff --git a/app/javascript/mastodon/utils/html.js b/app/javascript/mastodon/utils/html.js deleted file mode 100644 index 247e98c88..000000000 --- a/app/javascript/mastodon/utils/html.js +++ /dev/null @@ -1,6 +0,0 @@ -// NB: This function can still return unsafe HTML -export const unescapeHTML = (html) => { - const wrapper = document.createElement('div'); - wrapper.innerHTML = html.replace(//g, '\n').replace(/<\/p>

/g, '\n\n').replace(/<[^>]*>/g, ''); - return wrapper.textContent; -}; diff --git a/app/javascript/mastodon/utils/html.ts b/app/javascript/mastodon/utils/html.ts new file mode 100644 index 000000000..0145a0455 --- /dev/null +++ b/app/javascript/mastodon/utils/html.ts @@ -0,0 +1,9 @@ +// NB: This function can still return unsafe HTML +export const unescapeHTML = (html: string) => { + const wrapper = document.createElement('div'); + wrapper.innerHTML = html + .replace(//g, '\n') + .replace(/<\/p>

/g, '\n\n') + .replace(/<[^>]*>/g, ''); + return wrapper.textContent; +}; diff --git a/app/javascript/mastodon/utils/icons.jsx b/app/javascript/mastodon/utils/icons.tsx similarity index 69% rename from app/javascript/mastodon/utils/icons.jsx rename to app/javascript/mastodon/utils/icons.tsx index be566032e..6e432e32f 100644 --- a/app/javascript/mastodon/utils/icons.jsx +++ b/app/javascript/mastodon/utils/icons.tsx @@ -1,13 +1,23 @@ // Copied from emoji-mart for consistency with emoji picker and since // they don't export the icons in the package export const loupeIcon = ( - + ); export const deleteIcon = ( - + ); diff --git a/app/javascript/mastodon/utils/log_out.js b/app/javascript/mastodon/utils/log_out.ts similarity index 100% rename from app/javascript/mastodon/utils/log_out.js rename to app/javascript/mastodon/utils/log_out.ts diff --git a/app/javascript/mastodon/utils/notifications.js b/app/javascript/mastodon/utils/notifications.js deleted file mode 100644 index 42623ac7c..000000000 --- a/app/javascript/mastodon/utils/notifications.js +++ /dev/null @@ -1,30 +0,0 @@ -// Handles browser quirks, based on -// https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API/Using_the_Notifications_API - -const checkNotificationPromise = () => { - try { - // eslint-disable-next-line promise/valid-params, promise/catch-or-return - Notification.requestPermission().then(); - } catch(e) { - return false; - } - - return true; -}; - -const handlePermission = (permission, callback) => { - // Whatever the user answers, we make sure Chrome stores the information - if(!('permission' in Notification)) { - Notification.permission = permission; - } - - callback(Notification.permission); -}; - -export const requestNotificationPermission = (callback) => { - if (checkNotificationPromise()) { - Notification.requestPermission().then((permission) => handlePermission(permission, callback)).catch(console.warn); - } else { - Notification.requestPermission((permission) => handlePermission(permission, callback)); - } -}; diff --git a/app/javascript/mastodon/utils/notifications.ts b/app/javascript/mastodon/utils/notifications.ts new file mode 100644 index 000000000..08f677f8f --- /dev/null +++ b/app/javascript/mastodon/utils/notifications.ts @@ -0,0 +1,13 @@ +/** + * Tries Notification.requestPermission, console warning instead of rejecting on error. + * @param callback Runs with the permission result on completion. + */ +export const requestNotificationPermission = async ( + callback: NotificationPermissionCallback, +) => { + try { + callback(await Notification.requestPermission()); + } catch (error) { + console.warn(error); + } +}; diff --git a/app/javascript/mastodon/utils/react_router.jsx b/app/javascript/mastodon/utils/react_router.tsx similarity index 53% rename from app/javascript/mastodon/utils/react_router.jsx rename to app/javascript/mastodon/utils/react_router.tsx index fa8f0db2b..0682fee55 100644 --- a/app/javascript/mastodon/utils/react_router.jsx +++ b/app/javascript/mastodon/utils/react_router.tsx @@ -1,8 +1,8 @@ -import PropTypes from "prop-types"; +import PropTypes from 'prop-types'; -import { __RouterContext } from "react-router"; +import { __RouterContext } from 'react-router'; -import hoistStatics from "hoist-non-react-statics"; +import hoistStatics from 'hoist-non-react-statics'; export const WithRouterPropTypes = { match: PropTypes.object.isRequired, @@ -16,31 +16,37 @@ export const WithOptionalRouterPropTypes = { history: PropTypes.object, }; +export interface OptionalRouterProps { + ref: unknown; + wrappedComponentRef: unknown; +} + // This is copied from https://github.com/remix-run/react-router/blob/v5.3.4/packages/react-router/modules/withRouter.js // but does not fail if called outside of a React Router context -export function withOptionalRouter(Component) { - const displayName = `withRouter(${Component.displayName || Component.name})`; - const C = props => { +export function withOptionalRouter< + ComponentType extends React.ComponentType, +>(Component: ComponentType) { + const displayName = `withRouter(${Component.displayName ?? Component.name})`; + const C = (props: React.ComponentProps) => { const { wrappedComponentRef, ...remainingProps } = props; return ( <__RouterContext.Consumer> - {context => { - if(context) + {(context) => { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (context) { return ( + // @ts-expect-error - Dynamic covariant generic components are tough to type. ); - else - return ( - - ); + } else { + // @ts-expect-error - Dynamic covariant generic components are tough to type. + return ; + } }} ); @@ -53,8 +59,8 @@ export function withOptionalRouter(Component) { wrappedComponentRef: PropTypes.oneOfType([ PropTypes.string, PropTypes.func, - PropTypes.object - ]) + PropTypes.object, + ]), }; return hoistStatics(C, Component); diff --git a/app/javascript/mastodon/utils/scrollbar.js b/app/javascript/mastodon/utils/scrollbar.ts similarity index 70% rename from app/javascript/mastodon/utils/scrollbar.js rename to app/javascript/mastodon/utils/scrollbar.ts index ca87dd76f..d505df124 100644 --- a/app/javascript/mastodon/utils/scrollbar.js +++ b/app/javascript/mastodon/utils/scrollbar.ts @@ -1,11 +1,7 @@ import { isMobile } from '../is_mobile'; -/** @type {number | null} */ -let cachedScrollbarWidth = null; +let cachedScrollbarWidth: number | null = null; -/** - * @returns {number} - */ const getActualScrollbarWidth = () => { const outer = document.createElement('div'); outer.style.visibility = 'hidden'; @@ -16,20 +12,19 @@ const getActualScrollbarWidth = () => { outer.appendChild(inner); const scrollbarWidth = outer.offsetWidth - inner.offsetWidth; - outer.parentNode.removeChild(outer); + outer.remove(); return scrollbarWidth; }; -/** - * @returns {number} - */ export const getScrollbarWidth = () => { if (cachedScrollbarWidth !== null) { return cachedScrollbarWidth; } - const scrollbarWidth = isMobile(window.innerWidth) ? 0 : getActualScrollbarWidth(); + const scrollbarWidth = isMobile(window.innerWidth) + ? 0 + : getActualScrollbarWidth(); cachedScrollbarWidth = scrollbarWidth; return scrollbarWidth; diff --git a/package.json b/package.json index 2f324f77a..543af8a4b 100644 --- a/package.json +++ b/package.json @@ -161,6 +161,7 @@ "@types/object-assign": "^4.0.30", "@types/prop-types": "^15.7.5", "@types/punycode": "^2.1.0", + "@types/rails__ujs": "^6.0.4", "@types/react": "^18.2.7", "@types/react-dom": "^18.2.4", "@types/react-helmet": "^6.1.6", diff --git a/yarn.lock b/yarn.lock index 0dd9a627c..11c2724d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2317,6 +2317,7 @@ __metadata: "@types/object-assign": "npm:^4.0.30" "@types/prop-types": "npm:^15.7.5" "@types/punycode": "npm:^2.1.0" + "@types/rails__ujs": "npm:^6.0.4" "@types/react": "npm:^18.2.7" "@types/react-dom": "npm:^18.2.4" "@types/react-helmet": "npm:^6.1.6" @@ -3349,6 +3350,13 @@ __metadata: languageName: node linkType: hard +"@types/rails__ujs@npm:^6.0.4": + version: 6.0.4 + resolution: "@types/rails__ujs@npm:6.0.4" + checksum: 7477cb03a0e1339b9cd5c8ac4a197a153e2ff48742b2f527c5a39dcdf80f01493011e368483290d3717662c63066fada3ab203a335804cbb3573cf575f37007e + languageName: node + linkType: hard + "@types/range-parser@npm:*": version: 1.2.7 resolution: "@types/range-parser@npm:1.2.7" From 3a7f10c3f10e6a7ab154030a2abf5a1a68c1b8f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Tue, 28 Nov 2023 19:20:10 +0100 Subject: [PATCH 06/95] Converted hashtag.jsx to TypeScript (#27872) Co-authored-by: Claire Co-authored-by: Renaud Chaput --- .eslintrc.js | 3 +- .../mastodon/components/admin/Trends.jsx | 2 +- .../mastodon/components/hashtag.jsx | 120 --------------- .../mastodon/components/hashtag.tsx | 145 ++++++++++++++++++ .../account/components/featured_tags.jsx | 2 +- .../mastodon/features/followed_tags/index.jsx | 2 +- 6 files changed, 149 insertions(+), 125 deletions(-) delete mode 100644 app/javascript/mastodon/components/hashtag.jsx create mode 100644 app/javascript/mastodon/components/hashtag.tsx diff --git a/.eslintrc.js b/.eslintrc.js index a9aa86605..176879034 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -284,7 +284,6 @@ module.exports = defineConfig({ 'formatjs/no-id': 'off', // IDs are used for translation keys 'formatjs/no-invalid-icu': 'error', 'formatjs/no-literal-string-in-jsx': 'off', // Should be looked at, but mainly flagging punctuation outside of strings - 'formatjs/no-multiple-plurals': 'off', // Only used by hashtag.jsx 'formatjs/no-multiple-whitespaces': 'error', 'formatjs/no-offset': 'error', 'formatjs/no-useless-message': 'error', @@ -354,7 +353,7 @@ module.exports = defineConfig({ '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'], '@typescript-eslint/consistent-type-exports': 'error', '@typescript-eslint/consistent-type-imports': 'error', - "@typescript-eslint/prefer-nullish-coalescing": ['error', {ignorePrimitives: {boolean: true}}], + "@typescript-eslint/prefer-nullish-coalescing": ['error', { ignorePrimitives: { boolean: true } }], 'jsdoc/require-jsdoc': 'off', diff --git a/app/javascript/mastodon/components/admin/Trends.jsx b/app/javascript/mastodon/components/admin/Trends.jsx index 49976276e..c69b4a8cb 100644 --- a/app/javascript/mastodon/components/admin/Trends.jsx +++ b/app/javascript/mastodon/components/admin/Trends.jsx @@ -6,7 +6,7 @@ import { FormattedMessage } from 'react-intl'; import classNames from 'classnames'; import api from 'mastodon/api'; -import Hashtag from 'mastodon/components/hashtag'; +import { Hashtag } from 'mastodon/components/hashtag'; export default class Trends extends PureComponent { diff --git a/app/javascript/mastodon/components/hashtag.jsx b/app/javascript/mastodon/components/hashtag.jsx deleted file mode 100644 index 14bb4ddc6..000000000 --- a/app/javascript/mastodon/components/hashtag.jsx +++ /dev/null @@ -1,120 +0,0 @@ -// @ts-check -import PropTypes from 'prop-types'; -import { Component } from 'react'; - -import { FormattedMessage } from 'react-intl'; - -import classNames from 'classnames'; -import { Link } from 'react-router-dom'; - -import ImmutablePropTypes from 'react-immutable-proptypes'; - -import { Sparklines, SparklinesCurve } from 'react-sparklines'; - -import { ShortNumber } from 'mastodon/components/short_number'; -import { Skeleton } from 'mastodon/components/skeleton'; - -class SilentErrorBoundary extends Component { - - static propTypes = { - children: PropTypes.node, - }; - - state = { - error: false, - }; - - componentDidCatch() { - this.setState({ error: true }); - } - - render() { - if (this.state.error) { - return null; - } - - return this.props.children; - } - -} - -/** - * Used to render counter of how much people are talking about hashtag - * @type {(displayNumber: JSX.Element, pluralReady: number) => JSX.Element} - */ -export const accountsCountRenderer = (displayNumber, pluralReady) => ( - {displayNumber}, - days: 2, - }} - /> -); - -// @ts-expect-error -export const ImmutableHashtag = ({ hashtag }) => ( - day.get('uses')).toArray()} - /> -); - -ImmutableHashtag.propTypes = { - hashtag: ImmutablePropTypes.map.isRequired, -}; - -// @ts-expect-error -const Hashtag = ({ name, to, people, uses, history, className, description, withGraph }) => ( -

-
- - {name ? <>#{name} : } - - - {description ? ( - {description} - ) : ( - typeof people !== 'undefined' ? : - )} -
- - {typeof uses !== 'undefined' && ( -
- -
- )} - - {withGraph && ( -
- - 0)}> - - - -
- )} -
-); - -Hashtag.propTypes = { - name: PropTypes.string, - to: PropTypes.string, - people: PropTypes.number, - description: PropTypes.node, - uses: PropTypes.number, - history: PropTypes.arrayOf(PropTypes.number), - className: PropTypes.string, - withGraph: PropTypes.bool, -}; - -Hashtag.defaultProps = { - withGraph: true, -}; - -export default Hashtag; diff --git a/app/javascript/mastodon/components/hashtag.tsx b/app/javascript/mastodon/components/hashtag.tsx new file mode 100644 index 000000000..8963e4a40 --- /dev/null +++ b/app/javascript/mastodon/components/hashtag.tsx @@ -0,0 +1,145 @@ +import type { JSX } from 'react'; +import { Component } from 'react'; + +import { FormattedMessage } from 'react-intl'; + +import classNames from 'classnames'; +import { Link } from 'react-router-dom'; + +import type Immutable from 'immutable'; + +import { Sparklines, SparklinesCurve } from 'react-sparklines'; + +import { ShortNumber } from 'mastodon/components/short_number'; +import { Skeleton } from 'mastodon/components/skeleton'; + +interface SilentErrorBoundaryProps { + children: React.ReactNode; +} + +class SilentErrorBoundary extends Component { + state = { + error: false, + }; + + componentDidCatch() { + this.setState({ error: true }); + } + + render() { + if (this.state.error) { + return null; + } + + return this.props.children; + } +} + +/** + * Used to render counter of how much people are talking about hashtag + * @param displayNumber Counter number to display + * @param pluralReady Whether the count is plural + * @returns Formatted counter of how much people are talking about hashtag + */ +export const accountsCountRenderer = ( + displayNumber: JSX.Element, + pluralReady: number, +) => ( + {displayNumber}, + days: 2, + }} + /> +); + +interface ImmutableHashtagProps { + hashtag: Immutable.Map; +} + +export const ImmutableHashtag = ({ hashtag }: ImmutableHashtagProps) => ( + + > + ) + .reverse() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + .map((day) => day.get('uses')!) + .toArray()} + /> +); + +export interface HashtagProps { + className?: string; + description?: React.ReactNode; + history?: number[]; + name: string; + people: number; + to: string; + uses?: number; + withGraph?: boolean; +} + +export const Hashtag: React.FC = ({ + name, + to, + people, + uses, + history, + className, + description, + withGraph = true, +}) => ( +
+
+ + {name ? ( + <> + #{name} + + ) : ( + + )} + + + {description ? ( + {description} + ) : typeof people !== 'undefined' ? ( + + ) : ( + + )} +
+ + {typeof uses !== 'undefined' && ( +
+ +
+ )} + + {withGraph && ( +
+ + 0)} + > + + + +
+ )} +
+); diff --git a/app/javascript/mastodon/features/account/components/featured_tags.jsx b/app/javascript/mastodon/features/account/components/featured_tags.jsx index 4d7dd8656..56a9efac0 100644 --- a/app/javascript/mastodon/features/account/components/featured_tags.jsx +++ b/app/javascript/mastodon/features/account/components/featured_tags.jsx @@ -5,7 +5,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import Hashtag from 'mastodon/components/hashtag'; +import { Hashtag } from 'mastodon/components/hashtag'; const messages = defineMessages({ lastStatusAt: { id: 'account.featured_tags.last_status_at', defaultMessage: 'Last post on {date}' }, diff --git a/app/javascript/mastodon/features/followed_tags/index.jsx b/app/javascript/mastodon/features/followed_tags/index.jsx index 7042f2438..dec53f012 100644 --- a/app/javascript/mastodon/features/followed_tags/index.jsx +++ b/app/javascript/mastodon/features/followed_tags/index.jsx @@ -13,7 +13,7 @@ import { debounce } from 'lodash'; import { expandFollowedHashtags, fetchFollowedHashtags } from 'mastodon/actions/tags'; import ColumnHeader from 'mastodon/components/column_header'; -import Hashtag from 'mastodon/components/hashtag'; +import { Hashtag } from 'mastodon/components/hashtag'; import ScrollableList from 'mastodon/components/scrollable_list'; import Column from 'mastodon/features/ui/components/column'; From b45fe15c06c020ee9e5303e4640f12b1db20ff29 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 28 Nov 2023 13:36:16 -0500 Subject: [PATCH 07/95] Update temple to version 0.10.3 (#28086) --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 972adae38..793869157 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -755,7 +755,7 @@ GEM attr_required (>= 0.0.5) httpclient (>= 2.4) sysexits (1.2.0) - temple (0.10.2) + temple (0.10.3) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) terrapin (0.6.0) From b9492d84a0f367ec6eb4cb609958fab5f850608e Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 28 Nov 2023 13:36:21 -0500 Subject: [PATCH 08/95] Update `bcrypt` to version 3.1.20 (#28084) --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 793869157..7440f1b32 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -156,7 +156,7 @@ GEM nokogiri (~> 1, >= 1.10.8) base64 (0.2.0) bcp47_spec (0.2.1) - bcrypt (3.1.19) + bcrypt (3.1.20) better_errors (2.10.1) erubi (>= 1.0.0) rack (>= 0.9.0) From 6b46bf99539112cb119958a5d79ffa46fe9c0a9c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Tue, 28 Nov 2023 13:37:54 -0500 Subject: [PATCH 09/95] Handle scenario when webfinger response `subject` is missing host value (#28088) --- app/services/resolve_account_service.rb | 4 +++- spec/services/resolve_account_service_spec.rb | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/services/resolve_account_service.rb b/app/services/resolve_account_service.rb index 6204fefd6..078a0423f 100644 --- a/app/services/resolve_account_service.rb +++ b/app/services/resolve_account_service.rb @@ -100,7 +100,9 @@ class ResolveAccountService < BaseService end def split_acct(acct) - acct.delete_prefix('acct:').split('@') + acct.delete_prefix('acct:').split('@').tap do |parts| + raise Webfinger::Error, 'Webfinger response is missing user or host value' unless parts.size == 2 + end end def fetch_account! diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb index f446d0ca6..9f5fdca29 100644 --- a/spec/services/resolve_account_service_spec.rb +++ b/spec/services/resolve_account_service_spec.rb @@ -144,6 +144,19 @@ RSpec.describe ResolveAccountService, type: :service do end end + context 'with webfinger response subject missing a host value' do + let(:body) { Oj.dump({ subject: 'user@' }) } + let(:url) { 'https://host.example/.well-known/webfinger?resource=acct:user@host.example' } + + before do + stub_request(:get, url).to_return(status: 200, body: body) + end + + it 'returns nil with incomplete subject in response' do + expect(subject.call('user@host.example')).to be_nil + end + end + context 'with an ActivityPub account' do it 'returns new remote account' do account = subject.call('foo@ap.example.com') From 7a3b41eb54f24545854da6bb364f240fceff3c2f Mon Sep 17 00:00:00 2001 From: Michael Stanclift Date: Tue, 28 Nov 2023 14:44:04 -0600 Subject: [PATCH 10/95] Fix incorrect apt-get install block in Dockerfile (#28112) --- Dockerfile | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/Dockerfile b/Dockerfile index 623a4d4ea..73267f380 100644 --- a/Dockerfile +++ b/Dockerfile @@ -177,29 +177,7 @@ FROM build as yarn ARG TARGETPLATFORM # Copy Node package configuration files into working directory -RUN apt-get update && \ - apt-get -yq dist-upgrade && \ - apt-get install -y --no-install-recommends build-essential \ - git \ - libicu-dev \ - libidn-dev \ - libpq-dev \ - libjemalloc-dev \ - zlib1g-dev \ - libgdbm-dev \ - libgmp-dev \ - libssl-dev \ - libyaml-dev \ - ca-certificates \ - libreadline8 \ - python3 \ - shared-mime-info && \ - bundle config set --local deployment 'true' && \ - bundle config set --local without 'development test' && \ - bundle config set silence_root_warning true && \ - corepack enable - -COPY Gemfile* package.json yarn.lock .yarnrc.yml /opt/mastodon/ +COPY package.json yarn.lock .yarnrc.yml /opt/mastodon/ COPY streaming/package.json /opt/mastodon/streaming/ COPY .yarn /opt/mastodon/.yarn From c40cfc5d0905651dcf3c1e5fa04c0bd06da6e9d1 Mon Sep 17 00:00:00 2001 From: Michael Stanclift Date: Tue, 28 Nov 2023 16:23:44 -0600 Subject: [PATCH 11/95] Fix apt cache not being properly utilized in Dockerfile (#28115) --- Dockerfile | 4 ++-- streaming/Dockerfile | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 73267f380..ed5ebf1e0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -70,6 +70,8 @@ ARG TARGETPLATFORM RUN echo "Target platform is $TARGETPLATFORM" RUN \ +# Remove automatic apt cache Docker cleanup scripts + rm -f /etc/apt/apt.conf.d/docker-clean; \ # Sets timezone echo "${TZ}" > /etc/localtime; \ # Creates mastodon user/group and sets home directory @@ -127,7 +129,6 @@ RUN \ --mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \ --mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \ # Install build tools and bundler dependencies from APT - apt-get update; \ apt-get install -y --no-install-recommends \ g++ \ gcc \ @@ -221,7 +222,6 @@ RUN \ --mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \ --mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \ # Apt update install non-dev versions of necessary components - apt-get update; \ apt-get install -y --no-install-recommends \ libssl3 \ libpq5 \ diff --git a/streaming/Dockerfile b/streaming/Dockerfile index ee62e9ec7..6e0a84771 100644 --- a/streaming/Dockerfile +++ b/streaming/Dockerfile @@ -47,6 +47,8 @@ ARG TARGETPLATFORM RUN echo "Target platform is ${TARGETPLATFORM}" RUN \ +# Remove automatic apt cache Docker cleanup scripts + rm -f /etc/apt/apt.conf.d/docker-clean; \ # Sets timezone echo "${TZ}" > /etc/localtime; \ # Creates mastodon user/group and sets home directory @@ -60,7 +62,7 @@ RUN \ # Mount Apt cache and lib directories from Docker buildx caches --mount=type=cache,id=apt-cache-${TARGETPLATFORM},target=/var/cache/apt,sharing=locked \ --mount=type=cache,id=apt-lib-${TARGETPLATFORM},target=/var/lib/apt,sharing=locked \ -# upgrade to check for security updates to Debian image +# Upgrade to check for security updates to Debian image apt-get update; \ apt-get dist-upgrade -yq; \ apt-get install -y --no-install-recommends \ From 603b245cc62acba0d8f71ad4c31d2f1676e90190 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Wed, 29 Nov 2023 09:34:36 +0100 Subject: [PATCH 12/95] Add VAPID public key to instance serializer (#28006) Co-authored-by: Renaud Chaput --- app/serializers/rest/application_serializer.rb | 5 ++++- app/serializers/rest/instance_serializer.rb | 4 ++++ spec/serializers/rest/instance_serializer_spec.rb | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/serializers/rest/application_serializer.rb b/app/serializers/rest/application_serializer.rb index e4806a3c9..635508a17 100644 --- a/app/serializers/rest/application_serializer.rb +++ b/app/serializers/rest/application_serializer.rb @@ -2,7 +2,10 @@ class REST::ApplicationSerializer < ActiveModel::Serializer attributes :id, :name, :website, :scopes, :redirect_uri, - :client_id, :client_secret, :vapid_key + :client_id, :client_secret + + # NOTE: Deprecated in 4.3.0, needs to be removed in 5.0.0 + attribute :vapid_key def id object.id.to_s diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb index 14aeda946..d7ed381e1 100644 --- a/app/serializers/rest/instance_serializer.rb +++ b/app/serializers/rest/instance_serializer.rb @@ -48,6 +48,10 @@ class REST::InstanceSerializer < ActiveModel::Serializer status: object.status_page_url, }, + vapid: { + public_key: Rails.configuration.x.vapid_public_key, + }, + accounts: { max_featured_tags: FeaturedTag::LIMIT, }, diff --git a/spec/serializers/rest/instance_serializer_spec.rb b/spec/serializers/rest/instance_serializer_spec.rb index 8ac32f224..d8f2536d2 100644 --- a/spec/serializers/rest/instance_serializer_spec.rb +++ b/spec/serializers/rest/instance_serializer_spec.rb @@ -10,5 +10,11 @@ describe REST::InstanceSerializer do it 'returns recent usage data' do expect(serialization['usage']).to eq({ 'users' => { 'active_month' => 0 } }) end + + it 'returns the VAPID public key' do + expect(serialization['configuration']['vapid']).to eq({ + 'public_key' => Rails.configuration.x.vapid_public_key, + }) + end end end From 3e1e41054ba40294ecf70c1216759fc378f46ee0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:00:07 +0100 Subject: [PATCH 13/95] chore(deps): update dependency pghero to v3.4.0 (#28117) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7440f1b32..40f85c7d4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -522,7 +522,7 @@ GEM pastel (0.8.0) tty-color (~> 0.5) pg (1.5.4) - pghero (3.3.4) + pghero (3.4.0) activerecord (>= 6) posix-spawn (0.3.15) premailer (1.21.0) From 186895fc887d8729185fc51f994dca93a30592b1 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 29 Nov 2023 05:00:52 -0500 Subject: [PATCH 14/95] Refactor, lint fix, and bug fix on admin/roles/form partial (#27558) --- app/helpers/admin/roles_helper.rb | 24 ++++++++++++++++++++++++ app/models/user_role.rb | 6 +++--- app/views/admin/roles/_form.html.haml | 2 +- 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 app/helpers/admin/roles_helper.rb diff --git a/app/helpers/admin/roles_helper.rb b/app/helpers/admin/roles_helper.rb new file mode 100644 index 000000000..7b4702e26 --- /dev/null +++ b/app/helpers/admin/roles_helper.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Admin + module RolesHelper + def privilege_label(privilege) + safe_join( + [ + t("admin.roles.privileges.#{privilege}"), + content_tag(:span, t("admin.roles.privileges.#{privilege}_description"), class: 'hint'), + ] + ) + end + + def disable_permissions?(permissions) + permissions.filter { |privilege| role_flag_value(privilege).zero? } + end + + private + + def role_flag_value(privilege) + UserRole::FLAGS[privilege] & current_user.role.computed_permissions + end + end +end diff --git a/app/models/user_role.rb b/app/models/user_role.rb index 5472646c6..89354da54 100644 --- a/app/models/user_role.rb +++ b/app/models/user_role.rb @@ -49,7 +49,7 @@ class UserRole < ApplicationRecord invite_users ).freeze, - moderation: %w( + moderation: %i( view_dashboard view_audit_log manage_users @@ -63,7 +63,7 @@ class UserRole < ApplicationRecord manage_invites ).freeze, - administration: %w( + administration: %i( manage_settings manage_rules manage_roles @@ -72,7 +72,7 @@ class UserRole < ApplicationRecord manage_announcements ).freeze, - devops: %w( + devops: %i( view_devops ).freeze, diff --git a/app/views/admin/roles/_form.html.haml b/app/views/admin/roles/_form.html.haml index 240033214..46a1c537a 100644 --- a/app/views/admin/roles/_form.html.haml +++ b/app/views/admin/roles/_form.html.haml @@ -31,6 +31,6 @@ - (form.object.everyone? ? UserRole::Flags::CATEGORIES.slice(:invites) : UserRole::Flags::CATEGORIES).each do |category, permissions| %h4= t(category, scope: 'admin.roles.categories') - = form.input :permissions_as_keys, collection: permissions, wrapper: :with_block_label, include_blank: false, label_method: ->(privilege) { safe_join([t("admin.roles.privileges.#{privilege}"), content_tag(:span, t("admin.roles.privileges.#{privilege}_description"), class: 'hint')]) }, required: false, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', label: false, hint: false, disabled: permissions.filter { |privilege| UserRole::FLAGS[privilege] & current_user.role.computed_permissions == 0 } + = form.input :permissions_as_keys, collection: permissions, wrapper: :with_block_label, include_blank: false, label_method: ->(privilege) { privilege_label(privilege) }, required: false, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', label: false, hint: false, disabled: disable_permissions?(permissions) %hr.spacer/ From 9b47c5d53c24cd5bd9ee84169f4f05bb81f3ed88 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 29 Nov 2023 05:02:59 -0500 Subject: [PATCH 15/95] Extract helper methods for labels from filters/_filter_fields (#27574) --- app/helpers/filters_helper.rb | 12 ++++++++++++ app/views/filters/_filter_fields.html.haml | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 app/helpers/filters_helper.rb diff --git a/app/helpers/filters_helper.rb b/app/helpers/filters_helper.rb new file mode 100644 index 000000000..22a1c172d --- /dev/null +++ b/app/helpers/filters_helper.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module FiltersHelper + def filter_action_label(action) + safe_join( + [ + t("simple_form.labels.filters.actions.#{action}"), + content_tag(:span, t("simple_form.hints.filters.actions.#{action}"), class: 'hint'), + ] + ) + end +end diff --git a/app/views/filters/_filter_fields.html.haml b/app/views/filters/_filter_fields.html.haml index 0690e8dd5..a3260816e 100644 --- a/app/views/filters/_filter_fields.html.haml +++ b/app/views/filters/_filter_fields.html.haml @@ -10,7 +10,7 @@ %hr.spacer/ .fields-group - = f.input :filter_action, as: :radio_buttons, collection: %i(warn hide), include_blank: false, wrapper: :with_block_label, label_method: ->(action) { safe_join([t("simple_form.labels.filters.actions.#{action}"), content_tag(:span, t("simple_form.hints.filters.actions.#{action}"), class: 'hint')]) }, hint: t('simple_form.hints.filters.action'), required: true + = f.input :filter_action, as: :radio_buttons, collection: %i(warn hide), include_blank: false, wrapper: :with_block_label, label_method: ->(action) { filter_action_label(action) }, hint: t('simple_form.hints.filters.action'), required: true %hr.spacer/ From 72b7cd349ccffb557d2c55f4ba76b525ff47ef9e Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 29 Nov 2023 05:06:19 -0500 Subject: [PATCH 16/95] Extract helper methods for form label in admin/ area views (#27575) --- app/helpers/admin/account_actions_helper.rb | 12 ++++++++++++ app/helpers/admin/accounts_helper.rb | 19 +++++++++++++++++++ app/helpers/admin/ip_blocks_helper.rb | 12 ++++++++++++ .../admin/settings/discovery_helper.rb | 15 +++++++++++++++ app/views/admin/account_actions/new.html.haml | 2 +- app/views/admin/accounts/index.html.haml | 2 +- app/views/admin/ip_blocks/new.html.haml | 2 +- .../admin/settings/discovery/show.html.haml | 2 +- 8 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 app/helpers/admin/account_actions_helper.rb create mode 100644 app/helpers/admin/accounts_helper.rb create mode 100644 app/helpers/admin/ip_blocks_helper.rb create mode 100644 app/helpers/admin/settings/discovery_helper.rb diff --git a/app/helpers/admin/account_actions_helper.rb b/app/helpers/admin/account_actions_helper.rb new file mode 100644 index 000000000..e132680a6 --- /dev/null +++ b/app/helpers/admin/account_actions_helper.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Admin::AccountActionsHelper + def account_action_type_label(type) + safe_join( + [ + I18n.t("simple_form.labels.admin_account_action.types.#{type}"), + content_tag(:span, I18n.t("simple_form.hints.admin_account_action.types.#{type}"), class: 'hint'), + ] + ) + end +end diff --git a/app/helpers/admin/accounts_helper.rb b/app/helpers/admin/accounts_helper.rb new file mode 100644 index 000000000..a936797e8 --- /dev/null +++ b/app/helpers/admin/accounts_helper.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Admin::AccountsHelper + def admin_accounts_moderation_options + [ + [t('admin.accounts.moderation.active'), 'active'], + [t('admin.accounts.moderation.silenced'), 'silenced'], + [t('admin.accounts.moderation.disabled'), 'disabled'], + [t('admin.accounts.moderation.suspended'), 'suspended'], + [safe_join([t('admin.accounts.moderation.pending'), "(#{pending_user_count_label})"], ' '), 'pending'], + ] + end + + private + + def pending_user_count_label + number_with_delimiter User.pending.count + end +end diff --git a/app/helpers/admin/ip_blocks_helper.rb b/app/helpers/admin/ip_blocks_helper.rb new file mode 100644 index 000000000..4aae3aae7 --- /dev/null +++ b/app/helpers/admin/ip_blocks_helper.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Admin::IpBlocksHelper + def ip_blocks_severity_label(severity) + safe_join( + [ + I18n.t("simple_form.labels.ip_block.severities.#{severity}"), + content_tag(:span, I18n.t("simple_form.hints.ip_block.severities.#{severity}"), class: 'hint'), + ] + ) + end +end diff --git a/app/helpers/admin/settings/discovery_helper.rb b/app/helpers/admin/settings/discovery_helper.rb new file mode 100644 index 000000000..0aa4d4368 --- /dev/null +++ b/app/helpers/admin/settings/discovery_helper.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Admin::Settings::DiscoveryHelper + def discovery_warning_hint_text + authorized_fetch_overridden? ? t('admin.settings.security.authorized_fetch_overridden_hint') : nil + end + + def discovery_hint_text + t('admin.settings.security.authorized_fetch_hint') + end + + def discovery_recommended_value + authorized_fetch_overridden? ? :overridden : nil + end +end diff --git a/app/views/admin/account_actions/new.html.haml b/app/views/admin/account_actions/new.html.haml index 2a0cae15a..4cb4401c7 100644 --- a/app/views/admin/account_actions/new.html.haml +++ b/app/views/admin/account_actions/new.html.haml @@ -5,7 +5,7 @@ = f.input :report_id, as: :hidden .fields-group - = f.input :type, as: :radio_buttons, collection: Admin::AccountAction.types_for_account(@account), include_blank: false, wrapper: :with_block_label, label_method: ->(type) { safe_join([I18n.t("simple_form.labels.admin_account_action.types.#{type}"), content_tag(:span, I18n.t("simple_form.hints.admin_account_action.types.#{type}"), class: 'hint')]) }, hint: t('simple_form.hints.admin_account_action.type_html', acct: @account.pretty_acct) + = f.input :type, as: :radio_buttons, collection: Admin::AccountAction.types_for_account(@account), include_blank: false, wrapper: :with_block_label, label_method: ->(type) { account_action_type_label(type) }, hint: t('simple_form.hints.admin_account_action.type_html', acct: @account.pretty_acct) - if @account.local? %hr.spacer/ diff --git a/app/views/admin/accounts/index.html.haml b/app/views/admin/accounts/index.html.haml index 9cec8d632..835444189 100644 --- a/app/views/admin/accounts/index.html.haml +++ b/app/views/admin/accounts/index.html.haml @@ -10,7 +10,7 @@ .filter-subset.filter-subset--with-select %strong= t('admin.accounts.moderation.title') .input.select.optional - = select_tag :status, options_for_select([[t('admin.accounts.moderation.active'), 'active'], [t('admin.accounts.moderation.silenced'), 'silenced'], [t('admin.accounts.moderation.disabled'), 'disabled'], [t('admin.accounts.moderation.suspended'), 'suspended'], [safe_join([t('admin.accounts.moderation.pending'), "(#{number_with_delimiter(User.pending.count)})"], ' '), 'pending']], params[:status]), prompt: I18n.t('generic.all') + = select_tag :status, options_for_select(admin_accounts_moderation_options, params[:status]), prompt: I18n.t('generic.all') .filter-subset.filter-subset--with-select %strong= t('admin.accounts.role') .input.select.optional diff --git a/app/views/admin/ip_blocks/new.html.haml b/app/views/admin/ip_blocks/new.html.haml index 405c73c90..ecaf04315 100644 --- a/app/views/admin/ip_blocks/new.html.haml +++ b/app/views/admin/ip_blocks/new.html.haml @@ -11,7 +11,7 @@ = f.input :expires_in, wrapper: :with_block_label, collection: [1.day, 2.weeks, 1.month, 6.months, 1.year, 3.years].map(&:to_i), label_method: ->(i) { I18n.t("admin.ip_blocks.expires_in.#{i}") }, prompt: I18n.t('invites.expires_in_prompt') .fields-group - = f.input :severity, as: :radio_buttons, collection: IpBlock.severities.keys, include_blank: false, wrapper: :with_block_label, label_method: ->(severity) { safe_join([I18n.t("simple_form.labels.ip_block.severities.#{severity}"), content_tag(:span, I18n.t("simple_form.hints.ip_block.severities.#{severity}"), class: 'hint')]) } + = f.input :severity, as: :radio_buttons, collection: IpBlock.severities.keys, include_blank: false, wrapper: :with_block_label, label_method: ->(severity) { ip_blocks_severity_label(severity) } .fields-group = f.input :comment, as: :string, wrapper: :with_block_label diff --git a/app/views/admin/settings/discovery/show.html.haml b/app/views/admin/settings/discovery/show.html.haml index 62011d5c5..59fc3226a 100644 --- a/app/views/admin/settings/discovery/show.html.haml +++ b/app/views/admin/settings/discovery/show.html.haml @@ -42,7 +42,7 @@ %h4= t('admin.settings.security.federation_authentication') .fields-group - = f.input :authorized_fetch, as: :boolean, wrapper: :with_label, label: t('admin.settings.security.authorized_fetch'), warning_hint: authorized_fetch_overridden? ? t('admin.settings.security.authorized_fetch_overridden_hint') : nil, hint: t('admin.settings.security.authorized_fetch_hint'), disabled: authorized_fetch_overridden?, recommended: authorized_fetch_overridden? ? :overridden : nil + = f.input :authorized_fetch, as: :boolean, wrapper: :with_label, label: t('admin.settings.security.authorized_fetch'), warning_hint: discovery_warning_hint_text, hint: discovery_hint_text, disabled: authorized_fetch_overridden?, recommended: discovery_recommended_value %h4= t('admin.settings.discovery.follow_recommendations') From 31bef99b9edf8ad52182f9dcc5012d3c799b9fe4 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 29 Nov 2023 05:08:55 -0500 Subject: [PATCH 17/95] Move lib/mastodon/premailer_webpack_strategy to lib/ (#27636) --- .rubocop_todo.yml | 2 +- config/initializers/premailer_rails.rb | 2 +- lib/{mastodon => }/premailer_webpack_strategy.rb | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename lib/{mastodon => }/premailer_webpack_strategy.rb (100%) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 167204604..a79f88f97 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -307,7 +307,7 @@ Style/FetchEnvVar: - 'config/initializers/devise.rb' - 'config/initializers/paperclip.rb' - 'config/initializers/vapid.rb' - - 'lib/mastodon/premailer_webpack_strategy.rb' + - 'lib/premailer_webpack_strategy.rb' - 'lib/mastodon/redis_config.rb' - 'lib/tasks/repo.rake' - 'spec/features/profile_spec.rb' diff --git a/config/initializers/premailer_rails.rb b/config/initializers/premailer_rails.rb index 98b208271..52576ef88 100644 --- a/config/initializers/premailer_rails.rb +++ b/config/initializers/premailer_rails.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative '../../lib/mastodon/premailer_webpack_strategy' +require_relative '../../lib/premailer_webpack_strategy' Premailer::Rails.config.merge!(remove_ids: true, adapter: :nokogiri, diff --git a/lib/mastodon/premailer_webpack_strategy.rb b/lib/premailer_webpack_strategy.rb similarity index 100% rename from lib/mastodon/premailer_webpack_strategy.rb rename to lib/premailer_webpack_strategy.rb From a1636fce7f2eb630b6c8d0a4524b1687b61e3496 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 29 Nov 2023 05:10:21 -0500 Subject: [PATCH 18/95] Move lib/devise/* to lib/devise/strategies/* (#27638) --- .rubocop_todo.yml | 8 ++++---- config/application.rb | 4 ++-- .../{ => strategies}/two_factor_ldap_authenticatable.rb | 0 .../{ => strategies}/two_factor_pam_authenticatable.rb | 0 4 files changed, 6 insertions(+), 6 deletions(-) rename lib/devise/{ => strategies}/two_factor_ldap_authenticatable.rb (100%) rename lib/devise/{ => strategies}/two_factor_pam_authenticatable.rb (100%) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index a79f88f97..43af4f670 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -357,8 +357,8 @@ Style/GuardClause: - 'config/initializers/devise.rb' - 'db/migrate/20170901141119_truncate_preview_cards.rb' - 'db/post_migrate/20220704024901_migrate_settings_to_user_roles.rb' - - 'lib/devise/two_factor_ldap_authenticatable.rb' - - 'lib/devise/two_factor_pam_authenticatable.rb' + - 'lib/devise/strategies/two_factor_ldap_authenticatable.rb' + - 'lib/devise/strategies/two_factor_pam_authenticatable.rb' - 'lib/mastodon/cli/accounts.rb' - 'lib/mastodon/cli/maintenance.rb' - 'lib/mastodon/cli/media.rb' @@ -493,8 +493,8 @@ Style/SafeNavigation: # SupportedStyles: only_raise, only_fail, semantic Style/SignalException: Exclude: - - 'lib/devise/two_factor_ldap_authenticatable.rb' - - 'lib/devise/two_factor_pam_authenticatable.rb' + - 'lib/devise/strategies/two_factor_ldap_authenticatable.rb' + - 'lib/devise/strategies/two_factor_pam_authenticatable.rb' # This cop supports unsafe autocorrection (--autocorrect-all). Style/SingleArgumentDig: diff --git a/config/application.rb b/config/application.rb index 387827784..99ee4ffd7 100644 --- a/config/application.rb +++ b/config/application.rb @@ -39,8 +39,8 @@ require_relative '../lib/mastodon/snowflake' require_relative '../lib/mastodon/version' require_relative '../lib/mastodon/rack_middleware' require_relative '../lib/public_file_server_middleware' -require_relative '../lib/devise/two_factor_ldap_authenticatable' -require_relative '../lib/devise/two_factor_pam_authenticatable' +require_relative '../lib/devise/strategies/two_factor_ldap_authenticatable' +require_relative '../lib/devise/strategies/two_factor_pam_authenticatable' require_relative '../lib/chewy/settings_extensions' require_relative '../lib/chewy/index_extensions' require_relative '../lib/chewy/strategy/mastodon' diff --git a/lib/devise/two_factor_ldap_authenticatable.rb b/lib/devise/strategies/two_factor_ldap_authenticatable.rb similarity index 100% rename from lib/devise/two_factor_ldap_authenticatable.rb rename to lib/devise/strategies/two_factor_ldap_authenticatable.rb diff --git a/lib/devise/two_factor_pam_authenticatable.rb b/lib/devise/strategies/two_factor_pam_authenticatable.rb similarity index 100% rename from lib/devise/two_factor_pam_authenticatable.rb rename to lib/devise/strategies/two_factor_pam_authenticatable.rb From 291dc04e67f963313958a23de7ba935fa7130996 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 29 Nov 2023 05:38:05 -0500 Subject: [PATCH 19/95] Remove un-needed `action` and `template` options to `render` in controllers (#28022) --- .../admin/account_moderation_notes_controller.rb | 2 +- app/controllers/admin/relays_controller.rb | 2 +- app/controllers/admin/report_notes_controller.rb | 2 +- app/controllers/concerns/challengable_concern.rb | 2 +- app/controllers/disputes/appeals_controller.rb | 2 +- app/controllers/filters_controller.rb | 4 ++-- app/controllers/statuses_cleanup_controller.rb | 2 +- spec/controllers/concerns/challengable_concern_spec.rb | 6 +++--- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/controllers/admin/account_moderation_notes_controller.rb b/app/controllers/admin/account_moderation_notes_controller.rb index 4f36f33f4..8b6c1a445 100644 --- a/app/controllers/admin/account_moderation_notes_controller.rb +++ b/app/controllers/admin/account_moderation_notes_controller.rb @@ -16,7 +16,7 @@ module Admin @moderation_notes = @account.targeted_moderation_notes.latest @warnings = @account.strikes.custom.latest - render template: 'admin/accounts/show' + render 'admin/accounts/show' end end diff --git a/app/controllers/admin/relays_controller.rb b/app/controllers/admin/relays_controller.rb index c1297c8b9..c89380215 100644 --- a/app/controllers/admin/relays_controller.rb +++ b/app/controllers/admin/relays_controller.rb @@ -24,7 +24,7 @@ module Admin @relay.enable! redirect_to admin_relays_path else - render action: :new + render :new end end diff --git a/app/controllers/admin/report_notes_controller.rb b/app/controllers/admin/report_notes_controller.rb index 3fd815b60..b5f04a1ca 100644 --- a/app/controllers/admin/report_notes_controller.rb +++ b/app/controllers/admin/report_notes_controller.rb @@ -26,7 +26,7 @@ module Admin @form = Admin::StatusBatchAction.new @statuses = @report.statuses.with_includes - render template: 'admin/reports/show' + render 'admin/reports/show' end end diff --git a/app/controllers/concerns/challengable_concern.rb b/app/controllers/concerns/challengable_concern.rb index 2995a25e0..09874fb40 100644 --- a/app/controllers/concerns/challengable_concern.rb +++ b/app/controllers/concerns/challengable_concern.rb @@ -43,7 +43,7 @@ module ChallengableConcern def render_challenge @body_classes = 'lighter' - render template: 'auth/challenges/new', layout: 'auth' + render 'auth/challenges/new', layout: 'auth' end def challenge_passed? diff --git a/app/controllers/disputes/appeals_controller.rb b/app/controllers/disputes/appeals_controller.rb index eefd92b5a..98b58d211 100644 --- a/app/controllers/disputes/appeals_controller.rb +++ b/app/controllers/disputes/appeals_controller.rb @@ -11,7 +11,7 @@ class Disputes::AppealsController < Disputes::BaseController redirect_to disputes_strike_path(@strike), notice: I18n.t('disputes.strikes.appealed_msg') rescue ActiveRecord::RecordInvalid => e @appeal = e.record - render template: 'disputes/strikes/show' + render 'disputes/strikes/show' end private diff --git a/app/controllers/filters_controller.rb b/app/controllers/filters_controller.rb index bbe177ead..bd9964426 100644 --- a/app/controllers/filters_controller.rb +++ b/app/controllers/filters_controller.rb @@ -25,7 +25,7 @@ class FiltersController < ApplicationController if @filter.save redirect_to filters_path else - render action: :new + render :new end end @@ -33,7 +33,7 @@ class FiltersController < ApplicationController if @filter.update(resource_params) redirect_to filters_path else - render action: :edit + render :edit end end diff --git a/app/controllers/statuses_cleanup_controller.rb b/app/controllers/statuses_cleanup_controller.rb index 19ae971ce..4a3fc10ca 100644 --- a/app/controllers/statuses_cleanup_controller.rb +++ b/app/controllers/statuses_cleanup_controller.rb @@ -14,7 +14,7 @@ class StatusesCleanupController < ApplicationController if @policy.update(resource_params) redirect_to statuses_cleanup_path, notice: I18n.t('generic.changes_saved_msg') else - render action: :show + render :show end rescue ActionController::ParameterMissing # Do nothing diff --git a/spec/controllers/concerns/challengable_concern_spec.rb b/spec/controllers/concerns/challengable_concern_spec.rb index 3324bdd24..169e2122f 100644 --- a/spec/controllers/concerns/challengable_concern_spec.rb +++ b/spec/controllers/concerns/challengable_concern_spec.rb @@ -85,7 +85,7 @@ RSpec.describe ChallengableConcern do before { get :foo } it 'renders challenge' do - expect(response).to render_template('auth/challenges/new') + expect(response).to render_template('auth/challenges/new', layout: :auth) end # See Auth::ChallengesControllerSpec @@ -95,7 +95,7 @@ RSpec.describe ChallengableConcern do before { post :bar } it 'renders challenge' do - expect(response).to render_template('auth/challenges/new') + expect(response).to render_template('auth/challenges/new', layout: :auth) end it 'accepts correct password' do @@ -106,7 +106,7 @@ RSpec.describe ChallengableConcern do it 'rejects wrong password' do post :bar, params: { form_challenge: { current_password: 'dddfff888123' } } - expect(response.body).to render_template('auth/challenges/new') + expect(response.body).to render_template('auth/challenges/new', layout: :auth) expect(session[:challenge_passed_at]).to be_nil end end From e48ecd29294ec2790fd38fd1a35b8490d1350c4e Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 29 Nov 2023 05:39:59 -0500 Subject: [PATCH 20/95] Remove `default_scope` from `Admin::ActionLog` (#28026) --- app/controllers/admin/action_logs_controller.rb | 2 +- app/models/admin/action_log.rb | 4 ++-- app/models/admin/action_log_filter.rb | 12 ++++++++---- app/models/report.rb | 10 +++++----- ...on_log_fabricator.rb => action_log_fabricator.rb} | 2 +- spec/models/report_spec.rb | 6 +++--- 6 files changed, 20 insertions(+), 16 deletions(-) rename spec/fabricators/{admin_action_log_fabricator.rb => action_log_fabricator.rb} (68%) diff --git a/app/controllers/admin/action_logs_controller.rb b/app/controllers/admin/action_logs_controller.rb index 42edec15a..37a00ad22 100644 --- a/app/controllers/admin/action_logs_controller.rb +++ b/app/controllers/admin/action_logs_controller.rb @@ -6,7 +6,7 @@ module Admin def index authorize :audit_log, :index? - @auditable_accounts = Account.where(id: Admin::ActionLog.reorder(nil).select('distinct account_id')).select(:id, :username) + @auditable_accounts = Account.where(id: Admin::ActionLog.select('distinct account_id')).select(:id, :username) end private diff --git a/app/models/admin/action_log.rb b/app/models/admin/action_log.rb index f2c121d75..49ae67980 100644 --- a/app/models/admin/action_log.rb +++ b/app/models/admin/action_log.rb @@ -24,12 +24,12 @@ class Admin::ActionLog < ApplicationRecord belongs_to :account belongs_to :target, polymorphic: true, optional: true - default_scope -> { order('id desc') } - before_validation :set_human_identifier before_validation :set_route_param before_validation :set_permalink + scope :latest, -> { order(id: :desc) } + def action super.to_sym end diff --git a/app/models/admin/action_log_filter.rb b/app/models/admin/action_log_filter.rb index 011797462..d413cb386 100644 --- a/app/models/admin/action_log_filter.rb +++ b/app/models/admin/action_log_filter.rb @@ -72,7 +72,7 @@ class Admin::ActionLogFilter end def results - scope = Admin::ActionLog.includes(:target) + scope = latest_action_logs.includes(:target) params.each do |key, value| next if key.to_s == 'page' @@ -88,14 +88,18 @@ class Admin::ActionLogFilter def scope_for(key, value) case key when 'action_type' - Admin::ActionLog.where(ACTION_TYPE_MAP[value.to_sym]) + latest_action_logs.where(ACTION_TYPE_MAP[value.to_sym]) when 'account_id' - Admin::ActionLog.where(account_id: value) + latest_action_logs.where(account_id: value) when 'target_account_id' account = Account.find_or_initialize_by(id: value) - Admin::ActionLog.where(target: [account, account.user].compact) + latest_action_logs.where(target: [account, account.user].compact) else raise Mastodon::InvalidParameterError, "Unknown filter: #{key}" end end + + def latest_action_logs + Admin::ActionLog.latest + end end diff --git a/app/models/report.rb b/app/models/report.rb index 81ad721df..c565362cc 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -131,25 +131,25 @@ class Report < ApplicationRecord Admin::ActionLog.where( target_type: 'Report', target_id: id - ).unscope(:order).arel, + ).arel, Admin::ActionLog.where( target_type: 'Account', target_id: target_account_id - ).unscope(:order).arel, + ).arel, Admin::ActionLog.where( target_type: 'Status', target_id: status_ids - ).unscope(:order).arel, + ).arel, Admin::ActionLog.where( target_type: 'AccountWarning', target_id: AccountWarning.where(report_id: id).select(:id) - ).unscope(:order).arel, + ).arel, ].reduce { |union, query| Arel::Nodes::UnionAll.new(union, query) } - Admin::ActionLog.from(Arel::Nodes::As.new(subquery, Admin::ActionLog.arel_table)) + Admin::ActionLog.latest.from(Arel::Nodes::As.new(subquery, Admin::ActionLog.arel_table)) end private diff --git a/spec/fabricators/admin_action_log_fabricator.rb b/spec/fabricators/action_log_fabricator.rb similarity index 68% rename from spec/fabricators/admin_action_log_fabricator.rb rename to spec/fabricators/action_log_fabricator.rb index 3acedbffd..ce52cb73a 100644 --- a/spec/fabricators/admin_action_log_fabricator.rb +++ b/spec/fabricators/action_log_fabricator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -Fabricator('Admin::ActionLog') do +Fabricator(:action_log, from: Admin::ActionLog) do account { Fabricate.build(:account) } action 'MyString' target nil diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb index 0093dcd8d..c514c63b3 100644 --- a/spec/models/report_spec.rb +++ b/spec/models/report_spec.rb @@ -110,9 +110,9 @@ describe Report do let(:status) { Fabricate(:status) } before do - Fabricate('Admin::ActionLog', target_type: 'Report', account_id: target_account.id, target_id: report.id, created_at: 2.days.ago) - Fabricate('Admin::ActionLog', target_type: 'Account', account_id: target_account.id, target_id: report.target_account_id, created_at: 2.days.ago) - Fabricate('Admin::ActionLog', target_type: 'Status', account_id: target_account.id, target_id: status.id, created_at: 2.days.ago) + Fabricate(:action_log, target_type: 'Report', account_id: target_account.id, target_id: report.id, created_at: 2.days.ago) + Fabricate(:action_log, target_type: 'Account', account_id: target_account.id, target_id: report.target_account_id, created_at: 2.days.ago) + Fabricate(:action_log, target_type: 'Status', account_id: target_account.id, target_id: status.id, created_at: 2.days.ago) end it 'returns right logs' do From 6e55ff964325694c9a06914e59733babfc4854c7 Mon Sep 17 00:00:00 2001 From: Filippo Giunchedi Date: Thu, 30 Nov 2023 11:46:06 +0100 Subject: [PATCH 21/95] Change Vagrant to install Node 20 (#28100) Co-authored-by: Filippo Giunchedi --- Vagrantfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Vagrantfile b/Vagrantfile index e2c66a476..6f0f51109 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -10,7 +10,11 @@ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main' # Add repo for NodeJS -curl -sL https://deb.nodesource.com/setup_16.x | sudo bash - +sudo mkdir -p /etc/apt/keyrings +curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg +NODE_MAJOR=20 +echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list +sudo apt-get update # Add firewall rule to redirect 80 to PORT and save sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]} From b696ca6b1a0fee1e56fcf70f4c561ff2661ad81d Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 30 Nov 2023 05:48:46 -0500 Subject: [PATCH 22/95] Move self destruct CLI command definition code to module (#28131) --- lib/mastodon/cli/federation.rb | 74 ++++++++++++++++++++++++++++++++++ lib/mastodon/cli/main.rb | 62 +--------------------------- 2 files changed, 76 insertions(+), 60 deletions(-) create mode 100644 lib/mastodon/cli/federation.rb diff --git a/lib/mastodon/cli/federation.rb b/lib/mastodon/cli/federation.rb new file mode 100644 index 000000000..1b4cb467a --- /dev/null +++ b/lib/mastodon/cli/federation.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require 'tty-prompt' + +module Mastodon::CLI + module Federation + extend ActiveSupport::Concern + + included do + desc 'self-destruct', 'Erase the server from the federation' + long_desc <<~LONG_DESC + Erase the server from the federation by broadcasting account delete + activities to all known other servers. This allows a "clean exit" from + running a Mastodon server, as it leaves next to no cache behind on + other servers. + + This command is always interactive and requires confirmation twice. + + No local data is actually deleted, because emptying the + database or removing files is much faster through other, external + means, such as e.g. deleting the entire VPS. However, because other + servers will delete data about local users, but no local data will be + updated (such as e.g. followers), there will be a state mismatch + that will lead to glitches and issues if you then continue to run and use + the server. + + So either you know exactly what you are doing, or you are starting + from a blank slate afterwards by manually clearing out all the local + data! + LONG_DESC + def self_destruct + if SelfDestructHelper.self_destruct? + prompt.ok('Self-destruct mode is already enabled for this Mastodon server') + + pending_accounts = Account.local.without_suspended.count + Account.local.suspended.joins(:deletion_request).count + sidekiq_stats = Sidekiq::Stats.new + + if pending_accounts.positive? + prompt.warn("#{pending_accounts} accounts are still pending deletion.") + elsif sidekiq_stats.enqueued.positive? + prompt.warn('Deletion notices are still being processed') + elsif sidekiq_stats.retry_size.positive? + prompt.warn('At least one delivery attempt for each deletion notice has been made, but some have failed and are scheduled for retry') + else + prompt.ok('Every deletion notice has been sent! You can safely delete all data and decomission your servers!') + end + + exit(0) + end + + exit(1) unless prompt.ask('Type in the domain of the server to confirm:', required: true) == Rails.configuration.x.local_domain + + prompt.warn('This operation WILL NOT be reversible.') + prompt.warn('While the data won\'t be erased locally, the server will be in a BROKEN STATE afterwards.') + prompt.warn('The deletion process itself may take a long time, and will be handled by Sidekiq, so do not shut it down until it has finished (you will be able to re-run this command to see the state of the self-destruct process).') + + exit(1) if prompt.no?('Are you sure you want to proceed?') + + self_destruct_value = Rails.application.message_verifier('self-destruct').generate(Rails.configuration.x.local_domain) + prompt.ok('To switch Mastodon to self-destruct mode, add the following variable to your evironment (e.g. by adding a line to your `.env.production`) and restart all Mastodon processes:') + prompt.ok(" SELF_DESTRUCT=#{self_destruct_value}") + prompt.ok("\nYou can re-run this command to see the state of the self-destruct process.") + rescue TTY::Reader::InputInterrupt + exit(1) + end + + private + + def prompt + @prompt ||= TTY::Prompt.new + end + end + end +end diff --git a/lib/mastodon/cli/main.rb b/lib/mastodon/cli/main.rb index 64f1646f4..ef40b81f3 100644 --- a/lib/mastodon/cli/main.rb +++ b/lib/mastodon/cli/main.rb @@ -8,6 +8,7 @@ require_relative 'canonical_email_blocks' require_relative 'domains' require_relative 'email_domain_blocks' require_relative 'emoji' +require_relative 'federation' require_relative 'feeds' require_relative 'ip_blocks' require_relative 'maintenance' @@ -65,66 +66,7 @@ module Mastodon::CLI desc 'maintenance SUBCOMMAND ...ARGS', 'Various maintenance utilities' subcommand 'maintenance', Maintenance - desc 'self-destruct', 'Erase the server from the federation' - long_desc <<~LONG_DESC - Erase the server from the federation by broadcasting account delete - activities to all known other servers. This allows a "clean exit" from - running a Mastodon server, as it leaves next to no cache behind on - other servers. - - This command is always interactive and requires confirmation twice. - - No local data is actually deleted, because emptying the - database or removing files is much faster through other, external - means, such as e.g. deleting the entire VPS. However, because other - servers will delete data about local users, but no local data will be - updated (such as e.g. followers), there will be a state mismatch - that will lead to glitches and issues if you then continue to run and use - the server. - - So either you know exactly what you are doing, or you are starting - from a blank slate afterwards by manually clearing out all the local - data! - LONG_DESC - def self_destruct - require 'tty-prompt' - - prompt = TTY::Prompt.new - - if SelfDestructHelper.self_destruct? - prompt.ok('Self-destruct mode is already enabled for this Mastodon server') - - pending_accounts = Account.local.without_suspended.count + Account.local.suspended.joins(:deletion_request).count - sidekiq_stats = Sidekiq::Stats.new - - if pending_accounts.positive? - prompt.warn("#{pending_accounts} accounts are still pending deletion.") - elsif sidekiq_stats.enqueued.positive? - prompt.warn('Deletion notices are still being processed') - elsif sidekiq_stats.retry_size.positive? - prompt.warn('At least one delivery attempt for each deletion notice has been made, but some have failed and are scheduled for retry') - else - prompt.ok('Every deletion notice has been sent! You can safely delete all data and decomission your servers!') - end - - exit(0) - end - - exit(1) unless prompt.ask('Type in the domain of the server to confirm:', required: true) == Rails.configuration.x.local_domain - - prompt.warn('This operation WILL NOT be reversible.') - prompt.warn('While the data won\'t be erased locally, the server will be in a BROKEN STATE afterwards.') - prompt.warn('The deletion process itself may take a long time, and will be handled by Sidekiq, so do not shut it down until it has finished (you will be able to re-run this command to see the state of the self-destruct process).') - - exit(1) if prompt.no?('Are you sure you want to proceed?') - - self_destruct_value = Rails.application.message_verifier('self-destruct').generate(Rails.configuration.x.local_domain) - prompt.ok('To switch Mastodon to self-destruct mode, add the following variable to your evironment (e.g. by adding a line to your `.env.production`) and restart all Mastodon processes:') - prompt.ok(" SELF_DESTRUCT=#{self_destruct_value}") - prompt.ok("\nYou can re-run this command to see the state of the self-destruct process.") - rescue TTY::Reader::InputInterrupt - exit(1) - end + include Federation map %w(--version -v) => :version From c761cc4738c74519c65a99c5ab24874584605d2d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 11:53:49 +0100 Subject: [PATCH 23/95] fix(deps): update babel monorepo to v7.23.5 (#28122) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 260 +++++++++++++++++++++++++++--------------------------- 1 file changed, 130 insertions(+), 130 deletions(-) diff --git a/yarn.lock b/yarn.lock index 11c2724d5..69464dfa2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -42,55 +42,55 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13": - version: 7.22.13 - resolution: "@babel/code-frame@npm:7.22.13" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/code-frame@npm:7.23.5" dependencies: - "@babel/highlight": "npm:^7.22.13" + "@babel/highlight": "npm:^7.23.4" chalk: "npm:^2.4.2" - checksum: f4cc8ae1000265677daf4845083b72f88d00d311adb1a93c94eb4b07bf0ed6828a81ae4ac43ee7d476775000b93a28a9cddec18fbdc5796212d8dcccd5de72bd + checksum: a10e843595ddd9f97faa99917414813c06214f4d9205294013e20c70fbdf4f943760da37dec1d998bf3e6fc20fa2918a47c0e987a7e458663feb7698063ad7c6 languageName: node linkType: hard -"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9, @babel/compat-data@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/compat-data@npm:7.23.3" - checksum: c6af331753c34ee8a5678bc94404320826cb56b1dda3efc1311ec8fb0774e78225132f3c1acc988440ace667f14a838e297a822692b95758aa63da406e1f97a1 +"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9, @babel/compat-data@npm:^7.23.3, @babel/compat-data@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/compat-data@npm:7.23.5" + checksum: 081278ed46131a890ad566a59c61600a5f9557bd8ee5e535890c8548192532ea92590742fd74bd9db83d74c669ef8a04a7e1c85cdea27f960233e3b83c3a957c languageName: node linkType: hard "@babel/core@npm:^7.10.4, @babel/core@npm:^7.11.1, @babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.22.1": - version: 7.23.3 - resolution: "@babel/core@npm:7.23.3" + version: 7.23.5 + resolution: "@babel/core@npm:7.23.5" dependencies: "@ampproject/remapping": "npm:^2.2.0" - "@babel/code-frame": "npm:^7.22.13" - "@babel/generator": "npm:^7.23.3" + "@babel/code-frame": "npm:^7.23.5" + "@babel/generator": "npm:^7.23.5" "@babel/helper-compilation-targets": "npm:^7.22.15" "@babel/helper-module-transforms": "npm:^7.23.3" - "@babel/helpers": "npm:^7.23.2" - "@babel/parser": "npm:^7.23.3" + "@babel/helpers": "npm:^7.23.5" + "@babel/parser": "npm:^7.23.5" "@babel/template": "npm:^7.22.15" - "@babel/traverse": "npm:^7.23.3" - "@babel/types": "npm:^7.23.3" + "@babel/traverse": "npm:^7.23.5" + "@babel/types": "npm:^7.23.5" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 08d43b749e24052d12713a7fb1f0c0d1275d4fb056d00846faeb8da79ecf6d0ba91a11b6afec407b8b0f9388d00e2c2f485f282bef0ade4d6d0a17de191a4287 + checksum: 311a512a870ee330a3f9a7ea89e5df790b2b5af0b1bd98b10b4edc0de2ac440f0df4d69ea2c0ee38a4b89041b9a495802741d93603be7d4fd834ec8bb6970bd2 languageName: node linkType: hard -"@babel/generator@npm:^7.23.3, @babel/generator@npm:^7.7.2": - version: 7.23.3 - resolution: "@babel/generator@npm:7.23.3" +"@babel/generator@npm:^7.23.5, @babel/generator@npm:^7.7.2": + version: 7.23.5 + resolution: "@babel/generator@npm:7.23.5" dependencies: - "@babel/types": "npm:^7.23.3" + "@babel/types": "npm:^7.23.5" "@jridgewell/gen-mapping": "npm:^0.3.2" "@jridgewell/trace-mapping": "npm:^0.3.17" jsesc: "npm:^2.5.1" - checksum: d5fff1417eecfada040e01a7c77a4968e81c436aeb35815ce85b4e80cd01e731423613d61033044a6cb5563bb8449ee260e3379b63eb50b38ec0a9ea9c00abfd + checksum: 14c6e874f796c4368e919bed6003bb0adc3ce837760b08f9e646d20aeb5ae7d309723ce6e4f06bcb4a2b5753145446c8e4425851380f695e40e71e1760f49e7b languageName: node linkType: hard @@ -310,10 +310,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-string-parser@npm:7.22.5" - checksum: 6b0ff8af724377ec41e5587fffa7605198da74cb8e7d8d48a36826df0c0ba210eb9fedb3d9bef4d541156e0bd11040f021945a6cbb731ccec4aefb4affa17aa4 +"@babel/helper-string-parser@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/helper-string-parser@npm:7.23.4" + checksum: f348d5637ad70b6b54b026d6544bd9040f78d24e7ec245a0fc42293968181f6ae9879c22d89744730d246ce8ec53588f716f102addd4df8bbc79b73ea10004ac languageName: node linkType: hard @@ -324,10 +324,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/helper-validator-option@npm:7.22.15" - checksum: e9661bf80ba18e2dd978217b350fb07298e57ac417f4f1ab9fa011505e20e4857f2c3b4b538473516a9dc03af5ce3a831e5ed973311c28326f4c330b6be981c2 +"@babel/helper-validator-option@npm:^7.22.15, @babel/helper-validator-option@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/helper-validator-option@npm:7.23.5" + checksum: af45d5c0defb292ba6fd38979e8f13d7da63f9623d8ab9ededc394f67eb45857d2601278d151ae9affb6e03d5d608485806cd45af08b4468a0515cf506510e94 languageName: node linkType: hard @@ -342,34 +342,34 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.23.2": - version: 7.23.2 - resolution: "@babel/helpers@npm:7.23.2" +"@babel/helpers@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/helpers@npm:7.23.5" dependencies: "@babel/template": "npm:^7.22.15" - "@babel/traverse": "npm:^7.23.2" - "@babel/types": "npm:^7.23.0" - checksum: 3a6a939c5277a27486e7c626812f0643b35d1c053ac2eb66911f5ae6c0a4e4bcdd40750eba36b766b0ee8a753484287f50ae56232a5f8f2947116723e44b9e35 + "@babel/traverse": "npm:^7.23.5" + "@babel/types": "npm:^7.23.5" + checksum: a37e2728eb4378a4888e5d614e28de7dd79b55ac8acbecd0e5c761273e2a02a8f33b34b1932d9069db55417ace2937cbf8ec37c42f1030ce6d228857d7ccaa4f languageName: node linkType: hard -"@babel/highlight@npm:^7.22.13": - version: 7.22.20 - resolution: "@babel/highlight@npm:7.22.20" +"@babel/highlight@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/highlight@npm:7.23.4" dependencies: "@babel/helper-validator-identifier": "npm:^7.22.20" chalk: "npm:^2.4.2" js-tokens: "npm:^4.0.0" - checksum: f3c3a193afad23434297d88e81d1d6c0c2cf02423de2139ada7ce0a7fc62d8559abf4cc996533c1a9beca7fc990010eb8d544097f75e818ac113bf39ed810aa2 + checksum: fbff9fcb2f5539289c3c097d130e852afd10d89a3a08ac0b5ebebbc055cc84a4bcc3dcfed463d488cde12dd0902ef1858279e31d7349b2e8cee43913744bda33 languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/parser@npm:7.23.3" +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/parser@npm:7.23.5" bin: parser: ./bin/babel-parser.js - checksum: 0fe11eadd4146a9155305b5bfece0f8223a3b1b97357ffa163c0156940de92e76cd0e7a173de819b8692767147e62f33389b312d1537f84cede51092672df6ef + checksum: 3356aa90d7bafb4e2c7310e7c2c3d443c4be4db74913f088d3d577a1eb914ea4188e05fd50a47ce907a27b755c4400c4e3cbeee73dbeb37761f6ca85954f5a20 languageName: node linkType: hard @@ -661,9 +661,9 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-async-generator-functions@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-async-generator-functions@npm:7.23.3" +"@babel/plugin-transform-async-generator-functions@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-async-generator-functions@npm:7.23.4" dependencies: "@babel/helper-environment-visitor": "npm:^7.22.20" "@babel/helper-plugin-utils": "npm:^7.22.5" @@ -671,7 +671,7 @@ __metadata: "@babel/plugin-syntax-async-generators": "npm:^7.8.4" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e846f282658e097fce4fccf3ee29289bf05f0654846a5994727a36f0cdc2e47abdffd4be4fa65787e94aa975824fae894c90afbfdc8caacd46c12c7f43e99d7f + checksum: f2eef4de609975a3f7da7832576b5ffc93e43c80f87e1a99e886b0f8591096cfc4c37e2d5f52fdeaa2a9c09a25a59f3e621159abaca75d3193922a5c0e4cbe0c languageName: node linkType: hard @@ -699,14 +699,14 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-block-scoping@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-block-scoping@npm:7.23.3" +"@babel/plugin-transform-block-scoping@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-block-scoping@npm:7.23.4" dependencies: "@babel/helper-plugin-utils": "npm:^7.22.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: ccaeded7954c196811d22a35322579254cda52676e823682b6234885a3aaf88fe0d5152dacaec43db9031dcf35a050a5343e36028e5905b0ba9c02d36b30a57f + checksum: 83006804dddf980ab1bcd6d67bc381e24b58c776507c34f990468f820d0da71dba3697355ca4856532fa2eeb2a1e3e73c780f03760b5507a511cbedb0308e276 languageName: node linkType: hard @@ -722,22 +722,22 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-class-static-block@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-class-static-block@npm:7.23.3" +"@babel/plugin-transform-class-static-block@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-class-static-block@npm:7.23.4" dependencies: "@babel/helper-create-class-features-plugin": "npm:^7.22.15" "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" peerDependencies: "@babel/core": ^7.12.0 - checksum: 89cdb66d7bc834cd51659eb7286a6bee23add0bc114943d68c4b6c0c834178cf0d55183df0cf508fec9c55ed4155641360e6f55a91c16fe826ccaf1adf381922 + checksum: fdca96640ef29d8641a7f8de106f65f18871b38cc01c0f7b696d2b49c76b77816b30a812c08e759d06dd10b4d9b3af6b5e4ac22a2017a88c4077972224b77ab0 languageName: node linkType: hard -"@babel/plugin-transform-classes@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-classes@npm:7.23.3" +"@babel/plugin-transform-classes@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/plugin-transform-classes@npm:7.23.5" dependencies: "@babel/helper-annotate-as-pure": "npm:^7.22.5" "@babel/helper-compilation-targets": "npm:^7.22.15" @@ -750,7 +750,7 @@ __metadata: globals: "npm:^11.1.0" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 88bfd332db0ba5cbfb8557a2ba5a7185151aebc9cfe3035b014aa6d795556acbe672bb8c78da3c9fd1d23f55a333d14b5daa127ef037f5ced5198b6d79a146d6 + checksum: 07988f52b4893151887d1ea6ff79e5fe834078c5731bd09babd5659edbbae21ea4e2de326a02443a63fd776b4c945da6177f07875b56fe66e0b7899e830a9e92 languageName: node linkType: hard @@ -800,15 +800,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-dynamic-import@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-dynamic-import@npm:7.23.3" +"@babel/plugin-transform-dynamic-import@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-dynamic-import@npm:7.23.4" dependencies: "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: df3fd130312dc53d068fa76333991dce5e86987b023af8c3b502bd7d36a8e67da6f718e61dc838576a9fbacd06628e29607ee22d9bae30705485c14130eab201 + checksum: 19ae4a4a2ca86d35224734c41c48b2aa6a13139f3cfa1cbd18c0e65e461de8b65687dec7e52b7a72bb49db04465394c776aa1b13a2af5dc975b2a0cde3dcab67 languageName: node linkType: hard @@ -824,15 +824,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-export-namespace-from@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-export-namespace-from@npm:7.23.3" +"@babel/plugin-transform-export-namespace-from@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-export-namespace-from@npm:7.23.4" dependencies: "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 390c6626dcda99023629049d92090242b4575351a4a7b47f97febabd2381f2cd0f624de661d8de8d1f715fedd63753cfd1feddead19e5960c27b88e447465b81 + checksum: 38bf04f851e36240bbe83ace4169da626524f4107bfb91f05b4ad93a5fb6a36d5b3d30b8883c1ba575ccfc1bac7938e90ca2e3cb227f7b3f4a9424beec6fd4a7 languageName: node linkType: hard @@ -860,15 +860,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-json-strings@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-json-strings@npm:7.23.3" +"@babel/plugin-transform-json-strings@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-json-strings@npm:7.23.4" dependencies: "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-json-strings": "npm:^7.8.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: e1cef6a485b9da32aba9449fb459dac062dfc401f3d6ad48e7fbdcb73bbe470c995cc15ce5c421b95efe1e9a90d5507eb606360fe10b6d8cb869dd5dae7a2562 + checksum: 39e82223992a9ad857722ae051291935403852ad24b0dd64c645ca1c10517b6bf9822377d88643fed8b3e61a4e3f7e5ae41cf90eb07c40a786505d47d5970e54 languageName: node linkType: hard @@ -883,15 +883,15 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-logical-assignment-operators@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.23.3" +"@babel/plugin-transform-logical-assignment-operators@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.23.4" dependencies: "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 23b7588b26d420c8b132bd08916d49871ca0c8db892f6b58637b10e2a0d918163d413c505db880a9157fc2e61d089040f139298a60d837ccbd0efca0474ac7ca + checksum: 87b034dd13143904e405887e6125d76c27902563486efc66b7d9a9d8f9406b76c6ac42d7b37224014af5783d7edb465db0cdecd659fa3227baad0b3a6a35deff languageName: node linkType: hard @@ -980,7 +980,7 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.3, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.23.3": +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.22.3, @babel/plugin-transform-nullish-coalescing-operator@npm:^7.23.4": version: 7.23.4 resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.23.4" dependencies: @@ -992,21 +992,21 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-numeric-separator@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-numeric-separator@npm:7.23.3" +"@babel/plugin-transform-numeric-separator@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-numeric-separator@npm:7.23.4" dependencies: "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: d3748cce20e8752e61dfda55e275c699459a3ff8d0bb46585da813136e04066b1ce70b71beef504fcdc8d4cca3c955112cea96d5e9fd5a42a5bc8956d05236c2 + checksum: e34902da4f5588dc4812c92cb1f6a5e3e3647baf7b4623e30942f551bf1297621abec4e322ebfa50b320c987c0f34d9eb4355b3d289961d9035e2126e3119c12 languageName: node linkType: hard -"@babel/plugin-transform-object-rest-spread@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-object-rest-spread@npm:7.23.3" +"@babel/plugin-transform-object-rest-spread@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.23.4" dependencies: "@babel/compat-data": "npm:^7.23.3" "@babel/helper-compilation-targets": "npm:^7.22.15" @@ -1015,7 +1015,7 @@ __metadata: "@babel/plugin-transform-parameters": "npm:^7.23.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 31ab631aaba945c118662943e5f1f54a21f07d64f06e06b25d55871168c460f3eeeccdf7b05aa74a1340e2cfbe781ad3c7ceccd0c2585d39f7b73ba11ebaa9d0 + checksum: b56017992ffe7fcd1dd9a9da67c39995a141820316266bcf7d77dc912980d228ccbd3f36191d234f5cc389b09157b5d2a955e33e8fb368319534affd1c72b262 languageName: node linkType: hard @@ -1031,28 +1031,28 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-optional-catch-binding@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.23.3" +"@babel/plugin-transform-optional-catch-binding@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.23.4" dependencies: "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 85ac1e94ee8f21648816151628ff931cc16143ec8c904649a1ecfd8960160290eccc5a197b4ae3ee7a1c7a27a7c4189e61b4de24483d5bad4040784afe2d206f + checksum: 4ef61812af0e4928485e28301226ce61139a8b8cea9e9a919215ebec4891b9fea2eb7a83dc3090e2679b7d7b2c8653da601fbc297d2addc54a908b315173991e languageName: node linkType: hard -"@babel/plugin-transform-optional-chaining@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-optional-chaining@npm:7.23.3" +"@babel/plugin-transform-optional-chaining@npm:^7.23.3, @babel/plugin-transform-optional-chaining@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.23.4" dependencies: "@babel/helper-plugin-utils": "npm:^7.22.5" "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.22.5" "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 2b358962169d871392aa292a67527e5335909438da0ddbb0d19e7838c0f8a2081cc751a49e6e534ac4d6c932254531a205ac22b197f64fc4c89f41bf9f595497 + checksum: 305b773c29ad61255b0e83ec1e92b2f7af6aa58be4cba1e3852bddaa14f7d2afd7b4438f41c28b179d6faac7eb8d4fb5530a17920294f25d459b8f84406bfbfb languageName: node linkType: hard @@ -1079,9 +1079,9 @@ __metadata: languageName: node linkType: hard -"@babel/plugin-transform-private-property-in-object@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/plugin-transform-private-property-in-object@npm:7.23.3" +"@babel/plugin-transform-private-property-in-object@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/plugin-transform-private-property-in-object@npm:7.23.4" dependencies: "@babel/helper-annotate-as-pure": "npm:^7.22.5" "@babel/helper-create-class-features-plugin": "npm:^7.22.15" @@ -1089,7 +1089,7 @@ __metadata: "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 9211dd25a6e87a01535f2d97a663fa6de3472b963c8dcfaacce229a2e3fa6500f2e9fc690bc100a540fc7b66c8364faf7ef19b32e9c9b9791e4561b742c15ed3 + checksum: 8d31b28f24204b4d13514cd3a8f3033abf575b1a6039759ddd6e1d82dd33ba7281f9bc85c9f38072a665d69bfa26dc40737eefaf9d397b024654a483d2357bf5 languageName: node linkType: hard @@ -1333,13 +1333,13 @@ __metadata: linkType: hard "@babel/preset-env@npm:^7.11.0, @babel/preset-env@npm:^7.12.1, @babel/preset-env@npm:^7.22.4": - version: 7.23.3 - resolution: "@babel/preset-env@npm:7.23.3" + version: 7.23.5 + resolution: "@babel/preset-env@npm:7.23.5" dependencies: - "@babel/compat-data": "npm:^7.23.3" + "@babel/compat-data": "npm:^7.23.5" "@babel/helper-compilation-targets": "npm:^7.22.15" "@babel/helper-plugin-utils": "npm:^7.22.5" - "@babel/helper-validator-option": "npm:^7.22.15" + "@babel/helper-validator-option": "npm:^7.23.5" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.23.3" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.23.3" "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "npm:^7.23.3" @@ -1363,25 +1363,25 @@ __metadata: "@babel/plugin-syntax-top-level-await": "npm:^7.14.5" "@babel/plugin-syntax-unicode-sets-regex": "npm:^7.18.6" "@babel/plugin-transform-arrow-functions": "npm:^7.23.3" - "@babel/plugin-transform-async-generator-functions": "npm:^7.23.3" + "@babel/plugin-transform-async-generator-functions": "npm:^7.23.4" "@babel/plugin-transform-async-to-generator": "npm:^7.23.3" "@babel/plugin-transform-block-scoped-functions": "npm:^7.23.3" - "@babel/plugin-transform-block-scoping": "npm:^7.23.3" + "@babel/plugin-transform-block-scoping": "npm:^7.23.4" "@babel/plugin-transform-class-properties": "npm:^7.23.3" - "@babel/plugin-transform-class-static-block": "npm:^7.23.3" - "@babel/plugin-transform-classes": "npm:^7.23.3" + "@babel/plugin-transform-class-static-block": "npm:^7.23.4" + "@babel/plugin-transform-classes": "npm:^7.23.5" "@babel/plugin-transform-computed-properties": "npm:^7.23.3" "@babel/plugin-transform-destructuring": "npm:^7.23.3" "@babel/plugin-transform-dotall-regex": "npm:^7.23.3" "@babel/plugin-transform-duplicate-keys": "npm:^7.23.3" - "@babel/plugin-transform-dynamic-import": "npm:^7.23.3" + "@babel/plugin-transform-dynamic-import": "npm:^7.23.4" "@babel/plugin-transform-exponentiation-operator": "npm:^7.23.3" - "@babel/plugin-transform-export-namespace-from": "npm:^7.23.3" + "@babel/plugin-transform-export-namespace-from": "npm:^7.23.4" "@babel/plugin-transform-for-of": "npm:^7.23.3" "@babel/plugin-transform-function-name": "npm:^7.23.3" - "@babel/plugin-transform-json-strings": "npm:^7.23.3" + "@babel/plugin-transform-json-strings": "npm:^7.23.4" "@babel/plugin-transform-literals": "npm:^7.23.3" - "@babel/plugin-transform-logical-assignment-operators": "npm:^7.23.3" + "@babel/plugin-transform-logical-assignment-operators": "npm:^7.23.4" "@babel/plugin-transform-member-expression-literals": "npm:^7.23.3" "@babel/plugin-transform-modules-amd": "npm:^7.23.3" "@babel/plugin-transform-modules-commonjs": "npm:^7.23.3" @@ -1389,15 +1389,15 @@ __metadata: "@babel/plugin-transform-modules-umd": "npm:^7.23.3" "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.22.5" "@babel/plugin-transform-new-target": "npm:^7.23.3" - "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.23.3" - "@babel/plugin-transform-numeric-separator": "npm:^7.23.3" - "@babel/plugin-transform-object-rest-spread": "npm:^7.23.3" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.23.4" + "@babel/plugin-transform-numeric-separator": "npm:^7.23.4" + "@babel/plugin-transform-object-rest-spread": "npm:^7.23.4" "@babel/plugin-transform-object-super": "npm:^7.23.3" - "@babel/plugin-transform-optional-catch-binding": "npm:^7.23.3" - "@babel/plugin-transform-optional-chaining": "npm:^7.23.3" + "@babel/plugin-transform-optional-catch-binding": "npm:^7.23.4" + "@babel/plugin-transform-optional-chaining": "npm:^7.23.4" "@babel/plugin-transform-parameters": "npm:^7.23.3" "@babel/plugin-transform-private-methods": "npm:^7.23.3" - "@babel/plugin-transform-private-property-in-object": "npm:^7.23.3" + "@babel/plugin-transform-private-property-in-object": "npm:^7.23.4" "@babel/plugin-transform-property-literals": "npm:^7.23.3" "@babel/plugin-transform-regenerator": "npm:^7.23.3" "@babel/plugin-transform-reserved-words": "npm:^7.23.3" @@ -1418,7 +1418,7 @@ __metadata: semver: "npm:^6.3.1" peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 36b02a86817ab5474bb74a8d62a110723b0b05904a52ddc5627cf89457525b8d5ac0739b8e435a6ae12ef8b90cd5fc191169898c3dc2ac9d2c84026b02f2580a + checksum: 2a0e1274dec045186e131c6433659b75492583290e8d41633c616f6bff829cb2e4b2f9a57f556283a54db3bd6aa697911e56a36f607911a29b731c445a5b5a06 languageName: node linkType: hard @@ -1502,32 +1502,32 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:7, @babel/traverse@npm:^7.23.2, @babel/traverse@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/traverse@npm:7.23.3" +"@babel/traverse@npm:7, @babel/traverse@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/traverse@npm:7.23.5" dependencies: - "@babel/code-frame": "npm:^7.22.13" - "@babel/generator": "npm:^7.23.3" + "@babel/code-frame": "npm:^7.23.5" + "@babel/generator": "npm:^7.23.5" "@babel/helper-environment-visitor": "npm:^7.22.20" "@babel/helper-function-name": "npm:^7.23.0" "@babel/helper-hoist-variables": "npm:^7.22.5" "@babel/helper-split-export-declaration": "npm:^7.22.6" - "@babel/parser": "npm:^7.23.3" - "@babel/types": "npm:^7.23.3" + "@babel/parser": "npm:^7.23.5" + "@babel/types": "npm:^7.23.5" debug: "npm:^4.1.0" globals: "npm:^11.1.0" - checksum: 3c2784f4765185126d64fd5eebce0413b7aee6d54f779998594a343a7f973a9693a441ba27533df84e7ab7ce22f1239c6837f35e903132a1b25f7fc7a67bc30f + checksum: c5ea793080ca6719b0a1612198fd25e361cee1f3c14142d7a518d2a1eeb5c1d21f7eec1b26c20ea6e1ddd8ed12ab50b960ff95ffd25be353b6b46e1b54d6f825 languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.3, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.23.3 - resolution: "@babel/types@npm:7.23.3" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.0.0-beta.49, @babel/types@npm:^7.12.11, @babel/types@npm:^7.12.6, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.23.5 + resolution: "@babel/types@npm:7.23.5" dependencies: - "@babel/helper-string-parser": "npm:^7.22.5" + "@babel/helper-string-parser": "npm:^7.23.4" "@babel/helper-validator-identifier": "npm:^7.22.20" to-fast-properties: "npm:^2.0.0" - checksum: 371a10dd9c8d8ebf48fc5d9e1b327dafd74453f8ea582dcbddd1cee5ae34e8881b743e783a86c08c04dcd1849b1842455472a911ae8a1c185484fe9b7b5f1595 + checksum: 7dd5e2f59828ed046ad0b06b039df2524a8b728d204affb4fc08da2502b9dd3140b1356b5166515d229dc811539a8b70dcd4bc507e06d62a89f4091a38d0b0fb languageName: node linkType: hard From e6fd9a59e6f7a37fcd1276c75aadfddd79c784ca Mon Sep 17 00:00:00 2001 From: Filippo Giunchedi Date: Thu, 30 Nov 2023 11:58:40 +0100 Subject: [PATCH 24/95] Fix FamiliarFollowersController test response comparison (#28121) Co-authored-by: Filippo Giunchedi --- .../api/v1/accounts/familiar_followers_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb b/spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb index 226177309..3c7c7e8b8 100644 --- a/spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb @@ -28,7 +28,7 @@ describe Api::V1::Accounts::FamiliarFollowersController do account_ids = [account_a, account_b, account_b, account_a, account_a].map { |a| a.id.to_s } get :index, params: { id: account_ids } - expect(body_as_json.pluck(:id)).to eq [account_a.id.to_s, account_b.id.to_s] + expect(body_as_json.pluck(:id)).to contain_exactly(account_a.id.to_s, account_b.id.to_s) end end end From ce78a9c9ac3e579d9de1c410d005860a80774a3c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 30 Nov 2023 06:44:42 -0500 Subject: [PATCH 25/95] Clean up `two_factor_authentication/confirmations` controller spec (#28128) --- .../confirmations_controller_spec.rb | 126 +++++++++--------- 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb index a5a35e91d..1b3b0cb0a 100644 --- a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb @@ -20,37 +20,30 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do [true, false].each do |with_otp_secret| let(:user) { Fabricate(:user, email: 'local-part@domain', otp_secret: with_otp_secret ? 'oldotpsecret' : nil) } - describe 'GET #new' do - context 'when signed in and a new otp secret has been set in the session' do - subject do - sign_in user, scope: :user - get :new, session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' } + context 'when signed in' do + before { sign_in user, scope: :user } + + describe 'GET #new' do + context 'when a new otp secret has been set in the session' do + subject do + get :new, session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' } + end + + include_examples 'renders :new' end - include_examples 'renders :new' - end + it 'redirects if a new otp_secret has not been set in the session' do + get :new, session: { challenge_passed_at: Time.now.utc } - it 'redirects if not signed in' do - get :new - expect(response).to redirect_to('/auth/sign_in') - end - - it 'redirects if a new otp_secret has not been set in the session' do - sign_in user, scope: :user - get :new, session: { challenge_passed_at: Time.now.utc } - expect(response).to redirect_to('/settings/otp_authentication') - end - end - - describe 'POST #create' do - context 'when signed in' do - before do - sign_in user, scope: :user + expect(response).to redirect_to('/settings/otp_authentication') end + end + describe 'POST #create' do describe 'when form_two_factor_confirmation parameter is not provided' do it 'raises ActionController::ParameterMissing' do post :create, params: {}, session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' } + expect(response).to have_http_status(400) end end @@ -58,69 +51,78 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do describe 'when creation succeeds' do let!(:otp_backup_codes) { user.generate_otp_backup_codes! } - it 'renders page with success' do + before do prepare_user_otp_generation - prepare_user_otp_consumption + prepare_user_otp_consumption_response(true) allow(controller).to receive(:current_user).and_return(user) + end - expect do - post :create, - params: { form_two_factor_confirmation: { otp_attempt: '123456' } }, - session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' } - end.to change { user.reload.otp_secret }.to 'thisisasecretforthespecofnewview' + it 'renders page with success' do + expect { post_create_with_options } + .to change { user.reload.otp_secret }.to 'thisisasecretforthespecofnewview' expect(assigns(:recovery_codes)).to eq otp_backup_codes expect(flash[:notice]).to eq 'Two-factor authentication successfully enabled' expect(response).to have_http_status(200) expect(response).to render_template('settings/two_factor_authentication/recovery_codes/index') end - - def prepare_user_otp_generation - allow(user) - .to receive(:generate_otp_backup_codes!) - .and_return(otp_backup_codes) - end - - def prepare_user_otp_consumption - options = { otp_secret: 'thisisasecretforthespecofnewview' } - allow(user) - .to receive(:validate_and_consume_otp!) - .with('123456', options) - .and_return(true) - end end describe 'when creation fails' do subject do - options = { otp_secret: 'thisisasecretforthespecofnewview' } - allow(user) - .to receive(:validate_and_consume_otp!) - .with('123456', options) - .and_return(false) - allow(controller).to receive(:current_user).and_return(user) - - expect do - post :create, - params: { form_two_factor_confirmation: { otp_attempt: '123456' } }, - session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' } - end.to(not_change { user.reload.otp_secret }) + expect { post_create_with_options } + .to(not_change { user.reload.otp_secret }) end - it 'renders the new view' do + before do + prepare_user_otp_consumption_response(false) + allow(controller).to receive(:current_user).and_return(user) + end + + it 'renders page with error message' do subject expect(response.body).to include 'The entered code was invalid! Are server time and device time correct?' end include_examples 'renders :new' end - end - context 'when not signed in' do - it 'redirects if not signed in' do - post :create, params: { form_two_factor_confirmation: { otp_attempt: '123456' } } - expect(response).to redirect_to('/auth/sign_in') + private + + def post_create_with_options + post :create, + params: { form_two_factor_confirmation: { otp_attempt: '123456' } }, + session: { challenge_passed_at: Time.now.utc, new_otp_secret: 'thisisasecretforthespecofnewview' } + end + + def prepare_user_otp_generation + allow(user) + .to receive(:generate_otp_backup_codes!) + .and_return(otp_backup_codes) + end + + def prepare_user_otp_consumption_response(result) + options = { otp_secret: 'thisisasecretforthespecofnewview' } + allow(user) + .to receive(:validate_and_consume_otp!) + .with('123456', options) + .and_return(result) end end end end + + context 'when not signed in' do + it 'redirects on POST to create' do + post :create, params: { form_two_factor_confirmation: { otp_attempt: '123456' } } + + expect(response).to redirect_to('/auth/sign_in') + end + + it 'redirects on GET to new' do + get :new + + expect(response).to redirect_to('/auth/sign_in') + end + end end From 7faa27e17de515b8718075d89a00942c06c0ef48 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 30 Nov 2023 12:45:54 +0100 Subject: [PATCH 26/95] Change dismissed banners to be stored server-side (#27055) --- .../components/dismissable_banner.tsx | 25 ++++++++++++++++--- app/javascript/mastodon/reducers/settings.js | 9 +++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/components/dismissable_banner.tsx b/app/javascript/mastodon/components/dismissable_banner.tsx index 4feb74a3a..4e6d3bb9a 100644 --- a/app/javascript/mastodon/components/dismissable_banner.tsx +++ b/app/javascript/mastodon/components/dismissable_banner.tsx @@ -1,11 +1,18 @@ +/* eslint-disable @typescript-eslint/no-unsafe-call, + @typescript-eslint/no-unsafe-return, + @typescript-eslint/no-unsafe-assignment, + @typescript-eslint/no-unsafe-member-access + -- the settings store is not yet typed */ import type { PropsWithChildren } from 'react'; -import { useCallback, useState } from 'react'; +import { useCallback, useState, useEffect } from 'react'; import { defineMessages, useIntl } from 'react-intl'; import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; +import { changeSetting } from 'mastodon/actions/settings'; import { bannerSettings } from 'mastodon/settings'; +import { useAppSelector, useAppDispatch } from 'mastodon/store'; import { IconButton } from './icon_button'; @@ -21,13 +28,25 @@ export const DismissableBanner: React.FC> = ({ id, children, }) => { - const [visible, setVisible] = useState(!bannerSettings.get(id)); + const dismissed = useAppSelector((state) => + state.settings.getIn(['dismissed_banners', id], false), + ); + const dispatch = useAppDispatch(); + + const [visible, setVisible] = useState(!bannerSettings.get(id) && !dismissed); const intl = useIntl(); const handleDismiss = useCallback(() => { setVisible(false); bannerSettings.set(id, true); - }, [id]); + dispatch(changeSetting(['dismissed_banners', id], true)); + }, [id, dispatch]); + + useEffect(() => { + if (!visible && !dismissed) { + dispatch(changeSetting(['dismissed_banners', id], true)); + } + }, [id, dispatch, visible, dismissed]); if (!visible) { return null; diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js index 07d1bda0f..a605ecbb8 100644 --- a/app/javascript/mastodon/reducers/settings.js +++ b/app/javascript/mastodon/reducers/settings.js @@ -100,6 +100,15 @@ const initialState = ImmutableMap({ body: '', }), }), + + dismissed_banners: ImmutableMap({ + 'public_timeline': false, + 'community_timeline': false, + 'home.explore_prompt': false, + 'explore/links': false, + 'explore/statuses': false, + 'explore/tags': false, + }), }); const defaultColumns = fromJS([ From 0212e0cfbfe4b50b5286165ec9887523f82513d1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:22:25 +0100 Subject: [PATCH 27/95] fix(deps): update dependency @babel/runtime to v7.23.5 (#28136) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 69464dfa2..580407f34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1483,11 +1483,11 @@ __metadata: linkType: hard "@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.3, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": - version: 7.23.4 - resolution: "@babel/runtime@npm:7.23.4" + version: 7.23.5 + resolution: "@babel/runtime@npm:7.23.5" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: db2bf183cd0119599b903ca51ca0aeea8e0ab478a16be1aae10dd90473ed614159d3e5adfdd8f8f3d840402428ce0d90b5c01aae95da9e45a2dd83e02d85ca27 + checksum: ca679cc91bb7e424bc2db87bb58cc3b06ade916b9adb21fbbdc43e54cdaacb3eea201ceba2a0464b11d2eb65b9fe6a6ffcf4d7521fa52994f19be96f1af14788 languageName: node linkType: hard From 02a98210e891dff32b1cb958f799213ccfbbc29c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:22:40 +0100 Subject: [PATCH 28/95] fix(deps): update dependency jsdom to v23.0.1 (#28135) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 580407f34..ad792d26c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10640,8 +10640,8 @@ __metadata: linkType: hard "jsdom@npm:^23.0.0": - version: 23.0.0 - resolution: "jsdom@npm:23.0.0" + version: 23.0.1 + resolution: "jsdom@npm:23.0.1" dependencies: cssstyle: "npm:^3.0.0" data-urls: "npm:^5.0.0" @@ -10665,11 +10665,11 @@ __metadata: ws: "npm:^8.14.2" xml-name-validator: "npm:^5.0.0" peerDependencies: - canvas: ^3.0.0 + canvas: ^2.11.2 peerDependenciesMeta: canvas: optional: true - checksum: 2c876a02de49e0ed6b667a4eb9b08b8e76ac189a5571ff97791cc9564e713259314deea6d657cc7f59fc30af41b900e7d833c95017e576dfcaf25f32565722af + checksum: 13b2b3693ccb40215d1cce77bac7a295414ee4c0a06e30167f8087c9867145ba23dbd592bd95a801cadd7b3698bfd20b9c3f2c26fd8422607f22609ed2e404ef languageName: node linkType: hard From bb0efe16e62636895079b3a968e444cd6e80b467 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 30 Nov 2023 08:30:35 -0500 Subject: [PATCH 29/95] Remove `default_scope` from `MediaAttachment` class (#28043) --- app/controllers/accounts_controller.rb | 2 +- app/lib/account_statuses_filter.rb | 2 +- app/lib/vacuum/media_attachments_vacuum.rb | 4 ++-- app/models/admin/status_filter.rb | 2 +- app/models/media_attachment.rb | 9 ++++----- app/services/backup_service.rb | 2 +- app/services/clear_domain_media_service.rb | 2 +- app/services/delete_account_service.rb | 2 +- app/services/suspend_account_service.rb | 2 +- app/services/unsuspend_account_service.rb | 2 +- .../activitypub/process_status_update_service_spec.rb | 2 +- 11 files changed, 15 insertions(+), 16 deletions(-) diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 850bf881f..4e475fe78 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -50,7 +50,7 @@ class AccountsController < ApplicationController end def only_media_scope - Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id) + Status.joins(:media_attachments).merge(@account.media_attachments).group(:id) end def no_replies_scope diff --git a/app/lib/account_statuses_filter.rb b/app/lib/account_statuses_filter.rb index b34ebb477..d1365de58 100644 --- a/app/lib/account_statuses_filter.rb +++ b/app/lib/account_statuses_filter.rb @@ -70,7 +70,7 @@ class AccountStatusesFilter end def only_media_scope - Status.joins(:media_attachments).merge(account.media_attachments.reorder(nil)).group(Status.arel_table[:id]) + Status.joins(:media_attachments).merge(account.media_attachments).group(Status.arel_table[:id]) end def no_replies_scope diff --git a/app/lib/vacuum/media_attachments_vacuum.rb b/app/lib/vacuum/media_attachments_vacuum.rb index 7b21c84bb..ab7ea4092 100644 --- a/app/lib/vacuum/media_attachments_vacuum.rb +++ b/app/lib/vacuum/media_attachments_vacuum.rb @@ -27,11 +27,11 @@ class Vacuum::MediaAttachmentsVacuum end def media_attachments_past_retention_period - MediaAttachment.unscoped.remote.cached.where(MediaAttachment.arel_table[:created_at].lt(@retention_period.ago)).where(MediaAttachment.arel_table[:updated_at].lt(@retention_period.ago)) + MediaAttachment.remote.cached.where(MediaAttachment.arel_table[:created_at].lt(@retention_period.ago)).where(MediaAttachment.arel_table[:updated_at].lt(@retention_period.ago)) end def orphaned_media_attachments - MediaAttachment.unscoped.unattached.where(MediaAttachment.arel_table[:created_at].lt(TTL.ago)) + MediaAttachment.unattached.where(MediaAttachment.arel_table[:created_at].lt(TTL.ago)) end def retention_period? diff --git a/app/models/admin/status_filter.rb b/app/models/admin/status_filter.rb index 645c2e620..4708785e7 100644 --- a/app/models/admin/status_filter.rb +++ b/app/models/admin/status_filter.rb @@ -32,7 +32,7 @@ class Admin::StatusFilter def scope_for(key, _value) case key.to_s when 'media' - Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id).reorder('statuses.id desc') + Status.joins(:media_attachments).merge(@account.media_attachments).group(:id).reorder('statuses.id desc') else raise Mastodon::InvalidParameterError, "Unknown filter: #{key}" end diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index b567003fb..1f40e5725 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -205,12 +205,11 @@ class MediaAttachment < ApplicationRecord validates :thumbnail, absence: true, if: -> { local? && !audio_or_video? } scope :attached, -> { where.not(status_id: nil).or(where.not(scheduled_status_id: nil)) } - scope :unattached, -> { where(status_id: nil, scheduled_status_id: nil) } - scope :local, -> { where(remote_url: '') } - scope :remote, -> { where.not(remote_url: '') } scope :cached, -> { remote.where.not(file_file_name: nil) } - - default_scope { order(id: :asc) } + scope :local, -> { where(remote_url: '') } + scope :ordered, -> { order(id: :asc) } + scope :remote, -> { where.not(remote_url: '') } + scope :unattached, -> { where(status_id: nil, scheduled_status_id: nil) } attr_accessor :skip_download diff --git a/app/services/backup_service.rb b/app/services/backup_service.rb index 3ef0366c3..886bab1eb 100644 --- a/app/services/backup_service.rb +++ b/app/services/backup_service.rb @@ -72,7 +72,7 @@ class BackupService < BaseService end def dump_media_attachments!(zipfile) - MediaAttachment.attached.where(account: account).reorder(nil).find_in_batches do |media_attachments| + MediaAttachment.attached.where(account: account).find_in_batches do |media_attachments| media_attachments.each do |m| path = m.file&.path next unless path diff --git a/app/services/clear_domain_media_service.rb b/app/services/clear_domain_media_service.rb index 7bf2d62fb..d3ad43e70 100644 --- a/app/services/clear_domain_media_service.rb +++ b/app/services/clear_domain_media_service.rb @@ -43,7 +43,7 @@ class ClearDomainMediaService < BaseService end def media_from_blocked_domain - MediaAttachment.joins(:account).merge(blocked_domain_accounts).reorder(nil) + MediaAttachment.joins(:account).merge(blocked_domain_accounts) end def emojis_from_blocked_domains diff --git a/app/services/delete_account_service.rb b/app/services/delete_account_service.rb index 190a72e5c..7c7cb97df 100644 --- a/app/services/delete_account_service.rb +++ b/app/services/delete_account_service.rb @@ -165,7 +165,7 @@ class DeleteAccountService < BaseService end def purge_media_attachments! - @account.media_attachments.reorder(nil).find_each do |media_attachment| + @account.media_attachments.find_each do |media_attachment| next if keep_account_record? && reported_status_ids.include?(media_attachment.status_id) media_attachment.destroy diff --git a/app/services/suspend_account_service.rb b/app/services/suspend_account_service.rb index e79c2d3d8..8d5446f1a 100644 --- a/app/services/suspend_account_service.rb +++ b/app/services/suspend_account_service.rb @@ -65,7 +65,7 @@ class SuspendAccountService < BaseService def privatize_media_attachments! attachment_names = MediaAttachment.attachment_definitions.keys - @account.media_attachments.reorder(nil).find_each do |media_attachment| + @account.media_attachments.find_each do |media_attachment| attachment_names.each do |attachment_name| attachment = media_attachment.public_send(attachment_name) styles = MediaAttachment::DEFAULT_STYLES | attachment.styles.keys diff --git a/app/services/unsuspend_account_service.rb b/app/services/unsuspend_account_service.rb index 93cd04a94..652dd6a84 100644 --- a/app/services/unsuspend_account_service.rb +++ b/app/services/unsuspend_account_service.rb @@ -61,7 +61,7 @@ class UnsuspendAccountService < BaseService def publish_media_attachments! attachment_names = MediaAttachment.attachment_definitions.keys - @account.media_attachments.reorder(nil).find_each do |media_attachment| + @account.media_attachments.find_each do |media_attachment| attachment_names.each do |attachment_name| attachment = media_attachment.public_send(attachment_name) styles = MediaAttachment::DEFAULT_STYLES | attachment.styles.keys diff --git a/spec/services/activitypub/process_status_update_service_spec.rb b/spec/services/activitypub/process_status_update_service_spec.rb index 9d91f31cc..53cbaf4cc 100644 --- a/spec/services/activitypub/process_status_update_service_spec.rb +++ b/spec/services/activitypub/process_status_update_service_spec.rb @@ -384,7 +384,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do end it 'updates the existing media attachment in-place' do - media_attachment = status.media_attachments.reload.first + media_attachment = status.media_attachments.ordered.reload.first expect(media_attachment).to_not be_nil expect(media_attachment.remote_url).to eq 'https://example.com/foo.png' From 85662a5a57531af5402a6777d0b1089e78c56815 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 30 Nov 2023 14:47:01 +0100 Subject: [PATCH 30/95] Change `img-src` and `media-src` CSP directives to not include `https:` (#28025) --- app/lib/content_security_policy.rb | 4 ++-- config/initializers/content_security_policy.rb | 10 +++++----- spec/lib/content_security_policy_spec.rb | 14 +++++++------- spec/requests/content_security_policy_spec.rb | 6 +++--- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/lib/content_security_policy.rb b/app/lib/content_security_policy.rb index e8fcf76a6..966e41f03 100644 --- a/app/lib/content_security_policy.rb +++ b/app/lib/content_security_policy.rb @@ -9,8 +9,8 @@ class ContentSecurityPolicy url_from_configured_asset_host || url_from_base_host end - def media_host - cdn_host_value || assets_host + def media_hosts + [assets_host, cdn_host_value].compact end private diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index 3fb80bac4..a8b61e356 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -10,7 +10,7 @@ require_relative '../../app/lib/content_security_policy' policy = ContentSecurityPolicy.new assets_host = policy.assets_host -media_host = policy.media_host +media_hosts = policy.media_hosts def sso_host return unless ENV['ONE_CLICK_SSO_LOGIN'] == 'true' @@ -35,9 +35,9 @@ Rails.application.config.content_security_policy do |p| p.default_src :none p.frame_ancestors :none p.font_src :self, assets_host - p.img_src :self, :https, :data, :blob, assets_host + p.img_src :self, :data, :blob, *media_hosts p.style_src :self, assets_host - p.media_src :self, :https, :data, assets_host + p.media_src :self, :data, *media_hosts p.frame_src :self, :https p.manifest_src :self, assets_host @@ -54,10 +54,10 @@ Rails.application.config.content_security_policy do |p| webpacker_public_host = ENV.fetch('WEBPACKER_DEV_SERVER_PUBLIC', Webpacker.config.dev_server[:public]) webpacker_urls = %w(ws http).map { |protocol| "#{protocol}#{Webpacker.dev_server.https? ? 's' : ''}://#{webpacker_public_host}" } - p.connect_src :self, :data, :blob, assets_host, media_host, Rails.configuration.x.streaming_api_base_url, *webpacker_urls + p.connect_src :self, :data, :blob, *media_hosts, Rails.configuration.x.streaming_api_base_url, *webpacker_urls p.script_src :self, :unsafe_inline, :unsafe_eval, assets_host else - p.connect_src :self, :data, :blob, assets_host, media_host, Rails.configuration.x.streaming_api_base_url + p.connect_src :self, :data, :blob, *media_hosts, Rails.configuration.x.streaming_api_base_url p.script_src :self, assets_host, "'wasm-unsafe-eval'" end end diff --git a/spec/lib/content_security_policy_spec.rb b/spec/lib/content_security_policy_spec.rb index 2e92f815a..4286f1498 100644 --- a/spec/lib/content_security_policy_spec.rb +++ b/spec/lib/content_security_policy_spec.rb @@ -59,10 +59,10 @@ describe ContentSecurityPolicy do end end - describe '#media_host' do + describe '#media_hosts' do context 'when there is no configured CDN' do it 'defaults to using the assets_host value' do - expect(subject.media_host).to eq(subject.assets_host) + expect(subject.media_hosts).to contain_exactly(subject.assets_host) end end @@ -74,7 +74,7 @@ describe ContentSecurityPolicy do end it 'uses the s3 alias host value' do - expect(subject.media_host).to eq 'https://asset-host.s3-alias.example' + expect(subject.media_hosts).to contain_exactly(subject.assets_host, 'https://asset-host.s3-alias.example') end end @@ -86,7 +86,7 @@ describe ContentSecurityPolicy do end it 'uses the s3 alias host value and preserves the path' do - expect(subject.media_host).to eq 'https://asset-host.s3-alias.example/pathname/' + expect(subject.media_hosts).to contain_exactly(subject.assets_host, 'https://asset-host.s3-alias.example/pathname/') end end @@ -98,7 +98,7 @@ describe ContentSecurityPolicy do end it 'uses the s3 cloudfront host value' do - expect(subject.media_host).to eq 'https://asset-host.s3-cloudfront.example' + expect(subject.media_hosts).to contain_exactly(subject.assets_host, 'https://asset-host.s3-cloudfront.example') end end @@ -110,7 +110,7 @@ describe ContentSecurityPolicy do end it 'uses the azure alias host value' do - expect(subject.media_host).to eq 'https://asset-host.azure-alias.example' + expect(subject.media_hosts).to contain_exactly(subject.assets_host, 'https://asset-host.azure-alias.example') end end @@ -122,7 +122,7 @@ describe ContentSecurityPolicy do end it 'uses the s3 hostname host value' do - expect(subject.media_host).to eq 'https://asset-host.s3.example' + expect(subject.media_hosts).to contain_exactly(subject.assets_host, 'https://asset-host.s3.example') end end end diff --git a/spec/requests/content_security_policy_spec.rb b/spec/requests/content_security_policy_spec.rb index 7eb27d61d..7610e698c 100644 --- a/spec/requests/content_security_policy_spec.rb +++ b/spec/requests/content_security_policy_spec.rb @@ -12,15 +12,15 @@ describe 'Content-Security-Policy' do "default-src 'none'", "frame-ancestors 'none'", "font-src 'self' https://cb6e6126.ngrok.io", - "img-src 'self' https: data: blob: https://cb6e6126.ngrok.io", + "img-src 'self' data: blob: https://cb6e6126.ngrok.io", "style-src 'self' https://cb6e6126.ngrok.io 'nonce-ZbA+JmE7+bK8F5qvADZHuQ=='", - "media-src 'self' https: data: https://cb6e6126.ngrok.io", + "media-src 'self' data: https://cb6e6126.ngrok.io", "frame-src 'self' https:", "manifest-src 'self' https://cb6e6126.ngrok.io", "form-action 'self'", "child-src 'self' blob: https://cb6e6126.ngrok.io", "worker-src 'self' blob: https://cb6e6126.ngrok.io", - "connect-src 'self' data: blob: https://cb6e6126.ngrok.io https://cb6e6126.ngrok.io ws://localhost:4000", + "connect-src 'self' data: blob: https://cb6e6126.ngrok.io ws://localhost:4000", "script-src 'self' https://cb6e6126.ngrok.io 'wasm-unsafe-eval'" ) end From b20af17a2c7dec49bc7ad1ec6706c5b02bf4f498 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 30 Nov 2023 14:47:07 +0100 Subject: [PATCH 31/95] Fix onboarding step descriptions being truncated on narrow screens (#28021) --- app/javascript/styles/mastodon/components.scss | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 9c3d9dc2c..2106b529d 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2732,22 +2732,16 @@ $ui-header-height: 55px; &__description { flex: 1 1 auto; line-height: 20px; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; h6 { color: $highlight-text-color; font-weight: 500; font-size: 14px; - overflow: hidden; - text-overflow: ellipsis; } p { color: $darker-text-color; overflow: hidden; - text-overflow: ellipsis; } } } From a8473f582d0d25ac0a2ad385cf3915e8012a3646 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 30 Nov 2023 08:55:20 -0500 Subject: [PATCH 32/95] Add zeitwerk inflector for cli->CLI (#27635) --- config/initializers/inflections.rb | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 3d1750945..ba459e19f 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -13,21 +13,22 @@ # end ActiveSupport::Inflector.inflections(:en) do |inflect| - inflect.acronym 'StatsD' - inflect.acronym 'OEmbed' - inflect.acronym 'OStatus' inflect.acronym 'ActivityPub' - inflect.acronym 'PubSubHubbub' inflect.acronym 'ActivityStreams' - inflect.acronym 'JsonLd' - inflect.acronym 'Ed25519' - inflect.acronym 'TOC' - inflect.acronym 'RSS' - inflect.acronym 'REST' - inflect.acronym 'URL' inflect.acronym 'ASCII' + inflect.acronym 'CLI' inflect.acronym 'DeepL' inflect.acronym 'DSL' + inflect.acronym 'Ed25519' + inflect.acronym 'JsonLd' + inflect.acronym 'OEmbed' + inflect.acronym 'OStatus' + inflect.acronym 'PubSubHubbub' + inflect.acronym 'REST' + inflect.acronym 'RSS' + inflect.acronym 'StatsD' + inflect.acronym 'TOC' + inflect.acronym 'URL' inflect.singular 'data', 'data' end From e7c340a63475713d7f55b92fb8c963478f4d622a Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 30 Nov 2023 08:59:46 -0500 Subject: [PATCH 33/95] Spec coverage for missing `q` param and error conditions in `api/v2/search` controller (#27842) --- app/controllers/api/v2/search_controller.rb | 31 ++++++++++-- .../api/v2/search_controller_spec.rb | 48 +++++++++++++++++++ 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/v2/search_controller.rb b/app/controllers/api/v2/search_controller.rb index 35be54930..4339bee21 100644 --- a/app/controllers/api/v2/search_controller.rb +++ b/app/controllers/api/v2/search_controller.rb @@ -8,6 +8,11 @@ class Api::V2::SearchController < Api::BaseController before_action -> { authorize_if_got_token! :read, :'read:search' } before_action :validate_search_params! + with_options unless: :user_signed_in? do + before_action :query_pagination_error, if: :pagination_requested? + before_action :remote_resolve_error, if: :remote_resolve_requested? + end + def index @search = Search.new(search_results) render json: @search, serializer: REST::SearchSerializer @@ -21,12 +26,22 @@ class Api::V2::SearchController < Api::BaseController def validate_search_params! params.require(:q) + end - return if user_signed_in? + def query_pagination_error + render json: { error: 'Search queries pagination is not supported without authentication' }, status: 401 + end - return render json: { error: 'Search queries pagination is not supported without authentication' }, status: 401 if params[:offset].present? + def remote_resolve_error + render json: { error: 'Search queries that resolve remote resources are not supported without authentication' }, status: 401 + end - render json: { error: 'Search queries that resolve remote resources are not supported without authentication' }, status: 401 if truthy_param?(:resolve) + def remote_resolve_requested? + truthy_param?(:resolve) + end + + def pagination_requested? + params[:offset].present? end def search_results @@ -34,7 +49,15 @@ class Api::V2::SearchController < Api::BaseController params[:q], current_account, limit_param(RESULTS_LIMIT), - search_params.merge(resolve: truthy_param?(:resolve), exclude_unreviewed: truthy_param?(:exclude_unreviewed), following: truthy_param?(:following)) + combined_search_params + ) + end + + def combined_search_params + search_params.merge( + resolve: truthy_param?(:resolve), + exclude_unreviewed: truthy_param?(:exclude_unreviewed), + following: truthy_param?(:following) ) end diff --git a/spec/controllers/api/v2/search_controller_spec.rb b/spec/controllers/api/v2/search_controller_spec.rb index d3ff42d6a..a16716a10 100644 --- a/spec/controllers/api/v2/search_controller_spec.rb +++ b/spec/controllers/api/v2/search_controller_spec.rb @@ -34,6 +34,26 @@ RSpec.describe Api::V2::SearchController do expect(body_as_json[:accounts].pluck(:id)).to contain_exactly(bob.id.to_s, ana.id.to_s, tom.id.to_s) end + context 'with truthy `resolve`' do + let(:params) { { q: 'test1', resolve: '1' } } + + it 'returns http unauthorized' do + get :index, params: params + + expect(response).to have_http_status(200) + end + end + + context 'with `offset`' do + let(:params) { { q: 'test1', offset: 1 } } + + it 'returns http unauthorized' do + get :index, params: params + + expect(response).to have_http_status(200) + end + end + context 'with following=true' do let(:params) { { q: 'test', type: 'accounts', following: 'true' } } @@ -48,6 +68,26 @@ RSpec.describe Api::V2::SearchController do end end end + + context 'when search raises syntax error' do + before { allow(Search).to receive(:new).and_raise(Mastodon::SyntaxError) } + + it 'returns http unprocessable_entity' do + get :index, params: params + + expect(response).to have_http_status(422) + end + end + + context 'when search raises not found error' do + before { allow(Search).to receive(:new).and_raise(ActiveRecord::RecordNotFound) } + + it 'returns http not_found' do + get :index, params: params + + expect(response).to have_http_status(404) + end + end end end @@ -59,6 +99,12 @@ RSpec.describe Api::V2::SearchController do get :index, params: search_params end + context 'without a `q` param' do + it 'returns http bad_request' do + expect(response).to have_http_status(400) + end + end + context 'with a `q` shorter than 5 characters' do let(:search_params) { { q: 'test' } } @@ -79,6 +125,7 @@ RSpec.describe Api::V2::SearchController do it 'returns http unauthorized' do expect(response).to have_http_status(401) + expect(response.body).to match('resolve remote resources') end end @@ -87,6 +134,7 @@ RSpec.describe Api::V2::SearchController do it 'returns http unauthorized' do expect(response).to have_http_status(401) + expect(response.body).to match('pagination is not supported') end end end From 0530ce5e9575c09464847412f43852f438b0494e Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 30 Nov 2023 09:28:05 -0500 Subject: [PATCH 34/95] Convert accounts controller spec to request spec (#28126) --- .../accounts_spec.rb} | 158 +++++++++--------- 1 file changed, 80 insertions(+), 78 deletions(-) rename spec/{controllers/accounts_controller_spec.rb => requests/accounts_spec.rb} (56%) diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/requests/accounts_spec.rb similarity index 56% rename from spec/controllers/accounts_controller_spec.rb rename to spec/requests/accounts_spec.rb index 542a74878..bf067cdc3 100644 --- a/spec/controllers/accounts_controller_spec.rb +++ b/spec/requests/accounts_spec.rb @@ -2,23 +2,22 @@ require 'rails_helper' -RSpec.describe AccountsController do - render_views - +describe 'Accounts show response' do let(:account) { Fabricate(:account) } - describe 'unapproved account check' do + context 'with an unapproved account' do before { account.user.update(approved: false) } it 'returns http not found' do %w(html json rss).each do |format| - get :show, params: { username: account.username, format: format } + get short_account_path(username: account.username), as: format + expect(response).to have_http_status(404) end end end - describe 'permanently suspended account check' do + context 'with a permanently suspended account' do before do account.suspend! account.deletion_request.destroy @@ -26,25 +25,26 @@ RSpec.describe AccountsController do it 'returns http gone' do %w(html json rss).each do |format| - get :show, params: { username: account.username, format: format } + get short_account_path(username: account.username), as: format + expect(response).to have_http_status(410) end end end - describe 'temporarily suspended account check' do + context 'with a temporarily suspended account' do before { account.suspend! } it 'returns appropriate http response code' do { html: 403, json: 200, rss: 403 }.each do |format, code| - get :show, params: { username: account.username, format: format } + get short_account_path(username: account.username), as: format expect(response).to have_http_status(code) end end end - describe 'GET #show' do + describe 'GET to short username paths' do context 'with existing statuses' do let!(:status) { Fabricate(:status, account: account) } let!(:status_reply) { Fabricate(:status, account: account, thread: Fabricate(:status)) } @@ -66,17 +66,17 @@ RSpec.describe AccountsController do shared_examples 'common HTML response' do it 'returns a standard HTML response', :aggregate_failures do - expect(response).to have_http_status(200) + expect(response) + .to have_http_status(200) + .and render_template(:show) expect(response.headers['Link'].to_s).to include ActivityPub::TagManager.instance.uri_for(account) - - expect(response).to render_template(:show) end end context 'with a normal account in an HTML request' do before do - get :show, params: { username: account.username, format: format } + get short_account_path(username: account.username), as: format end it_behaves_like 'common HTML response' @@ -84,8 +84,7 @@ RSpec.describe AccountsController do context 'with replies' do before do - allow(controller).to receive(:replies_requested?).and_return(true) - get :show, params: { username: account.username, format: format } + get short_account_with_replies_path(username: account.username), as: format end it_behaves_like 'common HTML response' @@ -93,8 +92,7 @@ RSpec.describe AccountsController do context 'with media' do before do - allow(controller).to receive(:media_requested?).and_return(true) - get :show, params: { username: account.username, format: format } + get short_account_media_path(username: account.username), as: format end it_behaves_like 'common HTML response' @@ -106,9 +104,8 @@ RSpec.describe AccountsController do let!(:status_tag) { Fabricate(:status, account: account) } before do - allow(controller).to receive(:tag_requested?).and_return(true) status_tag.tags << tag - get :show, params: { username: account.username, format: format, tag: tag.to_param } + get short_account_tag_path(username: account.username, tag: tag), as: format end it_behaves_like 'common HTML response' @@ -117,21 +114,25 @@ RSpec.describe AccountsController do context 'with JSON' do let(:authorized_fetch_mode) { false } - let(:format) { 'json' } + let(:headers) { { 'ACCEPT' => 'application/json' } } - before do - allow(controller).to receive(:authorized_fetch_mode?).and_return(authorized_fetch_mode) + around do |example| + ClimateControl.modify AUTHORIZED_FETCH: authorized_fetch_mode.to_s do + example.run + end end context 'with a normal account in a JSON request' do before do - get :show, params: { username: account.username, format: format } + get short_account_path(username: account.username), headers: headers end it 'returns a JSON version of the account', :aggregate_failures do - expect(response).to have_http_status(200) - - expect(response.media_type).to eq 'application/activity+json' + expect(response) + .to have_http_status(200) + .and have_attributes( + media_type: eq('application/activity+json') + ) expect(body_as_json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary) end @@ -152,13 +153,15 @@ RSpec.describe AccountsController do before do sign_in(user) - get :show, params: { username: account.username, format: format } + get short_account_path(username: account.username), headers: headers.merge({ 'Cookie' => '123' }) end it 'returns a private JSON version of the account', :aggregate_failures do - expect(response).to have_http_status(200) - - expect(response.media_type).to eq 'application/activity+json' + expect(response) + .to have_http_status(200) + .and have_attributes( + media_type: eq('application/activity+json') + ) expect(response.headers['Cache-Control']).to include 'private' @@ -170,14 +173,15 @@ RSpec.describe AccountsController do let(:remote_account) { Fabricate(:account, domain: 'example.com') } before do - allow(controller).to receive(:signed_request_actor).and_return(remote_account) - get :show, params: { username: account.username, format: format } + get short_account_path(username: account.username), headers: headers, sign_with: remote_account end it 'returns a JSON version of the account', :aggregate_failures do - expect(response).to have_http_status(200) - - expect(response.media_type).to eq 'application/activity+json' + expect(response) + .to have_http_status(200) + .and have_attributes( + media_type: eq('application/activity+json') + ) expect(body_as_json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary) end @@ -188,12 +192,13 @@ RSpec.describe AccountsController do let(:authorized_fetch_mode) { true } it 'returns a private signature JSON version of the account', :aggregate_failures do - expect(response).to have_http_status(200) - - expect(response.media_type).to eq 'application/activity+json' + expect(response) + .to have_http_status(200) + .and have_attributes( + media_type: eq('application/activity+json') + ) expect(response.headers['Cache-Control']).to include 'private' - expect(response.headers['Vary']).to include 'Signature' expect(body_as_json).to include(:id, :type, :preferredUsername, :inbox, :publicKey, :name, :summary) @@ -207,60 +212,58 @@ RSpec.describe AccountsController do context 'with a normal account in an RSS request' do before do - get :show, params: { username: account.username, format: format } + get short_account_path(username: account.username, format: format) end it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie' it 'responds with correct statuses', :aggregate_failures do expect(response).to have_http_status(200) - expect(response.body).to include_status_tag(status_media) - expect(response.body).to include_status_tag(status_self_reply) - expect(response.body).to include_status_tag(status) - expect(response.body).to_not include_status_tag(status_direct) - expect(response.body).to_not include_status_tag(status_private) - expect(response.body).to_not include_status_tag(status_reblog.reblog) - expect(response.body).to_not include_status_tag(status_reply) + expect(response.body).to include(status_tag_for(status_media)) + expect(response.body).to include(status_tag_for(status_self_reply)) + expect(response.body).to include(status_tag_for(status)) + expect(response.body).to_not include(status_tag_for(status_direct)) + expect(response.body).to_not include(status_tag_for(status_private)) + expect(response.body).to_not include(status_tag_for(status_reblog.reblog)) + expect(response.body).to_not include(status_tag_for(status_reply)) end end context 'with replies' do before do - allow(controller).to receive(:replies_requested?).and_return(true) - get :show, params: { username: account.username, format: format } + get short_account_with_replies_path(username: account.username, format: format) end it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie' it 'responds with correct statuses with replies', :aggregate_failures do expect(response).to have_http_status(200) - expect(response.body).to include_status_tag(status_media) - expect(response.body).to include_status_tag(status_reply) - expect(response.body).to include_status_tag(status_self_reply) - expect(response.body).to include_status_tag(status) - expect(response.body).to_not include_status_tag(status_direct) - expect(response.body).to_not include_status_tag(status_private) - expect(response.body).to_not include_status_tag(status_reblog.reblog) + expect(response.body).to include(status_tag_for(status_media)) + expect(response.body).to include(status_tag_for(status_reply)) + expect(response.body).to include(status_tag_for(status_self_reply)) + expect(response.body).to include(status_tag_for(status)) + expect(response.body).to_not include(status_tag_for(status_direct)) + expect(response.body).to_not include(status_tag_for(status_private)) + expect(response.body).to_not include(status_tag_for(status_reblog.reblog)) end end context 'with media' do before do - allow(controller).to receive(:media_requested?).and_return(true) - get :show, params: { username: account.username, format: format } + get short_account_media_path(username: account.username, format: format) end it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie' it 'responds with correct statuses with media', :aggregate_failures do expect(response).to have_http_status(200) - expect(response.body).to include_status_tag(status_media) - expect(response.body).to_not include_status_tag(status_direct) - expect(response.body).to_not include_status_tag(status_private) - expect(response.body).to_not include_status_tag(status_reblog.reblog) - expect(response.body).to_not include_status_tag(status_reply) - expect(response.body).to_not include_status_tag(status_self_reply) - expect(response.body).to_not include_status_tag(status) + expect(response.body).to include(status_tag_for(status_media)) + expect(response.body).to_not include(status_tag_for(status_direct)) + expect(response.body).to_not include(status_tag_for(status_private)) + expect(response.body).to_not include(status_tag_for(status_reblog.reblog)) + expect(response.body).to_not include(status_tag_for(status_reply)) + expect(response.body).to_not include(status_tag_for(status_self_reply)) + expect(response.body).to_not include(status_tag_for(status)) end end @@ -270,30 +273,29 @@ RSpec.describe AccountsController do let!(:status_tag) { Fabricate(:status, account: account) } before do - allow(controller).to receive(:tag_requested?).and_return(true) status_tag.tags << tag - get :show, params: { username: account.username, format: format, tag: tag.to_param } + get short_account_tag_path(username: account.username, tag: tag, format: format) end it_behaves_like 'cacheable response', expects_vary: 'Accept, Accept-Language, Cookie' it 'responds with correct statuses with a tag', :aggregate_failures do expect(response).to have_http_status(200) - expect(response.body).to include_status_tag(status_tag) - expect(response.body).to_not include_status_tag(status_direct) - expect(response.body).to_not include_status_tag(status_media) - expect(response.body).to_not include_status_tag(status_private) - expect(response.body).to_not include_status_tag(status_reblog.reblog) - expect(response.body).to_not include_status_tag(status_reply) - expect(response.body).to_not include_status_tag(status_self_reply) - expect(response.body).to_not include_status_tag(status) + expect(response.body).to include(status_tag_for(status_tag)) + expect(response.body).to_not include(status_tag_for(status_direct)) + expect(response.body).to_not include(status_tag_for(status_media)) + expect(response.body).to_not include(status_tag_for(status_private)) + expect(response.body).to_not include(status_tag_for(status_reblog.reblog)) + expect(response.body).to_not include(status_tag_for(status_reply)) + expect(response.body).to_not include(status_tag_for(status_self_reply)) + expect(response.body).to_not include(status_tag_for(status)) end end end end end - def include_status_tag(status) - include ActivityPub::TagManager.instance.url_for(status) + def status_tag_for(status) + ActivityPub::TagManager.instance.url_for(status) end end From 1f1c75bba56f4b22c5f4c745b8f50a002001213c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 30 Nov 2023 09:39:41 -0500 Subject: [PATCH 35/95] File cleanup/organization in `controllers/concerns` (#27846) --- app/controllers/admin/export_domain_allows_controller.rb | 2 +- app/controllers/admin/export_domain_blocks_controller.rb | 2 +- app/controllers/api/base_controller.rb | 6 +++--- app/controllers/auth/confirmations_controller.rb | 2 +- app/controllers/auth/registrations_controller.rb | 2 +- app/controllers/auth/sessions_controller.rb | 2 +- .../export_controller_concern.rb} | 2 +- .../concerns/{ => api}/access_token_tracking_concern.rb | 2 +- .../{api_caching_concern.rb => api/caching_concern.rb} | 2 +- app/controllers/concerns/{ => api}/rate_limit_headers.rb | 2 +- app/controllers/concerns/{ => auth}/captcha_concern.rb | 2 +- .../concerns/{ => auth}/registration_spam_concern.rb | 2 +- .../{ => auth}/two_factor_authentication_concern.rb | 2 +- .../concerns/{ => settings}/export_controller_concern.rb | 2 +- .../settings/exports/blocked_accounts_controller.rb | 2 +- .../settings/exports/blocked_domains_controller.rb | 2 +- app/controllers/settings/exports/bookmarks_controller.rb | 2 +- .../settings/exports/following_accounts_controller.rb | 2 +- app/controllers/settings/exports/lists_controller.rb | 2 +- .../settings/exports/muted_accounts_controller.rb | 2 +- .../concerns/{ => api}/rate_limit_headers_spec.rb | 4 ++-- .../{ => settings}/export_controller_concern_spec.rb | 4 ++-- 22 files changed, 26 insertions(+), 26 deletions(-) rename app/controllers/concerns/{admin_export_controller_concern.rb => admin/export_controller_concern.rb} (92%) rename app/controllers/concerns/{ => api}/access_token_tracking_concern.rb (92%) rename app/controllers/concerns/{api_caching_concern.rb => api/caching_concern.rb} (93%) rename app/controllers/concerns/{ => api}/rate_limit_headers.rb (98%) rename app/controllers/concerns/{ => auth}/captcha_concern.rb (98%) rename app/controllers/concerns/{ => auth}/registration_spam_concern.rb (81%) rename app/controllers/concerns/{ => auth}/two_factor_authentication_concern.rb (98%) rename app/controllers/concerns/{ => settings}/export_controller_concern.rb (93%) rename spec/controllers/concerns/{ => api}/rate_limit_headers_spec.rb (95%) rename spec/controllers/concerns/{ => settings}/export_controller_concern_spec.rb (89%) diff --git a/app/controllers/admin/export_domain_allows_controller.rb b/app/controllers/admin/export_domain_allows_controller.rb index adfc39da2..ca88c6525 100644 --- a/app/controllers/admin/export_domain_allows_controller.rb +++ b/app/controllers/admin/export_domain_allows_controller.rb @@ -4,7 +4,7 @@ require 'csv' module Admin class ExportDomainAllowsController < BaseController - include AdminExportControllerConcern + include Admin::ExportControllerConcern before_action :set_dummy_import!, only: [:new] diff --git a/app/controllers/admin/export_domain_blocks_controller.rb b/app/controllers/admin/export_domain_blocks_controller.rb index 816422d4f..433b8a158 100644 --- a/app/controllers/admin/export_domain_blocks_controller.rb +++ b/app/controllers/admin/export_domain_blocks_controller.rb @@ -4,7 +4,7 @@ require 'csv' module Admin class ExportDomainBlocksController < BaseController - include AdminExportControllerConcern + include Admin::ExportControllerConcern before_action :set_dummy_import!, only: [:new] diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index 135c57565..dc760297d 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -4,9 +4,9 @@ class Api::BaseController < ApplicationController DEFAULT_STATUSES_LIMIT = 20 DEFAULT_ACCOUNTS_LIMIT = 40 - include RateLimitHeaders - include AccessTokenTrackingConcern - include ApiCachingConcern + include Api::RateLimitHeaders + include Api::AccessTokenTrackingConcern + include Api::CachingConcern include Api::ContentSecurityPolicy skip_before_action :require_functional!, unless: :limited_federation_mode? diff --git a/app/controllers/auth/confirmations_controller.rb b/app/controllers/auth/confirmations_controller.rb index 05e4605f4..9f6be9c42 100644 --- a/app/controllers/auth/confirmations_controller.rb +++ b/app/controllers/auth/confirmations_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Auth::ConfirmationsController < Devise::ConfirmationsController - include CaptchaConcern + include Auth::CaptchaConcern layout 'auth' diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 8be7c5f19..4535ecdbd 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -2,7 +2,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController include RegistrationHelper - include RegistrationSpamConcern + include Auth::RegistrationSpamConcern layout :determine_layout diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index 84d9d5e11..148ad5375 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -10,7 +10,7 @@ class Auth::SessionsController < Devise::SessionsController prepend_before_action :check_suspicious!, only: [:create] - include TwoFactorAuthenticationConcern + include Auth::TwoFactorAuthenticationConcern before_action :set_body_classes diff --git a/app/controllers/concerns/admin_export_controller_concern.rb b/app/controllers/concerns/admin/export_controller_concern.rb similarity index 92% rename from app/controllers/concerns/admin_export_controller_concern.rb rename to app/controllers/concerns/admin/export_controller_concern.rb index 4ac48a04b..6228ae67f 100644 --- a/app/controllers/concerns/admin_export_controller_concern.rb +++ b/app/controllers/concerns/admin/export_controller_concern.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module AdminExportControllerConcern +module Admin::ExportControllerConcern extend ActiveSupport::Concern private diff --git a/app/controllers/concerns/access_token_tracking_concern.rb b/app/controllers/concerns/api/access_token_tracking_concern.rb similarity index 92% rename from app/controllers/concerns/access_token_tracking_concern.rb rename to app/controllers/concerns/api/access_token_tracking_concern.rb index cf60cfb99..bc6ae51c7 100644 --- a/app/controllers/concerns/access_token_tracking_concern.rb +++ b/app/controllers/concerns/api/access_token_tracking_concern.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module AccessTokenTrackingConcern +module Api::AccessTokenTrackingConcern extend ActiveSupport::Concern ACCESS_TOKEN_UPDATE_FREQUENCY = 24.hours.freeze diff --git a/app/controllers/concerns/api_caching_concern.rb b/app/controllers/concerns/api/caching_concern.rb similarity index 93% rename from app/controllers/concerns/api_caching_concern.rb rename to app/controllers/concerns/api/caching_concern.rb index 12264d514..55d7fe56d 100644 --- a/app/controllers/concerns/api_caching_concern.rb +++ b/app/controllers/concerns/api/caching_concern.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module ApiCachingConcern +module Api::CachingConcern extend ActiveSupport::Concern def cache_if_unauthenticated! diff --git a/app/controllers/concerns/rate_limit_headers.rb b/app/controllers/concerns/api/rate_limit_headers.rb similarity index 98% rename from app/controllers/concerns/rate_limit_headers.rb rename to app/controllers/concerns/api/rate_limit_headers.rb index 5b83d8575..fe57b6f6b 100644 --- a/app/controllers/concerns/rate_limit_headers.rb +++ b/app/controllers/concerns/api/rate_limit_headers.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module RateLimitHeaders +module Api::RateLimitHeaders extend ActiveSupport::Concern class_methods do diff --git a/app/controllers/concerns/captcha_concern.rb b/app/controllers/concerns/auth/captcha_concern.rb similarity index 98% rename from app/controllers/concerns/captcha_concern.rb rename to app/controllers/concerns/auth/captcha_concern.rb index 170c8f5e0..cfd93978c 100644 --- a/app/controllers/concerns/captcha_concern.rb +++ b/app/controllers/concerns/auth/captcha_concern.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module CaptchaConcern +module Auth::CaptchaConcern extend ActiveSupport::Concern include Hcaptcha::Adapters::ViewMethods diff --git a/app/controllers/concerns/registration_spam_concern.rb b/app/controllers/concerns/auth/registration_spam_concern.rb similarity index 81% rename from app/controllers/concerns/registration_spam_concern.rb rename to app/controllers/concerns/auth/registration_spam_concern.rb index af434c985..9f4798b53 100644 --- a/app/controllers/concerns/registration_spam_concern.rb +++ b/app/controllers/concerns/auth/registration_spam_concern.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module RegistrationSpamConcern +module Auth::RegistrationSpamConcern extend ActiveSupport::Concern def set_registration_form_time diff --git a/app/controllers/concerns/two_factor_authentication_concern.rb b/app/controllers/concerns/auth/two_factor_authentication_concern.rb similarity index 98% rename from app/controllers/concerns/two_factor_authentication_concern.rb rename to app/controllers/concerns/auth/two_factor_authentication_concern.rb index bc2d194c3..effdb8d21 100644 --- a/app/controllers/concerns/two_factor_authentication_concern.rb +++ b/app/controllers/concerns/auth/two_factor_authentication_concern.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module TwoFactorAuthenticationConcern +module Auth::TwoFactorAuthenticationConcern extend ActiveSupport::Concern included do diff --git a/app/controllers/concerns/export_controller_concern.rb b/app/controllers/concerns/settings/export_controller_concern.rb similarity index 93% rename from app/controllers/concerns/export_controller_concern.rb rename to app/controllers/concerns/settings/export_controller_concern.rb index e1792fd6b..2cf28cced 100644 --- a/app/controllers/concerns/export_controller_concern.rb +++ b/app/controllers/concerns/settings/export_controller_concern.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module ExportControllerConcern +module Settings::ExportControllerConcern extend ActiveSupport::Concern included do diff --git a/app/controllers/settings/exports/blocked_accounts_controller.rb b/app/controllers/settings/exports/blocked_accounts_controller.rb index 2190caa36..906564a3d 100644 --- a/app/controllers/settings/exports/blocked_accounts_controller.rb +++ b/app/controllers/settings/exports/blocked_accounts_controller.rb @@ -3,7 +3,7 @@ module Settings module Exports class BlockedAccountsController < BaseController - include ExportControllerConcern + include Settings::ExportControllerConcern def index send_export_file diff --git a/app/controllers/settings/exports/blocked_domains_controller.rb b/app/controllers/settings/exports/blocked_domains_controller.rb index bee4b2431..09dc52392 100644 --- a/app/controllers/settings/exports/blocked_domains_controller.rb +++ b/app/controllers/settings/exports/blocked_domains_controller.rb @@ -3,7 +3,7 @@ module Settings module Exports class BlockedDomainsController < BaseController - include ExportControllerConcern + include Settings::ExportControllerConcern def index send_export_file diff --git a/app/controllers/settings/exports/bookmarks_controller.rb b/app/controllers/settings/exports/bookmarks_controller.rb index c12e2f147..0321565b9 100644 --- a/app/controllers/settings/exports/bookmarks_controller.rb +++ b/app/controllers/settings/exports/bookmarks_controller.rb @@ -3,7 +3,7 @@ module Settings module Exports class BookmarksController < BaseController - include ExportControllerConcern + include Settings::ExportControllerConcern def index send_export_file diff --git a/app/controllers/settings/exports/following_accounts_controller.rb b/app/controllers/settings/exports/following_accounts_controller.rb index acefcb15d..0ac9031fb 100644 --- a/app/controllers/settings/exports/following_accounts_controller.rb +++ b/app/controllers/settings/exports/following_accounts_controller.rb @@ -3,7 +3,7 @@ module Settings module Exports class FollowingAccountsController < BaseController - include ExportControllerConcern + include Settings::ExportControllerConcern def index send_export_file diff --git a/app/controllers/settings/exports/lists_controller.rb b/app/controllers/settings/exports/lists_controller.rb index bc65f56a0..d90c71e24 100644 --- a/app/controllers/settings/exports/lists_controller.rb +++ b/app/controllers/settings/exports/lists_controller.rb @@ -3,7 +3,7 @@ module Settings module Exports class ListsController < BaseController - include ExportControllerConcern + include Settings::ExportControllerConcern def index send_export_file diff --git a/app/controllers/settings/exports/muted_accounts_controller.rb b/app/controllers/settings/exports/muted_accounts_controller.rb index 50b7bf1f7..e4b115890 100644 --- a/app/controllers/settings/exports/muted_accounts_controller.rb +++ b/app/controllers/settings/exports/muted_accounts_controller.rb @@ -3,7 +3,7 @@ module Settings module Exports class MutedAccountsController < BaseController - include ExportControllerConcern + include Settings::ExportControllerConcern def index send_export_file diff --git a/spec/controllers/concerns/rate_limit_headers_spec.rb b/spec/controllers/concerns/api/rate_limit_headers_spec.rb similarity index 95% rename from spec/controllers/concerns/rate_limit_headers_spec.rb rename to spec/controllers/concerns/api/rate_limit_headers_spec.rb index 1cdf741f4..2050de2ae 100644 --- a/spec/controllers/concerns/rate_limit_headers_spec.rb +++ b/spec/controllers/concerns/api/rate_limit_headers_spec.rb @@ -2,9 +2,9 @@ require 'rails_helper' -describe RateLimitHeaders do +describe Api::RateLimitHeaders do controller(ApplicationController) do - include RateLimitHeaders + include Api::RateLimitHeaders def show head 200 diff --git a/spec/controllers/concerns/export_controller_concern_spec.rb b/spec/controllers/concerns/settings/export_controller_concern_spec.rb similarity index 89% rename from spec/controllers/concerns/export_controller_concern_spec.rb rename to spec/controllers/concerns/settings/export_controller_concern_spec.rb index 7f0a7c5b5..a19af8689 100644 --- a/spec/controllers/concerns/export_controller_concern_spec.rb +++ b/spec/controllers/concerns/settings/export_controller_concern_spec.rb @@ -2,9 +2,9 @@ require 'rails_helper' -describe ExportControllerConcern do +describe Settings::ExportControllerConcern do controller(ApplicationController) do - include ExportControllerConcern + include Settings::ExportControllerConcern def index send_export_file From 35deaaf90bec91f34b3e1f2afa472d5c3f2cdc75 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Thu, 30 Nov 2023 10:41:26 -0500 Subject: [PATCH 36/95] Reduce AbcSize complexity in `InitialStateSerializer` (#27782) --- .rubocop_todo.yml | 2 +- app/serializers/initial_state_serializer.rb | 48 ++++++++++++--------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 43af4f670..dbd5beac2 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -26,7 +26,7 @@ Lint/NonLocalExitFromIterator: # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. Metrics/AbcSize: - Max: 144 + Max: 125 # Configuration parameters: CountBlocks, Max. Metrics/BlockNesting: diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index b707d6fcb..a8af45990 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -39,18 +39,18 @@ class InitialStateSerializer < ActiveModel::Serializer if object.current_account store[:me] = object.current_account.id.to_s - store[:unfollow_modal] = object.current_account.user.setting_unfollow_modal - store[:boost_modal] = object.current_account.user.setting_boost_modal - store[:delete_modal] = object.current_account.user.setting_delete_modal - store[:auto_play_gif] = object.current_account.user.setting_auto_play_gif - store[:display_media] = object.current_account.user.setting_display_media - store[:expand_spoilers] = object.current_account.user.setting_expand_spoilers - store[:reduce_motion] = object.current_account.user.setting_reduce_motion - store[:disable_swiping] = object.current_account.user.setting_disable_swiping - store[:advanced_layout] = object.current_account.user.setting_advanced_layout - store[:use_blurhash] = object.current_account.user.setting_use_blurhash - store[:use_pending_items] = object.current_account.user.setting_use_pending_items - store[:show_trends] = Setting.trends && object.current_account.user.setting_trends + store[:unfollow_modal] = object_account_user.setting_unfollow_modal + store[:boost_modal] = object_account_user.setting_boost_modal + store[:delete_modal] = object_account_user.setting_delete_modal + store[:auto_play_gif] = object_account_user.setting_auto_play_gif + store[:display_media] = object_account_user.setting_display_media + store[:expand_spoilers] = object_account_user.setting_expand_spoilers + store[:reduce_motion] = object_account_user.setting_reduce_motion + store[:disable_swiping] = object_account_user.setting_disable_swiping + store[:advanced_layout] = object_account_user.setting_advanced_layout + store[:use_blurhash] = object_account_user.setting_use_blurhash + store[:use_pending_items] = object_account_user.setting_use_pending_items + store[:show_trends] = Setting.trends && object_account_user.setting_trends else store[:auto_play_gif] = Setting.auto_play_gif store[:display_media] = Setting.display_media @@ -71,9 +71,9 @@ class InitialStateSerializer < ActiveModel::Serializer if object.current_account store[:me] = object.current_account.id.to_s - store[:default_privacy] = object.visibility || object.current_account.user.setting_default_privacy - store[:default_sensitive] = object.current_account.user.setting_default_sensitive - store[:default_language] = object.current_account.user.preferred_posting_language + store[:default_privacy] = object.visibility || object_account_user.setting_default_privacy + store[:default_sensitive] = object_account_user.setting_default_sensitive + store[:default_language] = object_account_user.preferred_posting_language end store[:text] = object.text if object.text @@ -89,11 +89,11 @@ class InitialStateSerializer < ActiveModel::Serializer associations: [:account_stat, :user, { moved_to_account: [:account_stat, :user] }] ) - store[object.current_account.id.to_s] = ActiveModelSerializers::SerializableResource.new(object.current_account, serializer: REST::AccountSerializer) if object.current_account - store[object.admin.id.to_s] = ActiveModelSerializers::SerializableResource.new(object.admin, serializer: REST::AccountSerializer) if object.admin - store[object.owner.id.to_s] = ActiveModelSerializers::SerializableResource.new(object.owner, serializer: REST::AccountSerializer) if object.owner - store[object.disabled_account.id.to_s] = ActiveModelSerializers::SerializableResource.new(object.disabled_account, serializer: REST::AccountSerializer) if object.disabled_account - store[object.moved_to_account.id.to_s] = ActiveModelSerializers::SerializableResource.new(object.moved_to_account, serializer: REST::AccountSerializer) if object.moved_to_account + store[object.current_account.id.to_s] = serialized_account(object.current_account) if object.current_account + store[object.admin.id.to_s] = serialized_account(object.admin) if object.admin + store[object.owner.id.to_s] = serialized_account(object.owner) if object.owner + store[object.disabled_account.id.to_s] = serialized_account(object.disabled_account) if object.disabled_account + store[object.moved_to_account.id.to_s] = serialized_account(object.moved_to_account) if object.moved_to_account store end @@ -108,6 +108,14 @@ class InitialStateSerializer < ActiveModel::Serializer private + def object_account_user + object.current_account.user + end + + def serialized_account(account) + ActiveModelSerializers::SerializableResource.new(account, serializer: REST::AccountSerializer) + end + def instance_presenter @instance_presenter ||= InstancePresenter.new end From 963354978a0ba27c6ecea4a419b3330b3ad97733 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 30 Nov 2023 16:43:26 +0100 Subject: [PATCH 37/95] Add `Account#unavailable?` and `Account#permanently_unavailable?` aliases (#28053) --- app/controllers/api/base_controller.rb | 2 +- .../accounts/follower_accounts_controller.rb | 2 +- .../accounts/following_accounts_controller.rb | 2 +- .../api/v1/accounts/statuses_controller.rb | 2 +- .../auth/registrations_controller.rb | 2 +- .../concerns/account_owned_concern.rb | 6 ++-- .../authorized_applications_controller.rb | 2 +- app/controllers/settings/base_controller.rb | 2 +- .../settings/deletes_controller.rb | 2 +- .../well_known/webfinger_controller.rb | 2 +- app/lib/account_statuses_filter.rb | 6 +--- app/lib/activitypub/activity/move.rb | 2 +- app/models/account.rb | 3 ++ app/models/user.rb | 2 +- app/policies/status_policy.rb | 2 +- .../activitypub/actor_serializer.rb | 22 +++++++------- app/serializers/rest/account_serializer.rb | 30 +++++++++---------- app/services/follow_service.rb | 2 +- app/services/notify_service.rb | 2 +- app/services/process_mentions_service.rb | 2 +- app/services/report_service.rb | 2 +- app/views/admin/accounts/_account.html.haml | 8 ++--- .../authorized_applications/index.html.haml | 2 +- app/workers/account_deletion_worker.rb | 2 +- .../suspended_user_cleanup_scheduler.rb | 4 +-- 25 files changed, 57 insertions(+), 58 deletions(-) diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index dc760297d..c81ba32b0 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -105,7 +105,7 @@ class Api::BaseController < ApplicationController end def require_not_suspended! - render json: { error: 'Your login is currently disabled' }, status: 403 if current_user&.account&.suspended? + render json: { error: 'Your login is currently disabled' }, status: 403 if current_user&.account&.unavailable? end def require_user! diff --git a/app/controllers/api/v1/accounts/follower_accounts_controller.rb b/app/controllers/api/v1/accounts/follower_accounts_controller.rb index 1a996d362..21b1095f1 100644 --- a/app/controllers/api/v1/accounts/follower_accounts_controller.rb +++ b/app/controllers/api/v1/accounts/follower_accounts_controller.rb @@ -26,7 +26,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController end def hide_results? - @account.suspended? || (@account.hides_followers? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account)) + @account.unavailable? || (@account.hides_followers? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account)) end def default_accounts diff --git a/app/controllers/api/v1/accounts/following_accounts_controller.rb b/app/controllers/api/v1/accounts/following_accounts_controller.rb index 6e6ebae43..1db521f79 100644 --- a/app/controllers/api/v1/accounts/following_accounts_controller.rb +++ b/app/controllers/api/v1/accounts/following_accounts_controller.rb @@ -26,7 +26,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController end def hide_results? - @account.suspended? || (@account.hides_following? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account)) + @account.unavailable? || (@account.hides_following? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account)) end def default_accounts diff --git a/app/controllers/api/v1/accounts/statuses_controller.rb b/app/controllers/api/v1/accounts/statuses_controller.rb index 51f541bd2..fe4279302 100644 --- a/app/controllers/api/v1/accounts/statuses_controller.rb +++ b/app/controllers/api/v1/accounts/statuses_controller.rb @@ -19,7 +19,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController end def load_statuses - @account.suspended? ? [] : cached_account_statuses + @account.unavailable? ? [] : cached_account_statuses end def cached_account_statuses diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 4535ecdbd..acfc0af0d 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -120,7 +120,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController end def require_not_suspended! - forbidden if current_account.suspended? + forbidden if current_account.unavailable? end def set_rules diff --git a/app/controllers/concerns/account_owned_concern.rb b/app/controllers/concerns/account_owned_concern.rb index 3fc0938bf..2b132417f 100644 --- a/app/controllers/concerns/account_owned_concern.rb +++ b/app/controllers/concerns/account_owned_concern.rb @@ -34,8 +34,8 @@ module AccountOwnedConcern end def check_account_suspension - if @account.suspended_permanently? - permanent_suspension_response + if @account.permanently_unavailable? + permanent_unavailability_response elsif @account.suspended? && !skip_temporary_suspension_response? temporary_suspension_response end @@ -45,7 +45,7 @@ module AccountOwnedConcern false end - def permanent_suspension_response + def permanent_unavailability_response expires_in(3.minutes, public: true) gone end diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb index 350ae2e90..8440df6b7 100644 --- a/app/controllers/oauth/authorized_applications_controller.rb +++ b/app/controllers/oauth/authorized_applications_controller.rb @@ -31,7 +31,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio end def require_not_suspended! - forbidden if current_account.suspended? + forbidden if current_account.unavailable? end def set_cache_headers diff --git a/app/controllers/settings/base_controller.rb b/app/controllers/settings/base_controller.rb index 64dcd47d1..f15140aa2 100644 --- a/app/controllers/settings/base_controller.rb +++ b/app/controllers/settings/base_controller.rb @@ -18,6 +18,6 @@ class Settings::BaseController < ApplicationController end def require_not_suspended! - forbidden if current_account.suspended? + forbidden if current_account.unavailable? end end diff --git a/app/controllers/settings/deletes_controller.rb b/app/controllers/settings/deletes_controller.rb index bb096567a..16c201b6b 100644 --- a/app/controllers/settings/deletes_controller.rb +++ b/app/controllers/settings/deletes_controller.rb @@ -25,7 +25,7 @@ class Settings::DeletesController < Settings::BaseController end def require_not_suspended! - forbidden if current_account.suspended? + forbidden if current_account.unavailable? end def challenge_passed? diff --git a/app/controllers/well_known/webfinger_controller.rb b/app/controllers/well_known/webfinger_controller.rb index 4748940f7..364fbf8a1 100644 --- a/app/controllers/well_known/webfinger_controller.rb +++ b/app/controllers/well_known/webfinger_controller.rb @@ -42,7 +42,7 @@ module WellKnown end def check_account_suspension - gone if @account.suspended_permanently? + gone if @account.permanently_unavailable? end def gone diff --git a/app/lib/account_statuses_filter.rb b/app/lib/account_statuses_filter.rb index d1365de58..eb7592cdc 100644 --- a/app/lib/account_statuses_filter.rb +++ b/app/lib/account_statuses_filter.rb @@ -32,7 +32,7 @@ class AccountStatusesFilter private def initial_scope - return Status.none if suspended? + return Status.none if account.unavailable? if anonymous? account.statuses.where(visibility: %i(public unlisted)) @@ -95,10 +95,6 @@ class AccountStatusesFilter end end - def suspended? - account.suspended? - end - def anonymous? current_account.nil? end diff --git a/app/lib/activitypub/activity/move.rb b/app/lib/activitypub/activity/move.rb index 8576ceccd..7bd7e238e 100644 --- a/app/lib/activitypub/activity/move.rb +++ b/app/lib/activitypub/activity/move.rb @@ -9,7 +9,7 @@ class ActivityPub::Activity::Move < ActivityPub::Activity target_account = ActivityPub::FetchRemoteAccountService.new.call(target_uri) - if target_account.nil? || target_account.suspended? || !target_account.also_known_as.include?(origin_account.uri) + if target_account.nil? || target_account.unavailable? || !target_account.also_known_as.include?(origin_account.uri) unmark_as_processing! return end diff --git a/app/models/account.rb b/app/models/account.rb index a25ebc4aa..03edcb2a2 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -246,6 +246,9 @@ class Account < ApplicationRecord suspended? && deletion_request.present? end + alias unavailable? suspended? + alias permanently_unavailable? suspended_permanently? + def suspend!(date: Time.now.utc, origin: :local, block_email: true) transaction do create_deletion_request! diff --git a/app/models/user.rb b/app/models/user.rb index 5185343af..b0eba97c3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -250,7 +250,7 @@ class User < ApplicationRecord end def functional_or_moved? - confirmed? && approved? && !disabled? && !account.suspended? && !account.memorial? + confirmed? && approved? && !disabled? && !account.unavailable? && !account.memorial? end def unconfirmed? diff --git a/app/policies/status_policy.rb b/app/policies/status_policy.rb index f3d0ffdba..322d3aec5 100644 --- a/app/policies/status_policy.rb +++ b/app/policies/status_policy.rb @@ -8,7 +8,7 @@ class StatusPolicy < ApplicationPolicy end def show? - return false if author.suspended? + return false if author.unavailable? if requires_mention? owned? || mention_exists? diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb index 31f39954f..4ab48ff20 100644 --- a/app/serializers/activitypub/actor_serializer.rb +++ b/app/serializers/activitypub/actor_serializer.rb @@ -96,19 +96,19 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer end def discoverable - object.suspended? ? false : (object.discoverable || false) + object.unavailable? ? false : (object.discoverable || false) end def indexable - object.suspended? ? false : (object.indexable || false) + object.unavailable? ? false : (object.indexable || false) end def name - object.suspended? ? object.username : (object.display_name.presence || object.username) + object.unavailable? ? object.username : (object.display_name.presence || object.username) end def summary - object.suspended? ? '' : account_bio_format(object) + object.unavailable? ? '' : account_bio_format(object) end def icon @@ -132,23 +132,23 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer end def avatar_exists? - !object.suspended? && object.avatar? + !object.unavailable? && object.avatar? end def header_exists? - !object.suspended? && object.header? + !object.unavailable? && object.header? end def manually_approves_followers - object.suspended? ? false : object.locked + object.unavailable? ? false : object.locked end def virtual_tags - object.suspended? ? [] : (object.emojis + object.tags) + object.unavailable? ? [] : (object.emojis + object.tags) end def virtual_attachments - object.suspended? ? [] : object.fields + object.unavailable? ? [] : object.fields end def moved_to @@ -156,11 +156,11 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer end def moved? - !object.suspended? && object.moved? + !object.unavailable? && object.moved? end def also_known_as? - !object.suspended? && !object.also_known_as.empty? + !object.unavailable? && !object.also_known_as.empty? end def published diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb index 5d1292a6b..354d38446 100644 --- a/app/serializers/rest/account_serializer.rb +++ b/app/serializers/rest/account_serializer.rb @@ -61,7 +61,7 @@ class REST::AccountSerializer < ActiveModel::Serializer end def note - object.suspended? ? '' : account_bio_format(object) + object.unavailable? ? '' : account_bio_format(object) end def url @@ -73,19 +73,19 @@ class REST::AccountSerializer < ActiveModel::Serializer end def avatar - full_asset_url(object.suspended? ? object.avatar.default_url : object.avatar_original_url) + full_asset_url(object.unavailable? ? object.avatar.default_url : object.avatar_original_url) end def avatar_static - full_asset_url(object.suspended? ? object.avatar.default_url : object.avatar_static_url) + full_asset_url(object.unavailable? ? object.avatar.default_url : object.avatar_static_url) end def header - full_asset_url(object.suspended? ? object.header.default_url : object.header_original_url) + full_asset_url(object.unavailable? ? object.header.default_url : object.header_original_url) end def header_static - full_asset_url(object.suspended? ? object.header.default_url : object.header_static_url) + full_asset_url(object.unavailable? ? object.header.default_url : object.header_static_url) end def created_at @@ -97,39 +97,39 @@ class REST::AccountSerializer < ActiveModel::Serializer end def display_name - object.suspended? ? '' : object.display_name + object.unavailable? ? '' : object.display_name end def locked - object.suspended? ? false : object.locked + object.unavailable? ? false : object.locked end def bot - object.suspended? ? false : object.bot + object.unavailable? ? false : object.bot end def discoverable - object.suspended? ? false : object.discoverable + object.unavailable? ? false : object.discoverable end def indexable - object.suspended? ? false : object.indexable + object.unavailable? ? false : object.indexable end def moved_to_account - object.suspended? ? nil : AccountDecorator.new(object.moved_to_account) + object.unavailable? ? nil : AccountDecorator.new(object.moved_to_account) end def emojis - object.suspended? ? [] : object.emojis + object.unavailable? ? [] : object.emojis end def fields - object.suspended? ? [] : object.fields + object.unavailable? ? [] : object.fields end def suspended - object.suspended? + object.unavailable? end def silenced @@ -141,7 +141,7 @@ class REST::AccountSerializer < ActiveModel::Serializer end def roles - if object.suspended? || object.user.nil? + if object.unavailable? || object.user.nil? [] else [object.user.role].compact.filter(&:highlighted?) diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb index 1aa0241fe..af5f99607 100644 --- a/app/services/follow_service.rb +++ b/app/services/follow_service.rb @@ -50,7 +50,7 @@ class FollowService < BaseService end def following_not_possible? - @target_account.nil? || @target_account.id == @source_account.id || @target_account.suspended? + @target_account.nil? || @target_account.id == @source_account.id || @target_account.unavailable? end def following_not_allowed? diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index 125883b15..13eb20986 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -108,7 +108,7 @@ class NotifyService < BaseService end def blocked? - blocked = @recipient.suspended? + blocked = @recipient.unavailable? blocked ||= from_self? && @notification.type != :poll return blocked if message? && from_staff? diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb index f3fbb8021..1c4c7805f 100644 --- a/app/services/process_mentions_service.rb +++ b/app/services/process_mentions_service.rb @@ -51,7 +51,7 @@ class ProcessMentionsService < BaseService # If after resolving it still isn't found or isn't the right # protocol, then give up - next match if mention_undeliverable?(mentioned_account) || mentioned_account&.suspended? + next match if mention_undeliverable?(mentioned_account) || mentioned_account&.unavailable? mention = @previous_mentions.find { |x| x.account_id == mentioned_account.id } mention ||= @current_mentions.find { |x| x.account_id == mentioned_account.id } diff --git a/app/services/report_service.rb b/app/services/report_service.rb index 38e55c5b6..fe546c383 100644 --- a/app/services/report_service.rb +++ b/app/services/report_service.rb @@ -12,7 +12,7 @@ class ReportService < BaseService @rule_ids = options.delete(:rule_ids).presence @options = options - raise ActiveRecord::RecordNotFound if @target_account.suspended? + raise ActiveRecord::RecordNotFound if @target_account.unavailable? create_report! notify_staff! diff --git a/app/views/admin/accounts/_account.html.haml b/app/views/admin/accounts/_account.html.haml index 755b987a8..d2f6652a0 100644 --- a/app/views/admin/accounts/_account.html.haml +++ b/app/views/admin/accounts/_account.html.haml @@ -1,4 +1,4 @@ -.batch-table__row{ class: [!account.suspended? && account.user_pending? && 'batch-table__row--attention', (account.suspended? || account.user_unconfirmed?) && 'batch-table__row--muted'] } +.batch-table__row{ class: [!account.unavailable? && account.user_pending? && 'batch-table__row--attention', (account.unavailable? || account.user_unconfirmed?) && 'batch-table__row--muted'] } %label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox = f.check_box :account_ids, { multiple: true, include_hidden: false }, account.id .batch-table__row__content.batch-table__row__content--unpadded @@ -8,13 +8,13 @@ %td = account_link_to account, path: admin_account_path(account.id) %td.accounts-table__count.optional - - if account.suspended? || account.user_pending? + - if account.unavailable? || account.user_pending? \- - else = friendly_number_to_human account.statuses_count %small= t('accounts.posts', count: account.statuses_count).downcase %td.accounts-table__count.optional - - if account.suspended? || account.user_pending? + - if account.unavailable? || account.user_pending? \- - else = friendly_number_to_human account.followers_count @@ -30,6 +30,6 @@ \- %br/ %samp.ellipsized-ip= relevant_account_ip(account, params[:ip]) - - if !account.suspended? && account.user_pending? && account.user&.invite_request&.text.present? + - if !account.unavailable? && account.user_pending? && account.user&.invite_request&.text.present? .batch-table__row__content__quote %p= account.user&.invite_request&.text diff --git a/app/views/oauth/authorized_applications/index.html.haml b/app/views/oauth/authorized_applications/index.html.haml index 40b09d87f..92e24d30c 100644 --- a/app/views/oauth/authorized_applications/index.html.haml +++ b/app/views/oauth/authorized_applications/index.html.haml @@ -27,7 +27,7 @@ = t('doorkeeper.authorized_applications.index.authorized_at', date: l(application.created_at.to_date)) - - unless application.superapp? || current_account.suspended? + - unless application.superapp? || current_account.unavailable? %div = table_link_to 'times', t('doorkeeper.authorized_applications.buttons.revoke'), oauth_authorized_application_path(application), method: :delete, data: { confirm: t('doorkeeper.authorized_applications.confirmations.revoke') } diff --git a/app/workers/account_deletion_worker.rb b/app/workers/account_deletion_worker.rb index e4f943fbd..070352f95 100644 --- a/app/workers/account_deletion_worker.rb +++ b/app/workers/account_deletion_worker.rb @@ -7,7 +7,7 @@ class AccountDeletionWorker def perform(account_id, options = {}) account = Account.find(account_id) - return unless account.suspended? + return unless account.unavailable? reserve_username = options.with_indifferent_access.fetch(:reserve_username, true) skip_activitypub = options.with_indifferent_access.fetch(:skip_activitypub, false) diff --git a/app/workers/scheduler/suspended_user_cleanup_scheduler.rb b/app/workers/scheduler/suspended_user_cleanup_scheduler.rb index 90feead67..4ea81c785 100644 --- a/app/workers/scheduler/suspended_user_cleanup_scheduler.rb +++ b/app/workers/scheduler/suspended_user_cleanup_scheduler.rb @@ -21,12 +21,12 @@ class Scheduler::SuspendedUserCleanupScheduler def perform return if Sidekiq::Queue.new('pull').size > MAX_PULL_SIZE - clean_suspended_accounts! + process_deletion_requests! end private - def clean_suspended_accounts! + def process_deletion_requests! # This should be fine because we only process a small amount of deletion requests at once and # `id` and `created_at` should follow the same order. AccountDeletionRequest.reorder(id: :asc).take(MAX_DELETIONS_PER_JOB).each do |deletion_request| From 8710bdb183073381250085ffa20e21e22d9069b6 Mon Sep 17 00:00:00 2001 From: Michael Stanclift Date: Thu, 30 Nov 2023 10:11:21 -0600 Subject: [PATCH 38/95] Fix mastodon user not being owner of tmp folder in Dockerfile (#28137) --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ed5ebf1e0..4d5bd57f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -247,7 +247,9 @@ RUN \ RUN \ # Pre-create and chown system volume to Mastodon user mkdir -p /opt/mastodon/public/system; \ - chown mastodon:mastodon /opt/mastodon/public/system; + chown mastodon:mastodon /opt/mastodon/public/system; \ +# Set Mastodon user as owner of tmp folder + chown -R mastodon:mastodon /opt/mastodon/tmp; # Set the running user for resulting container USER mastodon From b751078fcd21d13f6e4b63d7a86dd2be893d2753 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 1 Dec 2023 05:38:15 -0500 Subject: [PATCH 39/95] Eliminate double subject call in admin/ controller specs (#28158) --- .../account_moderation_notes_controller_spec.rb | 6 +++--- .../admin/custom_emojis_controller_spec.rb | 16 ++++++++-------- .../admin/report_notes_controller_spec.rb | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/spec/controllers/admin/account_moderation_notes_controller_spec.rb b/spec/controllers/admin/account_moderation_notes_controller_spec.rb index 3e1b4b280..8d24a7af3 100644 --- a/spec/controllers/admin/account_moderation_notes_controller_spec.rb +++ b/spec/controllers/admin/account_moderation_notes_controller_spec.rb @@ -20,7 +20,7 @@ RSpec.describe Admin::AccountModerationNotesController do it 'successfully creates a note' do expect { subject }.to change(AccountModerationNote, :count).by(1) - expect(subject).to redirect_to admin_account_path(target_account.id) + expect(response).to redirect_to admin_account_path(target_account.id) end end @@ -29,7 +29,7 @@ RSpec.describe Admin::AccountModerationNotesController do it 'falls to create a note' do expect { subject }.to_not change(AccountModerationNote, :count) - expect(subject).to render_template 'admin/accounts/show' + expect(response).to render_template 'admin/accounts/show' end end end @@ -42,7 +42,7 @@ RSpec.describe Admin::AccountModerationNotesController do it 'destroys note' do expect { subject }.to change(AccountModerationNote, :count).by(-1) - expect(subject).to redirect_to admin_account_path(target_account.id) + expect(response).to redirect_to admin_account_path(target_account.id) end end end diff --git a/spec/controllers/admin/custom_emojis_controller_spec.rb b/spec/controllers/admin/custom_emojis_controller_spec.rb index 6c32a3a57..9e732200d 100644 --- a/spec/controllers/admin/custom_emojis_controller_spec.rb +++ b/spec/controllers/admin/custom_emojis_controller_spec.rb @@ -12,24 +12,24 @@ describe Admin::CustomEmojisController do end describe 'GET #index' do - subject { get :index } - before do Fabricate(:custom_emoji) end it 'renders index page' do - expect(subject).to have_http_status 200 - expect(subject).to render_template :index + get :index + + expect(response).to have_http_status 200 + expect(response).to render_template :index end end describe 'GET #new' do - subject { get :new } - it 'renders new page' do - expect(subject).to have_http_status 200 - expect(subject).to render_template :new + get :new + + expect(response).to have_http_status 200 + expect(response).to render_template :new end end diff --git a/spec/controllers/admin/report_notes_controller_spec.rb b/spec/controllers/admin/report_notes_controller_spec.rb index b5ba4a84d..4ddf4a4e2 100644 --- a/spec/controllers/admin/report_notes_controller_spec.rb +++ b/spec/controllers/admin/report_notes_controller_spec.rb @@ -27,7 +27,7 @@ describe Admin::ReportNotesController do it 'creates a report note and resolves report' do expect { subject }.to change(ReportNote, :count).by(1) expect(report.reload).to be_action_taken - expect(subject).to redirect_to admin_reports_path + expect(response).to redirect_to admin_reports_path end end @@ -37,7 +37,7 @@ describe Admin::ReportNotesController do it 'creates a report note and does not resolve report' do expect { subject }.to change(ReportNote, :count).by(1) expect(report.reload).to_not be_action_taken - expect(subject).to redirect_to admin_report_path(report) + expect(response).to redirect_to admin_report_path(report) end end end @@ -52,7 +52,7 @@ describe Admin::ReportNotesController do it 'creates a report note and unresolves report' do expect { subject }.to change(ReportNote, :count).by(1) expect(report.reload).to_not be_action_taken - expect(subject).to redirect_to admin_report_path(report) + expect(response).to redirect_to admin_report_path(report) end end @@ -62,7 +62,7 @@ describe Admin::ReportNotesController do it 'creates a report note and does not unresolve report' do expect { subject }.to change(ReportNote, :count).by(1) expect(report.reload).to be_action_taken - expect(subject).to redirect_to admin_report_path(report) + expect(response).to redirect_to admin_report_path(report) end end end @@ -86,7 +86,7 @@ describe Admin::ReportNotesController do it 'deletes note' do expect { subject }.to change(ReportNote, :count).by(-1) - expect(subject).to redirect_to admin_report_path(report_note.report) + expect(response).to redirect_to admin_report_path(report_note.report) end end end From 440b80b2e795c21a22de4514160679e68561d98b Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 1 Dec 2023 06:00:41 -0500 Subject: [PATCH 40/95] Model concerns organization into module namespaces (#28149) --- .rubocop_todo.yml | 18 ++++++------- app/models/account.rb | 25 ++++++++++--------- .../associations.rb} | 2 +- .../{account_avatar.rb => account/avatar.rb} | 2 +- .../counters.rb} | 2 +- .../finder_concern.rb} | 2 +- .../{account_header.rb => account/header.rb} | 2 +- .../interactions.rb} | 2 +- .../merging.rb} | 2 +- .../{account_search.rb => account/search.rb} | 2 +- .../statuses_search.rb} | 2 +- .../safe_reblog_insert.rb} | 2 +- .../search_concern.rb} | 2 +- .../snapshot_concern.rb} | 2 +- .../threading_concern.rb} | 2 +- .../has_settings.rb} | 2 +- .../concerns/{ => user}/ldap_authenticable.rb | 2 +- .../concerns/{ => user}/omniauthable.rb | 2 +- .../concerns/{ => user}/pam_authenticable.rb | 2 +- app/models/status.rb | 10 ++++---- app/models/user.rb | 11 ++++---- app/views/settings/profiles/show.html.haml | 4 +-- lib/mastodon/cli/maintenance.rb | 4 +-- spec/models/account_spec.rb | 4 +-- .../counters_spec.rb} | 2 +- .../finder_concern_spec.rb} | 2 +- .../interactions_spec.rb} | 2 +- .../statuses_search_spec.rb} | 2 +- .../threading_concern_spec.rb} | 2 +- .../search_spec.rb} | 2 +- .../statuses_search_spec.rb} | 2 +- 31 files changed, 62 insertions(+), 62 deletions(-) rename app/models/concerns/{account_associations.rb => account/associations.rb} (99%) rename app/models/concerns/{account_avatar.rb => account/avatar.rb} (98%) rename app/models/concerns/{account_counters.rb => account/counters.rb} (99%) rename app/models/concerns/{account_finder_concern.rb => account/finder_concern.rb} (98%) rename app/models/concerns/{account_header.rb => account/header.rb} (98%) rename app/models/concerns/{account_interactions.rb => account/interactions.rb} (99%) rename app/models/concerns/{account_merging.rb => account/merging.rb} (98%) rename app/models/concerns/{account_search.rb => account/search.rb} (99%) rename app/models/concerns/{account_statuses_search.rb => account/statuses_search.rb} (97%) rename app/models/concerns/{status_safe_reblog_insert.rb => status/safe_reblog_insert.rb} (98%) rename app/models/concerns/{status_search_concern.rb => status/search_concern.rb} (97%) rename app/models/concerns/{status_snapshot_concern.rb => status/snapshot_concern.rb} (96%) rename app/models/concerns/{status_threading_concern.rb => status/threading_concern.rb} (98%) rename app/models/concerns/{has_user_settings.rb => user/has_settings.rb} (99%) rename app/models/concerns/{ => user}/ldap_authenticable.rb (98%) rename app/models/concerns/{ => user}/omniauthable.rb (99%) rename app/models/concerns/{ => user}/pam_authenticable.rb (98%) rename spec/models/concerns/{account_counters_spec.rb => account/counters_spec.rb} (98%) rename spec/models/concerns/{account_finder_concern_spec.rb => account/finder_concern_spec.rb} (98%) rename spec/models/concerns/{account_interactions_spec.rb => account/interactions_spec.rb} (99%) rename spec/models/concerns/{account_statuses_search_spec.rb => account/statuses_search_spec.rb} (98%) rename spec/models/concerns/{status_threading_concern_spec.rb => status/threading_concern_spec.rb} (99%) rename spec/search/models/concerns/{account_search_spec.rb => account/search_spec.rb} (98%) rename spec/search/models/concerns/{account_statuses_search_spec.rb => account/statuses_search_spec.rb} (98%) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index dbd5beac2..9fb163ceb 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -107,7 +107,7 @@ Rails/ApplicationController: # Include: app/models/**/*.rb Rails/HasAndBelongsToMany: Exclude: - - 'app/models/concerns/account_associations.rb' + - 'app/models/concerns/account/associations.rb' - 'app/models/preview_card.rb' - 'app/models/status.rb' - 'app/models/tag.rb' @@ -116,7 +116,7 @@ Rails/HasAndBelongsToMany: # Include: app/models/**/*.rb Rails/HasManyOrHasOneDependent: Exclude: - - 'app/models/concerns/account_counters.rb' + - 'app/models/concerns/account/counters.rb' - 'app/models/conversation.rb' - 'app/models/custom_emoji.rb' - 'app/models/custom_emoji_category.rb' @@ -172,7 +172,7 @@ Rails/SkipsModelValidations: Exclude: - 'app/controllers/admin/invites_controller.rb' - 'app/controllers/concerns/session_tracking_concern.rb' - - 'app/models/concerns/account_merging.rb' + - 'app/models/concerns/account/merging.rb' - 'app/models/concerns/expireable.rb' - 'app/models/status.rb' - 'app/models/trends/links.rb' @@ -252,7 +252,7 @@ Rails/WhereExists: - 'app/lib/feed_manager.rb' - 'app/lib/status_cache_hydrator.rb' - 'app/lib/suspicious_sign_in_detector.rb' - - 'app/models/concerns/account_interactions.rb' + - 'app/models/concerns/account/interactions.rb' - 'app/models/featured_tag.rb' - 'app/models/poll.rb' - 'app/models/session_activation.rb' @@ -342,8 +342,8 @@ Style/GuardClause: - 'app/lib/request_pool.rb' - 'app/lib/webfinger.rb' - 'app/lib/webfinger_resource.rb' - - 'app/models/concerns/account_counters.rb' - - 'app/models/concerns/ldap_authenticable.rb' + - 'app/models/concerns/account/counters.rb' + - 'app/models/concerns/user/ldap_authenticable.rb' - 'app/models/tag.rb' - 'app/models/user.rb' - 'app/services/fan_out_on_write_service.rb' @@ -372,8 +372,8 @@ Style/HashAsLastArrayItem: Exclude: - 'app/controllers/admin/statuses_controller.rb' - 'app/controllers/api/v1/statuses_controller.rb' - - 'app/models/concerns/account_counters.rb' - - 'app/models/concerns/status_threading_concern.rb' + - 'app/models/concerns/account/counters.rb' + - 'app/models/concerns/status/threading_concern.rb' - 'app/models/status.rb' - 'app/services/batched_remove_status_service.rb' - 'app/services/notify_service.rb' @@ -486,7 +486,7 @@ Style/RedundantReturn: # AllowedMethods: present?, blank?, presence, try, try! Style/SafeNavigation: Exclude: - - 'app/models/concerns/account_finder_concern.rb' + - 'app/models/concerns/account/finder_concern.rb' # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. diff --git a/app/models/account.rb b/app/models/account.rb index 03edcb2a2..4119944e5 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -70,19 +70,20 @@ class Account < ApplicationRecord URL_PREFIX_RE = %r{\Ahttp(s?)://[^/]+} USERNAME_ONLY_RE = /\A#{USERNAME_RE}\z/i - include Attachmentable - include AccountAssociations - include AccountAvatar - include AccountFinderConcern - include AccountHeader - include AccountInteractions - include Paginable - include AccountCounters - include DomainNormalizable + include Attachmentable # Load prior to Avatar & Header concerns + + include Account::Associations + include Account::Avatar + include Account::Counters + include Account::FinderConcern + include Account::Header + include Account::Interactions + include Account::Merging + include Account::Search + include Account::StatusesSearch include DomainMaterializable - include AccountMerging - include AccountSearch - include AccountStatusesSearch + include DomainNormalizable + include Paginable enum protocol: { ostatus: 0, activitypub: 1 } enum suspension_origin: { local: 0, remote: 1 }, _prefix: true diff --git a/app/models/concerns/account_associations.rb b/app/models/concerns/account/associations.rb similarity index 99% rename from app/models/concerns/account_associations.rb rename to app/models/concerns/account/associations.rb index 592812e96..31902ae21 100644 --- a/app/models/concerns/account_associations.rb +++ b/app/models/concerns/account/associations.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module AccountAssociations +module Account::Associations extend ActiveSupport::Concern included do diff --git a/app/models/concerns/account_avatar.rb b/app/models/concerns/account/avatar.rb similarity index 98% rename from app/models/concerns/account_avatar.rb rename to app/models/concerns/account/avatar.rb index b5919a9a2..39f599db1 100644 --- a/app/models/concerns/account_avatar.rb +++ b/app/models/concerns/account/avatar.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module AccountAvatar +module Account::Avatar extend ActiveSupport::Concern IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze diff --git a/app/models/concerns/account_counters.rb b/app/models/concerns/account/counters.rb similarity index 99% rename from app/models/concerns/account_counters.rb rename to app/models/concerns/account/counters.rb index 3fabb5205..fb69be9b7 100644 --- a/app/models/concerns/account_counters.rb +++ b/app/models/concerns/account/counters.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module AccountCounters +module Account::Counters extend ActiveSupport::Concern ALLOWED_COUNTER_KEYS = %i(statuses_count following_count followers_count).freeze diff --git a/app/models/concerns/account_finder_concern.rb b/app/models/concerns/account/finder_concern.rb similarity index 98% rename from app/models/concerns/account_finder_concern.rb rename to app/models/concerns/account/finder_concern.rb index 37c3b8895..7faaddeb4 100644 --- a/app/models/concerns/account_finder_concern.rb +++ b/app/models/concerns/account/finder_concern.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module AccountFinderConcern +module Account::FinderConcern extend ActiveSupport::Concern class_methods do diff --git a/app/models/concerns/account_header.rb b/app/models/concerns/account/header.rb similarity index 98% rename from app/models/concerns/account_header.rb rename to app/models/concerns/account/header.rb index e184880f9..44ae774e9 100644 --- a/app/models/concerns/account_header.rb +++ b/app/models/concerns/account/header.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module AccountHeader +module Account::Header extend ActiveSupport::Concern IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze diff --git a/app/models/concerns/account_interactions.rb b/app/models/concerns/account/interactions.rb similarity index 99% rename from app/models/concerns/account_interactions.rb rename to app/models/concerns/account/interactions.rb index 3c64ebd9f..0ea26e628 100644 --- a/app/models/concerns/account_interactions.rb +++ b/app/models/concerns/account/interactions.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module AccountInteractions +module Account::Interactions extend ActiveSupport::Concern class_methods do diff --git a/app/models/concerns/account_merging.rb b/app/models/concerns/account/merging.rb similarity index 98% rename from app/models/concerns/account_merging.rb rename to app/models/concerns/account/merging.rb index 14e157a3d..960ee1819 100644 --- a/app/models/concerns/account_merging.rb +++ b/app/models/concerns/account/merging.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module AccountMerging +module Account::Merging extend ActiveSupport::Concern def merge_with!(other_account) diff --git a/app/models/concerns/account_search.rb b/app/models/concerns/account/search.rb similarity index 99% rename from app/models/concerns/account_search.rb rename to app/models/concerns/account/search.rb index b855727b4..b02b9bd46 100644 --- a/app/models/concerns/account_search.rb +++ b/app/models/concerns/account/search.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module AccountSearch +module Account::Search extend ActiveSupport::Concern DISALLOWED_TSQUERY_CHARACTERS = /['?\\:‘’]/ diff --git a/app/models/concerns/account_statuses_search.rb b/app/models/concerns/account/statuses_search.rb similarity index 97% rename from app/models/concerns/account_statuses_search.rb rename to app/models/concerns/account/statuses_search.rb index 4b2bc4117..334b71450 100644 --- a/app/models/concerns/account_statuses_search.rb +++ b/app/models/concerns/account/statuses_search.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module AccountStatusesSearch +module Account::StatusesSearch extend ActiveSupport::Concern included do diff --git a/app/models/concerns/status_safe_reblog_insert.rb b/app/models/concerns/status/safe_reblog_insert.rb similarity index 98% rename from app/models/concerns/status_safe_reblog_insert.rb rename to app/models/concerns/status/safe_reblog_insert.rb index 0007b46d4..60ddb78e5 100644 --- a/app/models/concerns/status_safe_reblog_insert.rb +++ b/app/models/concerns/status/safe_reblog_insert.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module StatusSafeReblogInsert +module Status::SafeReblogInsert extend ActiveSupport::Concern class_methods do diff --git a/app/models/concerns/status_search_concern.rb b/app/models/concerns/status/search_concern.rb similarity index 97% rename from app/models/concerns/status_search_concern.rb rename to app/models/concerns/status/search_concern.rb index 7252fde73..c16db8bd8 100644 --- a/app/models/concerns/status_search_concern.rb +++ b/app/models/concerns/status/search_concern.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module StatusSearchConcern +module Status::SearchConcern extend ActiveSupport::Concern included do diff --git a/app/models/concerns/status_snapshot_concern.rb b/app/models/concerns/status/snapshot_concern.rb similarity index 96% rename from app/models/concerns/status_snapshot_concern.rb rename to app/models/concerns/status/snapshot_concern.rb index 9741b9aeb..ba624d943 100644 --- a/app/models/concerns/status_snapshot_concern.rb +++ b/app/models/concerns/status/snapshot_concern.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module StatusSnapshotConcern +module Status::SnapshotConcern extend ActiveSupport::Concern included do diff --git a/app/models/concerns/status_threading_concern.rb b/app/models/concerns/status/threading_concern.rb similarity index 98% rename from app/models/concerns/status_threading_concern.rb rename to app/models/concerns/status/threading_concern.rb index 2ca3b66c2..2606fd2f2 100644 --- a/app/models/concerns/status_threading_concern.rb +++ b/app/models/concerns/status/threading_concern.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module StatusThreadingConcern +module Status::ThreadingConcern extend ActiveSupport::Concern def ancestors(limit, account = nil) diff --git a/app/models/concerns/has_user_settings.rb b/app/models/concerns/user/has_settings.rb similarity index 99% rename from app/models/concerns/has_user_settings.rb rename to app/models/concerns/user/has_settings.rb index b7c7104a1..bfa8aa2ca 100644 --- a/app/models/concerns/has_user_settings.rb +++ b/app/models/concerns/user/has_settings.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module HasUserSettings +module User::HasSettings extend ActiveSupport::Concern included do diff --git a/app/models/concerns/ldap_authenticable.rb b/app/models/concerns/user/ldap_authenticable.rb similarity index 98% rename from app/models/concerns/ldap_authenticable.rb rename to app/models/concerns/user/ldap_authenticable.rb index 775df0817..d84ff084b 100644 --- a/app/models/concerns/ldap_authenticable.rb +++ b/app/models/concerns/user/ldap_authenticable.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module LdapAuthenticable +module User::LdapAuthenticable extend ActiveSupport::Concern class_methods do diff --git a/app/models/concerns/omniauthable.rb b/app/models/concerns/user/omniauthable.rb similarity index 99% rename from app/models/concerns/omniauthable.rb rename to app/models/concerns/user/omniauthable.rb index 3983fbcda..6d1d1b8cc 100644 --- a/app/models/concerns/omniauthable.rb +++ b/app/models/concerns/user/omniauthable.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module Omniauthable +module User::Omniauthable extend ActiveSupport::Concern TEMP_EMAIL_PREFIX = 'change@me' diff --git a/app/models/concerns/pam_authenticable.rb b/app/models/concerns/user/pam_authenticable.rb similarity index 98% rename from app/models/concerns/pam_authenticable.rb rename to app/models/concerns/user/pam_authenticable.rb index f97f986a4..a682058cc 100644 --- a/app/models/concerns/pam_authenticable.rb +++ b/app/models/concerns/user/pam_authenticable.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module PamAuthenticable +module User::PamAuthenticable extend ActiveSupport::Concern included do diff --git a/app/models/status.rb b/app/models/status.rb index 41c895029..7b1ca69c7 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -30,14 +30,14 @@ # class Status < ApplicationRecord + include Cacheable include Discard::Model include Paginable - include Cacheable - include StatusThreadingConcern - include StatusSnapshotConcern include RateLimitable - include StatusSafeReblogInsert - include StatusSearchConcern + include Status::SafeReblogInsert + include Status::SearchConcern + include Status::SnapshotConcern + include Status::ThreadingConcern rate_limit by: :account, family: :statuses diff --git a/app/models/user.rb b/app/models/user.rb index b0eba97c3..550f8bfd3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -53,9 +53,12 @@ class User < ApplicationRecord filtered_languages ) - include Redisable include LanguagesHelper - include HasUserSettings + include Redisable + include User::HasSettings + include User::LdapAuthenticable + include User::Omniauthable + include User::PamAuthenticable # The home and list feeds will be stored in Redis for this amount # of time, and status fan-out to followers will include only people @@ -75,10 +78,6 @@ class User < ApplicationRecord devise :registerable, :recoverable, :validatable, :confirmable - include Omniauthable - include PamAuthenticable - include LdapAuthenticable - belongs_to :account, inverse_of: :user belongs_to :invite, counter_cache: :uses, optional: true belongs_to :created_by_application, class_name: 'Doorkeeper::Application', optional: true diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml index 7c13dc7f4..5f9613c93 100644 --- a/app/views/settings/profiles/show.html.haml +++ b/app/views/settings/profiles/show.html.haml @@ -33,7 +33,7 @@ .fields-row .fields-row__column.fields-row__column-6 .fields-group - = f.input :avatar, wrapper: :with_block_label, input_html: { accept: AccountAvatar::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.avatar', dimensions: '400x400', size: number_to_human_size(AccountAvatar::LIMIT)) + = f.input :avatar, wrapper: :with_block_label, input_html: { accept: Account::Avatar::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.avatar', dimensions: '400x400', size: number_to_human_size(Account::Avatar::LIMIT)) .fields-row__column.fields-row__column-6 .fields-group @@ -46,7 +46,7 @@ .fields-row .fields-row__column.fields-row__column-6 .fields-group - = f.input :header, wrapper: :with_block_label, input_html: { accept: AccountHeader::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.header', dimensions: '1500x500', size: number_to_human_size(AccountHeader::LIMIT)) + = f.input :header, wrapper: :with_block_label, input_html: { accept: Account::Header::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.header', dimensions: '1500x500', size: number_to_human_size(Account::Header::LIMIT)) .fields-row__column.fields-row__column-6 .fields-group diff --git a/lib/mastodon/cli/maintenance.rb b/lib/mastodon/cli/maintenance.rb index a95b7a30c..c53d74254 100644 --- a/lib/mastodon/cli/maintenance.rb +++ b/lib/mastodon/cli/maintenance.rb @@ -67,8 +67,8 @@ module Mastodon::CLI local? ? username : "#{username}@#{domain}" end - # This is a duplicate of the AccountMerging concern because we need it to - # be independent from code version. + # This is a duplicate of the Account::Merging concern because we need it + # to be independent from code version. def merge_with!(other_account) # Since it's the same remote resource, the remote resource likely # already believes we are following/blocking, so it's safe to diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index 9652ea191..522549125 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -451,7 +451,7 @@ RSpec.describe Account do end it 'limits via constant by default' do - stub_const('AccountSearch::DEFAULT_LIMIT', 1) + stub_const('Account::Search::DEFAULT_LIMIT', 1) 2.times.each { Fabricate(:account, display_name: 'Display Name') } results = described_class.search_for('display') expect(results.size).to eq 1 @@ -595,7 +595,7 @@ RSpec.describe Account do end it 'limits by 10 by default' do - stub_const('AccountSearch::DEFAULT_LIMIT', 1) + stub_const('Account::Search::DEFAULT_LIMIT', 1) 2.times { Fabricate(:account, display_name: 'Display Name') } results = described_class.advanced_search_for('display', account) expect(results.size).to eq 1 diff --git a/spec/models/concerns/account_counters_spec.rb b/spec/models/concerns/account/counters_spec.rb similarity index 98% rename from spec/models/concerns/account_counters_spec.rb rename to spec/models/concerns/account/counters_spec.rb index fb02d79f1..2e1cd700b 100644 --- a/spec/models/concerns/account_counters_spec.rb +++ b/spec/models/concerns/account/counters_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe AccountCounters do +describe Account::Counters do let!(:account) { Fabricate(:account) } describe '#increment_count!' do diff --git a/spec/models/concerns/account_finder_concern_spec.rb b/spec/models/concerns/account/finder_concern_spec.rb similarity index 98% rename from spec/models/concerns/account_finder_concern_spec.rb rename to spec/models/concerns/account/finder_concern_spec.rb index 3a94c4d54..ab5149e98 100644 --- a/spec/models/concerns/account_finder_concern_spec.rb +++ b/spec/models/concerns/account/finder_concern_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe AccountFinderConcern do +describe Account::FinderConcern do describe 'local finders' do let!(:account) { Fabricate(:account, username: 'Alice') } diff --git a/spec/models/concerns/account_interactions_spec.rb b/spec/models/concerns/account/interactions_spec.rb similarity index 99% rename from spec/models/concerns/account_interactions_spec.rb rename to spec/models/concerns/account/interactions_spec.rb index 6687c8443..6fac41e07 100644 --- a/spec/models/concerns/account_interactions_spec.rb +++ b/spec/models/concerns/account/interactions_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe AccountInteractions do +describe Account::Interactions do let(:account) { Fabricate(:account, username: 'account') } let(:account_id) { account.id } let(:account_ids) { [account_id] } diff --git a/spec/models/concerns/account_statuses_search_spec.rb b/spec/models/concerns/account/statuses_search_spec.rb similarity index 98% rename from spec/models/concerns/account_statuses_search_spec.rb rename to spec/models/concerns/account/statuses_search_spec.rb index 46362936f..ab249d62d 100644 --- a/spec/models/concerns/account_statuses_search_spec.rb +++ b/spec/models/concerns/account/statuses_search_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe AccountStatusesSearch do +describe Account::StatusesSearch do let(:account) { Fabricate(:account, indexable: indexable) } before do diff --git a/spec/models/concerns/status_threading_concern_spec.rb b/spec/models/concerns/status/threading_concern_spec.rb similarity index 99% rename from spec/models/concerns/status_threading_concern_spec.rb rename to spec/models/concerns/status/threading_concern_spec.rb index 2eac1ca6e..09fb21856 100644 --- a/spec/models/concerns/status_threading_concern_spec.rb +++ b/spec/models/concerns/status/threading_concern_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe StatusThreadingConcern do +describe Status::ThreadingConcern do describe '#ancestors' do let!(:alice) { Fabricate(:account, username: 'alice') } let!(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com') } diff --git a/spec/search/models/concerns/account_search_spec.rb b/spec/search/models/concerns/account/search_spec.rb similarity index 98% rename from spec/search/models/concerns/account_search_spec.rb rename to spec/search/models/concerns/account/search_spec.rb index 65e1e4de1..d8d7f355d 100644 --- a/spec/search/models/concerns/account_search_spec.rb +++ b/spec/search/models/concerns/account/search_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe AccountSearch do +describe Account::Search do describe 'a non-discoverable account becoming discoverable' do let(:account) { Account.find_by(username: 'search_test_account_1') } diff --git a/spec/search/models/concerns/account_statuses_search_spec.rb b/spec/search/models/concerns/account/statuses_search_spec.rb similarity index 98% rename from spec/search/models/concerns/account_statuses_search_spec.rb rename to spec/search/models/concerns/account/statuses_search_spec.rb index d35cfa563..bf2606eec 100644 --- a/spec/search/models/concerns/account_statuses_search_spec.rb +++ b/spec/search/models/concerns/account/statuses_search_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -describe AccountStatusesSearch do +describe Account::StatusesSearch do describe 'a non-indexable account becoming indexable' do let(:account) { Account.find_by(username: 'search_test_account_1') } From 272592d16d40e804ec325ef3b5e6de9bbad5f2dd Mon Sep 17 00:00:00 2001 From: Michael Stanclift Date: Fri, 1 Dec 2023 05:06:37 -0600 Subject: [PATCH 41/95] Change startup command for Puma in Docker Compose (#28138) --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index bcfa4c85f..93451d961 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -59,7 +59,7 @@ services: image: ghcr.io/mastodon/mastodon:v4.2.0 restart: always env_file: .env.production - command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000" + command: bundle exec puma -C config/puma.rb networks: - external_network - internal_network From 92fa9b109f78647710c52d86fa777ab3972e2f1b Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 1 Dec 2023 06:56:47 -0500 Subject: [PATCH 42/95] Add spec coverage for media CLI usage command (#28167) --- spec/lib/mastodon/cli/media_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/lib/mastodon/cli/media_spec.rb b/spec/lib/mastodon/cli/media_spec.rb index 9543640e9..305d67d28 100644 --- a/spec/lib/mastodon/cli/media_spec.rb +++ b/spec/lib/mastodon/cli/media_spec.rb @@ -78,4 +78,16 @@ describe Mastodon::CLI::Media do end end end + + describe '#usage' do + context 'without options' do + let(:options) { {} } + + it 'reports about storage size' do + expect { cli.invoke(:usage, [], options) }.to output( + a_string_including('0 Bytes') + ).to_stdout + end + end + end end From 7753e5f715ba105a8ec3dd335e8af4a9448aee63 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 1 Dec 2023 07:00:34 -0500 Subject: [PATCH 43/95] Add shared example for CLI command specs (#28165) --- spec/lib/mastodon/cli/accounts_spec.rb | 8 ++------ spec/lib/mastodon/cli/cache_spec.rb | 6 +----- spec/lib/mastodon/cli/canonical_email_blocks_spec.rb | 6 +----- spec/lib/mastodon/cli/domains_spec.rb | 6 +----- spec/lib/mastodon/cli/email_domain_blocks_spec.rb | 6 +----- spec/lib/mastodon/cli/emoji_spec.rb | 6 +----- spec/lib/mastodon/cli/feeds_spec.rb | 6 +----- spec/lib/mastodon/cli/ip_blocks_spec.rb | 6 +----- spec/lib/mastodon/cli/main_spec.rb | 6 +----- spec/lib/mastodon/cli/maintenance_spec.rb | 6 +----- spec/lib/mastodon/cli/media_spec.rb | 6 +----- spec/lib/mastodon/cli/preview_cards_spec.rb | 6 +----- spec/lib/mastodon/cli/search_spec.rb | 6 +----- spec/lib/mastodon/cli/settings_spec.rb | 6 +----- spec/lib/mastodon/cli/statuses_spec.rb | 6 +----- spec/lib/mastodon/cli/upgrade_spec.rb | 6 +----- spec/support/examples/cli.rb | 11 +++++++++++ 17 files changed, 28 insertions(+), 81 deletions(-) create mode 100644 spec/support/examples/cli.rb diff --git a/spec/lib/mastodon/cli/accounts_spec.rb b/spec/lib/mastodon/cli/accounts_spec.rb index 626cf4778..3216d0d1b 100644 --- a/spec/lib/mastodon/cli/accounts_spec.rb +++ b/spec/lib/mastodon/cli/accounts_spec.rb @@ -6,6 +6,8 @@ require 'mastodon/cli/accounts' describe Mastodon::CLI::Accounts do let(:cli) { described_class.new } + it_behaves_like 'CLI Command' + # `parallelize_with_progress` cannot run in transactions, so instead, # stub it with an alternative implementation that runs sequentially # and can run in transactions. @@ -24,12 +26,6 @@ describe Mastodon::CLI::Accounts do end end - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end - describe '#create' do shared_examples 'a new user with given email address and username' do it 'creates a new user with the specified email address' do diff --git a/spec/lib/mastodon/cli/cache_spec.rb b/spec/lib/mastodon/cli/cache_spec.rb index 3ab42dc8c..c1ce04710 100644 --- a/spec/lib/mastodon/cli/cache_spec.rb +++ b/spec/lib/mastodon/cli/cache_spec.rb @@ -6,11 +6,7 @@ require 'mastodon/cli/cache' describe Mastodon::CLI::Cache do let(:cli) { described_class.new } - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' describe '#clear' do before { allow(Rails.cache).to receive(:clear) } diff --git a/spec/lib/mastodon/cli/canonical_email_blocks_spec.rb b/spec/lib/mastodon/cli/canonical_email_blocks_spec.rb index eb57a3cd1..6e4675748 100644 --- a/spec/lib/mastodon/cli/canonical_email_blocks_spec.rb +++ b/spec/lib/mastodon/cli/canonical_email_blocks_spec.rb @@ -6,11 +6,7 @@ require 'mastodon/cli/canonical_email_blocks' describe Mastodon::CLI::CanonicalEmailBlocks do let(:cli) { described_class.new } - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' describe '#find' do let(:arguments) { ['user@example.com'] } diff --git a/spec/lib/mastodon/cli/domains_spec.rb b/spec/lib/mastodon/cli/domains_spec.rb index ea58845c0..765b63e2a 100644 --- a/spec/lib/mastodon/cli/domains_spec.rb +++ b/spec/lib/mastodon/cli/domains_spec.rb @@ -4,9 +4,5 @@ require 'rails_helper' require 'mastodon/cli/domains' describe Mastodon::CLI::Domains do - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' end diff --git a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb index 333ae3f2b..060943b18 100644 --- a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb +++ b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb @@ -4,9 +4,5 @@ require 'rails_helper' require 'mastodon/cli/email_domain_blocks' describe Mastodon::CLI::EmailDomainBlocks do - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' end diff --git a/spec/lib/mastodon/cli/emoji_spec.rb b/spec/lib/mastodon/cli/emoji_spec.rb index 9b5865372..5d109eb52 100644 --- a/spec/lib/mastodon/cli/emoji_spec.rb +++ b/spec/lib/mastodon/cli/emoji_spec.rb @@ -4,9 +4,5 @@ require 'rails_helper' require 'mastodon/cli/emoji' describe Mastodon::CLI::Emoji do - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' end diff --git a/spec/lib/mastodon/cli/feeds_spec.rb b/spec/lib/mastodon/cli/feeds_spec.rb index 030f08721..e16113c85 100644 --- a/spec/lib/mastodon/cli/feeds_spec.rb +++ b/spec/lib/mastodon/cli/feeds_spec.rb @@ -6,11 +6,7 @@ require 'mastodon/cli/feeds' describe Mastodon::CLI::Feeds do let(:cli) { described_class.new } - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' describe '#build' do before { Fabricate(:account) } diff --git a/spec/lib/mastodon/cli/ip_blocks_spec.rb b/spec/lib/mastodon/cli/ip_blocks_spec.rb index 030d9fcb1..684314dc7 100644 --- a/spec/lib/mastodon/cli/ip_blocks_spec.rb +++ b/spec/lib/mastodon/cli/ip_blocks_spec.rb @@ -6,11 +6,7 @@ require 'mastodon/cli/ip_blocks' describe Mastodon::CLI::IpBlocks do let(:cli) { described_class.new } - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' describe '#add' do let(:ip_list) do diff --git a/spec/lib/mastodon/cli/main_spec.rb b/spec/lib/mastodon/cli/main_spec.rb index e3709afe3..b5b5d6906 100644 --- a/spec/lib/mastodon/cli/main_spec.rb +++ b/spec/lib/mastodon/cli/main_spec.rb @@ -4,11 +4,7 @@ require 'rails_helper' require 'mastodon/cli/main' describe Mastodon::CLI::Main do - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' describe 'version' do it 'returns the Mastodon version' do diff --git a/spec/lib/mastodon/cli/maintenance_spec.rb b/spec/lib/mastodon/cli/maintenance_spec.rb index a6789ea5a..95e695ab5 100644 --- a/spec/lib/mastodon/cli/maintenance_spec.rb +++ b/spec/lib/mastodon/cli/maintenance_spec.rb @@ -6,11 +6,7 @@ require 'mastodon/cli/maintenance' describe Mastodon::CLI::Maintenance do let(:cli) { described_class.new } - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' describe '#fix_duplicates' do context 'when the database version is too old' do diff --git a/spec/lib/mastodon/cli/media_spec.rb b/spec/lib/mastodon/cli/media_spec.rb index 305d67d28..15f84c14b 100644 --- a/spec/lib/mastodon/cli/media_spec.rb +++ b/spec/lib/mastodon/cli/media_spec.rb @@ -6,11 +6,7 @@ require 'mastodon/cli/media' describe Mastodon::CLI::Media do let(:cli) { described_class.new } - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' describe '#remove' do context 'with --prune-profiles and --remove-headers' do diff --git a/spec/lib/mastodon/cli/preview_cards_spec.rb b/spec/lib/mastodon/cli/preview_cards_spec.rb index 1e064ed58..a766d250e 100644 --- a/spec/lib/mastodon/cli/preview_cards_spec.rb +++ b/spec/lib/mastodon/cli/preview_cards_spec.rb @@ -6,11 +6,7 @@ require 'mastodon/cli/preview_cards' describe Mastodon::CLI::PreviewCards do let(:cli) { described_class.new } - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' describe '#remove' do context 'with relevant preview cards' do diff --git a/spec/lib/mastodon/cli/search_spec.rb b/spec/lib/mastodon/cli/search_spec.rb index d5cae5bf4..785dc2bd6 100644 --- a/spec/lib/mastodon/cli/search_spec.rb +++ b/spec/lib/mastodon/cli/search_spec.rb @@ -4,9 +4,5 @@ require 'rails_helper' require 'mastodon/cli/search' describe Mastodon::CLI::Search do - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' end diff --git a/spec/lib/mastodon/cli/settings_spec.rb b/spec/lib/mastodon/cli/settings_spec.rb index ae58e74e5..7dcd1110b 100644 --- a/spec/lib/mastodon/cli/settings_spec.rb +++ b/spec/lib/mastodon/cli/settings_spec.rb @@ -4,11 +4,7 @@ require 'rails_helper' require 'mastodon/cli/settings' describe Mastodon::CLI::Settings do - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' describe 'subcommand "registrations"' do let(:cli) { Mastodon::CLI::Registrations.new } diff --git a/spec/lib/mastodon/cli/statuses_spec.rb b/spec/lib/mastodon/cli/statuses_spec.rb index 38ebcd993..70e4e2c08 100644 --- a/spec/lib/mastodon/cli/statuses_spec.rb +++ b/spec/lib/mastodon/cli/statuses_spec.rb @@ -6,11 +6,7 @@ require 'mastodon/cli/statuses' describe Mastodon::CLI::Statuses do let(:cli) { described_class.new } - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' describe '#remove', use_transactional_tests: false do context 'with small batch size' do diff --git a/spec/lib/mastodon/cli/upgrade_spec.rb b/spec/lib/mastodon/cli/upgrade_spec.rb index 9e0ab9d06..817044f7e 100644 --- a/spec/lib/mastodon/cli/upgrade_spec.rb +++ b/spec/lib/mastodon/cli/upgrade_spec.rb @@ -4,9 +4,5 @@ require 'rails_helper' require 'mastodon/cli/upgrade' describe Mastodon::CLI::Upgrade do - describe '.exit_on_failure?' do - it 'returns true' do - expect(described_class.exit_on_failure?).to be true - end - end + it_behaves_like 'CLI Command' end diff --git a/spec/support/examples/cli.rb b/spec/support/examples/cli.rb new file mode 100644 index 000000000..091c842bd --- /dev/null +++ b/spec/support/examples/cli.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +shared_examples 'CLI Command' do + it 'configures Thor to exit on failure' do + expect(described_class.exit_on_failure?).to be true + end + + it 'descends from the CLI base class' do + expect(described_class.new).to be_a(Mastodon::CLI::Base) + end +end From 1564799952ec4b8039eecb28125493814bd56ed0 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 1 Dec 2023 09:18:45 -0500 Subject: [PATCH 44/95] Add spec coverage for media CLI `refresh` command (#28166) --- lib/mastodon/cli/media.rb | 1 + spec/lib/mastodon/cli/media_spec.rb | 73 +++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/lib/mastodon/cli/media.rb b/lib/mastodon/cli/media.rb index c90616177..c3275b799 100644 --- a/lib/mastodon/cli/media.rb +++ b/lib/mastodon/cli/media.rb @@ -265,6 +265,7 @@ module Mastodon::CLI elsif options[:days].present? scope = MediaAttachment.remote else + say('Specify the source of media attachments', :red) exit(1) end diff --git a/spec/lib/mastodon/cli/media_spec.rb b/spec/lib/mastodon/cli/media_spec.rb index 15f84c14b..6d510c1f5 100644 --- a/spec/lib/mastodon/cli/media_spec.rb +++ b/spec/lib/mastodon/cli/media_spec.rb @@ -86,4 +86,77 @@ describe Mastodon::CLI::Media do end end end + + describe '#refresh' do + context 'without any options' do + let(:options) { {} } + + it 'warns about usage and exits' do + expect { cli.invoke(:refresh, [], options) }.to output( + a_string_including('Specify the source') + ).to_stdout.and raise_error(SystemExit) + end + end + + context 'with --status option' do + before do + media_attachment.update(file_file_name: nil) + end + + let(:media_attachment) { Fabricate(:media_attachment, status: status, remote_url: 'https://host.example/asset.jpg') } + let(:options) { { status: status.id } } + let(:status) { Fabricate(:status) } + + it 'redownloads the attachment file' do + expect { cli.invoke(:refresh, [], options) }.to output( + a_string_including('Downloaded 1 media') + ).to_stdout + end + end + + context 'with --account option' do + context 'when the account does not exist' do + let(:options) { { account: 'not-real-user@example.host' } } + + it 'warns about usage and exits' do + expect { cli.invoke(:refresh, [], options) }.to output( + a_string_including('No such account') + ).to_stdout.and raise_error(SystemExit) + end + end + + context 'when the account exists' do + before do + media_attachment.update(file_file_name: nil) + end + + let(:media_attachment) { Fabricate(:media_attachment, account: account) } + let(:options) { { account: account.acct } } + let(:account) { Fabricate(:account) } + + it 'redownloads the attachment file' do + expect { cli.invoke(:refresh, [], options) }.to output( + a_string_including('Downloaded 1 media') + ).to_stdout + end + end + end + + context 'with --domain option' do + before do + media_attachment.update(file_file_name: nil) + end + + let(:domain) { 'example.host' } + let(:media_attachment) { Fabricate(:media_attachment, account: account) } + let(:options) { { domain: domain } } + let(:account) { Fabricate(:account, domain: domain) } + + it 'redownloads the attachment file' do + expect { cli.invoke(:refresh, [], options) }.to output( + a_string_including('Downloaded 1 media') + ).to_stdout + end + end + end end From f70f39dd04886c2ced6d424cdcb8690fcddd52f7 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 1 Dec 2023 10:52:47 -0500 Subject: [PATCH 45/95] Add explicit `dependent: nil` to associations (#28169) --- .rubocop_todo.yml | 14 -------------- app/models/concerns/account/counters.rb | 2 +- app/models/conversation.rb | 2 +- app/models/custom_emoji.rb | 2 +- app/models/custom_emoji_category.rb | 2 +- app/models/domain_block.rb | 2 +- app/models/invite.rb | 2 +- app/models/status.rb | 14 +++++++++----- app/models/user.rb | 8 ++++---- app/models/web/push_subscription.rb | 2 +- 10 files changed, 20 insertions(+), 30 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 9fb163ceb..03543c8e0 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -112,20 +112,6 @@ Rails/HasAndBelongsToMany: - 'app/models/status.rb' - 'app/models/tag.rb' -# Configuration parameters: Include. -# Include: app/models/**/*.rb -Rails/HasManyOrHasOneDependent: - Exclude: - - 'app/models/concerns/account/counters.rb' - - 'app/models/conversation.rb' - - 'app/models/custom_emoji.rb' - - 'app/models/custom_emoji_category.rb' - - 'app/models/domain_block.rb' - - 'app/models/invite.rb' - - 'app/models/status.rb' - - 'app/models/user.rb' - - 'app/models/web/push_subscription.rb' - # Configuration parameters: Include. # Include: app/controllers/**/*.rb, app/mailers/**/*.rb Rails/LexicallyScopedActionFilter: diff --git a/app/models/concerns/account/counters.rb b/app/models/concerns/account/counters.rb index fb69be9b7..448839812 100644 --- a/app/models/concerns/account/counters.rb +++ b/app/models/concerns/account/counters.rb @@ -6,7 +6,7 @@ module Account::Counters ALLOWED_COUNTER_KEYS = %i(statuses_count following_count followers_count).freeze included do - has_one :account_stat, inverse_of: :account + has_one :account_stat, inverse_of: :account, dependent: nil after_save :save_account_stat end diff --git a/app/models/conversation.rb b/app/models/conversation.rb index 5de259962..a7fe14884 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -13,7 +13,7 @@ class Conversation < ApplicationRecord validates :uri, uniqueness: true, if: :uri? - has_many :statuses + has_many :statuses, dependent: nil def local? uri.nil? diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index 717de2772..97b1c63bf 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -37,7 +37,7 @@ class CustomEmoji < ApplicationRecord belongs_to :category, class_name: 'CustomEmojiCategory', optional: true - has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode, inverse_of: false + has_one :local_counterpart, -> { where(domain: nil) }, class_name: 'CustomEmoji', primary_key: :shortcode, foreign_key: :shortcode, inverse_of: false, dependent: nil has_attached_file :image, styles: { static: { format: 'png', convert_options: '-coalesce +profile "!icc,*" +set date:modify +set date:create +set date:timestamp' } }, validate_media_type: false diff --git a/app/models/custom_emoji_category.rb b/app/models/custom_emoji_category.rb index 3c87f2b2e..f48074651 100644 --- a/app/models/custom_emoji_category.rb +++ b/app/models/custom_emoji_category.rb @@ -11,7 +11,7 @@ # class CustomEmojiCategory < ApplicationRecord - has_many :emojis, class_name: 'CustomEmoji', foreign_key: 'category_id', inverse_of: :category + has_many :emojis, class_name: 'CustomEmoji', foreign_key: 'category_id', inverse_of: :category, dependent: nil validates :name, presence: true, uniqueness: true end diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb index a7af2d62e..ff23f8fcc 100644 --- a/app/models/domain_block.rb +++ b/app/models/domain_block.rb @@ -25,7 +25,7 @@ class DomainBlock < ApplicationRecord validates :domain, presence: true, uniqueness: true, domain: true - has_many :accounts, foreign_key: :domain, primary_key: :domain, inverse_of: false + has_many :accounts, foreign_key: :domain, primary_key: :domain, inverse_of: false, dependent: nil delegate :count, to: :accounts, prefix: true scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) } diff --git a/app/models/invite.rb b/app/models/invite.rb index 8e816cef0..c0cbc5845 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -20,7 +20,7 @@ class Invite < ApplicationRecord include Expireable belongs_to :user, inverse_of: :invites - has_many :users, inverse_of: :invite + has_many :users, inverse_of: :invite, dependent: nil scope :available, -> { where(expires_at: nil).or(where('expires_at >= ?', Time.now.utc)) } diff --git a/app/models/status.rb b/app/models/status.rb index 7b1ca69c7..eb7159dc6 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -66,12 +66,14 @@ class Status < ApplicationRecord has_many :bookmarks, inverse_of: :status, dependent: :destroy has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblog, dependent: :destroy has_many :reblogged_by_accounts, through: :reblogs, class_name: 'Account', source: :account - has_many :replies, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :thread + has_many :replies, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :thread, dependent: nil has_many :mentions, dependent: :destroy, inverse_of: :status has_many :mentioned_accounts, through: :mentions, source: :account, class_name: 'Account' - has_many :active_mentions, -> { active }, class_name: 'Mention', inverse_of: :status has_many :media_attachments, dependent: :nullify + # The `dependent` option is enabled by the initial `mentions` association declaration + has_many :active_mentions, -> { active }, class_name: 'Mention', inverse_of: :status # rubocop:disable Rails/HasManyOrHasOneDependent + # Those associations are used for the private search index has_many :local_mentioned, -> { merge(Account.local) }, through: :active_mentions, source: :account has_many :local_favorited, -> { merge(Account.local) }, through: :favourites, source: :account @@ -80,11 +82,13 @@ class Status < ApplicationRecord has_and_belongs_to_many :tags - has_one :preview_cards_status, inverse_of: :status # Because of a composite primary key, the dependent option cannot be used + # Because of a composite primary key, the `dependent` option cannot be used on this association + has_one :preview_cards_status, inverse_of: :status # rubocop:disable Rails/HasManyOrHasOneDependent + has_one :notification, as: :activity, dependent: :destroy - has_one :status_stat, inverse_of: :status + has_one :status_stat, inverse_of: :status, dependent: nil has_one :poll, inverse_of: :status, dependent: :destroy - has_one :trend, class_name: 'StatusTrend', inverse_of: :status + has_one :trend, class_name: 'StatusTrend', inverse_of: :status, dependent: nil validates :uri, uniqueness: true, presence: true, unless: :local? validates :text, presence: true, unless: -> { with_media? || reblog? } diff --git a/app/models/user.rb b/app/models/user.rb index 550f8bfd3..a1574c02a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -84,12 +84,12 @@ class User < ApplicationRecord belongs_to :role, class_name: 'UserRole', optional: true accepts_nested_attributes_for :account - has_many :applications, class_name: 'Doorkeeper::Application', as: :owner - has_many :backups, inverse_of: :user - has_many :invites, inverse_of: :user + has_many :applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: nil + has_many :backups, inverse_of: :user, dependent: nil + has_many :invites, inverse_of: :user, dependent: nil has_many :markers, inverse_of: :user, dependent: :destroy has_many :webauthn_credentials, dependent: :destroy - has_many :ips, class_name: 'UserIp', inverse_of: :user + has_many :ips, class_name: 'UserIp', inverse_of: :user, dependent: nil has_one :invite_request, class_name: 'UserInviteRequest', inverse_of: :user, dependent: :destroy accepts_nested_attributes_for :invite_request, reject_if: ->(attributes) { attributes['text'].blank? && !Setting.require_invite_text } diff --git a/app/models/web/push_subscription.rb b/app/models/web/push_subscription.rb index 0ffbe068e..a3a2ec3f0 100644 --- a/app/models/web/push_subscription.rb +++ b/app/models/web/push_subscription.rb @@ -19,7 +19,7 @@ class Web::PushSubscription < ApplicationRecord belongs_to :user, optional: true belongs_to :access_token, class_name: 'Doorkeeper::AccessToken', optional: true - has_one :session_activation, foreign_key: 'web_push_subscription_id', inverse_of: :web_push_subscription + has_one :session_activation, foreign_key: 'web_push_subscription_id', inverse_of: :web_push_subscription, dependent: nil validates :endpoint, presence: true validates :key_p256dh, presence: true From a98fccf84e9deed1ea1aa52b5adbc0c1f0bdfaab Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 1 Dec 2023 10:52:52 -0500 Subject: [PATCH 46/95] Fix `Style/SelfAssignment` cop (#28171) --- lib/mastodon/snowflake.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/snowflake.rb b/lib/mastodon/snowflake.rb index 0a596b294..ec4f6140c 100644 --- a/lib/mastodon/snowflake.rb +++ b/lib/mastodon/snowflake.rb @@ -99,7 +99,7 @@ module Mastodon::Snowflake def id_at(timestamp, with_random: true) id = timestamp.to_i * 1000 id += rand(1000) if with_random - id = id << 16 + id <<= 16 id += rand(2**16) if with_random id end From d83d01eb1e2f2ae56cd3f3a40e254fcfec51360c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 1 Dec 2023 10:52:56 -0500 Subject: [PATCH 47/95] Fix `Lint/RedundantSafeNavigation` cop (#28172) --- app/controllers/api/v1/accounts_controller.rb | 2 +- app/models/trends/history.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index 653529316..be251b425 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -49,7 +49,7 @@ class Api::V1::AccountsController < Api::BaseController end def mute - MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), duration: (params[:duration]&.to_i || 0)) + MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), duration: params[:duration].to_i) render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships end diff --git a/app/models/trends/history.rb b/app/models/trends/history.rb index db3689933..21331f00d 100644 --- a/app/models/trends/history.rb +++ b/app/models/trends/history.rb @@ -37,7 +37,7 @@ class Trends::History end def uses - with_redis { |redis| redis.get(key_for(:uses))&.to_i || 0 } + with_redis { |redis| redis.get(key_for(:uses)).to_i } end def add(account_id) From aa8563d43df93647db50e52eddccba63500e0c88 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 1 Dec 2023 10:53:15 -0500 Subject: [PATCH 48/95] Fix `Style/SuperWithArgsParentheses` cop (#28174) --- spec/support/signed_request_helpers.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/support/signed_request_helpers.rb b/spec/support/signed_request_helpers.rb index 33d7dba6b..eba4095e4 100644 --- a/spec/support/signed_request_helpers.rb +++ b/spec/support/signed_request_helpers.rb @@ -2,7 +2,7 @@ module SignedRequestHelpers def get(path, headers: nil, sign_with: nil, **args) - return super path, headers: headers, **args if sign_with.nil? + return super(path, headers: headers, **args) if sign_with.nil? headers ||= {} headers['Date'] = Time.now.utc.httpdate @@ -16,6 +16,6 @@ module SignedRequestHelpers headers['Signature'] = "keyId=\"#{key_id}\",algorithm=\"rsa-sha256\",headers=\"#{signed_headers.keys.join(' ').downcase}\",signature=\"#{signature}\"" - super path, headers: headers, **args + super(path, headers: headers, **args) end end From 5631f139c1f6f923923a82f8c55d5ab0820a2788 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 1 Dec 2023 10:53:35 -0500 Subject: [PATCH 49/95] Fix `Lint/SymbolConversion` cop (#28175) --- app/models/concerns/remotable.rb | 2 +- app/models/user_settings/setting.rb | 2 +- config/deploy.rb | 2 +- spec/models/concerns/remotable_spec.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/concerns/remotable.rb b/app/models/concerns/remotable.rb index cb8f46e68..bd8b6f4eb 100644 --- a/app/models/concerns/remotable.rb +++ b/app/models/concerns/remotable.rb @@ -5,7 +5,7 @@ module Remotable class_methods do def remotable_attachment(attachment_name, limit, suppress_errors: true, download_on_assign: true, attribute_name: nil) - attribute_name ||= "#{attachment_name}_remote_url".to_sym + attribute_name ||= :"#{attachment_name}_remote_url" define_method("download_#{attachment_name}!") do |url = nil| url ||= self[attribute_name] diff --git a/app/models/user_settings/setting.rb b/app/models/user_settings/setting.rb index 9c1701744..1ab6cbeab 100644 --- a/app/models/user_settings/setting.rb +++ b/app/models/user_settings/setting.rb @@ -62,7 +62,7 @@ class UserSettings::Setting def key if namespace - "#{namespace}.#{name}".to_sym + :"#{namespace}.#{name}" else name end diff --git a/config/deploy.rb b/config/deploy.rb index b19567a72..75bfcc26f 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -20,7 +20,7 @@ namespace :systemd do SYSTEMD_SERVICES.each do |service| SERVICE_ACTIONS.each do |action| desc "Perform a #{action} on #{service} service" - task "#{service}:#{action}".to_sym do + task :"#{service}:#{action}" do on roles(:app) do # runs e.g. "sudo restart mastodon-sidekiq.service" sudo :systemctl, action, "#{fetch(:application)}-#{service}.service" diff --git a/spec/models/concerns/remotable_spec.rb b/spec/models/concerns/remotable_spec.rb index db690da3c..9f6aeb7fb 100644 --- a/spec/models/concerns/remotable_spec.rb +++ b/spec/models/concerns/remotable_spec.rb @@ -31,7 +31,7 @@ RSpec.describe Remotable do end end - let(:attribute_name) { "#{hoge}_remote_url".to_sym } + let(:attribute_name) { :"#{hoge}_remote_url" } let(:code) { 200 } let(:file) { 'filename="foo.txt"' } let(:foo) { foo_class.new } From 3bc437b99a19bba26c0c637b18ce6c7410d5a606 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 1 Dec 2023 11:00:44 -0500 Subject: [PATCH 50/95] Fix `Style/RedundantParentheses` cop (#28176) --- app/controllers/api/base_controller.rb | 2 +- app/lib/feed_manager.rb | 4 ++-- lib/mastodon/cli/media.rb | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index c81ba32b0..b3c60fcaf 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -64,7 +64,7 @@ class Api::BaseController < ApplicationController end def doorkeeper_unauthorized_render_options(error: nil) - { json: { error: (error.try(:description) || 'Not authorized') } } + { json: { error: error.try(:description) || 'Not authorized' } } end def doorkeeper_forbidden_render_options(*) diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index 4e645a11f..53767486f 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -420,8 +420,8 @@ class FeedManager check_for_blocks = status.active_mentions.pluck(:account_id) check_for_blocks.push(status.in_reply_to_account) if status.reply? && !status.in_reply_to_account_id.nil? - should_filter = blocks_or_mutes?(receiver_id, check_for_blocks, :mentions) # Filter if it's from someone I blocked, in reply to someone I blocked, or mentioning someone I blocked (or muted) - should_filter ||= (status.account.silenced? && !Follow.where(account_id: receiver_id, target_account_id: status.account_id).exists?) # of if the account is silenced and I'm not following them + should_filter = blocks_or_mutes?(receiver_id, check_for_blocks, :mentions) # Filter if it's from someone I blocked, in reply to someone I blocked, or mentioning someone I blocked (or muted) + should_filter ||= status.account.silenced? && !Follow.where(account_id: receiver_id, target_account_id: status.account_id).exists? # of if the account is silenced and I'm not following them should_filter end diff --git a/lib/mastodon/cli/media.rb b/lib/mastodon/cli/media.rb index c3275b799..a796bb729 100644 --- a/lib/mastodon/cli/media.rb +++ b/lib/mastodon/cli/media.rb @@ -48,8 +48,8 @@ module Mastodon::CLI next if account.avatar.blank? && account.header.blank? next if options[:remove_headers] && account.header.blank? - size = (account.header_file_size || 0) - size += (account.avatar_file_size || 0) if options[:prune_profiles] + size = account.header_file_size || 0 + size += account.avatar_file_size || 0 if options[:prune_profiles] unless dry_run? account.header.destroy From 469ee2ae36d0c4b355fd5486c294133be75288a0 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 1 Dec 2023 11:07:19 -0500 Subject: [PATCH 51/95] Fix `Style/HashEachMethods` cop (#28173) --- db/seeds/03_roles.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/seeds/03_roles.rb b/db/seeds/03_roles.rb index 8b359582b..95a989a95 100644 --- a/db/seeds/03_roles.rb +++ b/db/seeds/03_roles.rb @@ -6,6 +6,6 @@ UserRole.everyone # Create default roles defined in config file default_roles = YAML.load_file(Rails.root.join('config', 'roles.yml')) -default_roles.each do |_, config| +default_roles.each_value do |config| UserRole.create_with(position: config['position'], permissions_as_keys: config['permissions'], highlighted: true).find_or_create_by(name: config['name']) end From 456597dae5251af841e46ab0608e0d44a7de1197 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Dec 2023 17:22:15 +0100 Subject: [PATCH 52/95] Update dependency doorkeeper to v5.6.8 (#28177) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 40f85c7d4..4c78e6b0a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -245,7 +245,7 @@ GEM docile (1.4.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - doorkeeper (5.6.7) + doorkeeper (5.6.8) railties (>= 5) dotenv (2.8.1) dotenv-rails (2.8.1) @@ -617,7 +617,7 @@ GEM redlock (1.3.2) redis (>= 3.0.0, < 6.0) regexp_parser (2.8.2) - reline (0.4.0) + reline (0.4.1) io-console (~> 0.5) request_store (1.5.1) rack (>= 1.4) From 0ca7a50e96f8ff596de7432462a5e92a98de5e38 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:50:28 +0100 Subject: [PATCH 53/95] Update devDependencies (non-major) (#28200) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 142 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 102 insertions(+), 40 deletions(-) diff --git a/yarn.lock b/yarn.lock index ad792d26c..3ec023533 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2893,8 +2893,8 @@ __metadata: linkType: hard "@testing-library/jest-dom@npm:^6.0.0": - version: 6.1.4 - resolution: "@testing-library/jest-dom@npm:6.1.4" + version: 6.1.5 + resolution: "@testing-library/jest-dom@npm:6.1.5" dependencies: "@adobe/css-tools": "npm:^4.3.1" "@babel/runtime": "npm:^7.9.2" @@ -2918,7 +2918,7 @@ __metadata: optional: true vitest: optional: true - checksum: 2e23f120613fd8ae6d5169bbc94f1a2e4c82b07182057dc94db8ec54ebf32555833442e6c43a187e59715d83704ffb5df49ba88a71f6f32d2683f3d95ba721c7 + checksum: f3643a56fcd970b5c7e8fd10faf3c4817d8ab0e74fb1198d726643bdc5ac675ceaac3b0068c5b4fbad254470e8f98ed50028741de875a29ceaa2f854570979c9 languageName: node linkType: hard @@ -4168,12 +4168,12 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^5.0.0": - version: 5.0.0 - resolution: "ansi-escapes@npm:5.0.0" +"ansi-escapes@npm:^6.2.0": + version: 6.2.0 + resolution: "ansi-escapes@npm:6.2.0" dependencies: - type-fest: "npm:^1.0.2" - checksum: f705cc7fbabb981ddf51562cd950792807bccd7260cc3d9478a619dda62bff6634c87ca100f2545ac7aade9b72652c4edad8c7f0d31a0b949b5fa58f33eaf0d0 + type-fest: "npm:^3.0.0" + checksum: 3eec75deedd8b10192c5f98e4cd9715cc3ff268d33fc463c24b7d22446668bfcd4ad1803993ea89c0f51f88b5a3399572bacb7c8cb1a067fc86e189c5f3b0c7e languageName: node linkType: hard @@ -4239,7 +4239,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0": +"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": version: 6.2.1 resolution: "ansi-styles@npm:6.2.1" checksum: 5d1ec38c123984bcedd996eac680d548f31828bd679a66db2bdf11844634dde55fec3efa9c6bb1d89056a5e79c1ac540c4c784d592ea1d25028a92227d2f2d5c @@ -5584,13 +5584,13 @@ __metadata: languageName: node linkType: hard -"cli-truncate@npm:^3.1.0": - version: 3.1.0 - resolution: "cli-truncate@npm:3.1.0" +"cli-truncate@npm:^4.0.0": + version: 4.0.0 + resolution: "cli-truncate@npm:4.0.0" dependencies: slice-ansi: "npm:^5.0.0" - string-width: "npm:^5.0.0" - checksum: a19088878409ec0e5dc2659a5166929629d93cfba6d68afc9cde2282fd4c751af5b555bf197047e31c87c574396348d011b7aa806fec29c4139ea4f7f00b324c + string-width: "npm:^7.0.0" + checksum: d7f0b73e3d9b88cb496e6c086df7410b541b56a43d18ade6a573c9c18bd001b1c3fba1ad578f741a4218fdc794d042385f8ac02c25e1c295a2d8b9f3cb86eb4c languageName: node linkType: hard @@ -6997,7 +6997,7 @@ __metadata: languageName: node linkType: hard -"emoji-regex@npm:10.3.0, emoji-regex@npm:^10.2.1": +"emoji-regex@npm:10.3.0, emoji-regex@npm:^10.2.1, emoji-regex@npm:^10.3.0": version: 10.3.0 resolution: "emoji-regex@npm:10.3.0" checksum: b4838e8dcdceb44cf47f59abe352c25ff4fe7857acaf5fb51097c427f6f75b44d052eb907a7a3b86f86bc4eae3a93f5c2b7460abe79c407307e6212d65c91163 @@ -8387,6 +8387,13 @@ __metadata: languageName: node linkType: hard +"get-east-asian-width@npm:^1.0.0": + version: 1.2.0 + resolution: "get-east-asian-width@npm:1.2.0" + checksum: 914b1e217cf38436c24b4c60b4c45289e39a45bf9e65ef9fd343c2815a1a02b8a0215aeec8bf9c07c516089004b6e3826332481f40a09529fcadbf6e579f286b + languageName: node + linkType: hard + "get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2": version: 1.2.2 resolution: "get-intrinsic@npm:1.2.2" @@ -9610,6 +9617,15 @@ __metadata: languageName: node linkType: hard +"is-fullwidth-code-point@npm:^5.0.0": + version: 5.0.0 + resolution: "is-fullwidth-code-point@npm:5.0.0" + dependencies: + get-east-asian-width: "npm:^1.0.0" + checksum: cd591b27d43d76b05fa65ed03eddce57a16e1eca0b7797ff7255de97019bcaf0219acfc0c4f7af13319e13541f2a53c0ace476f442b13267b9a6a7568f2b65c8 + languageName: node + linkType: hard + "is-generator-fn@npm:^2.0.0": version: 2.1.0 resolution: "is-generator-fn@npm:2.1.0" @@ -10892,7 +10908,14 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:2.1.0, lilconfig@npm:^2.1.0": +"lilconfig@npm:3.0.0": + version: 3.0.0 + resolution: "lilconfig@npm:3.0.0" + checksum: 7f5ee7a658dc016cacf146815e8d88b06f06f4402823b8b0934e305a57a197f55ccc9c5cd4fb5ea1b2b821c8ccaf2d54abd59602a4931af06eabda332388d3e6 + languageName: node + linkType: hard + +"lilconfig@npm:^2.1.0": version: 2.1.0 resolution: "lilconfig@npm:2.1.0" checksum: 64645641aa8d274c99338e130554abd6a0190533c0d9eb2ce7ebfaf2e05c7d9961f3ffe2bfa39efd3b60c521ba3dd24fa236fe2775fc38501bf82bf49d4678b8 @@ -10907,36 +10930,36 @@ __metadata: linkType: hard "lint-staged@npm:^15.0.0": - version: 15.1.0 - resolution: "lint-staged@npm:15.1.0" + version: 15.2.0 + resolution: "lint-staged@npm:15.2.0" dependencies: chalk: "npm:5.3.0" commander: "npm:11.1.0" debug: "npm:4.3.4" execa: "npm:8.0.1" - lilconfig: "npm:2.1.0" - listr2: "npm:7.0.2" + lilconfig: "npm:3.0.0" + listr2: "npm:8.0.0" micromatch: "npm:4.0.5" pidtree: "npm:0.6.0" string-argv: "npm:0.3.2" yaml: "npm:2.3.4" bin: lint-staged: bin/lint-staged.js - checksum: d427408be98df7558e918593cb765d5caaa67a5cdca89671fb54280a6c959f4e448db36d4f85e8e0bd9c2c1e996aa133916925cf47c9df573b47308d5e298d84 + checksum: 4a1ff25dd06dbd4346fd244c9a0ebb936532ba18c0caedeb895c2e232f3c6c5fd08f6667624716660bc29e3e0f9f0440a9175114394616e991ebd5fab4b1f092 languageName: node linkType: hard -"listr2@npm:7.0.2": - version: 7.0.2 - resolution: "listr2@npm:7.0.2" +"listr2@npm:8.0.0": + version: 8.0.0 + resolution: "listr2@npm:8.0.0" dependencies: - cli-truncate: "npm:^3.1.0" + cli-truncate: "npm:^4.0.0" colorette: "npm:^2.0.20" eventemitter3: "npm:^5.0.1" - log-update: "npm:^5.0.1" + log-update: "npm:^6.0.0" rfdc: "npm:^1.3.0" - wrap-ansi: "npm:^8.1.0" - checksum: 37b6501be84ebea66dcce07c5f86c224aff0c01c9fb43f5055cc38a063030281d58198aad0aad481f174438309831ddf5f763b890e820cd7b7b4f4a5dfa229c9 + wrap-ansi: "npm:^9.0.0" + checksum: 6e356df9127c68b69186c927c993645223557e941a76b0bb210e35786aedc53f577df437251db804606ff37ac509c5d945289a84b3daee7fadf2e3dcb889ecc9 languageName: node linkType: hard @@ -11104,16 +11127,16 @@ __metadata: languageName: node linkType: hard -"log-update@npm:^5.0.1": - version: 5.0.1 - resolution: "log-update@npm:5.0.1" +"log-update@npm:^6.0.0": + version: 6.0.0 + resolution: "log-update@npm:6.0.0" dependencies: - ansi-escapes: "npm:^5.0.0" + ansi-escapes: "npm:^6.2.0" cli-cursor: "npm:^4.0.0" - slice-ansi: "npm:^5.0.0" - strip-ansi: "npm:^7.0.1" - wrap-ansi: "npm:^8.0.1" - checksum: 1050ea2027e80f32e132aace909987cb00c2719368c78b82ffca681a5b3f4020eeb5f4b4e310c47c35c6c36aff258c1d1bc51485ac44d6fdac9eb0a4275c539f + slice-ansi: "npm:^7.0.0" + strip-ansi: "npm:^7.1.0" + wrap-ansi: "npm:^9.0.0" + checksum: e0b3c3401ef49ce3eb17e2f83d644765e4f7988498fc1344eaa4f31ab30e510dcc469a7fb64dc01bd1c8d9237d917598fa677a9818705fb3774c10f6e9d4b27c languageName: node linkType: hard @@ -15050,6 +15073,16 @@ __metadata: languageName: node linkType: hard +"slice-ansi@npm:^7.0.0": + version: 7.1.0 + resolution: "slice-ansi@npm:7.1.0" + dependencies: + ansi-styles: "npm:^6.2.1" + is-fullwidth-code-point: "npm:^5.0.0" + checksum: 631c971d4abf56cf880f034d43fcc44ff883624867bf11ecbd538c47343911d734a4656d7bc02362b40b89d765652a7f935595441e519b59e2ad3f4d5d6fe7ca + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -15493,7 +15526,7 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^5.0.0, string-width@npm:^5.0.1, string-width@npm:^5.1.2": +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": version: 5.1.2 resolution: "string-width@npm:5.1.2" dependencies: @@ -15504,6 +15537,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^7.0.0": + version: 7.0.0 + resolution: "string-width@npm:7.0.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 8ffaeeccf4a56ccce5b6235d0b99ee3a581e3e3e5d453708efe7aa8e264fa3a858b4fe2244310cb71c6a20d8c05921cedc8b2ccd88cbaad9f5c92051ff68edc6 + languageName: node + linkType: hard + "string.prototype.matchall@npm:^4.0.6, string.prototype.matchall@npm:^4.0.8": version: 4.0.8 resolution: "string.prototype.matchall@npm:4.0.8" @@ -15618,7 +15662,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^7.0.1": +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": version: 7.1.0 resolution: "strip-ansi@npm:7.1.0" dependencies: @@ -16379,13 +16423,20 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^1.0.1, type-fest@npm:^1.0.2, type-fest@npm:^1.2.1, type-fest@npm:^1.2.2": +"type-fest@npm:^1.0.1, type-fest@npm:^1.2.1, type-fest@npm:^1.2.2": version: 1.4.0 resolution: "type-fest@npm:1.4.0" checksum: a3c0f4ee28ff6ddf800d769eafafcdeab32efa38763c1a1b8daeae681920f6e345d7920bf277245235561d8117dab765cb5f829c76b713b4c9de0998a5397141 languageName: node linkType: hard +"type-fest@npm:^3.0.0": + version: 3.13.1 + resolution: "type-fest@npm:3.13.1" + checksum: 547d22186f73a8c04590b70dcf63baff390078c75ea8acd366bbd510fd0646e348bd1970e47ecf795b7cff0b41d26e9c475c1fedd6ef5c45c82075fbf916b629 + languageName: node + linkType: hard + "type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" @@ -17613,7 +17664,7 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^8.0.1, wrap-ansi@npm:^8.1.0": +"wrap-ansi@npm:^8.1.0": version: 8.1.0 resolution: "wrap-ansi@npm:8.1.0" dependencies: @@ -17624,6 +17675,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^9.0.0": + version: 9.0.0 + resolution: "wrap-ansi@npm:9.0.0" + dependencies: + ansi-styles: "npm:^6.2.1" + string-width: "npm:^7.0.0" + strip-ansi: "npm:^7.1.0" + checksum: a139b818da9573677548dd463bd626a5a5286271211eb6e4e82f34a4f643191d74e6d4a9bb0a3c26ec90e6f904f679e0569674ac099ea12378a8b98e20706066 + languageName: node + linkType: hard + "wrappy@npm:1": version: 1.0.2 resolution: "wrappy@npm:1.0.2" From b4fef6c26fe63aeda60a6359a17a7f357b2c923e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:50:43 +0100 Subject: [PATCH 54/95] Update DefinitelyTyped types (non-major) (#28199) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3ec023533..288d75925 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3495,13 +3495,13 @@ __metadata: linkType: hard "@types/react@npm:*, @types/react@npm:16 || 17 || 18, @types/react@npm:>=16.9.11, @types/react@npm:^18.2.7": - version: 18.2.38 - resolution: "@types/react@npm:18.2.38" + version: 18.2.41 + resolution: "@types/react@npm:18.2.41" dependencies: "@types/prop-types": "npm:*" "@types/scheduler": "npm:*" csstype: "npm:^3.0.2" - checksum: 56edd4756081b677e38ee23ad6d340658c5e2468785cb20968318cec357e1ea7ccf3ecd9534981741192dd1b894200acfaf0f1551b4795c6077668f6afc19345 + checksum: 5cc72491ce8be95e7bbedd8bf039ca971772ecd22d989feb045af7e73247c7e6cff25a2f1c2200be461fb2f6b5aacef739e1ba9fd83c744209dfd3ce8aa75afe languageName: node linkType: hard @@ -3657,11 +3657,11 @@ __metadata: linkType: hard "@types/ws@npm:^8.5.9": - version: 8.5.9 - resolution: "@types/ws@npm:8.5.9" + version: 8.5.10 + resolution: "@types/ws@npm:8.5.10" dependencies: "@types/node": "npm:*" - checksum: 678bdd6461c4653f2975c537fb673cb1918c331558e2d2422b69761c9ced67200bb07c664e2593f3864077a891cb7c13ef2a40d303b4aacb06173d095d8aa3ce + checksum: e9af279b984c4a04ab53295a40aa95c3e9685f04888df5c6920860d1dd073fcc57c7bd33578a04b285b2c655a0b52258d34bee0a20569dca8defb8393e1e5d29 languageName: node linkType: hard From 3ec263bf155c87dd5c513f181682e051e9f3e105 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:51:08 +0100 Subject: [PATCH 55/95] Update dependency irb to v1.10.0 (#28198) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4c78e6b0a..6820c12f9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -377,7 +377,7 @@ GEM terminal-table (>= 1.5.1) idn-ruby (0.1.5) io-console (0.6.0) - irb (1.9.1) + irb (1.10.0) rdoc reline (>= 0.3.8) jmespath (1.6.2) @@ -747,7 +747,7 @@ GEM statsd-ruby (1.5.0) stoplight (3.0.2) redlock (~> 1.0) - stringio (3.0.9) + stringio (3.1.0) strong_migrations (1.6.4) activerecord (>= 5.2) swd (1.3.0) From d848d8d87cbef49f5f4635b3378b582464bae98a Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 4 Dec 2023 03:52:21 -0500 Subject: [PATCH 56/95] Add helper methods for domains allow and export blocks files (#28196) --- .../admin/export_domain_allows_controller_spec.rb | 10 ++++++++-- .../admin/export_domain_blocks_controller_spec.rb | 8 +++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/spec/controllers/admin/export_domain_allows_controller_spec.rb b/spec/controllers/admin/export_domain_allows_controller_spec.rb index e1e5ecc1f..0a2e34262 100644 --- a/spec/controllers/admin/export_domain_allows_controller_spec.rb +++ b/spec/controllers/admin/export_domain_allows_controller_spec.rb @@ -24,7 +24,7 @@ RSpec.describe Admin::ExportDomainAllowsController do get :export, params: { format: :csv } expect(response).to have_http_status(200) - expect(response.body).to eq(File.read(File.join(file_fixture_path, 'domain_allows.csv'))) + expect(response.body).to eq(domain_allows_csv_file) end end @@ -40,7 +40,7 @@ RSpec.describe Admin::ExportDomainAllowsController do # Domains should now be added get :export, params: { format: :csv } expect(response).to have_http_status(200) - expect(response.body).to eq(File.read(File.join(file_fixture_path, 'domain_allows.csv'))) + expect(response.body).to eq(domain_allows_csv_file) end it 'displays error on no file selected' do @@ -49,4 +49,10 @@ RSpec.describe Admin::ExportDomainAllowsController do expect(flash[:error]).to eq(I18n.t('admin.export_domain_allows.no_file')) end end + + private + + def domain_allows_csv_file + File.read(File.join(file_fixture_path, 'domain_allows.csv')) + end end diff --git a/spec/controllers/admin/export_domain_blocks_controller_spec.rb b/spec/controllers/admin/export_domain_blocks_controller_spec.rb index 5a282c957..bfcccfa06 100644 --- a/spec/controllers/admin/export_domain_blocks_controller_spec.rb +++ b/spec/controllers/admin/export_domain_blocks_controller_spec.rb @@ -26,7 +26,13 @@ RSpec.describe Admin::ExportDomainBlocksController do get :export, params: { format: :csv } expect(response).to have_http_status(200) - expect(response.body).to eq(File.read(File.join(file_fixture_path, 'domain_blocks.csv'))) + expect(response.body).to eq(domain_blocks_csv_file) + end + + private + + def domain_blocks_csv_file + File.read(File.join(file_fixture_path, 'domain_blocks.csv')) end end From 154fb95e44da94d8473ecdc081cf8b9648f41a52 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:52:53 +0100 Subject: [PATCH 57/95] Update dependency postcss to v8.4.32 (#28185) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 288d75925..4226fcc0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11784,12 +11784,12 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.6": - version: 3.3.6 - resolution: "nanoid@npm:3.3.6" +"nanoid@npm:^3.3.7": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" bin: nanoid: bin/nanoid.cjs - checksum: 606b355960d0fcbe3d27924c4c52ef7d47d3b57208808ece73279420d91469b01ec1dce10fae512b6d4a8c5a5432b352b228336a8b2202a6ea68e67fa348e2ee + checksum: e3fb661aa083454f40500473bb69eedb85dc160e763150b9a2c567c7e9ff560ce028a9f833123b618a6ea742e311138b591910e795614a629029e86e180660f3 languageName: node linkType: hard @@ -13218,13 +13218,13 @@ __metadata: linkType: hard "postcss@npm:^8.2.15, postcss@npm:^8.4.24, postcss@npm:^8.4.28": - version: 8.4.31 - resolution: "postcss@npm:8.4.31" + version: 8.4.32 + resolution: "postcss@npm:8.4.32" dependencies: - nanoid: "npm:^3.3.6" + nanoid: "npm:^3.3.7" picocolors: "npm:^1.0.0" source-map-js: "npm:^1.0.2" - checksum: 748b82e6e5fc34034dcf2ae88ea3d11fd09f69b6c50ecdd3b4a875cfc7cdca435c958b211e2cb52355422ab6fccb7d8f2f2923161d7a1b281029e4a913d59acf + checksum: 39308a9195fa34d4dbdd7b58a896cff0c7809f84f7a4ac1b95b68ca86c9138a395addff33075668ed3983d41b90aac05754c445237a9365eb1c3a5602ebd03ad languageName: node linkType: hard From 19ad51253d255c26ba71b0754c8b21197fcd0113 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 4 Dec 2023 04:02:40 -0500 Subject: [PATCH 58/95] Prevent triple-subject run in admin/domain_blocks spec (#28195) --- .../admin/domain_blocks_controller_spec.rb | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/spec/controllers/admin/domain_blocks_controller_spec.rb b/spec/controllers/admin/domain_blocks_controller_spec.rb index 13826be36..22960f531 100644 --- a/spec/controllers/admin/domain_blocks_controller_spec.rb +++ b/spec/controllers/admin/domain_blocks_controller_spec.rb @@ -192,16 +192,11 @@ RSpec.describe Admin::DomainBlocksController do let(:original_severity) { 'suspend' } let(:new_severity) { 'silence' } - it 'changes the block severity' do - expect { subject }.to change { domain_block.reload.severity }.from('suspend').to('silence') - end - - it 'undoes individual suspensions' do - expect { subject }.to change { remote_account.reload.suspended? }.from(true).to(false) - end - - it 'performs individual silences' do - expect { subject }.to change { remote_account.reload.silenced? }.from(false).to(true) + it 'changes the block severity, suspensions, and silences' do + expect { subject } + .to change_severity('suspend', 'silence') + .and change_suspended(true, false) + .and change_silenced(false, true) end end @@ -209,17 +204,26 @@ RSpec.describe Admin::DomainBlocksController do let(:original_severity) { 'silence' } let(:new_severity) { 'suspend' } - it 'changes the block severity' do - expect { subject }.to change { domain_block.reload.severity }.from('silence').to('suspend') + it 'changes the block severity, silences, and suspensions' do + expect { subject } + .to change_severity('silence', 'suspend') + .and change_silenced(true, false) + .and change_suspended(false, true) end + end - it 'undoes individual silences' do - expect { subject }.to change { remote_account.reload.silenced? }.from(true).to(false) - end + private - it 'performs individual suspends' do - expect { subject }.to change { remote_account.reload.suspended? }.from(false).to(true) - end + def change_severity(from, to) + change { domain_block.reload.severity }.from(from).to(to) + end + + def change_silenced(from, to) + change { remote_account.reload.silenced? }.from(from).to(to) + end + + def change_suspended(from, to) + change { remote_account.reload.suspended? }.from(from).to(to) end end From 1bf2230fd1a25a1f8c012e4ff70c08a0a48d88d3 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 4 Dec 2023 04:08:47 -0500 Subject: [PATCH 59/95] Add spec coverage for `CLI::Upgrade#storage_schema` command (#28180) --- spec/lib/mastodon/cli/upgrade_spec.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/lib/mastodon/cli/upgrade_spec.rb b/spec/lib/mastodon/cli/upgrade_spec.rb index 817044f7e..0d6494eee 100644 --- a/spec/lib/mastodon/cli/upgrade_spec.rb +++ b/spec/lib/mastodon/cli/upgrade_spec.rb @@ -4,5 +4,24 @@ require 'rails_helper' require 'mastodon/cli/upgrade' describe Mastodon::CLI::Upgrade do + let(:cli) { described_class.new } + it_behaves_like 'CLI Command' + + describe '#storage_schema' do + context 'with records that dont need upgrading' do + let(:options) { {} } + + before do + Fabricate(:account) + Fabricate(:media_attachment) + end + + it 'does not upgrade storage for the attachments' do + expect { cli.invoke(:storage_schema, [], options) }.to output( + a_string_including('Upgraded storage schema of 0 records') + ).to_stdout + end + end + end end From 9603198982fce7e064a4ec60ac36420173a91cd5 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 4 Dec 2023 04:09:05 -0500 Subject: [PATCH 60/95] Add spec coverage for `CLI::Domains#purge` command (#28179) --- spec/lib/mastodon/cli/domains_spec.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/spec/lib/mastodon/cli/domains_spec.rb b/spec/lib/mastodon/cli/domains_spec.rb index 765b63e2a..add754159 100644 --- a/spec/lib/mastodon/cli/domains_spec.rb +++ b/spec/lib/mastodon/cli/domains_spec.rb @@ -4,5 +4,22 @@ require 'rails_helper' require 'mastodon/cli/domains' describe Mastodon::CLI::Domains do + let(:cli) { described_class.new } + it_behaves_like 'CLI Command' + + describe '#purge' do + context 'with accounts from the domain' do + let(:options) { {} } + let(:domain) { 'host.example' } + let!(:account) { Fabricate(:account, domain: domain) } + + it 'removes the account' do + expect { cli.invoke(:purge, [domain], options) }.to output( + a_string_including('Removed 1 accounts') + ).to_stdout + expect { account.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end end From a2bcfeb887a0ae1d437bb727333d769a8248b578 Mon Sep 17 00:00:00 2001 From: Claire Date: Mon, 4 Dec 2023 10:09:43 +0100 Subject: [PATCH 61/95] Fix `Style/HashEachMethods` cop in HAML files (#28178) --- app/views/admin/reports/index.html.haml | 2 +- app/views/settings/applications/_fields.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml index e94847d67..e2a9868aa 100644 --- a/app/views/admin/reports/index.html.haml +++ b/app/views/admin/reports/index.html.haml @@ -27,7 +27,7 @@ %button.button= t('admin.accounts.search') = link_to t('admin.accounts.reset'), admin_reports_path, class: 'button negative' -- @reports.group_by(&:target_account_id).each do |_target_account_id, reports| +- @reports.group_by(&:target_account_id).each_value do |reports| - target_account = reports.first.target_account .report-card .report-card__profile diff --git a/app/views/settings/applications/_fields.html.haml b/app/views/settings/applications/_fields.html.haml index 4f5077d83..29e2bcb5a 100644 --- a/app/views/settings/applications/_fields.html.haml +++ b/app/views/settings/applications/_fields.html.haml @@ -14,5 +14,5 @@ %label= t('activerecord.attributes.doorkeeper/application.scopes') %span.hint= t('simple_form.hints.defaults.scopes') - - Doorkeeper.configuration.scopes.group_by { |s| s.split(':').first }.each do |_key, value| + - Doorkeeper.configuration.scopes.group_by { |s| s.split(':').first }.each_value do |value| = f.input :scopes, label: false, hint: false, collection: value.sort, wrapper: :with_block_label, include_blank: false, label_method: ->(scope) { safe_join([content_tag(:samp, scope, class: class_for_scope(scope)), content_tag(:span, t("doorkeeper.scopes.#{scope}"), class: 'hint')]) }, selected: f.object.scopes.all, required: false, as: :check_boxes, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li' From 829457212ee2ae933db273b8dce402d2d8e659cf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 4 Dec 2023 10:38:49 +0100 Subject: [PATCH 62/95] Update dependency rubocop to v1.58.0 (#28170) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6820c12f9..037fe145d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -381,7 +381,7 @@ GEM rdoc reline (>= 0.3.8) jmespath (1.6.2) - json (2.6.3) + json (2.7.0) json-canonicalization (0.3.2) json-jwt (1.15.3) activesupport (>= 4.2) @@ -656,7 +656,7 @@ GEM rspec-mocks (~> 3.0) sidekiq (>= 5, < 8) rspec-support (3.12.1) - rubocop (1.57.2) + rubocop (1.58.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -664,7 +664,7 @@ GEM rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.1, < 2.0) + rubocop-ast (>= 1.30.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) rubocop-ast (1.30.0) From b3b009e6aa29d9051aa61113da344b3ab26d98e4 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 4 Dec 2023 04:44:54 -0500 Subject: [PATCH 63/95] Add spec coverage for `CLI::EmailDomainBlocks` commands (#28181) --- lib/mastodon/cli/email_domain_blocks.rb | 4 +- .../mastodon/cli/email_domain_blocks_spec.rb | 93 +++++++++++++++++++ 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/lib/mastodon/cli/email_domain_blocks.rb b/lib/mastodon/cli/email_domain_blocks.rb index 88a84ecb4..022b1dcbb 100644 --- a/lib/mastodon/cli/email_domain_blocks.rb +++ b/lib/mastodon/cli/email_domain_blocks.rb @@ -7,10 +7,10 @@ module Mastodon::CLI class EmailDomainBlocks < Base desc 'list', 'List blocked e-mail domains' def list - EmailDomainBlock.where(parent_id: nil).order(id: 'DESC').find_each do |entry| + EmailDomainBlock.where(parent_id: nil).find_each do |entry| say(entry.domain.to_s, :white) - EmailDomainBlock.where(parent_id: entry.id).order(id: 'DESC').find_each do |child| + EmailDomainBlock.where(parent_id: entry.id).find_each do |child| say(" #{child.domain}", :cyan) end end diff --git a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb index 060943b18..f5cb6c332 100644 --- a/spec/lib/mastodon/cli/email_domain_blocks_spec.rb +++ b/spec/lib/mastodon/cli/email_domain_blocks_spec.rb @@ -4,5 +4,98 @@ require 'rails_helper' require 'mastodon/cli/email_domain_blocks' describe Mastodon::CLI::EmailDomainBlocks do + let(:cli) { described_class.new } + it_behaves_like 'CLI Command' + + describe '#list' do + context 'with email domain block records' do + let!(:parent_block) { Fabricate(:email_domain_block) } + let!(:child_block) { Fabricate(:email_domain_block, parent: parent_block) } + let(:options) { {} } + + it 'lists the blocks' do + expect { cli.invoke(:list, [], options) }.to output( + a_string_including(parent_block.domain) + .and(a_string_including(child_block.domain)) + ).to_stdout + end + end + end + + describe '#add' do + context 'without any options' do + let(:options) { {} } + + it 'warns about usage and exits' do + expect { cli.invoke(:add, [], options) }.to output( + a_string_including('No domain(s) given') + ).to_stdout.and raise_error(SystemExit) + end + end + + context 'when blocks exist' do + let(:options) { {} } + let(:domain) { 'host.example' } + + before { Fabricate(:email_domain_block, domain: domain) } + + it 'does not add a new block' do + expect { cli.invoke(:add, [domain], options) }.to output( + a_string_including('is already blocked') + ).to_stdout + .and(not_change(EmailDomainBlock, :count)) + end + end + + context 'when no blocks exist' do + let(:options) { {} } + let(:domain) { 'host.example' } + + it 'adds a new block' do + expect { cli.invoke(:add, [domain], options) }.to output( + a_string_including('Added 1') + ).to_stdout + .and(change(EmailDomainBlock, :count).by(1)) + end + end + end + + describe '#remove' do + context 'without any options' do + let(:options) { {} } + + it 'warns about usage and exits' do + expect { cli.invoke(:remove, [], options) }.to output( + a_string_including('No domain(s) given') + ).to_stdout.and raise_error(SystemExit) + end + end + + context 'when blocks exist' do + let(:options) { {} } + let(:domain) { 'host.example' } + + before { Fabricate(:email_domain_block, domain: domain) } + + it 'removes the block' do + expect { cli.invoke(:remove, [domain], options) }.to output( + a_string_including('Removed 1') + ).to_stdout + .and(change(EmailDomainBlock, :count).by(-1)) + end + end + + context 'when no blocks exist' do + let(:options) { {} } + let(:domain) { 'host.example' } + + it 'does not remove a block' do + expect { cli.invoke(:remove, [domain], options) }.to output( + a_string_including('is not yet blocked') + ).to_stdout + .and(not_change(EmailDomainBlock, :count)) + end + end + end end From cca19f5fbb568bf7f145fe98d6d2497632c8987c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 4 Dec 2023 07:56:28 -0500 Subject: [PATCH 64/95] Use the `Admin::ActionLog` fabricator in admin/action_logs spec (#28194) --- .../admin/accounts_controller_spec.rb | 32 ++++++++++++------- .../admin/action_logs_controller_spec.rb | 31 +++++++++++++++--- .../api/v1/admin/account_actions_spec.rb | 17 +++++++--- spec/requests/api/v1/admin/accounts_spec.rb | 32 ++++++++++++------- 4 files changed, 78 insertions(+), 34 deletions(-) diff --git a/spec/controllers/admin/accounts_controller_spec.rb b/spec/controllers/admin/accounts_controller_spec.rb index 307e81950..1882ea838 100644 --- a/spec/controllers/admin/accounts_controller_spec.rb +++ b/spec/controllers/admin/accounts_controller_spec.rb @@ -161,12 +161,13 @@ RSpec.describe Admin::AccountsController do it 'logs action' do expect(subject).to have_http_status 302 - log_item = Admin::ActionLog.last - - expect(log_item).to_not be_nil - expect(log_item.action).to eq :approve - expect(log_item.account_id).to eq current_user.account_id - expect(log_item.target_id).to eq account.user.id + expect(latest_admin_action_log) + .to be_present + .and have_attributes( + action: eq(:approve), + account_id: eq(current_user.account_id), + target_id: eq(account.user.id) + ) end end @@ -201,12 +202,13 @@ RSpec.describe Admin::AccountsController do it 'logs action' do expect(subject).to have_http_status 302 - log_item = Admin::ActionLog.last - - expect(log_item).to_not be_nil - expect(log_item.action).to eq :reject - expect(log_item.account_id).to eq current_user.account_id - expect(log_item.target_id).to eq account.user.id + expect(latest_admin_action_log) + .to be_present + .and have_attributes( + action: eq(:reject), + account_id: eq(current_user.account_id), + target_id: eq(account.user.id) + ) end end @@ -427,4 +429,10 @@ RSpec.describe Admin::AccountsController do end end end + + private + + def latest_admin_action_log + Admin::ActionLog.last + end end diff --git a/spec/controllers/admin/action_logs_controller_spec.rb b/spec/controllers/admin/action_logs_controller_spec.rb index b7854469d..be4222df0 100644 --- a/spec/controllers/admin/action_logs_controller_spec.rb +++ b/spec/controllers/admin/action_logs_controller_spec.rb @@ -9,11 +9,9 @@ describe Admin::ActionLogsController do let!(:account) { Fabricate(:account) } before do - _orphaned_logs = %w( - Account User UserRole Report DomainBlock DomainAllow - EmailDomainBlock UnavailableDomain Status AccountWarning - Announcement IpBlock Instance CustomEmoji CanonicalEmailBlock Appeal - ).map { |type| Admin::ActionLog.new(account: account, action: 'destroy', target_type: type, target_id: 1312).save! } + orphaned_log_types.map do |type| + Fabricate(:action_log, account: account, action: 'destroy', target_type: type, target_id: 1312) + end end describe 'GET #index' do @@ -24,4 +22,27 @@ describe Admin::ActionLogsController do expect(response).to have_http_status(200) end end + + private + + def orphaned_log_types + %w( + Account + AccountWarning + Announcement + Appeal + CanonicalEmailBlock + CustomEmoji + DomainAllow + DomainBlock + EmailDomainBlock + Instance + IpBlock + Report + Status + UnavailableDomain + User + UserRole + ) + end end diff --git a/spec/requests/api/v1/admin/account_actions_spec.rb b/spec/requests/api/v1/admin/account_actions_spec.rb index c14e08c21..4167911a1 100644 --- a/spec/requests/api/v1/admin/account_actions_spec.rb +++ b/spec/requests/api/v1/admin/account_actions_spec.rb @@ -21,12 +21,19 @@ RSpec.describe 'Account actions' do it 'logs action' do subject - log_item = Admin::ActionLog.last + expect(latest_admin_action_log) + .to be_present + .and have_attributes( + action: eq(action_type), + account_id: eq(user.account_id), + target_id: eq(target_type == :user ? target_account.user.id : target_account.id) + ) + end - expect(log_item).to be_present - expect(log_item.action).to eq(action_type) - expect(log_item.account_id).to eq(user.account_id) - expect(log_item.target_id).to eq(target_type == :user ? target_account.user.id : target_account.id) + private + + def latest_admin_action_log + Admin::ActionLog.last end end diff --git a/spec/requests/api/v1/admin/accounts_spec.rb b/spec/requests/api/v1/admin/accounts_spec.rb index 8e158f623..1615581f0 100644 --- a/spec/requests/api/v1/admin/accounts_spec.rb +++ b/spec/requests/api/v1/admin/accounts_spec.rb @@ -151,12 +151,13 @@ RSpec.describe 'Accounts' do it 'logs action', :aggregate_failures do subject - log_item = Admin::ActionLog.last - - expect(log_item).to be_present - expect(log_item.action).to eq :approve - expect(log_item.account_id).to eq user.account_id - expect(log_item.target_id).to eq account.user.id + expect(latest_admin_action_log) + .to be_present + .and have_attributes( + action: eq(:approve), + account_id: eq(user.account_id), + target_id: eq(account.user.id) + ) end end @@ -202,12 +203,13 @@ RSpec.describe 'Accounts' do it 'logs action', :aggregate_failures do subject - log_item = Admin::ActionLog.last - - expect(log_item).to be_present - expect(log_item.action).to eq :reject - expect(log_item.account_id).to eq user.account_id - expect(log_item.target_id).to eq account.user.id + expect(latest_admin_action_log) + .to be_present + .and have_attributes( + action: eq(:reject), + account_id: eq(user.account_id), + target_id: eq(account.user.id) + ) end end @@ -398,4 +400,10 @@ RSpec.describe 'Accounts' do end end end + + private + + def latest_admin_action_log + Admin::ActionLog.last + end end From 71e5a16ebaf67d5e55d7f4e31436c36e6801ce5a Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 4 Dec 2023 10:28:19 -0500 Subject: [PATCH 65/95] Remove triple subject call in `api/v1/lists` spec (#28210) --- spec/requests/api/v1/lists_spec.rb | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/spec/requests/api/v1/lists_spec.rb b/spec/requests/api/v1/lists_spec.rb index 22dde43a1..4635e936f 100644 --- a/spec/requests/api/v1/lists_spec.rb +++ b/spec/requests/api/v1/lists_spec.rb @@ -135,8 +135,11 @@ RSpec.describe 'Lists' do it_behaves_like 'forbidden for wrong scope', 'read read:lists' - it 'returns the updated list', :aggregate_failures do - subject + it 'returns the updated list and updates values', :aggregate_failures do + expect { subject } + .to change_list_title + .and change_list_replies_policy + .and change_list_exclusive expect(response).to have_http_status(200) list.reload @@ -149,16 +152,16 @@ RSpec.describe 'Lists' do }) end - it 'updates the list title' do - expect { subject }.to change { list.reload.title }.from('my list').to('list') + def change_list_title + change { list.reload.title }.from('my list').to('list') end - it 'updates the list replies_policy' do - expect { subject }.to change { list.reload.replies_policy }.from('list').to('followed') + def change_list_replies_policy + change { list.reload.replies_policy }.from('list').to('followed') end - it 'updates the list exclusive' do - expect { subject }.to change { list.reload.exclusive }.from(false).to(true) + def change_list_exclusive + change { list.reload.exclusive }.from(false).to(true) end context 'when the list does not exist' do From 89a8e6e6227eb901b5811d8417d81dc8ab1427a9 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Mon, 4 Dec 2023 10:41:43 -0500 Subject: [PATCH 66/95] Remove 2x double subject call in `models/form/account_batch` spec (#28209) --- spec/models/form/account_batch_spec.rb | 40 +++++++++++++++++++------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/spec/models/form/account_batch_spec.rb b/spec/models/form/account_batch_spec.rb index fd8e90901..26fb1b953 100644 --- a/spec/models/form/account_batch_spec.rb +++ b/spec/models/form/account_batch_spec.rb @@ -37,12 +37,10 @@ RSpec.describe Form::AccountBatch do let(:select_all_matching) { '0' } let(:account_ids) { [target_account.id, target_account2.id] } - it 'suspends the expected users' do - expect { subject }.to change { [target_account.reload.suspended?, target_account2.reload.suspended?] }.from([false, false]).to([true, true]) - end - - it 'closes open reports targeting the suspended users' do - expect { subject }.to change { Report.unresolved.where(target_account: [target_account, target_account2]).count }.from(2).to(0) + it 'suspends the expected users and closes open reports' do + expect { subject } + .to change_account_suspensions + .and change_open_reports_for_accounts end end @@ -50,13 +48,33 @@ RSpec.describe Form::AccountBatch do let(:select_all_matching) { '1' } let(:query) { Account.where(id: [target_account.id, target_account2.id]) } - it 'suspends the expected users' do - expect { subject }.to change { [target_account.reload.suspended?, target_account2.reload.suspended?] }.from([false, false]).to([true, true]) + it 'suspends the expected users and closes open reports' do + expect { subject } + .to change_account_suspensions + .and change_open_reports_for_accounts end + end - it 'closes open reports targeting the suspended users' do - expect { subject }.to change { Report.unresolved.where(target_account: [target_account, target_account2]).count }.from(2).to(0) - end + private + + def change_account_suspensions + change { relevant_account_suspension_statuses } + .from([false, false]) + .to([true, true]) + end + + def change_open_reports_for_accounts + change(relevant_account_unresolved_reports, :count) + .from(2) + .to(0) + end + + def relevant_account_unresolved_reports + Report.unresolved.where(target_account: [target_account, target_account2]) + end + + def relevant_account_suspension_statuses + [target_account.reload, target_account2.reload].map(&:suspended?) end end end From f944a767c46e3d887e070d67958f4a9866c3d1f1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 13:57:16 +0100 Subject: [PATCH 67/95] Update dependency json-ld to v3.3.1 (#28229) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 037fe145d..602df6a22 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -382,15 +382,15 @@ GEM reline (>= 0.3.8) jmespath (1.6.2) json (2.7.0) - json-canonicalization (0.3.2) + json-canonicalization (1.0.0) json-jwt (1.15.3) activesupport (>= 4.2) aes_key_wrap bindata httpclient - json-ld (3.3.0) + json-ld (3.3.1) htmlentities (~> 4.3) - json-canonicalization (~> 0.3, >= 0.3.2) + json-canonicalization (~> 1.0) link_header (~> 0.0, >= 0.0.8) multi_json (~> 1.15) rack (>= 2.2, < 4) From 2d2e23c68dde6ac6fa14fe8fbeb0efac02ad9803 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:59:31 +0000 Subject: [PATCH 68/95] Update dependency brakeman to v6.1.0 (#28231) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 602df6a22..9308a41c8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -175,7 +175,7 @@ GEM blurhash (0.1.7) bootsnap (1.17.0) msgpack (~> 1.2) - brakeman (6.0.1) + brakeman (6.1.0) browser (5.3.1) brpoplpush-redis_script (0.1.3) concurrent-ruby (~> 1.0, >= 1.0.5) From d0a5ebf914f7ad74a69beb15d9c011e44e84c0dc Mon Sep 17 00:00:00 2001 From: Jonathan de Jong Date: Tue, 5 Dec 2023 14:59:15 +0100 Subject: [PATCH 69/95] Fix error when encountering malformed Tag objects from Kbin (#28235) --- app/services/activitypub/process_status_update_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb index 2db0e80e7..fb2b33114 100644 --- a/app/services/activitypub/process_status_update_service.rb +++ b/app/services/activitypub/process_status_update_service.rb @@ -170,9 +170,9 @@ class ActivityPub::ProcessStatusUpdateService < BaseService as_array(@json['tag']).each do |tag| if equals_or_includes?(tag['type'], 'Hashtag') - @raw_tags << tag['name'] + @raw_tags << tag['name'] if tag['name'].present? elsif equals_or_includes?(tag['type'], 'Mention') - @raw_mentions << tag['href'] + @raw_mentions << tag['href'] if tag['href'].present? elsif equals_or_includes?(tag['type'], 'Emoji') @raw_emojis << tag end From 4238ec844d61774a02daa4a677a69099ff494388 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 17:07:53 +0100 Subject: [PATCH 70/95] New Crowdin Translations (automated) (#28120) Co-authored-by: GitHub Actions Co-authored-by: Claire --- app/javascript/mastodon/locales/an.json | 1 + app/javascript/mastodon/locales/ca.json | 1 + app/javascript/mastodon/locales/es-MX.json | 2 +- app/javascript/mastodon/locales/fa.json | 7 +- app/javascript/mastodon/locales/fil.json | 1 + app/javascript/mastodon/locales/ko.json | 4 +- app/javascript/mastodon/locales/lt.json | 74 ++++++++++++++ app/javascript/mastodon/locales/ne.json | 1 + app/javascript/mastodon/locales/ry.json | 1 + app/javascript/mastodon/locales/sc.json | 36 +++++++ app/javascript/mastodon/locales/sk.json | 10 ++ app/javascript/mastodon/locales/sq.json | 1 + app/javascript/mastodon/locales/th.json | 14 +-- app/javascript/mastodon/locales/tlh.json | 1 + app/javascript/mastodon/locales/zh-TW.json | 2 +- config/i18n-tasks.yml | 1 + config/locales/activerecord.fil.yml | 1 + config/locales/activerecord.ne.yml | 1 + config/locales/activerecord.ry.yml | 1 + config/locales/activerecord.tlh.yml | 1 + config/locales/devise.fil.yml | 1 + config/locales/devise.ne.yml | 1 + config/locales/devise.ry.yml | 1 + config/locales/devise.tlh.yml | 1 + config/locales/doorkeeper.fil.yml | 1 + config/locales/doorkeeper.ne.yml | 1 + config/locales/doorkeeper.ry.yml | 1 + config/locales/doorkeeper.sc.yml | 15 +++ config/locales/doorkeeper.tlh.yml | 1 + config/locales/fil.yml | 1 + config/locales/fr-QC.yml | 1 + config/locales/fr.yml | 1 + config/locales/lt.yml | 26 ++++- config/locales/ne.yml | 1 + config/locales/ry.yml | 1 + config/locales/sc.yml | 113 +++++++++++++++++++++ config/locales/simple_form.fil.yml | 1 + config/locales/simple_form.lt.yml | 35 ++++++- config/locales/simple_form.ne.yml | 1 + config/locales/simple_form.ry.yml | 1 + config/locales/simple_form.sc.yml | 17 ++++ config/locales/simple_form.tlh.yml | 1 + config/locales/tlh.yml | 1 + 43 files changed, 366 insertions(+), 19 deletions(-) create mode 100644 app/javascript/mastodon/locales/fil.json create mode 100644 app/javascript/mastodon/locales/ne.json create mode 100644 app/javascript/mastodon/locales/ry.json create mode 100644 app/javascript/mastodon/locales/tlh.json create mode 100644 config/locales/activerecord.fil.yml create mode 100644 config/locales/activerecord.ne.yml create mode 100644 config/locales/activerecord.ry.yml create mode 100644 config/locales/activerecord.tlh.yml create mode 100644 config/locales/devise.fil.yml create mode 100644 config/locales/devise.ne.yml create mode 100644 config/locales/devise.ry.yml create mode 100644 config/locales/devise.tlh.yml create mode 100644 config/locales/doorkeeper.fil.yml create mode 100644 config/locales/doorkeeper.ne.yml create mode 100644 config/locales/doorkeeper.ry.yml create mode 100644 config/locales/doorkeeper.tlh.yml create mode 100644 config/locales/fil.yml create mode 100644 config/locales/ne.yml create mode 100644 config/locales/ry.yml create mode 100644 config/locales/simple_form.fil.yml create mode 100644 config/locales/simple_form.ne.yml create mode 100644 config/locales/simple_form.ry.yml create mode 100644 config/locales/simple_form.tlh.yml create mode 100644 config/locales/tlh.yml diff --git a/app/javascript/mastodon/locales/an.json b/app/javascript/mastodon/locales/an.json index a652272fa..b2134551b 100644 --- a/app/javascript/mastodon/locales/an.json +++ b/app/javascript/mastodon/locales/an.json @@ -499,6 +499,7 @@ "report_notification.open": "Ubrir informe", "search.placeholder": "Buscar", "search.search_or_paste": "Buscar u apegar URL", + "search_popout.full_text_search_logged_out_message": "Nomás disponible iniciando la sesión.", "search_results.all": "Totz", "search_results.hashtags": "Etiquetas", "search_results.nothing_found": "No se podió trobar cosa pa estes termins de busqueda", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 87121b7c5..5ae49325f 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -482,6 +482,7 @@ "onboarding.follows.lead": "La teva línia de temps inici només està a les teves mans. Com més gent segueixis, més activa i interessant serà. Aquests perfils poden ser un bon punt d'inici—sempre pots acabar deixant de seguir-los!:", "onboarding.follows.title": "Personalitza la pantalla d'inci", "onboarding.profile.discoverable": "Fes el meu perfil descobrible", + "onboarding.profile.discoverable_hint": "En acceptar d'ésser descobert a Mastodon els teus missatges poden aparèixer dins les tendències i els resultats de cerques, i el teu perfil es pot suggerir a qui tingui interessos semblants als teus.", "onboarding.profile.display_name": "Nom que es mostrarà", "onboarding.profile.display_name_hint": "El teu nom complet o el teu malnom…", "onboarding.profile.lead": "Sempre ho pots completar més endavant a la configuració, on hi ha encara més opcions disponibles.", diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json index f7cd1b330..0d26afef2 100644 --- a/app/javascript/mastodon/locales/es-MX.json +++ b/app/javascript/mastodon/locales/es-MX.json @@ -606,7 +606,7 @@ "search.quick_action.status_search": "Publicaciones que coinciden con {x}", "search.search_or_paste": "Buscar o pegar URL", "search_popout.full_text_search_disabled_message": "No disponible en {domain}.", - "search_popout.full_text_search_logged_out_message": "Solo disponible si inicias sesión.", + "search_popout.full_text_search_logged_out_message": "Sólo disponible al iniciar sesión.", "search_popout.language_code": "Código de idioma ISO", "search_popout.options": "Opciones de búsqueda", "search_popout.quick_actions": "Acciones rápidas", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 6951d5cb0..8e8930bfe 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -1,7 +1,7 @@ { "about.blocks": "کارسازهای نظارت شده", "about.contact": "تماس:", - "about.disclaimer": "ماستودون نرم‌افزار آزاد و یک شرکت غیر انتفاعی آلمانی با مسئولیت محدود است.", + "about.disclaimer": "ماستودون نرم‌افزار آزاد و نشان تجاری یک شرکت غیر انتفاعی با مسئولیت محدود آلمانی است.", "about.domain_blocks.no_reason_available": "دلیلی موجود نیست", "about.domain_blocks.preamble": "ماستودون عموماً می‌گذارد محتوا را از از هر کارساز دیگری در دنیای شبکه‌های اجتماعی غیرمتمرکز دیده و با آنان برهم‌کنش داشته باشید. این‌ها استثناهایی هستند که روی این کارساز خاص وضع شده‌اند.", "about.domain_blocks.silenced.explanation": "عموماً نمایه‌ها و محتوا از این کارساز را نمی‌بینید، مگر این که به طور خاص دنبالشان گشته یا با پی گیری، داوطلب دیدنشان شوید.", @@ -21,6 +21,7 @@ "account.blocked": "مسدود", "account.browse_more_on_origin_server": "مرور بیش‌تر روی نمایهٔ اصلی", "account.cancel_follow_request": "رد کردن درخواست پی‌گیری", + "account.copy": "رونوشت از پیوند به نمایه", "account.direct": "اشارهٔ خصوصی به ‪@{name}‬", "account.disable_notifications": "آگاه کردن من هنگام فرسته‌های ‎@{name} را متوقّف کن", "account.domain_blocked": "دامنه مسدود شد", @@ -191,6 +192,7 @@ "conversation.mark_as_read": "علامت‌گذاری به عنوان خوانده شده", "conversation.open": "دیدن گفتگو", "conversation.with": "با {names}", + "copy_icon_button.copied": "در بریده‌دان رونوشت شد", "copypaste.copied": "رونوشت شد", "copypaste.copy_to_clipboard": "رونوشت به تخته‌گیره", "directory.federated": "از کارسازهای شناخته‌شده", @@ -486,6 +488,8 @@ "onboarding.profile.note_hint": "می‌توانید افراد دیگر را @نام‌بردن یا #برچسب بزنید…", "onboarding.profile.save_and_continue": "ذخیره کن و ادامه بده", "onboarding.profile.title": "تنظیم نمایه", + "onboarding.profile.upload_avatar": "بازگذاری تصویر نمایه", + "onboarding.profile.upload_header": "بارگذاری تصویر سردر نمایه", "onboarding.share.lead": "بگذارید افراد بدانند چگونه می‌توانند در ماستادون بیابندتان!", "onboarding.share.message": "من {username} روی #ماستودون هستم! مرا در {url} پی‌بگیرید", "onboarding.share.next_steps": "گام‌های ممکن بعدی:", @@ -600,6 +604,7 @@ "search.quick_action.status_search": "فرسته‌های جور با {x}", "search.search_or_paste": "جست‌وجو یا جایگذاری نشانی", "search_popout.full_text_search_disabled_message": "روی {domain} موجود نیست.", + "search_popout.full_text_search_logged_out_message": "تنها زمانی که وارد شده‌اید دردسترس است.", "search_popout.language_code": "کد زبان ایزو", "search_popout.options": "گزینه‌های جست‌وجو", "search_popout.quick_actions": "کنش‌های سریع", diff --git a/app/javascript/mastodon/locales/fil.json b/app/javascript/mastodon/locales/fil.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/app/javascript/mastodon/locales/fil.json @@ -0,0 +1 @@ +{} diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 46caf32b0..5b76cd67c 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -302,8 +302,8 @@ "hashtag.counter_by_accounts": "{count, plural, other {{counter} 명의 참여자}}", "hashtag.counter_by_uses": "{count, plural, other {{counter} 개의 게시물}}", "hashtag.counter_by_uses_today": "오늘 {count, plural, other {{counter} 개의 게시물}}", - "hashtag.follow": "해시태그 팔로우", - "hashtag.unfollow": "해시태그 팔로우 해제", + "hashtag.follow": "팔로우", + "hashtag.unfollow": "팔로우 해제", "hashtags.and_other": "…그리고 {count, plural,other {#개 더}}", "home.actions.go_to_explore": "무엇이 유행인지 보기", "home.actions.go_to_suggestions": "팔로우할 사람 찾기", diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index a976d5907..e588b8538 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -180,29 +180,71 @@ "confirmations.logout.message": "Ar tikrai nori atsijungti?", "confirmations.mute.confirm": "Nutildyti", "confirmations.mute.explanation": "Tai paslėps jų įrašus ir įrašus, kuriuose jie menėmi, tačiau jie vis tiek galės matyti tavo įrašus ir sekti.", + "confirmations.mute.message": "Ar tikrai norite nutildyti {name}?", + "confirmations.redraft.confirm": "Ištrinti ir perrašyti", "confirmations.reply.confirm": "Atsakyti", "confirmations.reply.message": "Atsakant dabar, bus perrašyta metu kuriama žinutė. Ar tikrai nori tęsti?", "confirmations.unfollow.confirm": "Nebesekti", + "confirmations.unfollow.message": "Ar tikrai norite atsisakyti sekimo {name}?", + "conversation.delete": "Ištrinti pokalbį", "conversation.mark_as_read": "Žymėti kaip skaitytą", "conversation.open": "Peržiūrėti pokalbį", "conversation.with": "Su {names}", "copy_icon_button.copied": "Nukopijuota į iškarpinę", "copypaste.copied": "Nukopijuota", "copypaste.copy_to_clipboard": "Kopijuoti į iškarpinę", + "directory.local": "Iš {domain} tik", + "directory.new_arrivals": "Naujos prekės", + "directory.recently_active": "Neseniai aktyvus", "disabled_account_banner.account_settings": "Paskyros nustatymai", + "disabled_account_banner.text": "Jūsų paskyra {disabledAccount} šiuo metu yra išjungta.", + "dismissable_banner.dismiss": "Atmesti", "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_statuses": "Tai įrašai iš viso socialinio tinklo, kurie šiandien sulaukia vis daugiau dėmesio. Naujesni įrašai, turintys daugiau boosts ir mėgstamiausių įrašų, yra vertinami aukščiau.", "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", "embed.instructions": "Embed this status on your website by copying the code below.", "embed.preview": "Štai kaip tai atrodys:", + "emoji_button.activity": "Veikla", + "emoji_button.clear": "Išvalyti", + "emoji_button.custom": "Pasirinktinis", + "emoji_button.flags": "Vėliavos", + "emoji_button.food": "Maistas ir Gėrimai", + "emoji_button.label": "Įterpti veidelius", + "emoji_button.nature": "Gamta", + "emoji_button.not_found": "Nerasta jokių tinkamų jaustukų", "emoji_button.objects": "Objektai", + "emoji_button.people": "Žmonės", + "emoji_button.recent": "Dažniausiai naudojama", "emoji_button.search": "Paieška...", + "emoji_button.search_results": "Paieškos rezultatai", + "emoji_button.symbols": "Simboliai", + "emoji_button.travel": "Kelionės ir Vietos", "empty_column.account_hides_collections": "Šis naudotojas (-a) pasirinko nepadaryti šią informaciją prieinamą", + "empty_column.account_suspended": "Paskyra sustabdyta", "empty_column.account_timeline": "No toots here!", + "empty_column.account_unavailable": "Profilis neprieinamas", + "empty_column.blocks": "Dar neužblokavote nė vieno naudotojo.", "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.", + "empty_column.community": "Vietinė laiko juosta yra tuščia. Parašykite ką nors viešai, kad pradėtumėte veikti!", + "empty_column.direct": "Dar neturite jokių privačių paminėjimų. Kai išsiųsite arba gausite tokį pranešimą, jis bus rodomas čia.", "empty_column.domain_blocks": "There are no hidden domains yet.", + "empty_column.favourited_statuses": "Dar neturite mėgstamiausių įrašų. Kai vieną iš jų pamėgsite, jis bus rodomas čia.", + "empty_column.follow_requests": "Dar neturite jokių sekimo užklausų. Kai gausite tokį prašymą, jis bus rodomas čia.", + "empty_column.followed_tags": "Dar nesekėte jokių grotažymių. Kai tai padarysite, jie bus rodomi čia.", "empty_column.hashtag": "Nėra nieko šiame saitažodyje kol kas.", "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}", "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": "Dar neturite jokių sąrašų. Kai jį sukursite, jis bus rodomas čia.", + "empty_column.mutes": "Dar nesate nutildę nė vieno naudotojo.", + "empty_column.notifications": "Dar neturite jokių pranešimų. Kai kiti žmonės su jumis bendraus, matysite tai čia.", + "empty_column.public": "Čia nieko nėra! Parašykite ką nors viešai arba rankiniu būdu sekite naudotojus iš kitų serverių, kad jį užpildytumėte", + "error.unexpected_crash.explanation": "Dėl mūsų kodo klaidos arba naršyklės suderinamumo problemos šis puslapis negalėjo būti rodomas teisingai.", + "error.unexpected_crash.explanation_addons": "Šį puslapį nepavyko teisingai parodyti. Šią klaidą greičiausiai sukėlė naršyklės priedas arba automatinio vertimo įrankiai.", + "error.unexpected_crash.next_steps": "Pabandykite atnaujinti puslapį. Jei tai nepadeda, galbūt vis dar galėsite naudotis \"Mastodon\" naudodami kitą naršyklę arba vietinę programėlę.", + "error.unexpected_crash.next_steps_addons": "Pabandykite juos išjungti ir atnaujinti puslapį. Jei tai nepadeda, galbūt vis dar galėsite naudotis \"Mastodon\" naudodami kitą naršyklę arba vietinę programėlę.", + "errors.unexpected_crash.report_issue": "Pranešti apie triktį", + "explore.search_results": "Paieškos rezultatai", + "explore.suggested_follows": "Žmonės", "explore.title": "Naršyti", "explore.trending_links": "Naujienos", "explore.trending_statuses": "Įrašai", @@ -304,7 +346,13 @@ "moved_to_account_banner.text": "Tavo paskyra {disabledAccount} šiuo metu yra išjungta, nes persikėlei į {movedToAccount}.", "mute_modal.duration": "Trukmė", "mute_modal.hide_notifications": "Slėpti šio naudotojo pranešimus?", + "mute_modal.indefinite": "Neribotas", + "navigation_bar.about": "Apie", + "navigation_bar.advanced_interface": "Atidarykite išplėstinę žiniatinklio sąsają", + "navigation_bar.blocks": "Užblokuoti naudotojai", + "navigation_bar.bookmarks": "Žymės", "navigation_bar.compose": "Compose new toot", + "navigation_bar.direct": "Privatūs paminėjimai", "navigation_bar.discover": "Atrasti", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Redaguoti profilį", @@ -372,6 +420,7 @@ "notifications.permission_required": "Darbalaukio pranešimai nepasiekiami, nes nesuteiktas reikiamas leidimas.", "notifications_permission_banner.enable": "Įjungti darbalaukio pranešimus", "notifications_permission_banner.how_to_control": "Jei norite gauti pranešimus, kai \"Mastodon\" nėra atidarytas, įjunkite darbalaukio pranešimus. Įjungę darbalaukio pranešimus, galite tiksliai valdyti, kokių tipų sąveikos generuoja darbalaukio pranešimus, naudodamiesi pirmiau esančiu mygtuku {icon}.", + "notifications_permission_banner.title": "Niekada nieko nepraleiskite", "onboarding.action.back": "Gražinkite mane atgal", "onboarding.actions.back": "Gražinkite mane atgal", "onboarding.actions.go_to_explore": "See what's trending", @@ -394,8 +443,10 @@ "onboarding.share.lead": "Praneškite žmonėms, kaip jus rasti \"Mastodon\"!", "onboarding.share.message": "Aš {username} #Mastodon! Ateik sekti manęs adresu {url}", "onboarding.share.next_steps": "Galimi kiti žingsniai:", + "onboarding.share.title": "Bendrinkite savo profilį", "onboarding.start.lead": "Dabar esi Mastodon dalis – unikalios decentralizuotos socialinės žiniasklaidos platformos, kurioje tu, o ne algoritmas, pats nustatai savo patirtį. Pradėkime tavo kelionę šioje naujoje socialinėje erdvėje:", "onboarding.start.skip": "Want to skip right ahead?", + "onboarding.start.title": "Jums pavyko!", "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.", @@ -404,23 +455,46 @@ "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", + "picture_in_picture.restore": "Padėkite jį atgal", + "poll.closed": "Uždaryti", + "poll.refresh": "Atnaujinti", + "poll.reveal": "Peržiūrėti rezultatus", "poll.vote": "Balsuoti", "poll.voted": "Tu balsavai už šį atsakymą", "poll.votes": "{votes, plural, one {# balsas} few {# balsai} many {# balso} other {# balsų}}", + "poll_button.add_poll": "Pridėti apklausą", + "poll_button.remove_poll": "Šalinti apklausą", "privacy.change": "Adjust status privacy", "privacy.direct.long": "Post to mentioned users only", "privacy.direct.short": "Direct", "privacy.private.long": "Post to followers only", "privacy.private.short": "Followers-only", + "privacy.public.long": "Visiems matomas", + "privacy.public.short": "Viešas", "privacy.unlisted.long": "Matomas visiems, bet atsisakyta atradimo funkcijų", "privacy.unlisted.short": "Neįtrauktas į sąrašą", "privacy_policy.last_updated": "Paskutinį kartą atnaujinta {date}", + "privacy_policy.title": "Privatumo politika", "recommended": "Rekomenduojama", + "refresh": "Atnaujinti", + "regeneration_indicator.label": "Kraunasi…", + "relative_time.full.just_now": "ką tik", "relative_time.hours": "{number} val.", "relative_time.just_now": "dabar", "relative_time.minutes": "{number} min.", "relative_time.seconds": "{number} sek.", "relative_time.today": "šiandien", + "reply_indicator.cancel": "Atšaukti", + "report.block": "Blokuoti", + "report.categories.legal": "Legalus", + "report.categories.other": "Kita", + "report.categories.spam": "Šlamštas", + "report.categories.violation": "Turinys pažeidžia vieną ar daugiau serverio taisyklių", + "report.category.subtitle": "Pasirinkite tinkamiausią variantą", + "report.category.title_account": "profilis", + "report.category.title_status": "įrašas", + "report.close": "Atlikta", + "report.comment.title": "Ar yra dar kas nors, ką, jūsų manymu, turėtume žinoti?", "report.mute_explanation": "Jų įrašų nematysi. Jie vis tiek gali tave sekti ir matyti įrašus, bet nežinos, kad jie nutildyti.", "report.next": "Tęsti", "report.placeholder": "Papildomi komentarai", diff --git a/app/javascript/mastodon/locales/ne.json b/app/javascript/mastodon/locales/ne.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/app/javascript/mastodon/locales/ne.json @@ -0,0 +1 @@ +{} diff --git a/app/javascript/mastodon/locales/ry.json b/app/javascript/mastodon/locales/ry.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/app/javascript/mastodon/locales/ry.json @@ -0,0 +1 @@ +{} diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json index 59c834b95..7f29525e7 100644 --- a/app/javascript/mastodon/locales/sc.json +++ b/app/javascript/mastodon/locales/sc.json @@ -14,6 +14,7 @@ "account.badges.group": "Grupu", "account.block": "Bloca @{name}", "account.block_domain": "Bloca su domìniu {domain}", + "account.block_short": "Bloca", "account.blocked": "Blocadu", "account.browse_more_on_origin_server": "Esplora de prus in su profilu originale", "account.cancel_follow_request": "Withdraw follow request", @@ -31,17 +32,20 @@ "account.follows.empty": "Custa persone non sighit ancora a nemos.", "account.follows_you": "Ti sighit", "account.hide_reblogs": "Cua is cumpartziduras de @{name}", + "account.in_memoriam": "In memoriam.", "account.joined_short": "At aderidu", "account.link_verified_on": "Sa propiedade de custu ligòngiu est istada controllada su {date}", "account.locked_info": "S'istadu de riservadesa de custu contu est istadu cunfiguradu comente blocadu. Sa persone chi tenet sa propiedade revisionat a manu chie dda podet sighire.", "account.media": "Cuntenutu multimediale", "account.mention": "Mèntova a @{name}", "account.mute": "Pone a @{name} a sa muda", + "account.mute_short": "A sa muda", "account.muted": "A sa muda", "account.posts": "Publicatziones", "account.posts_with_replies": "Publicatziones e rispostas", "account.report": "Signala @{name}", "account.requested": "Abetende s'aprovatzione. Incarca pro annullare sa rechesta de sighidura", + "account.requested_follow": "{name} at dimandadu de ti sighire", "account.share": "Cumpartzi su profilu de @{name}", "account.show_reblogs": "Ammustra is cumpartziduras de @{name}", "account.statuses_counter": "{count, plural, one {{counter} publicatzione} other {{counter} publicatziones}}", @@ -106,6 +110,7 @@ "compose_form.publish": "Pùblica", "compose_form.publish_form": "Publish", "compose_form.publish_loud": "{publish}!", + "compose_form.save_changes": "Sarva is modìficas", "compose_form.sensitive.hide": "{count, plural, one {Marca elementu multimediale comente a sensìbile} other {Marca elementos multimediales comente sensìbiles}}", "compose_form.sensitive.marked": "{count, plural, one {Elementu multimediale marcadu comente a sensìbile} other {Elementos multimediales marcados comente a sensìbiles}}", "compose_form.sensitive.unmarked": "{count, plural, one {Elementu multimediale non marcadu comente a sensìbile} other {Elementos multimediales non marcados comente a sensìbiles}}", @@ -122,6 +127,7 @@ "confirmations.delete_list.message": "Seguru chi boles cantzellare custa lista in manera permanente?", "confirmations.domain_block.confirm": "Bloca totu su domìniu", "confirmations.domain_block.message": "Boles de seguru, ma a beru a beru, blocare {domain}? In sa parte manna de is casos, pagos blocos o silentziamentos de persones sunt sufitzientes e preferìbiles. No as a bìdere cuntenutos dae custu domìniu in peruna lìnia de tempus pùblica o in is notìficas tuas. Sa gente chi ti sighit dae cussu domìniu at a èssere bogada.", + "confirmations.edit.confirm": "Modìfica", "confirmations.logout.confirm": "Essi·nche", "confirmations.logout.message": "Seguru chi boles essire?", "confirmations.mute.confirm": "A sa muda", @@ -140,6 +146,7 @@ "directory.local": "Isceti dae {domain}", "directory.new_arrivals": "Arribos noos", "directory.recently_active": "Cun atividade dae pagu", + "disabled_account_banner.account_settings": "Cunfiguratziones de su contu", "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.", "embed.instructions": "Inserta custa publicatzione in su situ web tuo copiende su còdighe de suta.", @@ -180,13 +187,19 @@ "errors.unexpected_crash.copy_stacktrace": "Còpia stacktrace in punta de billete", "errors.unexpected_crash.report_issue": "Sinnala unu problema", "explore.search_results": "Resurtados de sa chirca", + "explore.suggested_follows": "Gente", + "explore.trending_statuses": "Publicatziones", + "explore.trending_tags": "Etichetas", "filter_modal.select_filter.expired": "iscadidu", + "firehose.all": "Totus", "follow_request.authorize": "Autoriza", "follow_request.reject": "Refuda", "follow_requests.unlocked_explanation": "Fintzas si su contu tuo no est blocadu, su personale de {domain} at pensadu chi forsis bolias revisionare a manu is rechestas de custos contos.", "footer.about": "Informatziones", "footer.invite": "Invita gente", + "footer.keyboard_shortcuts": "Incurtzaduras de tecladu", "footer.privacy_policy": "Polìtica de riservadesa", + "footer.status": "Istadu", "generic.saved": "Sarvadu", "getting_started.heading": "Comente cumintzare", "hashtag.column_header.tag_mode.all": "e {additional}", @@ -263,6 +276,7 @@ "lists.search": "Chirca intre sa gente chi ses sighende", "lists.subheading": "Is listas tuas", "load_pending": "{count, plural, one {# elementu nou} other {# elementos noos}}", + "loading_indicator.label": "Carrighende…", "media_gallery.toggle_visible": "Cua {number, plural, one {immàgine} other {immàgines}}", "mute_modal.duration": "Durada", "mute_modal.hide_notifications": "Boles cuare is notìficas de custa persone?", @@ -288,6 +302,7 @@ "navigation_bar.search": "Chirca", "navigation_bar.security": "Seguresa", "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.", + "notification.favourite": "{name} at marcadu comente a preferidu s'istadu tuo", "notification.follow": "{name} ti sighit", "notification.follow_request": "{name} at dimandadu de ti sighire", "notification.mention": "{name} t'at mentovadu", @@ -328,6 +343,8 @@ "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.title": "Popular on Mastodon", + "onboarding.profile.display_name": "Nòmine visìbile", + "onboarding.profile.note": "Biografia", "onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:", "onboarding.start.skip": "Want to skip right ahead?", "onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.", @@ -344,6 +361,7 @@ "poll.total_votes": "{count, plural, one {# votu} other {# votos}}", "poll.vote": "Vota", "poll.voted": "As votadu custa risposta", + "poll.votes": "{votes, plural, one {# votu} other {# votos}}", "poll_button.add_poll": "Agiunghe unu sondàgiu", "poll_button.remove_poll": "Cantzella su sondàgiu", "privacy.change": "Modìfica s'istadu de riservadesa", @@ -353,25 +371,41 @@ "privacy.private.short": "Followers-only", "privacy.public.short": "Pùblicu", "privacy.unlisted.short": "Esclùidu de sa lista", + "recommended": "Cussigiadu", "refresh": "Atualiza", "regeneration_indicator.label": "Carrighende…", "regeneration_indicator.sublabel": "Preparende sa lìnia de tempus printzipale tua.", "relative_time.days": "{number} dies a oe", + "relative_time.full.just_now": "immoe etotu", "relative_time.hours": "{number} oras a immoe", "relative_time.just_now": "immoe", "relative_time.minutes": "{number} minutos a immoe", "relative_time.seconds": "{number} segundos a immoe", "relative_time.today": "oe", "reply_indicator.cancel": "Annulla", + "report.block": "Bloca", + "report.categories.other": "Àteru", + "report.category.title_account": "profilu", + "report.category.title_status": "publicatzione", + "report.close": "Fatu", "report.forward": "Torra a imbiare a {target}", "report.forward_hint": "Custu contu est de un'àteru serbidore. Ddi boles imbiare puru una còpia anònima de custu informe?", + "report.mute": "A sa muda", + "report.next": "Imbeniente", "report.placeholder": "Cummentos additzionales", "report.submit": "Imbia", "report.target": "Informende de {target}", "report_notification.attached_statuses": "{count, plural, one {# post} other {# posts}} attached", + "report_notification.categories.other": "Àteru", "search.placeholder": "Chirca", + "search_popout.user": "utente", + "search_results.accounts": "Profilos", + "search_results.all": "Totus", "search_results.hashtags": "Etichetas", "search_results.statuses": "Publicatziones", + "server_banner.administered_by": "Amministradu dae:", + "server_banner.learn_more": "Àteras informatziones", + "server_banner.server_stats": "Istatìsticas de su serbidore:", "sign_in_banner.sign_in": "Sign in", "status.admin_account": "Aberi s'interfache de moderatzione pro @{name}", "status.admin_status": "Aberi custa publicatzione in s'interfache de moderatzione", @@ -382,6 +416,7 @@ "status.copy": "Còpia su ligòngiu a sa publicatzione tua", "status.delete": "Cantzella", "status.detailed_status": "Visualizatzione de detàlliu de arresonada", + "status.edit": "Modìfica", "status.edited_x_times": "Edited {count, plural, one {# time} other {# times}}", "status.embed": "Afissa", "status.filtered": "Filtradu", @@ -413,6 +448,7 @@ "status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}", "status.unmute_conversation": "Torra a ativare s'arresonada", "status.unpin": "Boga dae pitzu de su profilu", + "subscribed_languages.save": "Sarva is modìficas", "tabs_bar.home": "Printzipale", "tabs_bar.notifications": "Notìficas", "time_remaining.days": "{number, plural, one {abarrat # die} other {abarrant # dies}}", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 9115695d8..c4ce6f8cf 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -391,6 +391,7 @@ "lists.search": "Vyhľadávaj medzi užívateľmi, ktorých sleduješ", "lists.subheading": "Tvoje zoznamy", "load_pending": "{count, plural, one {# nová položka} other {# nových položiek}}", + "loading_indicator.label": "Načítam…", "media_gallery.toggle_visible": "Zapni/Vypni viditeľnosť", "moved_to_account_banner.text": "Vaše konto {disabledAccount} je momentálne zablokované, pretože ste sa presunuli na {movedToAccount}.", "mute_modal.duration": "Trvanie", @@ -480,6 +481,14 @@ "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.title": "Popular on Mastodon", "onboarding.profile.discoverable": "Urob môj profil objaviteľný", + "onboarding.profile.display_name": "Zobrazované meno", + "onboarding.profile.display_name_hint": "Tvoje plné meno, alebo tvoje zábavné meno…", + "onboarding.profile.lead": "Toto môžeš vždy dokončiť neskôr v nastaveniach, kde je dostupných ešte viac volieb na prispôsobenie.", + "onboarding.profile.note": "O tebe", + "onboarding.profile.note_hint": "Môžeš @spomenúť iných ľudí, alebo #haštagy…", + "onboarding.profile.save_and_continue": "Ulož a pokračuj", + "onboarding.profile.upload_avatar": "Nahraj profilový obrázok", + "onboarding.profile.upload_header": "Nahraj profilové záhlavie", "onboarding.share.lead": "Daj ľudom vedieť, ako ťa môžu na Mastodone nájsť!", "onboarding.share.message": "Na Mastodone som {username}. Príď ma nasledovať na {url}", "onboarding.share.next_steps": "Ďalšie možné kroky:", @@ -594,6 +603,7 @@ "search.quick_action.status_search": "Príspevky zodpovedajúce {x}", "search.search_or_paste": "Hľadaj, alebo vlož URL adresu", "search_popout.full_text_search_disabled_message": "Nie je k dispozícii v doméne {domain}.", + "search_popout.full_text_search_logged_out_message": "Dostupné iba keď si prihlásený/á.", "search_popout.language_code": "ISO kód jazyka", "search_popout.options": "Možnosti vyhľadávania", "search_popout.quick_actions": "Rýchle akcie", diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json index 86042a91e..710224e1c 100644 --- a/app/javascript/mastodon/locales/sq.json +++ b/app/javascript/mastodon/locales/sq.json @@ -606,6 +606,7 @@ "search.quick_action.status_search": "Postime me përputhje me {x}", "search.search_or_paste": "Kërkoni, ose hidhni një URL", "search_popout.full_text_search_disabled_message": "Jo i passhëm në {domain}.", + "search_popout.full_text_search_logged_out_message": "E përdorshme vetëm kur keni bërë hyrjen në llogari.", "search_popout.language_code": "Kod ISO gjuhe", "search_popout.options": "Mundësi kërkimi", "search_popout.quick_actions": "Veprime të shpejta", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index c020dc362..0be9bf6d7 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -60,7 +60,7 @@ "account.report": "รายงาน @{name}", "account.requested": "กำลังรอการอนุมัติ คลิกเพื่อยกเลิกคำขอติดตาม", "account.requested_follow": "{name} ได้ขอติดตามคุณ", - "account.share": "แบ่งปันโปรไฟล์ของ @{name}", + "account.share": "แชร์โปรไฟล์ของ @{name}", "account.show_reblogs": "แสดงการดันจาก @{name}", "account.statuses_counter": "{count, plural, other {{counter} โพสต์}}", "account.unblock": "เลิกปิดกั้น @{name}", @@ -319,7 +319,7 @@ "home.show_announcements": "แสดงประกาศ", "interaction_modal.description.favourite": "ด้วยบัญชีใน Mastodon คุณสามารถชื่นชอบโพสต์นี้เพื่อแจ้งให้ผู้สร้างทราบว่าคุณชื่นชมโพสต์และบันทึกโพสต์ไว้สำหรับภายหลัง", "interaction_modal.description.follow": "ด้วยบัญชีใน Mastodon คุณสามารถติดตาม {name} เพื่อรับโพสต์ของเขาในฟีดหน้าแรกของคุณ", - "interaction_modal.description.reblog": "ด้วยบัญชีใน Mastodon คุณสามารถดันโพสต์นี้เพื่อแบ่งปันโพสต์กับผู้ติดตามของคุณเอง", + "interaction_modal.description.reblog": "ด้วยบัญชีใน Mastodon คุณสามารถดันโพสต์นี้เพื่อแชร์โพสต์กับผู้ติดตามของคุณเอง", "interaction_modal.description.reply": "ด้วยบัญชีใน Mastodon คุณสามารถตอบกลับโพสต์นี้", "interaction_modal.login.action": "นำฉันกลับบ้าน", "interaction_modal.login.prompt": "โดเมนของเซิร์ฟเวอร์บ้านของคุณ เช่น mastodon.social", @@ -495,7 +495,7 @@ "onboarding.share.lead": "แจ้งให้ผู้คนทราบวิธีที่เขาสามารถค้นหาคุณใน Mastodon!", "onboarding.share.message": "ฉันคือ {username} ใน #Mastodon! มาติดตามฉันที่ {url}", "onboarding.share.next_steps": "ขั้นตอนถัดไปที่เป็นไปได้:", - "onboarding.share.title": "แบ่งปันโปรไฟล์ของคุณ", + "onboarding.share.title": "แชร์โปรไฟล์ของคุณ", "onboarding.start.lead": "ตอนนี้คุณเป็นส่วนหนึ่งของ Mastodon แพลตฟอร์มสื่อสังคมที่มีเอกลักษณ์เฉพาะตัว กระจายศูนย์ ที่ซึ่งคุณ—ไม่ใช่อัลกอริทึม—เรียบเรียงประสบการณ์ของคุณเอง มาช่วยให้คุณเริ่มต้นใช้งานพรมแดนทางสังคมใหม่นี้กันเลย:", "onboarding.start.skip": "ไม่ต้องการความช่วยเหลือในการเริ่มต้นใช้งาน?", "onboarding.start.title": "คุณทำสำเร็จแล้ว!", @@ -506,7 +506,7 @@ "onboarding.steps.setup_profile.body": "เพิ่มการโต้ตอบของคุณโดยการมีโปรไฟล์ที่ครอบคลุม", "onboarding.steps.setup_profile.title": "ปรับแต่งโปรไฟล์ของคุณ", "onboarding.steps.share_profile.body": "แจ้งให้เพื่อน ๆ ของคุณทราบวิธีค้นหาคุณใน Mastodon", - "onboarding.steps.share_profile.title": "แบ่งปันโปรไฟล์ Mastodon ของคุณ", + "onboarding.steps.share_profile.title": "แชร์โปรไฟล์ Mastodon ของคุณ", "onboarding.tips.2fa": "คุณทราบหรือไม่? คุณสามารถรักษาความปลอดภัยบัญชีของคุณได้โดยตั้งค่าการรับรองความถูกต้องด้วยสองปัจจัยในการตั้งค่าบัญชีของคุณ การรับรองความถูกต้องด้วยสองปัจจัยทำงานร่วมกับแอป TOTP ใด ๆ ที่คุณเลือก ไม่จำเป็นต้องมีหมายเลขโทรศัพท์!", "onboarding.tips.accounts_from_other_servers": "คุณทราบหรือไม่? เนื่องจาก Mastodon เป็นแบบกระจายศูนย์ โปรไฟล์บางส่วนที่คุณเจอจะได้รับการโฮสต์ในเซิร์ฟเวอร์อื่น ๆ ที่ไม่ใช่ของคุณ และคุณยังสามารถโต้ตอบกับเขาได้อย่างไร้รอยต่อ! เซิร์ฟเวอร์ของเขาอยู่ในครึ่งหลังของชื่อผู้ใช้ของเขา!", "onboarding.tips.migration": "คุณทราบหรือไม่? หากคุณรู้สึกว่า {domain} ไม่ใช่ตัวเลือกเซิร์ฟเวอร์ที่ยอดเยี่ยมสำหรับคุณในอนาคต คุณสามารถย้ายไปยังเซิร์ฟเวอร์ Mastodon อื่นได้โดยไม่สูญเสียผู้ติดตามของคุณ คุณยังสามารถโฮสต์เซิร์ฟเวอร์ของคุณเอง!", @@ -558,7 +558,7 @@ "report.categories.spam": "สแปม", "report.categories.violation": "เนื้อหาละเมิดกฎของเซิร์ฟเวอร์จำนวนหนึ่งหรือมากกว่า", "report.category.subtitle": "เลือกที่ตรงกันที่สุด", - "report.category.title": "บอกเราถึงสิ่งที่กำลังเกิดขึ้นกับ {type} นี้", + "report.category.title": "บอกเราถึงสิ่งที่กำลังเกิดขึ้นกับ{type}นี้", "report.category.title_account": "โปรไฟล์", "report.category.title_status": "โพสต์", "report.close": "เสร็จสิ้น", @@ -629,7 +629,7 @@ "sign_in_banner.create_account": "สร้างบัญชี", "sign_in_banner.sign_in": "เข้าสู่ระบบ", "sign_in_banner.sso_redirect": "เข้าสู่ระบบหรือลงทะเบียน", - "sign_in_banner.text": "เข้าสู่ระบบเพื่อติดตามโปรไฟล์หรือแฮชแท็ก ชื่นชอบ แบ่งปัน และตอบกลับโพสต์ คุณยังสามารถโต้ตอบจากบัญชีของคุณในเซิร์ฟเวอร์อื่น", + "sign_in_banner.text": "เข้าสู่ระบบเพื่อติดตามโปรไฟล์หรือแฮชแท็ก ชื่นชอบ แชร์ และตอบกลับโพสต์ คุณยังสามารถโต้ตอบจากบัญชีของคุณในเซิร์ฟเวอร์อื่น", "status.admin_account": "เปิดส่วนติดต่อการควบคุมสำหรับ @{name}", "status.admin_domain": "เปิดส่วนติดต่อการควบคุมสำหรับ {domain}", "status.admin_status": "เปิดโพสต์นี้ในส่วนติดต่อการควบคุม", @@ -675,7 +675,7 @@ "status.replyAll": "ตอบกลับกระทู้", "status.report": "รายงาน @{name}", "status.sensitive_warning": "เนื้อหาที่ละเอียดอ่อน", - "status.share": "แบ่งปัน", + "status.share": "แชร์", "status.show_filter_reason": "แสดงต่อไป", "status.show_less": "แสดงน้อยลง", "status.show_less_all": "แสดงน้อยลงทั้งหมด", diff --git a/app/javascript/mastodon/locales/tlh.json b/app/javascript/mastodon/locales/tlh.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/app/javascript/mastodon/locales/tlh.json @@ -0,0 +1 @@ +{} diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 7ba66ae8c..e6dd008bf 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -376,7 +376,7 @@ "lightbox.previous": "上一步", "limited_account_hint.action": "一律顯示個人檔案", "limited_account_hint.title": "此個人檔案已被 {domain} 的管理員隱藏。", - "link_preview.author": "由 {name} 提供", + "link_preview.author": "來自 {name}", "lists.account.add": "新增至列表", "lists.account.remove": "自列表中移除", "lists.delete": "刪除列表", diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index e2ea4153c..8463d4297 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -70,6 +70,7 @@ ignore_unused: - 'imports.preambles.{following,blocking,muting,domain_blocking,bookmarks,lists}_html' - 'mail_subscriptions.unsubscribe.emails.*' - 'preferences.other' # some locales are missing other keys, therefore leading i18n-tasks to detect `preferences` as plural and not finding use + - 'edit_profile.other' # some locales are missing other keys, therefore leading i18n-tasks to detect `preferences` as plural and not finding use ignore_inconsistent_interpolations: - '*.one' diff --git a/config/locales/activerecord.fil.yml b/config/locales/activerecord.fil.yml new file mode 100644 index 000000000..4084bf2f9 --- /dev/null +++ b/config/locales/activerecord.fil.yml @@ -0,0 +1 @@ +fil: diff --git a/config/locales/activerecord.ne.yml b/config/locales/activerecord.ne.yml new file mode 100644 index 000000000..db03c5186 --- /dev/null +++ b/config/locales/activerecord.ne.yml @@ -0,0 +1 @@ +ne: diff --git a/config/locales/activerecord.ry.yml b/config/locales/activerecord.ry.yml new file mode 100644 index 000000000..6fe57b65c --- /dev/null +++ b/config/locales/activerecord.ry.yml @@ -0,0 +1 @@ +ry: diff --git a/config/locales/activerecord.tlh.yml b/config/locales/activerecord.tlh.yml new file mode 100644 index 000000000..884714fb7 --- /dev/null +++ b/config/locales/activerecord.tlh.yml @@ -0,0 +1 @@ +tlh: diff --git a/config/locales/devise.fil.yml b/config/locales/devise.fil.yml new file mode 100644 index 000000000..4084bf2f9 --- /dev/null +++ b/config/locales/devise.fil.yml @@ -0,0 +1 @@ +fil: diff --git a/config/locales/devise.ne.yml b/config/locales/devise.ne.yml new file mode 100644 index 000000000..db03c5186 --- /dev/null +++ b/config/locales/devise.ne.yml @@ -0,0 +1 @@ +ne: diff --git a/config/locales/devise.ry.yml b/config/locales/devise.ry.yml new file mode 100644 index 000000000..6fe57b65c --- /dev/null +++ b/config/locales/devise.ry.yml @@ -0,0 +1 @@ +ry: diff --git a/config/locales/devise.tlh.yml b/config/locales/devise.tlh.yml new file mode 100644 index 000000000..884714fb7 --- /dev/null +++ b/config/locales/devise.tlh.yml @@ -0,0 +1 @@ +tlh: diff --git a/config/locales/doorkeeper.fil.yml b/config/locales/doorkeeper.fil.yml new file mode 100644 index 000000000..4084bf2f9 --- /dev/null +++ b/config/locales/doorkeeper.fil.yml @@ -0,0 +1 @@ +fil: diff --git a/config/locales/doorkeeper.ne.yml b/config/locales/doorkeeper.ne.yml new file mode 100644 index 000000000..db03c5186 --- /dev/null +++ b/config/locales/doorkeeper.ne.yml @@ -0,0 +1 @@ +ne: diff --git a/config/locales/doorkeeper.ry.yml b/config/locales/doorkeeper.ry.yml new file mode 100644 index 000000000..6fe57b65c --- /dev/null +++ b/config/locales/doorkeeper.ry.yml @@ -0,0 +1 @@ +ry: diff --git a/config/locales/doorkeeper.sc.yml b/config/locales/doorkeeper.sc.yml index 1f1d38f3a..297d6bd8f 100644 --- a/config/locales/doorkeeper.sc.yml +++ b/config/locales/doorkeeper.sc.yml @@ -69,6 +69,7 @@ sc: confirmations: revoke: Seguru? index: + scopes: Permissos title: Is aplicatziones autorizadas tuas errors: messages: @@ -104,6 +105,20 @@ sc: authorized_applications: destroy: notice: Aplicatzione revocada. + grouped_scopes: + title: + accounts: Contos + bookmarks: Sinnalibros + conversations: Arresonadas + filters: Filtros + follows: Sighende + lists: Listas + media: Allegados multimediales + notifications: Notìficas + push: Notìficas push + reports: Informes + search: Chirca + statuses: Publicatziones layouts: admin: nav: diff --git a/config/locales/doorkeeper.tlh.yml b/config/locales/doorkeeper.tlh.yml new file mode 100644 index 000000000..884714fb7 --- /dev/null +++ b/config/locales/doorkeeper.tlh.yml @@ -0,0 +1 @@ +tlh: diff --git a/config/locales/fil.yml b/config/locales/fil.yml new file mode 100644 index 000000000..4084bf2f9 --- /dev/null +++ b/config/locales/fil.yml @@ -0,0 +1 @@ +fil: diff --git a/config/locales/fr-QC.yml b/config/locales/fr-QC.yml index 3aba8713f..00a59463c 100644 --- a/config/locales/fr-QC.yml +++ b/config/locales/fr-QC.yml @@ -611,6 +611,7 @@ fr-QC: created_at: Signalé delete_and_resolve: Supprimer les messages forwarded: Transféré + forwarded_replies_explanation: Ce rapport provient d'un utilisateur sur une autre instance et concerne du contenu non-local. Il vous a été transmis car le contenu signalé est en réponse à l'un de vos utilisateurs. forwarded_to: Transféré à %{domain} mark_as_resolved: Marquer comme résolu mark_as_sensitive: Marquer comme sensible diff --git a/config/locales/fr.yml b/config/locales/fr.yml index a69a5b535..0a6601bbc 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -611,6 +611,7 @@ fr: created_at: Signalé delete_and_resolve: Supprimer les messages forwarded: Transféré + forwarded_replies_explanation: Ce rapport provient d'un utilisateur sur une autre instance et concerne du contenu non-local. Il vous a été transmis car le contenu signalé est en réponse à l'un de vos utilisateurs. forwarded_to: Transféré à %{domain} mark_as_resolved: Marquer comme résolu mark_as_sensitive: Marquer comme sensible diff --git a/config/locales/lt.yml b/config/locales/lt.yml index 111127749..b1d8772b6 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -37,10 +37,12 @@ lt: accounts: add_email_domain_block: Blokuoti el. pašto domeną approve: Patvirtinti + approved_msg: Sėkmingai patvirtinta %{username} registracijos paraiška are_you_sure: Ar esi įsitikinęs (-usi)? avatar: Avataras by_domain: Domenas change_email: + changed_msg: El. paštas sėkmingai pakeistas! current_email: Dabartinis el paštas label: Pakeisti el pašto adresą new_email: Naujas el pašto adresas @@ -466,6 +468,8 @@ lt: prev: Ankstesnis preferences: other: Kita + privacy: + hint_html: "Tikrink, kaip nori, kad tavo profilis ir įrašai būtų randami. Įjungus įvairias Mastodon funkcijas, jos gali padėti pasiekti platesnę auditoriją. Akimirką peržiūrėk šiuos nustatymus, kad įsitikintum, jog jie atitinka tavo naudojimo būdą." remote_follow: missing_resource: Jūsų paskyros nukreipimo URL nerasta scheduled_statuses: @@ -515,6 +519,11 @@ lt: public_long: Visi gali matyti unlisted: Neįtrauktas į sąrašus unlisted_long: Matyti gali visi, tačiau nėra įtraukti į viešąsias laiko skales + statuses_cleanup: + enabled_hint: Automatiškai ištrina įrašus, kai jie pasiekia nustatytą amžiaus ribą, nebent jie atitinka vieną iš toliau nurodytų išimčių + keep_polls_hint: Neištrina jokių tavo apklausų + keep_self_bookmark: Laikyti įrašus, kuriuos pažymėjai + keep_self_bookmark_hint: Neištrina tavo pačių įrašų, jei esi juos pažymėjęs (-usi) stream_entries: sensitive_content: Jautrus turinys themes: @@ -551,22 +560,31 @@ lt: explanation: Štai keletas patarimų, kaip pradėti final_action: Pradėti kelti įrašus final_step: 'Pradėk skelbti! Net jei ir neturi sekėjų, tavo viešus įrašus gali matyti kiti, pavyzdžiui, vietinėje laiko skalėje arba saitažodžiuose. Galbūt norėsi prisistatyti saitažodyje #introductions.' - full_handle: Jūsų pilnas slapyvardis - full_handle_hint: Štai ką jūs sakytumėte savo draugams, kad jie galėtų jums siųsti žinutes arba just sekti iš kitų serverių. + full_handle: Tavo pilnas slapyvardis + full_handle_hint: Štai ką pasakytum savo draugams, kad jie galėtų parašyti arba sekti tave iš kito serverio. subject: Sveiki atvykę į Mastodon title: Sveiki atvykę, %{name}! users: - follow_limit_reached: Negalite sekti daugiau nei %{limit} žmonių + follow_limit_reached: Negali sekti daugiau nei %{limit} žmonių + go_to_sso_account_settings: Eik į savo tapatybės teikėjo paskyros nustatymus invalid_otp_token: Netinkamas dviejų veiksnių kodas otp_lost_help_html: Jei praradai prieigą prie abiejų, gali susisiek su %{email} - seamless_external_login: Jūs esate prisijungę per išorini įrenginį, todėl slaptąžodis ir el pašto nustatymai neprieinami. + seamless_external_login: Esi prisijungęs (-usi) per išorinę paslaugą, todėl slaptažodžio ir el. pašto nustatymai nepasiekiami. signed_in_as: 'Prisijungta kaip:' verification: + extra_instructions_html: Patarimas: nuoroda tavo svetainėje gali būti nematoma. Svarbi dalis – tai, kas rel="me" neleidžia apsimesti interneto svetainėse, kuriose yra naudotojų sukurto turinio. Vietoj to gali naudoti net nuorodą puslapio antraštėje esančią žymę a, tačiau HTML turi būti pasiekiamas nevykdant JavaScript. hint_html: "Savo tapatybės patvirtinimas Mastodon skirtas visiems. Remiantis atviraisiais žiniatinklio standartais, dabar ir visam laikui nemokamas. Viskas, ko tau reikia, yra asmeninė svetainė, pagal kurią žmonės tave atpažįsta. Kai iš savo profilio pateiksi nuorodą į šią svetainę, patikrinsime, ar svetainėje yra nuoroda į tavo profilį, ir parodysime vizualinį indikatorių." + instructions_html: Nukopijuok ir įklijuok toliau pateiktą kodą į savo svetainės HTML. Tada į vieną iš papildomų profilio laukų skirtuke „Redaguoti profilį“ įrašyk savo svetainės adresą ir išsaugok pakeitimus. verification: Patvirtinimas verified_links: Tavo patikrintos nuorodos webauthn_credentials: create: error: Kilo problema pridedant saugumo raktą. Bandyk dar kartą. + success: Tavo saugumo raktas buvo sėkmingai pridėtas. + delete_confirmation: Ar tikrai nori ištrinti šį saugumo raktą? + description_html: Jei įjungsi saugumo rakto tapatybės nustatymą, prisijungiant reikės naudoti vieną iš savo saugumo raktų. + destroy: + error: Kilo problema ištrinant saugumo raktą. Bandyk dar kartą. + success: Tavo saugumo raktas buvo sėkmingai ištrintas. nickname_hint: Įvesk naujojo saugumo rakto slapyvardį not_enabled: Dar neįjungei WebAuthn diff --git a/config/locales/ne.yml b/config/locales/ne.yml new file mode 100644 index 000000000..db03c5186 --- /dev/null +++ b/config/locales/ne.yml @@ -0,0 +1 @@ +ne: diff --git a/config/locales/ry.yml b/config/locales/ry.yml new file mode 100644 index 000000000..6fe57b65c --- /dev/null +++ b/config/locales/ry.yml @@ -0,0 +1 @@ +ry: diff --git a/config/locales/sc.yml b/config/locales/sc.yml index fa7603f2b..d92b64783 100644 --- a/config/locales/sc.yml +++ b/config/locales/sc.yml @@ -5,6 +5,7 @@ sc: contact_missing: No cunfiguradu contact_unavailable: No a disponimentu hosted_on: Mastodon allogiadu in %{domain} + title: Informatziones accounts: follow: Sighi followers: @@ -45,6 +46,7 @@ sc: confirm: Cunfirma confirmed: Cunfirmadu confirming: Cunfirmende + custom: Personalizadu delete: Cantzella datos deleted: Cantzelladu demote: Degrada @@ -81,7 +83,9 @@ sc: moderation: active: Ativu all: Totus + disabled: Disativadu pending: De imbiare + silenced: Limitadu suspended: Suspèndidu title: Moderatzione moderation_notes: Notas de moderatzione @@ -112,6 +116,7 @@ sc: search: Chirca search_same_email_domain: Àteras persones cun su pròpiu domìniu de posta search_same_ip: Àteras persones cun sa pròpiu IP + security: Seguresa sensitive: Sensìbile sensitized: marcadu comente a sensìbile shared_inbox_url: URL de intrada cumpartzida @@ -122,6 +127,7 @@ sc: silenced: Limitadas statuses: Tuts subscribe: Sutascrie·ti + suspend: Suspensione suspended: Suspèndidu suspension_irreversible: Is datos de custu contu sunt istados cantzellados in manera irreversìbile. Podes bogare sa suspensione a su contu pro chi si potzat impreare, ma no at a recuperare datu perunu de is chi teniat in antis. suspension_reversible_hint_html: Su contu est istadu suspèndidu, e is datos ant a èssere cantzelladu de su totu su %{date}. Finas a tando, su contu si podet ripristinare sena efetu malu perunu. Si boles cantzellare totu is datos de su contu immediatamente ddu podes fàghere inoghe in bassu. @@ -274,6 +280,7 @@ sc: updated_msg: Emoji atualizadu upload: Càrriga dashboard: + media_storage: Immagasinamentu software: Programmas space: Impreu de ispàtziu title: Pannellu @@ -281,21 +288,28 @@ sc: add_new: Permite sa federatzione cun domìniu created_msg: Sa federatzione cun su domìniu est istada permìtida destroyed_msg: Sa federatzione cun su domìniu no est istada permìtida + import: Importatzione undo: Non permitas sa federatzione cun su domìniu domain_blocks: add_new: Agiunghe blocu de domìniu nou + confirm_suspension: + cancel: Annulla + confirm: Suspensione created_msg: Protzessende su blocu de domìniu destroyed_msg: Su blocu de domìniu est istadu iscontzadu domain: Domìniu edit: Modìfica su blocu de su domìniu existing_domain_block_html: As giai impostu lìmites prus astrintos a %{name}, ddu dias dèpere isblocare prima. + import: Importatzione new: create: Crea unu blocu hint: Su blocu de domìniu no at a impedire sa creatzione de contos noos in sa base de datos, ma ant a èssere aplicados in manera retroativa mètodos de moderatzione ispetzìficos subra custos contos. severity: noop: Perunu + silence: A sa muda suspend: Suspensione title: Blocu de domìniu nou + not_permitted: Non tenes su permissu de fàghere custa atzione obfuscate: Cua su nòmine de domìniu obfuscate_hint: Cua una parte de su nòmine de domìniu in sa lista si sa visualizatzione de sa lista de domìnios limitados est ativa private_comment: Cummentu privadu @@ -326,9 +340,24 @@ sc: title: Cussìgios de sighidura unsuppress: Recùpera su cussìgiu de sighidura instances: + back_to_all: Totus + back_to_limited: Limitadu + back_to_warning: Atentzione by_domain: Domìniu + content_policies: + policies: + reject_reports: Refuda informes + silence: A sa muda + suspend: Suspensione + dashboard: + instance_reports_measure: informes a subra de àtere + delivery: + all: Totus delivery_available: Sa cunsigna est a disponimentu empty: Perunu domìniu agatadu. + known_accounts: + one: "%{count} contu connòschidu" + other: "%{count} contos connòschidos" moderation: all: Totus limited: Limitadas @@ -390,18 +419,23 @@ sc: notes: one: "%{count} nota" other: "%{count} notas" + action_log: Registru de controllu action_taken_by: Mesuras adotadas dae are_you_sure: Seguru? assign_to_self: Assigna a mie assigned: Moderatzione assignada by_target_domain: Domìniu de su contu signaladu + cancel: Annulla comment: none: Perunu + confirm: Cunfirma created_at: Sinnaladu forwarded: Torradu a imbiare forwarded_to: Torradu a imbiare a %{domain} mark_as_resolved: Marca comente a isòrvidu + mark_as_sensitive: Signala comente a sensìbile mark_as_unresolved: Marcare comente a non isòrvidu + no_one_assigned: Nemos notes: create: Agiunghe una nota create_and_resolve: Isorve cun una nota @@ -419,6 +453,15 @@ sc: unassign: Boga s'assignatzione unresolved: No isòrvidu updated_at: Atualizadu + view_profile: Visualiza profilu + roles: + categories: + administration: Amministratzione + invites: Invitos + moderation: Moderatzione + delete: Cantzella + privileges: + administrator: Amministratzione rules: add_new: Agiunghe règula delete: Cantzella @@ -427,10 +470,26 @@ sc: empty: Peruna règula de serbidore definida ancora. title: Règulas de su serbidore settings: + about: + manage_rules: Gesti is règulas de su serbidore + title: Informatziones + appearance: + title: Aspetu + default_noindex: + desc_html: Ìmplicat a totu is utentes chi no apant modificadu custa cunfiguratzione + title: Esclude in manera predefinida is utentes dae s'inditzamentu de is motores de chirca + discovery: + follow_recommendations: Cussìgios de sighidura + profile_directory: Diretòriu de profilos + public_timelines: Lìnias de tempos pùblicas + title: Iscoberta + trends: Tendèntzias domain_blocks: all: Pro totus disabled: Pro nemos users: Pro utentes locales in lìnia + registrations: + title: Registros registrations_mode: modes: approved: Aprovatzione rechesta pro si registrare @@ -439,7 +498,10 @@ sc: site_uploads: delete: Cantzella s'archìviu carrigadu destroyed_msg: Càrriga de su situ cantzellada. + software_updates: + documentation_link: Àteras informatziones statuses: + application: Aplicatzione back_to_account: Torra a sa pàgina de su contu deleted: Cantzelladu media: @@ -447,6 +509,10 @@ sc: no_status_selected: Perunu istadu est istadu mudadu dae chi non nd'as seletzionadu title: Istados de su contu with_media: Cun elementos multimediales + strikes: + actions: + none: "%{name} at imbiadu un'avisu a %{target}" + suspend: "%{name} at suspèndidu su contu de %{target}" system_checks: database_schema_check: message_html: Ddoe at tràmudas de base de datos in suspesu. Pone·ddas in esecutzione pro ti assegurare chi s'aplicatzione funtzionet comente si tocat @@ -459,12 +525,24 @@ sc: review: Revisiona s'istadu updated_msg: Cunfiguratzione de etichetas atualizada title: Amministratzione + trends: + pending_review: De revisionare + tags: + title: Etichetas de tendèntzia + title: Tendèntzias warning_presets: add_new: Agiunghe noa delete: Cantzella edit_preset: Modìfica s'avisu predefinidu empty: No as cunfiguradu ancora perunu avisu predefinidu. title: Gesti is cunfiguratziones predefinidas de is avisos + webhooks: + delete: Cantzella + disable: Disativa + disabled: Disativadu + enable: Ativa + enabled: Ativu + status: Istadu admin_mailer: new_pending_account: body: Is detàllios de su contu nou sunt a suta. Podes aprovare o refudare custa rechesta. @@ -473,6 +551,9 @@ sc: body: "%{reporter} at sinnaladu %{target}" body_remote: Una persone de su domìniu %{domain} at sinnaladu %{target} subject: Informe nou pro %{instance} (#%{id}) + new_trends: + new_trending_tags: + title: Etichetas de tendèntzia aliases: add_new: Crea unu nomìngiu created_msg: Nomìngiu creadu. Immoe podes cumintzare a tramudare dae su contu betzu. @@ -495,17 +576,21 @@ sc: notification_preferences: Muda is preferèntzias de posta salutation: "%{name}," settings: 'Muda is preferèntzias de posta: %{link}' + unsubscribe: Annulla sa sutiscritzione view: 'Visualizatzione:' view_profile: Visualiza profilu view_status: Ammustra s'istadu applications: created: Aplicatzione creada destroyed: Aplicatzione cantzellada + logout: Essi regenerate_token: Torra a generare s'identificadore de atzessu token_regenerated: Identificadore de atzessu generadu warning: Dae cara a custos datos. Non ddos cumpartzas mai cun nemos! your_token: S'identificadore tuo de atzessu auth: + confirmations: + login_link: intra delete_account: Cantzella su contu delete_account_html: Si boles cantzellare su contu, ddu podes fàghere inoghe. T'amus a dimandare una cunfirmatzione. description: @@ -528,11 +613,14 @@ sc: register: Registru registration_closed: "%{instance} no atzetat àteras persones" reset_password: Reseta sa crae + rules: + back: A coa security: Seguresa set_new_password: Cunfigura una crae noa status: account_status: Istadu de su contu confirming: Isetende chi sa posta eletrònica siat cumpletada. + functional: Su contu tuo est operativu. pending: Sa dimanda tua est in protzessu de revisione dae su personale nostru. Podet serbire unu pagu de tempus. As a retzire unu messàgiu eletrònicu si sa dimanda est aprovada. redirecting_to: Su contu tuo est inativu pro ite in die de oe est torrende a indiritzare a %{acct}. too_fast: Formulàriu imbiadu tropu a lestru, torra a proare. @@ -581,8 +669,14 @@ sc: more_details_html: Pro àteros detàllios, bide sa normativa de riservadesa. username_available: Su nòmine de utente tuo at a torrare a èssere a disponimentu username_unavailable: Su nòmine de utente tuo no at a abarrare a disponimentu + disputes: + strikes: + title_actions: + none: Atentzione domain_validator: invalid_domain: no est unu nòmine de domìniu vàlidu + edit_profile: + other: Àteru errors: '400': Sa dimanda chi as imbiadu non fiat vàlida o non fiat curreta. '403': Non tenes permissu pro bìdere custa pàgina. @@ -638,11 +732,15 @@ sc: title: Agiunghe unu filtru nou generic: all: Totus + cancel: Annulla changes_saved_msg: Modìficas sarvadas. + confirm: Cunfirma copy: Còpia delete: Cantzella + none: Perunu order_by: Òrdina pro save_changes: Sarva is modìficas + today: oe validation_errors: one: Calicuna cosa ancora no est andende. Bide sa faddina in bàsciu other: Calicuna cosa ancora no est andende. Bide is %{count} faddinas in bàsciu @@ -655,12 +753,15 @@ sc: overwrite: Subrascrie overwrite_long: Sostitui is registros atuales cun cussos noos preface: Podes importare datos chi as esportadu dae unu àteru serbidore, che a sa lista de sa gente chi ses sighende o blochende. + status: Istadu success: Datos carrigados; ant a èssere protzessados luego + type: Casta de importatzione types: blocking: Lista de blocos bookmarks: Sinnalibros domain_blocking: Lista domìnios blocados following: Lista de sighiduras + lists: Listas muting: Lista gente a sa muda upload: Càrriga invites: @@ -685,6 +786,13 @@ sc: expires_at: Iscadit uses: Impreos title: Invita gente + login_activities: + authentication_methods: + password: crae + webauthn: craes de seguresa + mail_subscriptions: + unsubscribe: + title: Annulla sa sutiscritzione media_attachments: validations: images_and_video: Non si podet allegare unu vìdeu in una publicatzione chi cuntenet giai immàgines @@ -797,6 +905,8 @@ sc: other: Àteru posting_defaults: Valores predefinidos de publicatzione public_timelines: Lìnias de tempos pùblicas + privacy: + search: Chirca reactions: errors: limit_reached: Lìmite de reatziones diferentes cròmpidu @@ -850,6 +960,7 @@ sc: platforms: adobe_air: Adobe Air android: Android + chrome_os: ChromeOS firefox_os: Firefox OS ios: iOS linux: Linux @@ -934,6 +1045,7 @@ sc: '2629746': 1 mese '31556952': 1 annu '5259492': 2 meses + '604800': 1 chida '63113904': 2 annos '7889238': 3 meses stream_entries: @@ -969,6 +1081,7 @@ sc: subject: S'archìviu tuo est prontu pro èssere iscarrigadu title: Collida dae s'archìviu warning: + reason: 'Resone:' subject: disable: Su contu tuo %{acct} est istadu cungeladu none: Avisu pro %{acct} diff --git a/config/locales/simple_form.fil.yml b/config/locales/simple_form.fil.yml new file mode 100644 index 000000000..4084bf2f9 --- /dev/null +++ b/config/locales/simple_form.fil.yml @@ -0,0 +1 @@ +fil: diff --git a/config/locales/simple_form.lt.yml b/config/locales/simple_form.lt.yml index 39caaf6ba..d53b7105e 100644 --- a/config/locales/simple_form.lt.yml +++ b/config/locales/simple_form.lt.yml @@ -27,14 +27,33 @@ lt: none: Naudok šią parinktį norėdamas (-a) išsiųsti įspėjimą naudotojui, nesukeldamas (-a) jokio kito veiksmo. sensitive: Priversk visus šio naudotojo medijos priedus pažymėti kaip jautrius. silence: Neleisk naudotojui skelbti viešai matomų įrašų, paslėpk jų įrašus ir pranešimus nuo žmonių, kurie neseka jo. Uždaro visus su šia paskyra susijusius ataskaitas. + suspend: Neleisk jokios sąveikos iš šios paskyros arba į ją ir ištrink jos turinį. Sugrąžinama per 30 dienų. Uždaro visas su šia paskyra susijusias ataskaitas. + warning_preset_id: Pasirinktinai. Gali pridėti pasirinktinį tekstą iš anksto nustatyto rinkinio pabaigoje + announcement: + all_day: Jei pažymėta, bus rodomos tik laikotarpio datos + ends_at: Pasirinktinai. Skelbimas šiuo laiku bus automatiškai panaikintas + scheduled_at: Palik tuščią, kad skelbimas būtų paskelbtas iš karto + starts_at: Pasirinktinai. Jei skelbimas susietas su tam tikru laiko tarpu + text: Gali naudoti įrašo sintaksę. Būk dėmesingas (-a), kiek vietos naudotojo ekrane užims skelbimas + appeal: + text: Gali pateikti apeliaciją dėl streiko tik vieną kartą defaults: + autofollow: Žmonės, kurie užsiregistruos per kvietimą, automatiškai seks tave avatar: PNG, GIF arba JPG. Ne daugiau kaip %{size}. Bus sumažintas iki %{dimensions} tšk. - header: PNG, GIF arba JPG. Ne daugiau kaip %{size}. Bus sumažintas iki %{dimensions}tšk. + bot: Signalizuoti kitiems, kad paskyroje daugiausia atliekami automatiniai veiksmai ir kad ji gali būti nestebima + context: Vienas arba keli kontekstai, kuriems turėtų būti taikomas filtras + current_password: Saugumo sumetimais įvesk dabartinės paskyros slaptažodį + current_username: Kad patvirtintum, įvesk dabartinės paskyros naudotojo vardą + digest: Siunčiama tik po ilgo neaktyvumo laikotarpio ir tik tuo atveju, jei negavai jokių asmeninių žinučių + email: Tau bus išsiųstas patvirtinimo el. laiškas + header: PNG, GIF arba JPG. Ne daugiau kaip %{size}. Bus sumažintas iki %{dimensions} tšk. inbox_url: Nukopijuok URL adresą iš pradinio puslapio perdavėjo, kurį nori naudoti irreversible: Filtruoti įrašai išnyks negrįžtamai, net jei vėliau filtras bus pašalintas locale: Naudotojo sąsajos kalba, el. laiškai ir stumiamieji pranešimai password: Naudok bent 8 simbolius - phrase: Bus suderinta, neatsižvelgiant į teksto korpusą arba įrašo turinio įspėjimą + phrase: Bus suderinta, neatsižvelgiant į teksto lygį arba įrašo turinio įspėjimą + scopes: Prie kurių API programai bus leidžiama pasiekti. Pasirinkus aukščiausio lygio sritį, atskirų sričių pasirinkti nereikia. + setting_aggregate_reblogs: Nerodyti naujų pakėlimų įrašams, kurie neseniai buvo pakelti (taikoma tik naujai gautiems pakėlimams) setting_always_send_emails: Paprastai pranešimai el. paštu nebus siunčiami, kai aktyviai naudoji Mastodon setting_default_sensitive: Jautrioji medija pagal numatytuosius nustatymus yra paslėpta ir gali būti atskleista paspaudus setting_display_media_default: Slėpti mediją, pažymėtą kaip jautrią @@ -42,16 +61,26 @@ lt: setting_display_media_show_all: Visada rodyti mediją setting_use_blurhash: Gradientai pagrįsti paslėptų vaizdų spalvomis, tačiau užgožia bet kokias detales setting_use_pending_items: Slėpti laiko skalės naujienas po paspaudimo, vietoj automatinio kanalo slinkimo + username: Gali naudoti raides, skaičius ir pabraukimus + whole_word: Kai raktažodis ar frazė yra tik raidinis ir skaitmeninis, jis bus taikomas tik tada, jei atitiks visą žodį featured_tag: name: 'Štai keletas pastaruoju metu dažniausiai saitažodžių, kurių tu naudojai:' + filters: + action: Pasirink, kokį veiksmą atlikti, kai įrašas atitinka filtrą + actions: + hide: Visiškai paslėpti filtruotą turinį ir elgtis taip, tarsi jo neegzistuotų + warn: Slėpti filtruojamą turinį po įspėjimu, paminint filtro pavadinimą form_admin_settings: + activity_api_enabled: Vietinių paskelbtų įrašų, aktyvių naudotojų ir naujų registracijų skaičiai kas savaitę + backups_retention_period: Laikyti sukurtus naudotojų archyvus nurodytą dienų skaičių. peers_api_enabled: Domenų pavadinimų sąrašas, su kuriais šis serveris susidūrė fediverse. Čia nėra duomenų apie tai, ar tu bendrauji su tam tikru serveriu, tik apie tai, kad tavo serveris apie jį žino. Tai naudojama tarnybose, kurios renka federacijos statistiką bendrąja prasme. site_contact_email: Kaip žmonės gali su tavimi susisiekti teisiniais ar pagalbos užklausimais. site_contact_username: Kaip žmonės gali tave pasiekti Mastodon. site_extended_description: Bet kokia papildoma informacija, kuri gali būti naudinga lankytojams ir naudotojams. Gali būti struktūrizuota naudojant Markdown sintaksę. trends: Trendai rodo, kurios įrašai, saitažodžiai ir naujienų istorijos tavo serveryje sulaukia didžiausio susidomėjimo. sessions: - webauthn: Jei tai USB raktas, būtinai jį įkišk ir, jei reikia, paliesk. + otp: 'Įvesk telefono programėlėje sugeneruotą dviejų tapatybės kodą arba naudok vieną iš atkūrimo kodų:' + webauthn: Jei tai USB raktas, būtinai jį įkišk ir, jei reikia, paspausk. settings: indexable: Tavo profilio puslapis gali būti rodomas paieškos rezultatuose Google, Bing ir kituose. labels: diff --git a/config/locales/simple_form.ne.yml b/config/locales/simple_form.ne.yml new file mode 100644 index 000000000..db03c5186 --- /dev/null +++ b/config/locales/simple_form.ne.yml @@ -0,0 +1 @@ +ne: diff --git a/config/locales/simple_form.ry.yml b/config/locales/simple_form.ry.yml new file mode 100644 index 000000000..6fe57b65c --- /dev/null +++ b/config/locales/simple_form.ry.yml @@ -0,0 +1 @@ +ry: diff --git a/config/locales/simple_form.sc.yml b/config/locales/simple_form.sc.yml index 2c4725996..5f5d63307 100644 --- a/config/locales/simple_form.sc.yml +++ b/config/locales/simple_form.sc.yml @@ -53,6 +53,8 @@ sc: domain: Custu domìniu at a pòdere recuperare datos dae custu serbidore e is datos in intrada dae cue ant a èssere protzessados e archiviados email_domain_block: with_dns_records: S'at a fàghere unu tentativu de risòlvere is registros DNS de su domìniu e fintzas is risultados ant a èssere blocados + form_admin_settings: + activity_api_enabled: Nùmeru de tuts publicados in locale, utentes ativos e registros noos in perìodos chidajolos form_challenge: current_password: Ses intrende in un'àrea segura imports: @@ -155,6 +157,7 @@ sc: setting_use_pending_items: Modalidade lenta severity: Severidade sign_in_token_attempt: Còdighe de seguresa + title: Tìtulu type: Casta de importatzione username: Nòmine utente username_or_email: Nòmine utente o indiritzu de posta eletrònica @@ -163,6 +166,16 @@ sc: with_dns_records: Include registros MX e indiritzos IP de su domìniu featured_tag: name: Eticheta + form_admin_settings: + activity_api_enabled: Pùblica istatìsticas agregadas subra s'atividade de s'utente + custom_css: CSS personalizadu + peers_api_enabled: Pùblica sa lista de serbidores iscobertos in s'API + profile_directory: Ativa diretòriu de profilos + show_domain_blocks: Ammustra blocos de domìniu + site_contact_username: Nòmine de utente de su cuntatu + site_short_description: Descritzione de su serbidore + site_title: Nòmine de su serbidore + thumbnail: Miniadura de su serbidore interactions: must_be_follower: Bloca is notìficas dae chie non ti sighit must_be_following: Bloca is notìficas dae gente chi non sighis @@ -186,6 +199,7 @@ sc: mention: Una persone t'at mentovadu pending_account: Unu contu nou tenet bisòngiu de una revisione reblog: Una persone at cumpartzidu s'istadu tuo + report: Imbiu de un'informe nou rule: text: Règula tag: @@ -193,6 +207,9 @@ sc: name: Eticheta trendable: Permite a custa eticheta de apàrrere in is tendèntzias usable: Permite a is tuts de impreare custa eticheta + user_role: + name: Nòmine + permissions_as_keys: Permissos 'no': Nono recommended: Cussigiadu required: diff --git a/config/locales/simple_form.tlh.yml b/config/locales/simple_form.tlh.yml new file mode 100644 index 000000000..884714fb7 --- /dev/null +++ b/config/locales/simple_form.tlh.yml @@ -0,0 +1 @@ +tlh: diff --git a/config/locales/tlh.yml b/config/locales/tlh.yml new file mode 100644 index 000000000..884714fb7 --- /dev/null +++ b/config/locales/tlh.yml @@ -0,0 +1 @@ +tlh: From 996c13a24d941e02458fa21d30e8334d04bdb2b5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:33:11 +0100 Subject: [PATCH 71/95] Update dependency core-js to v3.34.0 (#28241) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4226fcc0f..9e1622680 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5960,9 +5960,9 @@ __metadata: linkType: hard "core-js@npm:^3.30.2": - version: 3.33.3 - resolution: "core-js@npm:3.33.3" - checksum: 08abdc9470c8228b9d09f61e62ab312738681202c4c34e9638889125b304b235f34c4fe22e9d41c20906ac0fcc807dca57c5ff7d6b90021bf64e8fe23461d9ab + version: 3.34.0 + resolution: "core-js@npm:3.34.0" + checksum: 408a77898abe03bf3e5dec2a451c36f4745081cca9022f8bdf9b817d57bb6d3a534d555f47a4b95e1daa5e21dbc79122eac2402e25720d425f5925127e55dcd8 languageName: node linkType: hard From 3b710b96cf4a7ff743313f20b054c6220da3dd6b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:33:27 +0100 Subject: [PATCH 72/95] Update dependency irb to v1.10.1 (#28240) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9308a41c8..4a409a0ad 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -377,7 +377,7 @@ GEM terminal-table (>= 1.5.1) idn-ruby (0.1.5) io-console (0.6.0) - irb (1.10.0) + irb (1.10.1) rdoc reline (>= 0.3.8) jmespath (1.6.2) @@ -608,7 +608,7 @@ GEM link_header (~> 0.0, >= 0.0.8) rdf-normalize (0.6.1) rdf (~> 3.2) - rdoc (6.6.0) + rdoc (6.6.1) psych (>= 4.0.0) redcarpet (3.6.0) redis (4.8.1) From faffd81976092fc5a95fb359ec5844c2af76101d Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 6 Dec 2023 03:44:07 -0500 Subject: [PATCH 73/95] Remove double subject call in `services/unsuspend_account_service` spec (#28215) --- .../unsuspend_account_service_spec.rb | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/spec/services/unsuspend_account_service_spec.rb b/spec/services/unsuspend_account_service_spec.rb index c555b661e..2f737c621 100644 --- a/spec/services/unsuspend_account_service_spec.rb +++ b/spec/services/unsuspend_account_service_spec.rb @@ -45,14 +45,19 @@ RSpec.describe UnsuspendAccountService, type: :service do remote_follower.follow!(account) end - it "merges back into local followers' feeds" do + it 'merges back into feeds of local followers and sends update' do subject + + expect_feeds_merged + expect_updates_sent + end + + def expect_feeds_merged expect(FeedManager.instance).to have_received(:merge_into_home).with(account, local_follower) expect(FeedManager.instance).to have_received(:merge_into_list).with(account, list) end - it 'sends an update actor to followers and reporters' do - subject + def expect_updates_sent expect(a_request(:post, remote_follower.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once expect(a_request(:post, remote_reporter.inbox_url).with { |req| match_update_actor_request(req, account) }).to have_been_made.once end @@ -73,19 +78,20 @@ RSpec.describe UnsuspendAccountService, type: :service do allow(resolve_account_service).to receive(:call).with(account).and_return(account) end - it 're-fetches the account' do - subject + it 're-fetches the account, merges feeds, and preserves suspended' do + expect { subject } + .to_not change_suspended_flag + expect_feeds_merged expect(resolve_account_service).to have_received(:call).with(account) end - it "merges back into local followers' feeds" do - subject + def expect_feeds_merged expect(FeedManager.instance).to have_received(:merge_into_home).with(account, local_follower) expect(FeedManager.instance).to have_received(:merge_into_list).with(account, list) end - it 'does not change the “suspended” flag' do - expect { subject }.to_not change(account, :suspended?) + def change_suspended_flag + change(account, :suspended?) end end @@ -97,19 +103,20 @@ RSpec.describe UnsuspendAccountService, type: :service do end end - it 're-fetches the account' do - subject + it 're-fetches the account, does not merge feeds, marks suspended' do + expect { subject } + .to change_suspended_to_true expect(resolve_account_service).to have_received(:call).with(account) + expect_feeds_not_merged end - it "does not merge back into local followers' feeds" do - subject + def expect_feeds_not_merged expect(FeedManager.instance).to_not have_received(:merge_into_home).with(account, local_follower) expect(FeedManager.instance).to_not have_received(:merge_into_list).with(account, list) end - it 'marks account as suspended' do - expect { subject }.to change(account, :suspended?).from(false).to(true) + def change_suspended_to_true + change(account, :suspended?).from(false).to(true) end end @@ -118,13 +125,14 @@ RSpec.describe UnsuspendAccountService, type: :service do allow(resolve_account_service).to receive(:call).with(account).and_return(nil) end - it 're-fetches the account' do + it 're-fetches the account and does not merge feeds' do subject + expect(resolve_account_service).to have_received(:call).with(account) + expect_feeds_not_merged end - it "does not merge back into local followers' feeds" do - subject + def expect_feeds_not_merged expect(FeedManager.instance).to_not have_received(:merge_into_home).with(account, local_follower) expect(FeedManager.instance).to_not have_received(:merge_into_list).with(account, list) end From 5517df61de1e867afa531268755ee893ffca3c99 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 6 Dec 2023 03:44:51 -0500 Subject: [PATCH 74/95] Remove double subject call in `services/activitypub/process_account_service` spec (#28214) --- .../process_account_service_spec.rb | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index c02a0800a..09eb5ddee 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -129,12 +129,10 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do stub_const 'ActivityPub::ProcessAccountService::SUBDOMAINS_RATELIMIT', 5 end - it 'creates at least some accounts' do - expect { subject }.to change { Account.remote.count }.by_at_least(2) - end - - it 'creates no more account than the limit allows' do - expect { subject }.to change { Account.remote.count }.by_at_most(5) + it 'creates accounts without exceeding rate limit' do + expect { subject } + .to create_some_remote_accounts + .and create_fewer_than_rate_limit_accounts end end @@ -195,12 +193,20 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do end end - it 'creates at least some accounts' do - expect { subject.call('user1', 'foo.test', payload) }.to change { Account.remote.count }.by_at_least(2) - end - - it 'creates no more account than the limit allows' do - expect { subject.call('user1', 'foo.test', payload) }.to change { Account.remote.count }.by_at_most(5) + it 'creates accounts without exceeding rate limit' do + expect { subject.call('user1', 'foo.test', payload) } + .to create_some_remote_accounts + .and create_fewer_than_rate_limit_accounts end end + + private + + def create_some_remote_accounts + change(Account.remote, :count).by_at_least(2) + end + + def create_fewer_than_rate_limit_accounts + change(Account.remote, :count).by_at_most(5) + end end From be6bb1a10d6f1f23151198c6487f44145a0692ee Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 6 Dec 2023 03:45:19 -0500 Subject: [PATCH 75/95] Remove double subject call in `services/suspend_account_service` spec (#28213) --- spec/services/suspend_account_service_spec.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb index edb705008..c258995b7 100644 --- a/spec/services/suspend_account_service_spec.rb +++ b/spec/services/suspend_account_service_spec.rb @@ -18,14 +18,15 @@ RSpec.describe SuspendAccountService, type: :service do account.suspend! end - it "unmerges from local followers' feeds" do - subject + it 'unmerges from feeds of local followers and preserves suspended flag' do + expect { subject } + .to_not change_suspended_flag expect(FeedManager.instance).to have_received(:unmerge_from_home).with(account, local_follower) expect(FeedManager.instance).to have_received(:unmerge_from_list).with(account, list) end - it 'does not change the “suspended” flag' do - expect { subject }.to_not change(account, :suspended?) + def change_suspended_flag + change(account, :suspended?) end end From ed7b5c091b62d63e695d2f1ff946e021fb37fa18 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 6 Dec 2023 03:51:09 -0500 Subject: [PATCH 76/95] Remove double subject call in `services/delete_account_service` spec (#28212) --- spec/services/delete_account_service_spec.rb | 25 +++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/spec/services/delete_account_service_spec.rb b/spec/services/delete_account_service_spec.rb index 68ab491e4..8a19d3cf7 100644 --- a/spec/services/delete_account_service_spec.rb +++ b/spec/services/delete_account_service_spec.rb @@ -27,8 +27,15 @@ RSpec.describe DeleteAccountService, type: :service do let!(:account_note) { Fabricate(:account_note, account: account) } - it 'deletes associated owned records' do - expect { subject }.to change { + it 'deletes associated owned and target records and target notifications' do + expect { subject } + .to delete_associated_owned_records + .and delete_associated_target_records + .and delete_associated_target_notifications + end + + def delete_associated_owned_records + change do [ account.statuses, account.media_attachments, @@ -39,23 +46,23 @@ RSpec.describe DeleteAccountService, type: :service do account.polls, account.account_notes, ].map(&:count) - }.from([2, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0]) + end.from([2, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0]) end - it 'deletes associated target records' do - expect { subject }.to change { + def delete_associated_target_records + change do [ AccountPin.where(target_account: account), ].map(&:count) - }.from([1]).to([0]) + end.from([1]).to([0]) end - it 'deletes associated target notifications' do - expect { subject }.to change { + def delete_associated_target_notifications + change do %w( poll favourite status mention follow ).map { |type| Notification.where(type: type).count } - }.from([1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0]) + end.from([1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0]) end end From 0e8ba19113182f74a0adde0ac75a35694833316c Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 6 Dec 2023 03:52:30 -0500 Subject: [PATCH 77/95] Add spec coverage for `CLI::Emoji` class (#28182) --- spec/fixtures/files/elite-assets.tar.gz | Bin 0 -> 17590 bytes spec/lib/mastodon/cli/emoji_spec.rb | 54 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 spec/fixtures/files/elite-assets.tar.gz diff --git a/spec/fixtures/files/elite-assets.tar.gz b/spec/fixtures/files/elite-assets.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..7b4f4425705c62fe4b538fa52148937e51a69552 GIT binary patch literal 17590 zcmV(nK=QvIiwFRdRBB}a1MIp5RGrPTD7bO=;0aE!;O_43Zo%Ds;~s)r(2ctVcMCy- zyKf-424`c#f6jUL-Z^vMc{6X_H*03i)L!-V*WJ}6-Bs27_1a9v&i1D6HjW++#-^@j z)=bWhR)GI%U}0h5;@}|rtK{b5{9DPw_V@bVfP;&bjFp3vosENogPoI;jD?l!?FS%Z z`7hVxKLHOncT?9lRUB+h9qpXF-0W=rbGkP&OUr-3=kKPF{ag9}7J$P;06+r(il$~{ z%Iah~WdDG_+5kvz$o7V>Z}?V+V*NkIQdC1j?eA}Y8v%Yp)qf&^`A0h@06=8s}+qrWN+%`?%`%(Zf@#sL8+qt588`2L;(PB|DDd+)ydPs(bUn*;;;V4H{|-?^}BeO zx|%w^d9?*#Q{MJ(MDo#le1Fpj0DxRo(ss~F3Ar${u<@|-b8&K+a(p)D=I7^?aKVBr)KW8wIpo8@>`1k%V)8Jpg|HtE>jhD>9-NwN}kd>Q-m4l0eorRT&larT)_ia^> zSy@@h6lBFj)x>3FwIrFmP2JsHnf|jd6I7ItV^h-5vsRLpvs3bQ<5t$xWKm_& zWmWPObyxKjlTcD;VNtTvWmnLYl2nq^bXU}{mr}A7Wl@yS5?6gIk$HH@)ZZj2=>987 z{}VBb%*p+)%=q_gA}FdVD$3xg>?*I|=A>rJfz0vW)#yJA`VT1unTz*dN&2V!Cdkg> z?xOc5QdCq-!Nx+DOZ0Dm%=zDy`p*phIlCeMUta(J^}GN7DE>KES=rhCDgHURIXM3R z{r~?Ch*nXOMnfk0%W=?TWhDOEw}bvdMEJMo4@;=PWv@f*HCsPx2!@J@!5>c77W-Z<*Y?f@ah# zMmtmN#gC@dpx>RCh;tV*=D9xzw%Vxbkm46ua48;rPia$n`kOtLd#+H;Le3uvyC!5=gc zT#I%WEA`fG$z`N*EKwfH)yh$Gs`}t>Z6Sw)wwp8auc}0&?9(}PibJ{8_8I$#n}|Zm z#2yqW`dC5U`7#5X;2&$d@L|XoNco@G`HYF~<#^r~cRBk!=@qLHHf0{c5&(^_o^f>XRvrwv>lY52+b@D`O|4HNO4TQTx({8a{t-MGsJR)yYcG{|5 z*K7Al*XwC@vMuh+NSiL5n&(Th94O*;BbM0DxYZeoq{=%(@=EF=1Nq0M?cIt30MH}d zgIUJYTl?<*I-i9Naq&wKv#+3kXlTZmnb}VNwf)DO?otKj8iUc|kfxWKTqAAz3Mhah zVj|#Mu}W^T=qz69C{lsp_^$Oci__0B)Mwhp&`CGk%X`h*kxz7Up~~Uf$UrK=Amt6j zzLbD@F&8(ae7(KP-XQWU#bPCMwzXyf)TfMcS#7-Dr@8)0;SQQ=7Ixlpc;m6O&*koT z6o>Aa_1IdSzjCXPwD|AWR7~vbj^v?+0Ccd}wJ9r+O#T7-gv-WES%4z;jPppjgu-?h zZhlJ1P^mJMM)&sM&fL;@LkSl(=*@9F`BLDu-TM`!0RD&UGSlM@KyRrN7rfL^YRYhP zsB#8-c!-C3{}pT=#8+dko}Ecp(4rcYHO;Q1$Unt+k?mcSq&0PcH86%K!pEO0C0||E zj4cTL6rg$kei4v8JETf*H2ysYdzndUFYum{RCGl%^cSHN_*(j*ZNcybBl{< zL5;w6ChgH0GBgou!@paSRKZsx0B!sgt5GbD@)*V0J2f zM;W!p=p*Yy!u;xODO;1@zqWov`s^V~dQqiT8>IV^s@T=aG?fDG54P}8z1>hHFO}|8 zeoc<5e3HYwmY@uB|Clqg^@LK|UeXz^yCFxvdd`bVeRRKilWJ3-Ll|MZBQJk)}Fnb+Lk+VgY>C8ZcWx*{Ceuf&9 zD9|G0hnv>VV`$u{PlA36=@_Kl9HPOb0b||#2277x%kB_zh%;f}+|Tw_d+E}w3<59h zIRDw5NF^_ksuy#ZGDeFdTa)I z99^79@Jef8XWzd@-$+CXp2Izij5)}mD2P<#uAwbE zYhL9dy5~XO>#R7`ap(A~<&hu~*%G!wfB4-O>R5dB*PHJ&7%%V`m?8OziI*2^v|F4Q zLc3A`vEzMT-uzQ=t?rH*aeK>eBLTNPWH^8CSK>g4Q#_9zLAATQ6v=ffxR-+mb+NXS zjjP(1YQL*cCpVZ%EzNlWTu@Gq=CO8?3=zu{A3wiCnDfo3#oc`1{g=mn&m#=Nf9MulH>*lK~lQy&u%R?lm1MlXGJ&k!8r$2}TJtIuyo06X_=A%94tmjc|jJb!cebSgYk?wZ`uTkGsy(tVus%;GJCR7Y`Kr zP19I{fWxs#;IZ43olBz%$m6Nq5E$q3G1t&;|CC-s>c~yxXm;*-s>|=}^zigW!iwpx zCgSMj=l*)A@a?SH-MO2-*%65c2}TT@a4w!Wt2@e2{0!q$liyjx>!L&ZqYz=7bu=Nd zMujBqF8A=X|H0E_L(Me`h}ZexePcwYzsZ7m_AT`O>2?F!b9x2TidvbV->thsF7GU* zgK)F-k=yaYO;4F*4(YK<$IMh^k=CFqiSFG-sQ*iG>3Xx@2D!&8^V(TQ@!7l_O2Cqq z=3Q{x7?ki-*&&{#=*#G7V| zHOy4&5aB%VIlg4JT-n{j-F`^4&}2c7M(5sa-^e9(`>Wr%vj1F{#VbauX6VRK?tK`~ zy}>MBXcMW(Za`%)N2V=T=%2L=_OtTD+EyB&o2(kUaV_rF0Jr%%>hat@L~B z*f_2BLqeQw4a$6{MFboZ_etFU+6Y*Q>Qv6$z4L{9W%4Dj!FM@i#c4? ziS|6EU&V7d(~Mt_mbtH!orfspdT&0)Nby2{A^b$0;5|}8pj^6sE)4nI08%CLR!e2d zX(0lDnn++_jHP2s^igEN?2SlC%TaQ+-b;TNgrC6O4s2(Enw*fl!ShOhB>Ne(O$}33 zx2j!#RKYwq>LPQlC;Q?=dtCPo+75VwQ#0qL8p}T<7SQv&()&<9pP+LBRT%QwWuaIi zJx!V96A$BfTw6`>c#Sb7%o!(=WO4GOYrxC=LzK?v?P4nMQ}W%P=lBmi_sbW1>YrA` zN`o*W!g`&v34fM+Z9WhphZiry9fG5IA3^wx-{+jaMPlq9hGK)y`KA#K zxvRkZ!kQW4gx-DW8OrV8RnEx2CpR#TDWT|g-`x7cQL>mzDpL&!3*YCRp|b^tNpcL9 zAuR7#E|o`%d8gon;u=Md0mBvAkw3V*-9WlyFrR zneq(LJ)hp|=yz2a+Xs{o=FjC@GBYF_uO-51E6+%63K@zGNJR6()yV1uOJzj1)e#u& z*TFABI*(f$on{}1M=i>M8g5N+a^V#op2L3H_#PzXQrv*7Mr&UTq4#qiUQnLA<j9b^U8pnS9XK>J+>(f_ z+YvO3X$=SDu3Bukt`z8eeGxu78GZ>fsvTu;QEJ=Qh%V48YlnO#07ny91j<4E#9`dX z&UIf@3e-pW!=Wr}rShkh2{A9Ff;_K7%fZv|x$-8v!1qF456b;zslj!R>UPYd@rWta ziBEb*c5GjH-hWw+$hM&GQ0;Tx$`wR7=xeC0^RZF-@kvGWWD5Hj{vRhY4Wovs;;2?2 zirefWR%C&cCP+H;nCD&s37kdBUt~iC2%1!ZQo`1Q50rri^)grN1~yWrKgjlKL(pCE z6O-!3<%-AgzN9iAc$X!)Y#xv91sFUZARG{J3{QSF7TQ1a8}eIw#t1M#y|jBs?G(); zO*_KHF@~I(-WB&ia9V7CjBBlqOvjvcxAd&8_q=Lz`szeY055}vo}cOf3JdlU(vClY9(ok8i^c6Me` zPX!D4Vc?Go$+}jQCs}A=7a~4cK-$LT;T`8MKkI{+>iP>I@>Gn2Mj*Ya794vx@DIEC z!&9xmL+m+_S!3J(@m%{g4FZ@p5i_;y_we-SSUp>FNb8&zlBnS&vOpWMjJ08(opn@m zx~)o!&D#Y!-a>e}TRn3+%q9)@$U;0TgIkvvP$^lTP;H?m)&MsBR#JnNEOOM zEcIacMTf?glzuAQT&Xa)@eoGUlA)9Z#1Jhr4h|-fF|c@n5rBZwH6G%7)?9XryBTYp zea=CX%a|ryGwMFGA0wkQAotnYlFRt%T>1UeATzz~{zezEUyJ=Av(U?ZqHb4^$BF{9 zez?BtdL>sYJB{)Z3r5LB6<()`!1?dK$Hnpt0ZG?3q3Y|{AfavY}Bd9(h)&N62PawH(~s`H_)0RZzD5Kt$gcXU;lR4g?I*?gGr zuUxY~lC~C{QeWpdanX(Sf5p4t?BKSJ;WgsnfB3;{AS0|)RK-tTsAE>CQL5oXy*Qsk z)aAXgyK9WF1dvOKNAISd<%2@5eSgO{ugB-N0I?qBS)F!>;g`V(s)jv@PXb($$>+h& zJTzFXgbznf|rDbpTKb`y2>))qIA(=s zm&@vBM;Y3`@P?7@<2GKr$4aSFIZ=*E5+PL6?{ah`>t&eBiD6I; z|1}h2iiL1f$e+E^zb$fR;L9M>x6Sad6T5h$)5^(s>zO8jn_U`%*Ao6U#ah46RYXs)1jXV{AV0LMX6WbH&qON+qm+*(`-^h;O$brxU~`+`g>X1hl^I~ z3tT6Xdn$X~Q}H~Z;o(H5KqZlC+}61ah&R$A^JDvAG(qa|v6nWJAsY)!!wxud0pY$; zDkAuzY8KBQ@&tt{KG6<g%!=Xrh@86|O$A4Jupns4FKO;hZ_AbtFrzhdcZ2wmW@Z}Me_9U&qTtx~HO zv^Y#FUx;ZB$SvebLlv6+7B5hsmm*i6^|fl;vP?}+G1*XsV*bP!9RMX_waMr8upM4; zV}v2}3ypDehMWVsTK;+e*DlKT2l0w3qdOAqz#Ol2x>}=SLRF0J;7@#WO*S~hnDs$>7L`}6M~u1?cB6>TvSJ}eP$UPJQ2vA{IGj{j>ohVq~u>`43__jrWJAwa2 zbUade-t2&{$UR{XYl>HQ2Nj(|{v$|i3fAR^;_d1!Tpsc#$Wq2#FW(~;qNY% zI(prh_oDi$X(J;@UZ?$ZEuL5CP!96sOJ2hpT4^f6Oy^5ZfiJ!6#;D;i_gI8J5L{`g z{`Dgl$-YQ>#s+epdHXDoPW~JM0-l|cnPWti&rDlsJSmT}E*!7CUP8&{2f@I8!;XOQ z#$hq=svFeC^14F|r}YK=_DKrrOzUlmwj4B+40!@qt>I81~1xzpQ2f-T_O%-H{S9qYa-sem<;S#pR?4%21WEhMw=!A0PiG1BpPbA6DzgVCKqQ>5O3o zBfW3hWj9@24K(w5hf_YSDS6$m7K%hm2~22!b-SOv49y=&MevXjG0rqgj2QutD$i#X zWAu9hOHuwNXrcbQ5#vW(uiI4wLsci5wTG#WwMpQKaHaeKx9@Lh8EDerp9!5*;>~U* zp*ai{)j*suX5Ieq*IzpgNuTsjrI)v@Scu8m9Ul@j%9bOApG?)IS$EIdW}%l^urXC= z0CGq_X=7o15!SznfQ>4H7YyijB|}fBIrspbw*cki?)+zg zksYU??pgo4@64?`(((7Q`7&fX;AFgf0I)GK7QiF}C=f)r;So1Xx#n>Z@8VTPj0S3T zT2%T^n`{iN*r3*iQT*zeFH>o_8Oz$IaR9!3sRqzpo4ee0&Iwyv5g=U;8wY5jW+$>o zq;(|V1wAN`q{i^DOR+#BNN4WS|g0MvJCcN zSTxkg9ny`ZMC55tQ2OV!S?y?E(bkL%`%?PflbWhKG z8vPyeK?C$D+jA2T5fyGdwV}3^P7xVye!Xt6QKoq3<~(Ty5dc6oocX%e7M9lA^6e#l z8AVn8?l7b6y6Ynm^%C?H8lx~Gy|fl%yfI(7refNl6TiNT)yv-4kzN75(9!*k&@3{4fx3Qf@rF_$zXC9W*Ls5Jv>48hUY+k1&bYcCYgVn6j zGXjQj6zPZ6k$p(GkooeGI6i|8KZ>TI`^us=1&FXsGQKWTy6VJX?j;7ydC@D5Z$JxZ^-YmH5fQj&jeJE38 z9!(@{8J(Keim=MhldT9I1}>dwKrr{T65miNr#4UmnDcVND?(~RWPa9ixfj0}Ut1J; z6Cquf6Pu{X?0lq2)rRr>4D6Ei8{khxg(IUAN z392z@FZ05;-NZ>i?R>vI5O=!Ao*LQ12~O@gpW?|PO+x;;aN)^xt{TWX^iG4$pTpOR!U2a(Pufd&ww%D+rOB>1{+Ttky1Tnjoy>#nDuUKp zBk$psuE{(LJMO3PLCUo^CKkXRX{Nyh#(9gX5N+ZZCPbPSE0=+&kH!C!Xh{v>d}-cd zwlTRP2edT}=$M!gJgI-o+5;Z|l-b5CJO85dn9lS8{wXgm55vs*!Gc?$3Wkr~PtEA3 zENh~J9w+tz9_8fHwh{q}My+Jr3qU9$!$mjf`#V(jkn)4Zy>~iu!7Q9Oc_Nbsg;4K6 zoY7O}rid$1^(L?p+%e*r5XxQi-~IJmS`Za$DXvQo5e zW{yXrNMpu7r=p~&={wYwJ1_dF_#?jl>Z+7R(n^}VVI(EIjs2Q^BxV5M8Ilz-l^JkZ zW(3vfFb9ih<3YkaLiiNvH7vZnc{+t&kQt?@r}lDexY>O-A~X_JL`KEX?%Tx~PLGb} zs@8+fuRP-OsVcGw`}TT$GM|dKKhc0l78R(tf}riL{dT|gBf z(O!-Ai(S+bM1)mnyVmEo%HyA2#JrZ;Ne?jU7fbK~w7q7+2BG~|z3eYjD#xHi2(zu0 z>iF^{g7gUrj#!M6CUywLwud=v!ZFT7BLJm@GtPCz_?EPBicSI$xD3RLrMkd*sA`=D z{Gba{L_LtPX=|YfR};=u{%V_TXtt&ilB}UFpr^w!m0-N%Kj-X875fk)% zpCj#V6Pd;g$^muPz=)p6s_L4pX8ID7|H~3a`t_I0E+lh!bLv71m#TDu8tavvL5kWG zht#=C{hbEf!O_ow0HoTH`aXmrfKbYfRvu^QdWRRc!-Mz=d*r&Vz81M2_&E^zUi9aVkdk)DRGFro1y+nA zW>iApr?wo5GsJihY*JvBYuTn*b=Zs2FNIXy59_KY$Du=Gdkxl~P=Ls>@MN$Ts?TG{ z;jSZd0c2vE%gc$Z?MP>nnB#G5(A5qWu5bh%ge)e_ipVxvX#8XV^SeU0Y!?bwYC8*O z(CMa?i|U9TSrW+ML^>)I!w^+@)Ns`99l-9or^-~Q80J_ECqHW#dTm0(LxkrNTlQ6t@DoSSefkz|gmE(ek8%0Sjm%X3R%w!4Cu;j?nxe z6{$sTMj+KwK0)f0Nqj}irK`(d;(2|!OyQ% z1o!4&p_cggVq;bWx!6OK5q5q+`xlo7IT!vkJNTG#Ye5y=&CgJrbULvN5y_%A3eq~Q zAk%20GDvG15l`Vl7it0EZ3;5&x_TMz_)0iRRPq2El()9z2?xER_k zE;%RgUDTLy8Tw$^j-fIj3t8yaaESdtF2>;e21WBySFH=}7q#L$HHN(zzXw}2Z`rP8 zL)@&&-k;edhEepjwvL{5bcO@4PjB-6DGzHKhd;;?N3_%CS2#2%-;%&u?8Ew%^4`nm zC#AC2{qQuVSObDS>snG=*>ZXXSH@OU`?#g223!Qtr!)q@yqM|X7bEJdi8`F&r|lMR zD_SC?UwFu+gmU!OYGp-U$BWAhT7czm-3h82Z$3_KZ_f~EZ1{^G5@d)W)VK}SXT_aV zhRuMPb+pNn_F)V4F>A4X0cYi{_AU@~x%cg%i5RXEtgfYvWt<>khvkUaVKW0&cirCM zV%htYErtm=)=)=gipfs+cgPfrrO)K$a7>v$VFFyr@H$m# z%HJII26$U z$Zn>iRjCO+yHn)88*9af-%;dz6QAL9NQYber6#41VX7@AY4F*|(HPT$^2|HnIYPdv zyq;)Z!fs_OUdobDC_GQdO|gAAx+)J6D=A!z2;N$8JW{@p6T>O=cv|sr`0T?}^7 z5~#I#!0MHXw1Oa%RgoiB2FoNLo?K;0?vtPM+>^-@EW)Zu>W_*__eBKp2@i%#@x*L- zQGg@|uLwirQ7+n6S{O@2%opoa`h3$9ghSY+#Cw&x@HUFIb8D^SGHMfak_BUtoxnJe z3FoJ~?PS6)@~VN%_7>50=0gO;`CrcTpDucEcXhRvHy8tu8pNzX^d zDiK|_sfOU4F9s=R{zMd+6+<%cU7s^E7Q8QIiwMv{<)eDN!^s2y`On*>cOI#r%AiPz zDz!!&q-9cLU9wk-rh4-_+_ysX%F+Um0B4%<)4cGMPP>*JkUw3k=BOyV-qMp=H)Ra0 z2{*rHvT8SGp)~YboR(eTq}BVLh`0pL8|uCo}+d=1O4BbJka`U8#_i zDm{$A+QZEfpkrqJgZTY><73eiHi~sQ8G4Nw4N5ffn}cAhKyB#&JoEb^oDH}AWq}J1 z<{rO0m8}cYQ?C4cbqn5y& zcrSbeX*A_H#=&irO{375k#3EuhWX7#*EqUg)Hr_DXJw58y~DB~UyUfDmY+#Tfs3~5 zJ=9gD<%Gxx55z?VF~XJXTF)X9Yp}pjre=Gn`|9ss^wFG>yX?XJoYe+3^+_1tU=>Ex z>z&GP@>nwx^vA+5YF#1uSGrfl-aX@^+jtMthB?8KfsDZRAu>eaS?kZCX-3z70QEI4 zdJSNjk$CPrs)gA8&kt)+O3%;Ulqld?HU%*|I~`D?EJ#@RsNie>MN({3G?Iug!PC&k z`t{|eZLX>@NWa*s#1IhP1{GK?ZU~0M41>RUAHrJ#Tfy}isRd79#&7_}BujY_f1G5Q zMY$grC!tX^2=ybf|th zeD4L3=S`WnBcOqWc@=d2ZlwNPry-;8cNKCoCxQ0M$H=n}zxLf$IXj#G|- z?3-In9~~Um_p%(Y;d>F^XRvaZ=9NobCTZx(V?L9YU~FjjS_jD%dFS<5Njn|F%zm

;7GnD`+#;L;Z<56=U)IRpaheI<-Ls7jo_Gjk;W&amhb*up%Dz23~U6}T6 zD`)|i$Y>VtF`#>{O{)uEg(J~fH;d+TZ|8{E=|Nhf2{Dw6AZDOmi%B6u3?5BOZyQ1f zJaD>*xR(;i|1y|!IQ+Jup>v8uwe2KhVprVrZ0Wn1&CTcQjz7+n`a)ptT{ldCfp%Xb zJaA5PhR3)CA2$i|1c#yt`Z4zW4DTl=e@x+U_6Q6PmO@-@Yp?{p@J2`pmc!LTktUqF zIb%`x)*45O1RtPR;HVF0rm7)0xJpVrtG4mR_uC-@N5cRBYy-+L+fK{gyPp-$F{+k~ zDoPnHr&OL=6Zjh4<SY9g7>*)clZiMvqW%>=y0|qMs8qr16Z=4 zNzNt_i#g&?0{RLvwhxME-CEIpcALyXy_)<1yCK9q-X+L7d9yMP>V0Ipe_BzmE=U2L zKcjl30Y`mkjjXWCi^5$OcTtX1_c7aU2hd=+PVzr?cu&CcZ->1o`>aL3_U@F6x6Wr^ zV(~93e@$_!cT`)y!-?Cg7XDfRW)9w|WUHuOJATnPu6n#4KLzxZvG1ng%5#L z_Mw#^Sl?ggF;Rf(UGXiRqzS=rf8ufawIt@_edxK8xw3@0J;KXoh2j6PKfvB>+|Zl3^bnV=#GoW82KicWH^aQR1v(~v}QThJ5e?Ls58R4(A=~hpWU|0g?*`?SJn7xJGQS! zEK`Es1x#xyU`d}(gAGMN$qi_QBpyz^V;o&0O#)z6x)m7mpOQ0f_JU$oAQ_jI9#{`o zzJso2m1ml}Xwdj8Yb zIpJSI&zqe{e`WvmKdk=s4;ocn>t^a z_e6W_7o^~qVM~JwCzxSul=1PFTq8B3f#z(=g@N5&OfPBdk^HDd_x3{oQ9ypcj<6oj zScAeT%)3K4?1u19gz-ZT?v7w78`;VD&S>WSz?2{PL2 zx4{S`OL@pT)b93pDeB36tREk zb@4)#V&mHxHvOh8QtI2=`Q4HE3JyEC9p`6GvLr@@Ma17V4Nd<1F$oogA@BuZeIJX% zr_%b_;n7xupQ9%Gr8{fR)x{{3nsmdT-X;t*d(u-vdyU-YcI>%%DvX)$?^1_kZe2sK zaoHi6=|$Nnl($9m`As)K0H#=+^|{_=yW?D>CmuZ#Fe_q#n8S!gJ(YW_m7L`&a>(Hv z=5=8GCK1%IWngeKWZs)2=szm>@G#lx$ard5Wt7mcw`b_pit#)r-Kj9Q$kxt9B$mb* z@p^y1zt+Tn-?RYmlYKF0%i4Udea#Uf=GO5bEwovR>4lYkV4?RuJA^rlN{hd<_GxWr z^ru$HPgdU^fe0$wnTT)SF&BpEq(YvOa=xX*#{ekQLN zIxU#^?-a|{tJ^ZcY z0cX$rxK`dUR|?$b_jkOMKt7_nIlDI*pomWj>snmCU+lf>AM zIc5Bh_B*uOmnb&6y}HlgRt3R;x@iqnVnwJ=dS#z7?O#kLVTGhGQLcHgonm3#PZ{s- z7iZp)V}o-LNS9hdebF$qe=?OPHYHD9^>Pbc(csa(gNMn(36ZF|S5a=<4_1!I2c@uk zkSb{Pwr!Wx8oTIwow%;AH<3D)>@Ic|Gnj(s&y@TnRzgI!D=8bT)t?)G!G(IFVGTbJ zb{$pU7nuzaO5w&fN7JB(ELDydwVZSsuFe{zJDY<+X5*cIl$cJb(=IlLDz|x`A>UuAcsW7$YRr7nc(k*}ZM68`44l0J!uL)-A`Yg0%6A^VI{j-o^sna7%2(P%@ z1eEUoNv+h03L>vvlV>U}k_mW?xZ-^W0RR0SGb;|FMOBf@$HI zp-4WngLKWaMZdu@2Wkots7D$jF<2XKGjstJW2cwzmvel0T8t}JYm>)Mmv>|_&E6~> z%imE;5#rqLR)a#LY9%9lb!R7>l~OvtRhVE=^=q6 z=*M7UnI2d8o;U`O6ZXP;V_GnwwMRJ*D{fw~$Q_25tWuHuNkv^xf>=qxHW?`_9!LZi zkx&V*-4V-wcMzKFn`)p{!iA*HO3z(wyxJgtXbC?Wdhx7zcy&CcC@PkxrI6M`4_1#d z0mz}AUom@^y=n;adE8{LHoK+YoTJ`|MTKH4!9un&=sX{8UB`(48#h*a@%OP}xWHg} z0sEjcp*_k(%3Fpr7aRyOVvH0K65wux)FHHwPp~FOu~?7;n|6zN*uwRW_H{3++@*lj?vXEF&!yTkg)~Ma4vzEkx2$*Q^Ztk}9gG5jO)=_lb*TIuU z`9*MKU6oo{@Q602q9@Alf#L@4zBjn8Tp1Tvi2bV219;ATfLQ8UUbNkncK^t=!67PP zBmE2Q!A~TjUXQCA3pGMLTIY;}K#SgEa509zwQ^+drX76V zZF*?_2gn69IvGS;1YH3g9NEvR2YwW}ZC1hrEW@r7WiFEhszNY9X77Oq+xQ15vr!`- z3`UwyJ540=N@#{GNDqGCs1k0w_qUZMx)NK|O~gfZ)WiJ7Ok(_=&R=2m!?7;2;9DeqILKLl`fdv(g2jCx&H zT4Tm5BW#Ny3oPu`$T9cwh-2Gs{sHekO;gmyxAxxDqu_c@r=4FLSprf)9?i*%((7rb z9aYA~uQ%%L0U;&b3XKXv*DBu@v>d9&ra4J{4`$Ck^Q2nmkPr0jleoP3=71S${09}S zTwmKopu`s}XcuC*uM*u_8{AXpT&Jz;<3}jlpDinnVYO zApWVX(#==$mt$&N_Py9)-1IJRpAuVw%)^n11at$uE^yu05%jPyPBD8i?lAMJSDVMO zyf%rGUY1e^SqKY5Oyf$!1$U%=UqdHneZY@Yw~M(W_$u^EIvcPRJg55~ydz2w z^GSd>WHwdREo;~Xn46QRS{&>=C;j~iRJxh+JCj;TlP)VJJ+4`jF-rxJWceJX@XjBF zJxbqQ@aaBs!3^tj>pQLrK&;jrD=-c7Bnvhxs*g_q5q5gL`~u@l|H^Zf9p&?^%>l3S_|OsM z8HOjN8UxuKwc)a$vZqnOj4d0MM!dwhh$$ta)kzLpvplaz?GraiQpCc5?$Y(f-AH#t zL|Et7h4lj?$_p3Sogh7HOJ{TIGQ!u@pH|m}T^%xpS-8F4s&}P~ z;l3M_3DJ|d>XM(^FujJW)76th@&M;2MkYG>20jM4+xu%a!+-TV@vo#*ZYh<-pNvBtY-&&5R~vQi3pNUw-bhn zgnNcmxD{I|XPtAgPUhD~_-;E7EoWW+m2oZDxYrp_1`g~&jMuMnFa(XGBY#}R*KD*@ z_Mh&3OE>onn_8Gdn#!(W>4l!{?`&&jsT4kRg+iikG-sj5`%3Ym*+Wl0e2SJmf9NA! zTaDSDrNCZ~Z2qJBJ3n>+9!`4WrEr4J2Jh{VIiq35Uf>%caf^E3K>xw2X~wc8ZQNZR zCFKIZ1~WWLblPa~Y-8e0p%^Tgw`o!8EE~Eu7{C~474HR87*~iZ6-{;IG*hAboII;Q z!JkrbxPrRV^u4kfO>kG%@xd*{)(YMMCx+uHM6SKfu4Kn!;5`Wm`cZW?P)p0@!o>-a zj~8x@O@wRP)@=_10*=AF$gkwIopk9;IA`LslngRiOF5JVn^c#`k_VG|j78=SHn|H> zw=fC?sj)J9oe?qa-j4MFs^&!$fKc|FlVO-Pe>g7a2uB+*?-3FIRIrC$T0<0TbSXY& zFk2ZZ>qOUJE|D0jUI5`^!O$M0$ElnGxzr^ul1j0}eVR!vMI{ zGCZaBVltH>-#|f@>_hL0LK5uTB;^NcNVv&wbY=V`H??kYMw`ik|U+f?asyK-gwgNpR^d>=!)KZ&h}TX@o(x zU!!f34xHmfjx9FwJDhqKAU}{{O+KI03-0jui{fw;;8;7Xbm~@qkxUOcsI7LFYvVo; zaQjo>@xx)4-RB75;%dQ{Di&I7OnN*yp}#4Aw?pG@Z#;au!RpG&*UhVhVfU0Jq)zTo=0xBm6d zPt3f!I=sBEuPH=Sjq?R*$yu2SXXD<52@IyZ9sr_yO&qXi?=CpWL+KLuRB-ea|rg{vcSS7vqnoU>->N%T9KT0XIn4N>kPJEp|f@!Jkhvr$s&2}Spw?<+-k#bOzU`iZ?E;Ue3f=r zcCOnK&YxYO$nYnj<^gA`V`{Ku-79gN~|eYmYe0a!{7p>E1LednudCV(nR{&YU_>n)~4?N9ODPuRjC% zlN%Pk*9>egF)K9=ul{1VGU`|NuBeXlzMZTeG#TtqaXQFM7hb@<;R5IWSs$0b3pe4O zaj~4CWPedW{7jY!Qf?9#40L}L_AV%p%sqL~a1Z%Y>`dH75 zv*~xRo+3F*$o#kZu9xfla&zAYv1@$x`0S~;$b@UHbIIX*3=9nMptArz>1-m?SW0V*^ zIy~F*=4sn5qv*XMX*qNKUuP%T?>%t+$gI@;5_aZGKL2M}J2^G?`$gl2pc5QCUHx3v dIVCjF|1679FbYP&C>RCA0|1y&z})~+0046f`8NOn literal 0 HcmV?d00001 diff --git a/spec/lib/mastodon/cli/emoji_spec.rb b/spec/lib/mastodon/cli/emoji_spec.rb index 5d109eb52..530da91e7 100644 --- a/spec/lib/mastodon/cli/emoji_spec.rb +++ b/spec/lib/mastodon/cli/emoji_spec.rb @@ -4,5 +4,59 @@ require 'rails_helper' require 'mastodon/cli/emoji' describe Mastodon::CLI::Emoji do + subject { cli.invoke(action, args, options) } + + let(:cli) { described_class.new } + let(:args) { [] } + let(:options) { {} } + it_behaves_like 'CLI Command' + + describe '#purge' do + let(:action) { :purge } + + context 'with existing custom emoji' do + before { Fabricate(:custom_emoji) } + + it 'reports a successful purge' do + expect { subject } + .to output_results('OK') + end + end + end + + describe '#import' do + context 'with existing custom emoji' do + let(:import_path) { Rails.root.join('spec', 'fixtures', 'files', 'elite-assets.tar.gz') } + let(:action) { :import } + let(:args) { [import_path] } + + it 'reports about imported emoji' do + expect { subject } + .to output_results('Imported 1') + .and change(CustomEmoji, :count).by(1) + end + end + end + + describe '#export' do + context 'with existing custom emoji' do + before { Fabricate(:custom_emoji) } + after { File.delete(export_path) } + + let(:export_path) { Rails.root.join('tmp', 'export.tar.gz') } + let(:args) { [Rails.root.join('tmp')] } + let(:action) { :export } + + it 'reports about exported emoji' do + expect { subject } + .to output_results('Exported 1') + .and change { File.exist?(export_path) }.from(false).to(true) + end + end + end + + def output_results(string) + output(a_string_including(string)).to_stdout + end end From 954169966b23214a044fa8deda16be4b903526d5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Dec 2023 08:52:55 +0000 Subject: [PATCH 78/95] New Crowdin Translations (automated) (#28245) Co-authored-by: GitHub Actions --- app/javascript/mastodon/locales/lt.json | 26 +++---- config/locales/simple_form.lt.yml | 91 ++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 13 deletions(-) diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json index e588b8538..58c80e411 100644 --- a/app/javascript/mastodon/locales/lt.json +++ b/app/javascript/mastodon/locales/lt.json @@ -40,7 +40,7 @@ "account.follows.empty": "Šis (-i) naudotojas (-a) dar nieko neseka.", "account.follows_you": "Seka tave", "account.go_to_profile": "Eiti į profilį", - "account.hide_reblogs": "Slėpti \"boosts\" iš @{name}", + "account.hide_reblogs": "Slėpti pakėlimus iš @{name}", "account.in_memoriam": "Atminimui.", "account.joined_short": "Prisijungė", "account.languages": "Keisti prenumeruojamas kalbas", @@ -49,19 +49,19 @@ "account.media": "Medija", "account.mention": "Paminėti @{name}", "account.moved_to": "{name} nurodė, kad dabar jų nauja paskyra yra:", - "account.mute": "Užtildyti @{name}", + "account.mute": "Nutildyti @{name}", "account.mute_notifications_short": "Nutildyti pranešimus", "account.mute_short": "Nutildyti", - "account.muted": "Užtildytas", + "account.muted": "Nutildytas", "account.no_bio": "Nėra pateikto aprašymo.", - "account.open_original_page": "Atidaryti originalinį tinklalapį", + "account.open_original_page": "Atidaryti originalinį puslapį", "account.posts": "Įrašai", "account.posts_with_replies": "Įrašai ir atsakymai", "account.report": "Pranešti @{name}", - "account.requested": "Laukiama patvirtinimo. Spausk, kad atšaukti sekimo užklausą.", + "account.requested": "Laukiama patvirtinimo. Spausk, kad atšaukti sekimo užklausą", "account.requested_follow": "{name} paprašė tave sekti", "account.share": "Bendrinti @{name} profilį", - "account.show_reblogs": "Rodyti \"boosts\" iš @{name}", + "account.show_reblogs": "Rodyti pakėlimus iš @{name}", "account.statuses_counter": "{count, plural, one {{counter} įrašas} few {{counter} įrašai} many {{counter} įrašo} other {{counter} įrašų}}", "account.unblock": "Atblokuoti @{name}", "account.unblock_domain": "Atblokuoti domeną {domain}", @@ -73,7 +73,7 @@ "account.unmute_short": "Atitildyti", "account_note.placeholder": "Spausk norėdamas (-a) pridėti pastabą", "admin.dashboard.daily_retention": "Vartotojų išbuvimo rodiklis pagal dieną po registracijos", - "admin.dashboard.monthly_retention": "Vartotojų išbuvimo rodiklis pagal mėnesį po registracijos", + "admin.dashboard.monthly_retention": "Naudotojų išlaikymo rodiklis pagal mėnesį po registracijos", "admin.dashboard.retention.average": "Vidurkis", "admin.dashboard.retention.cohort": "Registravimo mėnuo", "admin.dashboard.retention.cohort_size": "Nauji naudotojai", @@ -117,9 +117,9 @@ "column.favourites": "Mėgstamiausi", "column.firehose": "Tiesioginiai padavimai", "column.follow_requests": "Sekti prašymus", - "column.home": "Pradžia", + "column.home": "Pagrindinis", "column.lists": "Sąrašai", - "column.mutes": "Užtildyti naudotojai", + "column.mutes": "Nutildyti naudotojai", "column.notifications": "Pranešimai", "column.pins": "Prisegti įrašai", "column.public": "Federacinė laiko skalė", @@ -141,13 +141,13 @@ "compose.saved.body": "Įrašas išsaugotas.", "compose_form.direct_message_warning_learn_more": "Sužinoti daugiau", "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.", - "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.", - "compose_form.lock_disclaimer": "Jūsų paskyra nėra {locked}. Kiekvienas gali jus sekti ir peržiūrėti tik sekėjams skirtus įrašus.", + "compose_form.hashtag_warning": "Šis įrašas nebus įtraukta į jokį saitažodį, nes ji nėra vieša. Tik viešų įrašų galima ieškoti pagal saitažodį.", + "compose_form.lock_disclaimer": "Tavo paskyra nėra {locked}. Bet kas gali sekti tave ir peržiūrėti tik sekėjams skirtus įrašus.", "compose_form.lock_disclaimer.lock": "užrakinta", "compose_form.placeholder": "Kas tavo mintyse?", "compose_form.poll.add_option": "Pridėti pasirinkimą", "compose_form.poll.duration": "Apklausos trukmė", - "compose_form.poll.option_placeholder": "Pasirinkimas {number}", + "compose_form.poll.option_placeholder": "{number} pasirinkimas", "compose_form.poll.remove_option": "Pašalinti šį pasirinkimą", "compose_form.poll.switch_to_multiple": "Keisti apklausą, kad būtų galima pasirinkti kelis pasirinkimus", "compose_form.poll.switch_to_single": "Pakeisti apklausą, kad būtų galima pasirinkti vieną variantą", @@ -528,6 +528,8 @@ "search_results.hashtags": "Saitažodžiai", "search_results.nothing_found": "Nepavyko rasti nieko pagal šiuos paieškos terminus.", "search_results.statuses": "Toots", + "server_banner.about_active_users": "Žmonės, kurie naudojosi šiuo serveriu per pastarąsias 30 dienų (mėnesio aktyvūs naudotojai)", + "server_banner.active_users": "aktyvūs naudotojai", "sign_in_banner.sign_in": "Prisijungimas", "sign_in_banner.text": "Prisijunk, kad galėtum sekti profilius arba saitažodžius, mėgsti, bendrinti ir atsakyti į įrašus. Taip pat gali bendrauti iš savo paskyros kitame serveryje.", "status.admin_status": "Open this status in the moderation interface", diff --git a/config/locales/simple_form.lt.yml b/config/locales/simple_form.lt.yml index d53b7105e..6eb90340d 100644 --- a/config/locales/simple_form.lt.yml +++ b/config/locales/simple_form.lt.yml @@ -63,6 +63,8 @@ lt: setting_use_pending_items: Slėpti laiko skalės naujienas po paspaudimo, vietoj automatinio kanalo slinkimo username: Gali naudoti raides, skaičius ir pabraukimus whole_word: Kai raktažodis ar frazė yra tik raidinis ir skaitmeninis, jis bus taikomas tik tada, jei atitiks visą žodį + email_domain_block: + with_dns_records: Bus bandoma išspręsti nurodyto domeno DNS įrašus, o rezultatai taip pat bus blokuojami featured_tag: name: 'Štai keletas pastaruoju metu dažniausiai saitažodžių, kurių tu naudojai:' filters: @@ -77,15 +79,98 @@ lt: site_contact_email: Kaip žmonės gali su tavimi susisiekti teisiniais ar pagalbos užklausimais. site_contact_username: Kaip žmonės gali tave pasiekti Mastodon. site_extended_description: Bet kokia papildoma informacija, kuri gali būti naudinga lankytojams ir naudotojams. Gali būti struktūrizuota naudojant Markdown sintaksę. + thumbnail: Maždaug 2:1 dydžio vaizdas, rodomas šalia tavo serverio informacijos. + timeline_preview: Atsijungę lankytojai galės naršyti naujausius viešus įrašus, esančius serveryje. trends: Trendai rodo, kurios įrašai, saitažodžiai ir naujienų istorijos tavo serveryje sulaukia didžiausio susidomėjimo. sessions: otp: 'Įvesk telefono programėlėje sugeneruotą dviejų tapatybės kodą arba naudok vieną iš atkūrimo kodų:' webauthn: Jei tai USB raktas, būtinai jį įkišk ir, jei reikia, paspausk. settings: indexable: Tavo profilio puslapis gali būti rodomas paieškos rezultatuose Google, Bing ir kituose. + user: + chosen_languages: Kai pažymėta, viešose laiko skalėse bus rodomi tik įrašai pasirinktomis kalbomis + role: Vaidmuo valdo, kokius leidimus naudotojas (-a) turi labels: + account: + indexable: Įtraukti viešus įrašus į paieškos rezultatus + show_collections: Rodyti sekimus ir sekėjus profilyje + unlocked: Automatiškai priimti naujus sekėjus + account_warning_preset: + title: Pavadinimas + admin_account_action: + include_statuses: Įtraukti praneštus įrašus į el. laišką + defaults: + avatar: Profilio nuotrauka + bot: Tai automatinė paskyra + chosen_languages: Filtruoti kalbas + display_name: Rodomas vardas + email: El. pašto adresas + expires_in: Nustoja galioti po + fields: Papildomi laukai + irreversible: Mesti vietoj slėpti + locale: Sąsajos kalba + max_uses: Maksimalus naudojimo skaičius + new_password: Naujas slaptažodis + note: Biografija + password: Slaptažodis + phrase: Raktažodis arba frazė + setting_auto_play_gif: Automatiškai leisti animuotų GIF + setting_boost_modal: Rodyti patvirtinimo dialogą prieš pakėliant įrašą + setting_default_language: Skelbimo kalba + setting_default_privacy: Skelbimo privatumas + setting_default_sensitive: Visada žymėti mediją kaip jautrią + setting_delete_modal: Rodyti patvirtinimo dialogą prieš ištrinant įrašą + setting_display_media: Medijos rodymas + setting_display_media_hide_all: Slėpti viską + setting_display_media_show_all: Rodyti viską + setting_expand_spoilers: Visada išplėsti įrašus, pažymėtus turinio įspėjimais + setting_hide_network: Slėpti savo socialinę diagramą + setting_system_font_ui: Naudoti numatytąjį sistemos šriftą + setting_theme: Svetainės tema + setting_use_pending_items: Lėtas režimas + title: Pavadinimas + type: Importo tipas + username: Naudotojo vardas + username_or_email: Naudotojo vardas arba el. paštas + whole_word: Visas žodis + email_domain_block: + with_dns_records: Įtraukti MX įrašus ir domeno IP adresus featured_tag: name: Saitažodis + filters: + actions: + hide: Slėpti visiškai + warn: Slėpti su įspėjimu + form_admin_settings: + activity_api_enabled: Skelbti suvestinį statistiką apie naudotojų veiklą per API + bootstrap_timeline_accounts: Visada rekomenduoti šias paskyras naujiems naudotojams + content_cache_retention_period: Turinio talpyklos išlaikymo laikotarpis + custom_css: Pasirinktinis CSS + mascot: Pasirinktinis talismanas (pasenęs) + registrations_mode: Kas gali užsiregistruoti + show_domain_blocks_rationale: Rodyti, kodėl domenai buvo užblokuoti + site_extended_description: Išplėstas aprašymas + site_short_description: Serverio aprašymas + site_terms: Privatumo politika + site_title: Serverio pavadinimas + theme: Numatytoji tema + thumbnail: Serverio miniatūra + invite_request: + text: Kodėl nori prisijungti? + notification_emails: + favourite: Kažkas pamėgo tavo įrašą + follow: Kažkas seka tave + follow_request: Kažkas paprašė sekti tave + mention: Kažkas paminėjo tave + pending_account: Reikia peržiūros naujam paskyrui + reblog: Kažkas pakėlė tavo įrašą + software_updates: + label: Yra nauja Mastodon versija + patch: Pranešti apie klaidų ištaisymo atnaujinimus + rule: + text: Taisyklė + settings: + show_application: Rodyti, iš kurios programėles išsiuntei įrašą tag: listable: Leisti šį saitažodį rodyti paieškose ir pasiūlymuose name: Saitažodis @@ -93,11 +178,15 @@ lt: usable: Leisti įrašams naudoti šį saitažodį user: role: Vaidmuo + time_zone: Laiko juosta user_role: + color: Ženklelio spalva + highlighted: Rodyti vaidmenį kaip ženklelį naudotojo profiliuose + name: Pavadinimas permissions_as_keys: Leidimai position: Prioritetas webhook: - events: Įgalinti įvykiai + events: Įjungti įvykiai template: Naudingosios apkrovos šablonas url: Galutinio taško URL 'no': Ne From 42afd303246abe1cf61752ab53cca466ea3cefdf Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 6 Dec 2023 05:19:24 -0500 Subject: [PATCH 79/95] Replace Sprockets with Propshaft (#28239) --- .github/renovate.json5 | 1 - Gemfile | 3 +-- Gemfile.lock | 15 ++++++--------- config/application.rb | 1 - config/environments/development.rb | 11 ----------- config/initializers/assets.rb | 16 ---------------- 6 files changed, 7 insertions(+), 40 deletions(-) delete mode 100644 config/initializers/assets.rb diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 895dbfbad..a7998ddfd 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -50,7 +50,6 @@ matchManagers: ['bundler'], matchPackageNames: [ 'rack', // Needs to be synced with Rails version - 'sprockets', // Requires manual upgrade https://github.com/rails/sprockets/blob/master/UPGRADING.md#guide-to-upgrading-from-sprockets-3x-to-4x 'strong_migrations', // Requires manual upgrade 'sidekiq', // Requires manual upgrade 'sidekiq-unique-jobs', // Requires manual upgrades and sync with Sidekiq version diff --git a/Gemfile b/Gemfile index e3fb39e16..cfcbcc0d3 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,7 @@ ruby '>= 3.0.0' gem 'puma', '~> 6.3' gem 'rails', '~> 7.1.1' -gem 'sprockets', '~> 3.7.2' +gem 'propshaft' gem 'thor', '~> 1.2' gem 'rack', '~> 2.2.7' @@ -89,7 +89,6 @@ gem 'sidekiq-unique-jobs', '~> 7.1' gem 'sidekiq-bulk', '~> 0.2.0' gem 'simple-navigation', '~> 4.4' gem 'simple_form', '~> 5.2' -gem 'sprockets-rails', '~> 3.4', require: 'sprockets/railtie' gem 'stoplight', '~> 3.0.1' gem 'strong_migrations', '1.6.4' gem 'tty-prompt', '~> 0.23', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 4a409a0ad..738562116 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -534,6 +534,11 @@ GEM net-smtp premailer (~> 1.7, >= 1.7.9) private_address_check (0.5.0) + propshaft (0.8.0) + actionpack (>= 7.0.0) + activesupport (>= 7.0.0) + rack + railties (>= 7.0.0) psych (5.1.1.1) stringio public_suffix (5.0.4) @@ -736,13 +741,6 @@ GEM simplecov-lcov (0.8.0) simplecov_json_formatter (0.1.4) smart_properties (1.17.0) - sprockets (3.7.2) - concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) - sprockets (>= 3.0.0) stackprof (0.2.25) statsd-ruby (1.5.0) stoplight (3.0.2) @@ -911,6 +909,7 @@ DEPENDENCIES posix-spawn premailer-rails private_address_check (~> 0.5) + propshaft public_suffix (~> 5.0) puma (~> 6.3) pundit (~> 2.3) @@ -949,8 +948,6 @@ DEPENDENCIES simple_form (~> 5.2) simplecov (~> 0.22) simplecov-lcov (~> 0.8) - sprockets (~> 3.7.2) - sprockets-rails (~> 3.4) stackprof stoplight (~> 3.0.1) strong_migrations (= 1.6.4) diff --git a/config/application.rb b/config/application.rb index 99ee4ffd7..b6426516e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -14,7 +14,6 @@ require 'active_job/railtie' # require 'action_mailbox/engine' # require 'action_text/engine' # require 'rails/test_unit/railtie' -require 'sprockets/railtie' # Used to be implicitly required in action_mailbox/engine require 'mail' diff --git a/config/environments/development.rb b/config/environments/development.rb index e601fc014..3c13ada38 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -71,17 +71,6 @@ Rails.application.configure do # Highlight code that enqueued background job in logs. config.active_job.verbose_enqueue_logs = true - # Debug mode disables concatenation and preprocessing of assets. - config.assets.debug = true - - # Suppress logger output for asset requests. - config.assets.quiet = true - - # Adds additional error checking when serving assets at runtime. - # Checks for improperly declared sprockets dependencies. - # Raises helpful error messages. - config.assets.raise_runtime_errors = true - # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb deleted file mode 100644 index e1fd5f8ce..000000000 --- a/config/initializers/assets.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -# Be sure to restart your server when you modify this file. - -# Version of your assets, change this if you want to expire all your assets. -Rails.application.config.assets.version = '1.0' - -# Add additional assets to the asset load path. -# Rails.application.config.assets.paths << Emoji.images_path - -# Precompile additional assets. -# application.js, application.css, and all non-JS/CSS in the app/assets -# folder are already added. -# Rails.application.config.assets.precompile += %w( admin.js admin.css ) - -Rails.application.config.assets.initialize_on_precompile = true From ee83d5c7600b2cdc96db78c5ec40d8747853ba30 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Wed, 6 Dec 2023 08:42:12 -0500 Subject: [PATCH 80/95] Enable the eslint `react/no-unknown-property` rule (#28217) --- .eslintrc.js | 1 - .../mastodon/features/interaction_modal/index.jsx | 6 +++--- .../mastodon/features/ui/components/navigation_panel.jsx | 2 +- app/javascript/mastodon/features/video/index.jsx | 1 - package.json | 2 +- yarn.lock | 4 ++-- 6 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 176879034..e2d16a54a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -120,7 +120,6 @@ module.exports = defineConfig({ 'react/jsx-uses-react': 'off', // not needed with new JSX transform 'react/jsx-wrap-multilines': 'error', 'react/no-deprecated': 'off', - 'react/no-unknown-property': 'off', 'react/react-in-jsx-scope': 'off', // not needed with new JSX transform 'react/self-closing-comp': 'error', diff --git a/app/javascript/mastodon/features/interaction_modal/index.jsx b/app/javascript/mastodon/features/interaction_modal/index.jsx index 4f145f9ed..216c63a7e 100644 --- a/app/javascript/mastodon/features/interaction_modal/index.jsx +++ b/app/javascript/mastodon/features/interaction_modal/index.jsx @@ -298,9 +298,9 @@ class LoginForm extends React.PureComponent { onFocus={this.handleFocus} onBlur={this.handleBlur} onKeyDown={this.handleKeyDown} - autocomplete='off' - autocapitalize='off' - spellcheck='false' + autoComplete='off' + autoCapitalize='off' + spellCheck='false' /> diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx index 4b0e03a0f..d1b2a0910 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx @@ -82,7 +82,7 @@ class NavigationPanel extends Component { {banner && -