diff options
99 files changed, 2346 insertions, 803 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8b0381d11..8daa9f434 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,6 +8,13 @@ variables: &global_variables    DB_PORT: 5432    MIX_ENV: test +workflow: +  rules: +    - if: $CI_PIPELINE_SOURCE == "merge_request_event" +    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS +      when: never +    - if: $CI_COMMIT_BRANCH +  cache: &global_cache_policy    key:      files: @@ -17,12 +24,14 @@ cache: &global_cache_policy      - _build  stages: +  - check-changelog    - build    - test    - benchmark    - deploy    - release    - docker +  - docker-combine  before_script:    - echo $MIX_ENV @@ -32,24 +41,43 @@ before_script:  after_script:    - rm -rf _build/*/lib/pleroma +check-changelog: +  stage: check-changelog +  image: alpine +  rules: +    - if: $CI_MERGE_REQUEST_SOURCE_PROJECT_PATH == 'pleroma/pleroma' && $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == 'weblate-extract' +      when: never +    - if: $CI_MERGE_REQUEST_SOURCE_PROJECT_PATH == 'pleroma/pleroma' && $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == 'weblate' +      when: never +    - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" +  before_script: '' +  after_script: '' +  cache: {} +  script: +    - apk add git +    - sh ./tools/check-changelog + +.build_changes_policy: +  rules: +    - changes: +        - ".gitlab-ci.yml" +        - "**/*.ex" +        - "**/*.exs" +        - "mix.lock" +  build: +  extends: .build_changes_policy    stage: build -  only: -    changes: &build_changes_policy -      - ".gitlab-ci.yml" -      - "**/*.ex" -      - "**/*.exs" -      - "mix.lock"    script:    - mix compile --force  spec-build:    stage: test -  only: -    changes: -      - ".gitlab-ci.yml" -      - "lib/pleroma/web/api_spec/**/*.ex" -      - "lib/pleroma/web/api_spec.ex" +  rules: +    - changes: +        - ".gitlab-ci.yml" +        - "lib/pleroma/web/api_spec/**/*.ex" +        - "lib/pleroma/web/api_spec.ex"    artifacts:      paths:      - spec.json @@ -71,9 +99,8 @@ benchmark:      - mix pleroma.load_testing  unit-testing: +  extends: .build_changes_policy    stage: test -  only: -    changes: *build_changes_policy    cache: &testing_cache_policy      <<: *global_cache_policy      policy: pull @@ -94,11 +121,10 @@ unit-testing:          path: coverage.xml  unit-testing-erratic: +  extends: .build_changes_policy    stage: test    retry: 2    allow_failure: true -  only: -    changes: *build_changes_policy    cache: &testing_cache_policy      <<: *global_cache_policy      policy: pull @@ -129,9 +155,8 @@ unit-testing-erratic:  #     - mix test --trace --only federated  unit-testing-rum: +  extends: .build_changes_policy    stage: test -  only: -    changes: *build_changes_policy    cache: *testing_cache_policy    services:    - name: minibikini/postgres-with-rum:12 @@ -147,10 +172,9 @@ unit-testing-rum:      - mix test --preload-modules  lint: +  extends: .build_changes_policy    image: ¤t_elixir elixir:1.12-alpine    stage: test -  only: -    changes: *build_changes_policy    cache: *testing_cache_policy    before_script: ¤t_bfr_script      - apk update @@ -162,18 +186,16 @@ lint:      - mix format --check-formatted  analysis: +  extends: .build_changes_policy    stage: test -  only: -    changes: *build_changes_policy    cache: *testing_cache_policy    script:      - mix credo --strict --only=warnings,todo,fixme,consistency,readability  cycles: +  extends: .build_changes_policy    image: *current_elixir    stage: test -  only: -    changes: *build_changes_policy    cache: {}    before_script: *current_bfr_script    script: @@ -354,104 +376,167 @@ arm64-musl:    before_script: *before-release-musl    script: *release -docker: +.kaniko:    stage: docker -  image: docker:latest +  image: +    name: gcr.io/kaniko-project/executor:debug +    entrypoint: [""]    cache: {}    dependencies: [] -  variables: &docker-variables -    DOCKER_DRIVER: overlay2 -    DOCKER_HOST: unix:///var/run/docker.sock -    IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA -    IMAGE_TAG_SLUG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG -    IMAGE_TAG_LATEST: $CI_REGISTRY_IMAGE:latest -    IMAGE_TAG_LATEST_STABLE: $CI_REGISTRY_IMAGE:latest-stable -    DOCKER_BUILDX_URL: https://github.com/docker/buildx/releases/download/v0.6.3/buildx-v0.6.3.linux-amd64 -    DOCKER_BUILDX_HASH: 980e6b9655f971991fbbb5fd6cd19f1672386195 -  before_script: &before-docker -    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY -    - docker pull $IMAGE_TAG_SLUG || true +  before_script: &before-kaniko      - export CI_JOB_TIMESTAMP=$(date --utc -Iseconds)      - export CI_VCS_REF=$CI_COMMIT_SHORT_SHA -  allow_failure: true -  script: -    - mkdir -p /root/.docker/cli-plugins -    - wget "${DOCKER_BUILDX_URL}" -O ~/.docker/cli-plugins/docker-buildx -    - echo "${DOCKER_BUILDX_HASH}  /root/.docker/cli-plugins/docker-buildx" | sha1sum -c -    - chmod +x ~/.docker/cli-plugins/docker-buildx -    - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes -    - docker buildx create --name mbuilder --driver docker-container --use -    - docker buildx inspect --bootstrap -    - docker buildx build --platform linux/amd64,linux/arm/v7,linux/arm64/v8 --push --cache-from $IMAGE_TAG_SLUG --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP -t $IMAGE_TAG -t $IMAGE_TAG_SLUG -t $IMAGE_TAG_LATEST . -  tags: -    - dind +    - export IMAGE_TAG=$CI_REGISTRY_IMAGE/$BUILD_ARCH_IMG_SUFFIX:$CI_COMMIT_SHORT_SHA +    - export IMAGE_TAG_SLUG=$CI_REGISTRY_IMAGE/$BUILD_ARCH_IMG_SUFFIX:$CI_COMMIT_REF_SLUG +    - export IMAGE_TAG_LATEST=$CI_REGISTRY_IMAGE/$BUILD_ARCH_IMG_SUFFIX:latest +    - export IMAGE_TAG_LATEST_STABLE=$CI_REGISTRY_IMAGE/$BUILD_ARCH_IMG_SUFFIX:latest-stable +    - mkdir -p /kaniko/.docker +    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json + +.kaniko-latest: +  extends: .kaniko    only:      - develop@pleroma/pleroma - -docker-stable: -  stage: docker -  image: docker:latest -  cache: {} -  dependencies: [] -  variables: *docker-variables -  before_script: *before-docker -  allow_failure: true    script: -    - mkdir -p /root/.docker/cli-plugins -    - wget "${DOCKER_BUILDX_URL}" -O ~/.docker/cli-plugins/docker-buildx -    - echo "${DOCKER_BUILDX_HASH}  /root/.docker/cli-plugins/docker-buildx" | sha1sum -c -    - chmod +x ~/.docker/cli-plugins/docker-buildx -    - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes -    - docker buildx create --name mbuilder --driver docker-container --use -    - docker buildx inspect --bootstrap -    - docker buildx build --platform linux/amd64,linux/arm/v7,linux/arm64/v8 --push --cache-from $IMAGE_TAG_SLUG --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP -t $IMAGE_TAG -t $IMAGE_TAG_SLUG -t $IMAGE_TAG_LATEST_STABLE . -  tags: -    - dind +    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --custom-platform=$BUILD_ARCH --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP --build-arg ELIXIR_IMG=$ELIXIR_IMG --destination $IMAGE_TAG --destination $IMAGE_TAG_SLUG --destination $IMAGE_TAG_LATEST + +.kaniko-stable: +  extends: .kaniko    only:      - stable@pleroma/pleroma +  script: +    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --custom-platform=$BUILD_ARCH --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP --build-arg ELIXIR_IMG=$ELIXIR_IMG --destination $IMAGE_TAG --destination $IMAGE_TAG_SLUG --destination $IMAGE_TAG_LATEST_STABLE -docker-release: -  stage: docker -  image: docker:latest -  cache: {} -  dependencies: [] -  variables: *docker-variables -  before_script: *before-docker -  allow_failure: true +.kaniko-release: +  extends: .kaniko +  only: +    - /^release/.*$/@pleroma/pleroma    script: +    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --custom-platform=$BUILD_ARCH --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP --build-arg ELIXIR_IMG=$ELIXIR_IMG --destination $IMAGE_TAG --destination $IMAGE_TAG_SLUG + +.kaniko-adhoc: +  extends: .kaniko +  only: +    - /^build-docker/.*$/@pleroma/pleroma    script: -    - mkdir -p /root/.docker/cli-plugins -    - wget "${DOCKER_BUILDX_URL}" -O ~/.docker/cli-plugins/docker-buildx -    - echo "${DOCKER_BUILDX_HASH}  /root/.docker/cli-plugins/docker-buildx" | sha1sum -c -    - chmod +x ~/.docker/cli-plugins/docker-buildx -    - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes -    - docker buildx create --name mbuilder --driver docker-container --use -    - docker buildx inspect --bootstrap -    - docker buildx build --platform linux/amd64,linux/arm/v7,linux/arm64/v8 --push --cache-from $IMAGE_TAG_SLUG --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP -t $IMAGE_TAG -t $IMAGE_TAG_SLUG . +    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --custom-platform=$BUILD_ARCH --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP --build-arg ELIXIR_IMG=$ELIXIR_IMG --destination $IMAGE_TAG --destination $IMAGE_TAG_SLUG + +.kaniko:linux/amd64: +  variables: +    BUILD_ARCH: linux/amd64 +    BUILD_ARCH_IMG_SUFFIX: linux-amd64 +    ELIXIR_IMG: hexpm/elixir    tags: -    - dind -  only: -    - /^release/.*$/@pleroma/pleroma +    - amd64 -docker-adhoc: -  stage: docker -  image: docker:latest +.kaniko:linux/arm64: +  variables: +    BUILD_ARCH: linux/arm64/v8 +    BUILD_ARCH_IMG_SUFFIX: linux-arm64-v8 +    ELIXIR_IMG: hexpm/elixir +  tags: +    - arm + +.kaniko:linux/arm: +  variables: +    BUILD_ARCH: linux/arm/v7 +    BUILD_ARCH_IMG_SUFFIX: linux-arm-v7 +    ELIXIR_IMG: git.pleroma.social:5050/pleroma/ci-image/elixir-linux-arm-v7 +  tags: +    - arm32-specified + +kaniko-latest:linux/amd64: +  extends: +    - .kaniko-latest +    - .kaniko:linux/amd64 + +kaniko-latest:linux/arm64: +  extends: +    - .kaniko-latest +    - .kaniko:linux/arm64 + +kaniko-latest:linux/arm: +  extends: +    - .kaniko-latest +    - .kaniko:linux/arm + +kaniko-stable:linux/amd64: +  extends: +    - .kaniko-stable +    - .kaniko:linux/amd64 + +kaniko-stable:linux/arm64: +  extends: +    - .kaniko-stable +    - .kaniko:linux/arm64 + +kaniko-stable:linux/arm: +  extends: +    - .kaniko-stable +    - .kaniko:linux/arm + +kaniko-release:linux/amd64: +  extends: +    - .kaniko-release +    - .kaniko:linux/amd64 + +kaniko-release:linux/arm64: +  extends: +    - .kaniko-release +    - .kaniko:linux/arm64 + +kaniko-release:linux/arm: +  extends: +    - .kaniko-release +    - .kaniko:linux/arm + +.docker-combine: +  stage: docker-combine +  image: docker:cli    cache: {} -  dependencies: [] -  variables: *docker-variables -  before_script: *before-docker -  allow_failure: true +  before_script: +    - 'BUILD_ARCHES="linux-amd64 linux-arm64-v8 linux-arm-v7"' +    - export IMAGE_TAG=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA +    - export IMAGE_TAG_SLUG=$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG +    - export IMAGE_TAG_LATEST=$CI_REGISTRY_IMAGE:latest +    - export IMAGE_TAG_LATEST_STABLE=$CI_REGISTRY_IMAGE:latest-stable +    - 'IMAGES=; for arch in $BUILD_ARCHES; do IMAGES="$IMAGES $CI_REGISTRY_IMAGE/$arch:$CI_COMMIT_SHORT_SHA"; done' +    - 'IMAGES_SLUG=; for arch in $BUILD_ARCHES; do IMAGES_SLUG="$IMAGES_SLUG $CI_REGISTRY_IMAGE/$arch:$CI_COMMIT_REF_SLUG"; done' +    - 'IMAGES_LATEST=; for arch in $BUILD_ARCHES; do IMAGES_LATEST="$IMAGES_LATEST $CI_REGISTRY_IMAGE/$arch:latest"; done' +    - 'IMAGES_LATEST_STABLE=; for arch in $BUILD_ARCHES; do IMAGES_LATEST_STABLE="$IMAGES_LATEST_STABLE $CI_REGISTRY_IMAGE/$arch:latest"; done' +    - mkdir -p ~/.docker +    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json + +docker-combine:latest: +  extends: .docker-combine +  only: +    - develop@pleroma/pleroma    script: +    - 'docker manifest create $IMAGE_TAG $IMAGES' +    - 'docker manifest push $IMAGE_TAG' +    - 'docker manifest create $IMAGE_TAG_SLUG $IMAGES_SLUG' +    - 'docker manifest push $IMAGE_TAG_SLUG' +    - 'docker manifest create $IMAGE_TAG_LATEST $IMAGES_LATEST' +    - 'docker manifest push $IMAGE_TAG_LATEST' + +docker-combine:stable: +  extends: .docker-combine +  only: +    - stable@pleroma/pleroma    script: -    - mkdir -p /root/.docker/cli-plugins -    - wget "${DOCKER_BUILDX_URL}" -O ~/.docker/cli-plugins/docker-buildx -    - echo "${DOCKER_BUILDX_HASH}  /root/.docker/cli-plugins/docker-buildx" | sha1sum -c -    - chmod +x ~/.docker/cli-plugins/docker-buildx -    - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes -    - docker buildx create --name mbuilder --driver docker-container --use -    - docker buildx inspect --bootstrap -    - docker buildx build --platform linux/amd64,linux/arm/v7,linux/arm64/v8 --push --cache-from $IMAGE_TAG_SLUG --build-arg VCS_REF=$CI_VCS_REF --build-arg BUILD_DATE=$CI_JOB_TIMESTAMP -t $IMAGE_TAG -t $IMAGE_TAG_SLUG . -  tags: -    - dind +    - 'docker manifest create $IMAGE_TAG $IMAGES' +    - 'docker manifest push $IMAGE_TAG' +    - 'docker manifest create $IMAGE_TAG_SLUG $IMAGES_SLUG' +    - 'docker manifest push $IMAGE_TAG_SLUG' +    - 'docker manifest create $IMAGE_TAG_LATEST_STABLE $IMAGES_LATEST_STABLE' +    - 'docker manifest push $IMAGE_TAG_LATEST_STABLE' + +docker-combine:release: +  extends: .docker-combine    only: -    - /^build-docker/.*$/@pleroma/pleroma +    - /^release/.*$/@pleroma/pleroma +  script: +    - 'docker manifest create $IMAGE_TAG $IMAGES' +    - 'docker manifest push $IMAGE_TAG' +    - 'docker manifest create $IMAGE_TAG_SLUG $IMAGES_SLUG' +    - 'docker manifest push $IMAGE_TAG_SLUG' diff --git a/.gitlab/merge_request_templates/Default.md b/.gitlab/merge_request_templates/Default.md new file mode 100644 index 000000000..fdf219f99 --- /dev/null +++ b/.gitlab/merge_request_templates/Default.md @@ -0,0 +1,10 @@ +### Checklist +- [ ] Adding a changelog: In the `changelog.d` directory, create a file named `<code>.<type>`. + +  `<code>` can be anything, but we recommend using a more or less unique identifier to avoid collisions, such as the branch name. + +  `<type>` can be `add`, `remove`, `fix`, `security` or `skip`. `skip` is only used if there is no user-visible change in the MR (for example, only editing comments in the code). Otherwise, choose a type that corresponds to your change. + +  In the file, write the changelog entry. For example, if an MR adds group functionality, we can create a file named `group.add` and write `Add group functionality` in it. + +  If one changelog entry is not enough, you may add more. But that might mean you can split it into two MRs. Only use more than one changelog entry if you really need to (for example, when one change in the code fix two different bugs, or when refactoring). diff --git a/CHANGELOG.md b/CHANGELOG.md index f6fc6aaee..42a1bbb8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).  ### Changed  ### Added +- Support for Image activities, namely from Hubzilla  ### Fixed +- rel="me" was missing its cache +  ### Removed +- BREAKING: Support for passwords generated with `crypt(3)` (Gnu Social migration artifact)  ## 2.5.2 diff --git a/Dockerfile b/Dockerfile index 8c3ff3ac5..310d18104 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,9 @@ +ARG ELIXIR_IMG=hexpm/elixir  ARG ELIXIR_VER=1.11.4  ARG ERLANG_VER=24.2.1  ARG ALPINE_VER=3.17.0 -FROM hexpm/elixir:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} as build +FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} as build  COPY . . diff --git a/changelog.d/3739.skip b/changelog.d/3739.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/3739.skip diff --git a/changelog.d/3848.add b/changelog.d/3848.add new file mode 100644 index 000000000..d7b1b0a84 --- /dev/null +++ b/changelog.d/3848.add @@ -0,0 +1 @@ +Add OAuth scope descriptions diff --git a/changelog.d/3870.skip b/changelog.d/3870.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/3870.skip diff --git a/changelog.d/3872.remove b/changelog.d/3872.remove new file mode 100644 index 000000000..54cbb660e --- /dev/null +++ b/changelog.d/3872.remove @@ -0,0 +1 @@ +remove BBS/SSH feature, replaced by an external bridge.
\ No newline at end of file diff --git a/changelog.d/3873.fix b/changelog.d/3873.fix new file mode 100644 index 000000000..4699f7b58 --- /dev/null +++ b/changelog.d/3873.fix @@ -0,0 +1 @@ +UploadedMedia: Add missing disposition_type to Content-Disposition
\ No newline at end of file diff --git a/changelog.d/3876.skip b/changelog.d/3876.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/3876.skip diff --git a/changelog.d/3877.skip b/changelog.d/3877.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/3877.skip diff --git a/changelog.d/3878.skip b/changelog.d/3878.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/3878.skip diff --git a/changelog.d/3882.add b/changelog.d/3882.add new file mode 100644 index 000000000..4712de1dc --- /dev/null +++ b/changelog.d/3882.add @@ -0,0 +1 @@ +Allow lang attribute in status text diff --git a/changelog.d/3893.skip b/changelog.d/3893.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/3893.skip diff --git a/changelog.d/changelog-improve.skip b/changelog.d/changelog-improve.skip new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/changelog.d/changelog-improve.skip diff --git a/config/config.exs b/config/config.exs index e41ec2f91..178b6be99 100644 --- a/config/config.exs +++ b/config/config.exs @@ -617,9 +617,6 @@ config :pleroma, :ldap,    base: System.get_env("LDAP_BASE") || "dc=example,dc=com",    uid: System.get_env("LDAP_UID") || "cn" -config :esshd, -  enabled: false -  oauth_consumer_strategies =    System.get_env("OAUTH_CONSUMER_STRATEGIES")    |> to_string() diff --git a/config/description.exs b/config/description.exs index 78dc8770d..5f8add8a4 100644 --- a/config/description.exs +++ b/config/description.exs @@ -2629,45 +2629,6 @@ config :pleroma, :config_description, [      ]    },    %{ -    group: :esshd, -    label: "ESSHD", -    type: :group, -    description: -      "Before enabling this you must add :esshd to mix.exs as one of the extra_applications " <> -        "and generate host keys in your priv dir with ssh-keygen -m PEM -N \"\" -b 2048 -t rsa -f ssh_host_rsa_key", -    children: [ -      %{ -        key: :enabled, -        type: :boolean, -        description: "Enables SSH" -      }, -      %{ -        key: :priv_dir, -        type: :string, -        description: "Dir with SSH keys", -        suggestions: ["/some/path/ssh_keys"] -      }, -      %{ -        key: :handler, -        type: :string, -        description: "Handler module", -        suggestions: ["Pleroma.BBS.Handler"] -      }, -      %{ -        key: :port, -        type: :integer, -        description: "Port to connect", -        suggestions: [10_022] -      }, -      %{ -        key: :password_authenticator, -        type: :string, -        description: "Authenticator module", -        suggestions: ["Pleroma.BBS.Authenticator"] -      } -    ] -  }, -  %{      group: :mime,      label: "Mime Types",      type: :group, diff --git a/docs/clients.md b/docs/clients.md index 31d2d27c3..ad7eb7807 100644 --- a/docs/clients.md +++ b/docs/clients.md @@ -3,12 +3,6 @@ Note: Additional clients may be working but theses are officially supporting Ple  Feel free to contact us to be added to this list!  ## Desktop -### Roma for Desktop -- Homepage: <https://www.pleroma.com/#desktopApp> -- Source Code: <https://github.com/roma-apps/roma-desktop> -- Platforms: Windows, Mac, Linux -- Features: MastoAPI, Streaming Ready -  ### Social  - Source Code: <https://gitlab.gnome.org/World/Social>  - Contact: [@brainblasted@social.libre.fi](https://social.libre.fi/users/brainblasted) @@ -19,7 +13,14 @@ Feel free to contact us to be added to this list!  ### Whalebird  - Homepage: <https://whalebird.social/>  - Source Code: <https://github.com/h3poteto/whalebird-desktop> -- Contact: [@h3poteto@pleroma.io](https://pleroma.io/users/h3poteto) +- Contact: [@whalebird@pleroma.io](https://pleroma.io/users/whalebird) +- Platforms: Windows, Mac, Linux +- Features: MastoAPI, Streaming Ready + +### Fedistar +- Homepage: <https://fedistar.net> +- Source Code: <https://github.com/h3poteto/fedistar> +- Contact: [@fedistar@pleroma.io](https://pleroma.io/users/fedistar)  - Platforms: Windows, Mac, Linux  - Features: MastoAPI, Streaming Ready diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index bbdf30a0f..70abe3e0c 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -873,21 +873,8 @@ This will probably take a long time.  ### BBS / SSH access -To enable simple command line interface accessible over ssh, add a setting like this to your configuration file: - -```exs -app_dir = File.cwd! -priv_dir = Path.join([app_dir, "priv/ssh_keys"]) - -config :esshd, -  enabled: true, -  priv_dir: priv_dir, -  handler: "Pleroma.BBS.Handler", -  port: 10_022, -  password_authenticator: "Pleroma.BBS.Authenticator" -``` - -Feel free to adjust the priv_dir and port number. Then you will have to create the key for the keys (in the example `priv/ssh_keys`) and create the host keys with `ssh-keygen -m PEM -N "" -b 2048 -t rsa -f ssh_host_rsa_key`. After restarting, you should be able to connect to your Pleroma instance with `ssh username@server -p $PORT` +This feature has been removed from Pleroma core. +However, a client has been made and is available at https://git.pleroma.social/Duponin/sshocial.  ### :gopher  * `enabled`: Enables the gopher interface diff --git a/docs/development/API/admin_api.md b/docs/development/API/admin_api.md index f6e9f7d2a..7d31ee262 100644 --- a/docs/development/API/admin_api.md +++ b/docs/development/API/admin_api.md @@ -1585,6 +1585,7 @@ Returns the content of the document      "build_url": "https://git.pleroma.social/pleroma/fedi-fe/-/jobs/artifacts/${ref}/download?job=build",      "git": "https://git.pleroma.social/pleroma/fedi-fe",      "installed": true, +    "installed_refs": ["master"],      "name": "fedi-fe",      "ref": "master"    }, @@ -1592,6 +1593,7 @@ Returns the content of the document      "build_url": "https://git.pleroma.social/lambadalambda/kenoma/-/jobs/artifacts/${ref}/download?job=build",      "git": "https://git.pleroma.social/lambadalambda/kenoma",      "installed": false, +    "installed_refs": [],      "name": "kenoma",      "ref": "master"    } diff --git a/lib/mix/tasks/pleroma/openapi_spec.ex b/lib/mix/tasks/pleroma/openapi_spec.ex index 884f931f8..1ea468476 100644 --- a/lib/mix/tasks/pleroma/openapi_spec.ex +++ b/lib/mix/tasks/pleroma/openapi_spec.ex @@ -6,7 +6,70 @@ defmodule Mix.Tasks.Pleroma.OpenapiSpec do    def run([path]) do      # Load Pleroma application to get version info      Application.load(:pleroma) -    spec = Pleroma.Web.ApiSpec.spec(server_specific: false) |> Jason.encode!() -    File.write(path, spec) + +    spec_json = Pleroma.Web.ApiSpec.spec(server_specific: false) |> Jason.encode!() +    # to get rid of the structs +    spec_regened = spec_json |> Jason.decode!() + +    check_specs!(spec_regened) + +    File.write(path, spec_json) +  end + +  defp check_specs!(spec) do +    with :ok <- check_specs(spec) do +      :ok +    else +      {_, errors} -> +        IO.puts(IO.ANSI.format([:red, :bright, "Spec check failed, errors:"])) +        Enum.map(errors, &IO.puts/1) + +        raise "Spec check failed" +    end +  end + +  def check_specs(spec) do +    errors = +      spec["paths"] +      |> Enum.flat_map(fn {path, %{} = endpoints} -> +        Enum.map( +          endpoints, +          fn {method, endpoint} -> +            with :ok <- check_endpoint(spec, endpoint) do +              :ok +            else +              error -> +                "#{endpoint["operationId"]} (#{method} #{path}): #{error}" +            end +          end +        ) +        |> Enum.reject(fn res -> res == :ok end) +      end) + +    if errors == [] do +      :ok +    else +      {:error, errors} +    end +  end + +  defp check_endpoint(spec, endpoint) do +    valid_tags = available_tags(spec) + +    with {_, [_ | _] = tags} <- {:tags, endpoint["tags"]}, +         {_, []} <- {:unavailable, Enum.reject(tags, &(&1 in valid_tags))} do +      :ok +    else +      {:tags, _} -> +        "No tags specified" + +      {:unavailable, tags} -> +        "Tags #{inspect(tags)} not available. Please add it in \"x-tagGroups\" in Pleroma.Web.ApiSpec" +    end +  end + +  defp available_tags(spec) do +    spec["x-tagGroups"] +    |> Enum.flat_map(fn %{"tags" => tags} -> tags end)    end  end diff --git a/lib/pleroma/bbs/authenticator.ex b/lib/pleroma/bbs/authenticator.ex deleted file mode 100644 index 0f7543ff5..000000000 --- a/lib/pleroma/bbs/authenticator.ex +++ /dev/null @@ -1,20 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.BBS.Authenticator do -  use Sshd.PasswordAuthenticator -  alias Pleroma.User -  alias Pleroma.Web.Plugs.AuthenticationPlug - -  def authenticate(username, password) do -    username = to_string(username) -    password = to_string(password) - -    with %User{} = user <- User.get_by_nickname(username) do -      AuthenticationPlug.checkpw(password, user.password_hash) -    else -      _e -> false -    end -  end -end diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex deleted file mode 100644 index 27799338f..000000000 --- a/lib/pleroma/bbs/handler.ex +++ /dev/null @@ -1,246 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.BBS.Handler do -  use Sshd.ShellHandler -  alias Pleroma.Activity -  alias Pleroma.HTML -  alias Pleroma.Web.ActivityPub.ActivityPub -  alias Pleroma.Web.CommonAPI - -  def on_shell(username, _pubkey, _ip, _port) do -    :ok = IO.puts("Welcome to #{Pleroma.Config.get([:instance, :name])}!") -    user = Pleroma.User.get_cached_by_nickname(to_string(username)) -    Logger.debug("#{inspect(user)}") -    loop(run_state(user: user)) -  end - -  def on_connect(username, ip, port, method) do -    Logger.debug(fn -> -      """ -      Incoming SSH shell #{inspect(self())} requested for #{username} from #{inspect(ip)}:#{inspect(port)} using #{inspect(method)} -      """ -    end) -  end - -  def on_disconnect(username, ip, port) do -    Logger.debug(fn -> -      "Disconnecting SSH shell for #{username} from #{inspect(ip)}:#{inspect(port)}" -    end) -  end - -  defp loop(state) do -    self_pid = self() -    counter = state.counter -    prefix = state.prefix -    user = state.user - -    input = spawn(fn -> io_get(self_pid, prefix, counter, user.nickname) end) -    wait_input(state, input) -  end - -  def puts_activity(activity) do -    status = Pleroma.Web.MastodonAPI.StatusView.render("show.json", %{activity: activity}) - -    IO.puts("-- #{status.id} by #{status.account.display_name} (#{status.account.acct})") - -    status.content -    |> String.split("<br/>") -    |> Enum.map(&HTML.strip_tags/1) -    |> Enum.map(&HtmlEntities.decode/1) -    |> Enum.map(&IO.puts/1) -  end - -  def puts_notification(activity, user) do -    notification = -      Pleroma.Web.MastodonAPI.NotificationView.render("show.json", %{ -        notification: activity, -        for: user -      }) - -    IO.puts( -      "== (#{notification.type}) #{notification.status.id} by #{notification.account.display_name} (#{notification.account.acct})" -    ) - -    notification.status.content -    |> String.split("<br/>") -    |> Enum.map(&HTML.strip_tags/1) -    |> Enum.map(&HtmlEntities.decode/1) -    |> (fn x -> -          case x do -            [content] -> -              "> " <> content - -            [head | _tail] -> -              # "> " <> hd <> "..." -              head -              |> String.slice(1, 80) -              |> (fn x -> "> " <> x <> "..." end).() -          end -        end).() -    |> IO.puts() - -    IO.puts("") -  end - -  def handle_command(state, "help") do -    IO.puts("Available commands:") -    IO.puts("help - This help") -    IO.puts("home - Show the home timeline") -    IO.puts("p <text> - Post the given text") -    IO.puts("r <id> <text> - Reply to the post with the given id") -    IO.puts("t <id> - Show a thread from the given id") -    IO.puts("n - Show notifications") -    IO.puts("n read - Mark all notifactions as read") -    IO.puts("f <id> - Favourites the post with the given id") -    IO.puts("R <id> - Repeat the post with the given id") -    IO.puts("quit - Quit") - -    state -  end - -  def handle_command(%{user: user} = state, "r " <> text) do -    text = String.trim(text) -    [activity_id, rest] = String.split(text, " ", parts: 2) - -    with %Activity{} <- Activity.get_by_id(activity_id), -         {:ok, _activity} <- -           CommonAPI.post(user, %{status: rest, in_reply_to_status_id: activity_id}) do -      IO.puts("Replied!") -    else -      _e -> IO.puts("Could not reply...") -    end - -    state -  end - -  def handle_command(%{user: user} = state, "t " <> activity_id) do -    with %Activity{} = activity <- Activity.get_by_id(activity_id) do -      activities = -        ActivityPub.fetch_activities_for_context(activity.data["context"], %{ -          blocking_user: user, -          user: user, -          exclude_id: activity.id -        }) - -      case activities do -        [] -> -          activity_id -          |> Activity.get_by_id() -          |> puts_activity() - -        _ -> -          activities -          |> Enum.reverse() -          |> Enum.each(&puts_activity/1) -      end -    else -      _e -> IO.puts("Could not show this thread...") -    end - -    state -  end - -  def handle_command(%{user: user} = state, "n read") do -    Pleroma.Notification.clear(user) -    IO.puts("All notifications were marked as read") - -    state -  end - -  def handle_command(%{user: user} = state, "n") do -    user -    |> Pleroma.Web.MastodonAPI.MastodonAPI.get_notifications(%{}) -    |> Enum.each(&puts_notification(&1, user)) - -    state -  end - -  def handle_command(%{user: user} = state, "p " <> text) do -    text = String.trim(text) - -    with {:ok, activity} <- CommonAPI.post(user, %{status: text}) do -      IO.puts("Posted! ID: #{activity.id}") -    else -      _e -> IO.puts("Could not post...") -    end - -    state -  end - -  def handle_command(%{user: user} = state, "f " <> id) do -    id = String.trim(id) - -    with %Activity{} = activity <- Activity.get_by_id(id), -         {:ok, _activity} <- CommonAPI.favorite(user, activity) do -      IO.puts("Favourited!") -    else -      _e -> IO.puts("Could not Favourite...") -    end - -    state -  end - -  def handle_command(state, "home") do -    user = state.user - -    params = -      %{} -      |> Map.put(:type, ["Create"]) -      |> Map.put(:blocking_user, user) -      |> Map.put(:muting_user, user) -      |> Map.put(:user, user) - -    activities = -      [user.ap_id | Pleroma.User.following(user)] -      |> ActivityPub.fetch_activities(params) - -    Enum.each(activities, fn activity -> -      puts_activity(activity) -    end) - -    state -  end - -  def handle_command(state, command) do -    IO.puts("Unknown command '#{command}'") -    state -  end - -  defp wait_input(state, input) do -    receive do -      {:input, ^input, "quit\n"} -> -        IO.puts("Exiting...") - -      {:input, ^input, code} when is_binary(code) -> -        code = String.trim(code) - -        state = handle_command(state, code) - -        loop(%{state | counter: state.counter + 1}) - -      {:input, ^input, {:error, :interrupted}} -> -        IO.puts("Caught Ctrl+C...") -        loop(%{state | counter: state.counter + 1}) - -      {:input, ^input, msg} -> -        :ok = Logger.warn("received unknown message: #{inspect(msg)}") -        loop(%{state | counter: state.counter + 1}) -    end -  end - -  defp run_state(opts) do -    %{prefix: "pleroma", counter: 1, user: opts[:user]} -  end - -  defp io_get(pid, prefix, counter, username) do -    prompt = prompt(prefix, counter, username) -    send(pid, {:input, self(), IO.gets(:stdio, prompt)}) -  end - -  defp prompt(prefix, counter, username) do -    prompt = "#{username}@#{prefix}:#{counter}>" -    prompt <> " " -  end -end diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex index dd65d56ae..43a3447c3 100644 --- a/lib/pleroma/emoji.ex +++ b/lib/pleroma/emoji.ex @@ -51,6 +51,8 @@ defmodule Pleroma.Emoji do    @doc "Returns the path of the emoji `name`."    @spec get(String.t()) :: String.t() | nil    def get(name) do +    name = maybe_strip_name(name) +      case :ets.lookup(@ets, name) do        [{_, path}] -> path        _ -> nil @@ -139,6 +141,57 @@ defmodule Pleroma.Emoji do    def is_unicode_emoji?(_), do: false +  @emoji_regex ~r/:[A-Za-z0-9_-]+(@.+)?:/ + +  def is_custom_emoji?(s) when is_binary(s), do: Regex.match?(@emoji_regex, s) + +  def is_custom_emoji?(_), do: false + +  def maybe_strip_name(name) when is_binary(name), do: String.trim(name, ":") + +  def maybe_strip_name(name), do: name + +  def maybe_quote(name) when is_binary(name) do +    if is_unicode_emoji?(name) do +      name +    else +      if String.starts_with?(name, ":") do +        name +      else +        ":#{name}:" +      end +    end +  end + +  def maybe_quote(name), do: name + +  def emoji_url(%{"type" => "EmojiReact", "content" => _, "tag" => []}), do: nil + +  def emoji_url(%{"type" => "EmojiReact", "content" => emoji, "tag" => tags}) do +    emoji = maybe_strip_name(emoji) + +    tag = +      tags +      |> Enum.find(fn tag -> +        tag["type"] == "Emoji" && !is_nil(tag["name"]) && tag["name"] == emoji +      end) + +    if is_nil(tag) do +      nil +    else +      tag +      |> Map.get("icon") +      |> Map.get("url") +    end +  end + +  def emoji_url(_), do: nil + +  def emoji_name_with_instance(name, url) do +    url = url |> URI.parse() |> Map.get(:host) +    "#{name}@#{url}" +  end +    emoji_qualification_map =      emojis      |> Enum.filter(&String.contains?(&1, "\uFE0F")) diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 38accae5d..aa137d250 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -425,4 +425,30 @@ defmodule Pleroma.Object do    end    def object_data_hashtags(_), do: [] + +  def get_emoji_reactions(object) do +    reactions = object.data["reactions"] + +    if is_list(reactions) or is_map(reactions) do +      reactions +      |> Enum.map(fn +        [_emoji, users, _maybe_url] = item when is_list(users) -> +          item + +        [emoji, users] when is_list(users) -> +          [emoji, users, nil] + +        # This case is here to process the Map situation, which will happen +        # only with the legacy two-value format. +        {emoji, users} when is_list(users) -> +          [emoji, users, nil] + +        _ -> +          nil +      end) +      |> Enum.reject(&is_nil/1) +    else +      [] +    end +  end  end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 1ab2db94a..f22756015 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -96,7 +96,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    defp increase_replies_count_if_reply(_create_data), do: :noop -  @object_types ~w[ChatMessage Question Answer Audio Video Event Article Note Page] +  @object_types ~w[ChatMessage Question Answer Audio Video Image Event Article Note Page]    @impl true    def persist(%{"type" => type} = object, meta) when type in @object_types do      with {:ok, object} <- Object.create(object) do diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index 532047599..8eab3a241 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -16,6 +16,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do    alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Web.ActivityPub.Visibility    alias Pleroma.Web.CommonAPI.ActivityDraft +  alias Pleroma.Web.Endpoint    require Pleroma.Constants @@ -54,13 +55,87 @@ defmodule Pleroma.Web.ActivityPub.Builder do      {:ok, data, []}    end +  defp unicode_emoji_react(_object, data, emoji) do +    data +    |> Map.put("content", emoji) +    |> Map.put("type", "EmojiReact") +  end + +  defp add_emoji_content(data, emoji, url) do +    tag = [ +      %{ +        "id" => url, +        "type" => "Emoji", +        "name" => Emoji.maybe_quote(emoji), +        "icon" => %{ +          "type" => "Image", +          "url" => url +        } +      } +    ] + +    data +    |> Map.put("content", Emoji.maybe_quote(emoji)) +    |> Map.put("type", "EmojiReact") +    |> Map.put("tag", tag) +  end + +  defp remote_custom_emoji_react( +         %{data: %{"reactions" => existing_reactions}}, +         data, +         emoji +       ) do +    [emoji_code, instance] = String.split(Emoji.maybe_strip_name(emoji), "@") + +    matching_reaction = +      Enum.find( +        existing_reactions, +        fn [name, _, url] -> +          if url != nil do +            url = URI.parse(url) +            url.host == instance && name == emoji_code +          end +        end +      ) + +    if matching_reaction do +      [name, _, url] = matching_reaction +      add_emoji_content(data, name, url) +    else +      {:error, "Could not react"} +    end +  end + +  defp remote_custom_emoji_react(_object, _data, _emoji) do +    {:error, "Could not react"} +  end + +  defp local_custom_emoji_react(data, emoji) do +    with %{file: path} = emojo <- Emoji.get(emoji) do +      url = "#{Endpoint.url()}#{path}" +      add_emoji_content(data, emojo.code, url) +    else +      _ -> {:error, "Emoji does not exist"} +    end +  end + +  defp custom_emoji_react(object, data, emoji) do +    if String.contains?(emoji, "@") do +      remote_custom_emoji_react(object, data, emoji) +    else +      local_custom_emoji_react(data, emoji) +    end +  end +    @spec emoji_react(User.t(), Object.t(), String.t()) :: {:ok, map(), keyword()}    def emoji_react(actor, object, emoji) do      with {:ok, data, meta} <- object_action(actor, object) do        data = -        data -        |> Map.put("content", emoji) -        |> Map.put("type", "EmojiReact") +        if Emoji.is_unicode_emoji?(emoji) do +          unicode_emoji_react(object, data, emoji) +        else +          custom_emoji_react(object, data, emoji) +        end        {:ok, data, meta}      end diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index 5bcd6da46..5e0d1aa8e 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -21,7 +21,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do    alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator    alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator    alias Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator -  alias Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator +  alias Pleroma.Web.ActivityPub.ObjectValidators.AudioImageVideoValidator    alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator    alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator    alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator @@ -102,7 +102,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do          %{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,          meta        ) -      when objtype in ~w[Question Answer Audio Video Event Article Note Page] do +      when objtype in ~w[Question Answer Audio Video Image Event Article Note Page] do      with {:ok, object_data} <- cast_and_apply_and_stringify_with_history(object),           meta = Keyword.put(meta, :object_data, object_data),           {:ok, create_activity} <- @@ -115,13 +115,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do    end    def validate(%{"type" => type} = object, meta) -      when type in ~w[Event Question Audio Video Article Note Page] do +      when type in ~w[Event Question Audio Video Image Article Note Page] do      validator =        case type do          "Event" -> EventValidator          "Question" -> QuestionValidator -        "Audio" -> AudioVideoValidator -        "Video" -> AudioVideoValidator +        "Audio" -> AudioImageVideoValidator +        "Video" -> AudioImageVideoValidator +        "Image" -> AudioImageVideoValidator          "Article" -> ArticleNotePageValidator          "Note" -> ArticleNotePageValidator          "Page" -> ArticleNotePageValidator @@ -233,8 +234,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do      AnswerValidator.cast_and_apply(object)    end -  def cast_and_apply(%{"type" => type} = object) when type in ~w[Audio Video] do -    AudioVideoValidator.cast_and_apply(object) +  def cast_and_apply(%{"type" => type} = object) when type in ~w[Audio Image Video] do +    AudioImageVideoValidator.cast_and_apply(object)    end    def cast_and_apply(%{"type" => "Event"} = object) do diff --git a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex b/lib/pleroma/web/activity_pub/object_validators/audio_image_video_validator.ex index 671a7ef0c..79ff76104 100644 --- a/lib/pleroma/web/activity_pub/object_validators/audio_video_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/audio_image_video_validator.ex @@ -2,7 +2,7 @@  # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>  # SPDX-License-Identifier: AGPL-3.0-only -defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do +defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioImageVideoValidator do    use Ecto.Schema    alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes @@ -55,9 +55,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do      url      |> Enum.concat(mpeg_url["tag"] || [])      |> Enum.find(fn -      %{"mediaType" => mime_type} -> String.starts_with?(mime_type, ["video/", "audio/"]) -      %{"mimeType" => mime_type} -> String.starts_with?(mime_type, ["video/", "audio/"]) -      _ -> false +      %{"mediaType" => mime_type} -> +        String.starts_with?(mime_type, ["video/", "audio/", "image/"]) + +      %{"mimeType" => mime_type} -> +        String.starts_with?(mime_type, ["video/", "audio/", "image/"]) + +      _ -> +        false      end)    end @@ -110,7 +115,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioVideoValidator do    defp validate_data(data_cng) do      data_cng -    |> validate_inclusion(:type, ["Audio", "Video"]) +    |> validate_inclusion(:type, ~w[Audio Image Video])      |> validate_required([:id, :actor, :attributedTo, :type, :context])      |> CommonValidations.validate_any_presence([:cc, :to])      |> CommonValidations.validate_fields_match([:actor, :attributedTo]) diff --git a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex index 0858281e5..a0b82b325 100644 --- a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex @@ -5,8 +5,10 @@  defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do    use Ecto.Schema +  alias Pleroma.Emoji    alias Pleroma.Object    alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes +  alias Pleroma.Web.ActivityPub.ObjectValidators.TagValidator    import Ecto.Changeset    import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations @@ -19,6 +21,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do          import Elixir.Pleroma.Web.ActivityPub.ObjectValidators.CommonFields          message_fields()          activity_fields() +        embeds_many(:tag, TagValidator)        end      end @@ -43,7 +46,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do    def changeset(struct, data) do      struct -    |> cast(data, __schema__(:fields)) +    |> cast(data, __schema__(:fields) -- [:tag]) +    |> cast_embed(:tag)    end    defp fix(data) do @@ -53,12 +57,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do        |> CommonFixes.fix_actor()        |> CommonFixes.fix_activity_addressing() -    with %Object{} = object <- Object.normalize(data["object"]) do -      data -      |> CommonFixes.fix_activity_context(object) -      |> CommonFixes.fix_object_action_recipients(object) -    else -      _ -> data +    data = Map.put_new(data, "tag", []) + +    case Object.normalize(data["object"]) do +      %Object{} = object -> +        data +        |> CommonFixes.fix_activity_context(object) +        |> CommonFixes.fix_object_action_recipients(object) + +      _ -> +        data      end    end @@ -82,11 +90,31 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do    defp validate_emoji(cng) do      content = get_field(cng, :content) -    if Pleroma.Emoji.is_unicode_emoji?(content) do +    if Emoji.is_unicode_emoji?(content) || Emoji.is_custom_emoji?(content) do        cng      else        cng -      |> add_error(:content, "must be a single character emoji") +      |> add_error(:content, "is not a valid emoji") +    end +  end + +  defp maybe_validate_tag_presence(cng) do +    content = get_field(cng, :content) + +    if Emoji.is_unicode_emoji?(content) do +      cng +    else +      tag = get_field(cng, :tag) +      emoji_name = Emoji.maybe_strip_name(content) + +      case tag do +        [%{name: ^emoji_name, type: "Emoji", icon: %{url: _}}] -> +          cng + +        _ -> +          cng +          |> add_error(:tag, "does not contain an Emoji tag") +      end      end    end @@ -97,5 +125,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do      |> validate_actor_presence()      |> validate_object_presence()      |> validate_emoji() +    |> maybe_validate_tag_presence()    end  end diff --git a/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex b/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex index 9f15f1981..cfd510c19 100644 --- a/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/tag_validator.ex @@ -68,6 +68,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.TagValidator do      |> validate_required([:type, :name, :icon])    end +  def changeset(struct, %{"type" => _} = data) do +    struct +    |> cast(data, []) +    |> Map.put(:action, :ignore) +  end +    def icon_changeset(struct, data) do      struct      |> cast(data, [:type, :url]) diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index fc5dec362..098c177c7 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -496,7 +496,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do    end    def handle_object_creation(%{"type" => objtype} = object, _activity, meta) -      when objtype in ~w[Audio Video Event Article Note Page] do +      when objtype in ~w[Audio Video Image Event Article Note Page] do      with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do        {:ok, object, meta}      end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index e4c04da0d..3141f8437 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -447,7 +447,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do          %{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data,          options        ) -      when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note Page} do +      when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note Page Image} do      fetch_options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)      object = diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index b898d6fe8..437220077 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -31,7 +31,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do      "Page",      "Question",      "Answer", -    "Audio" +    "Audio", +    "Image"    ]    @strip_status_report_states ~w(closed resolved)    @supported_report_states ~w(open closed resolved) @@ -325,21 +326,29 @@ defmodule Pleroma.Web.ActivityPub.Utils do            {:ok, Object.t()} | {:error, Ecto.Changeset.t()}    def add_emoji_reaction_to_object( -        %Activity{data: %{"content" => emoji, "actor" => actor}}, +        %Activity{data: %{"content" => emoji, "actor" => actor}} = activity,          object        ) do      reactions = get_cached_emoji_reactions(object) +    emoji = Pleroma.Emoji.maybe_strip_name(emoji) +    url = maybe_emoji_url(emoji, activity)      new_reactions = -      case Enum.find_index(reactions, fn [candidate, _] -> emoji == candidate end) do +      case Enum.find_index(reactions, fn [candidate, _, candidate_url] -> +             if is_nil(candidate_url) do +               emoji == candidate +             else +               url == candidate_url +             end +           end) do          nil -> -          reactions ++ [[emoji, [actor]]] +          reactions ++ [[emoji, [actor], url]]          index ->            List.update_at(              reactions,              index, -            fn [emoji, users] -> [emoji, Enum.uniq([actor | users])] end +            fn [emoji, users, url] -> [emoji, Enum.uniq([actor | users]), url] end            )        end @@ -348,18 +357,40 @@ defmodule Pleroma.Web.ActivityPub.Utils do      update_element_in_object("reaction", new_reactions, object, count)    end +  defp maybe_emoji_url( +         name, +         %Activity{ +           data: %{ +             "tag" => [ +               %{"type" => "Emoji", "name" => name, "icon" => %{"url" => url}} +             ] +           } +         } +       ), +       do: url + +  defp maybe_emoji_url(_, _), do: nil +    def emoji_count(reactions_list) do -    Enum.reduce(reactions_list, 0, fn [_, users], acc -> acc + length(users) end) +    Enum.reduce(reactions_list, 0, fn [_, users, _], acc -> acc + length(users) end)    end    def remove_emoji_reaction_from_object( -        %Activity{data: %{"content" => emoji, "actor" => actor}}, +        %Activity{data: %{"content" => emoji, "actor" => actor}} = activity,          object        ) do +    emoji = Pleroma.Emoji.maybe_strip_name(emoji)      reactions = get_cached_emoji_reactions(object) +    url = maybe_emoji_url(emoji, activity)      new_reactions = -      case Enum.find_index(reactions, fn [candidate, _] -> emoji == candidate end) do +      case Enum.find_index(reactions, fn [candidate, _, candidate_url] -> +             if is_nil(candidate_url) do +               emoji == candidate +             else +               url == candidate_url +             end +           end) do          nil ->            reactions @@ -367,9 +398,9 @@ defmodule Pleroma.Web.ActivityPub.Utils do            List.update_at(              reactions,              index, -            fn [emoji, users] -> [emoji, List.delete(users, actor)] end +            fn [emoji, users, url] -> [emoji, List.delete(users, actor), url] end            ) -          |> Enum.reject(fn [_, users] -> Enum.empty?(users) end) +          |> Enum.reject(fn [_, users, _] -> Enum.empty?(users) end)        end      count = emoji_count(new_reactions) @@ -377,11 +408,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do    end    def get_cached_emoji_reactions(object) do -    if is_list(object.data["reactions"]) do -      object.data["reactions"] -    else -      [] -    end +    Object.get_emoji_reactions(object)    end    @spec add_like_to_object(Activity.t(), Object.t()) :: @@ -489,17 +516,37 @@ defmodule Pleroma.Web.ActivityPub.Utils do    def get_latest_reaction(internal_activity_id, %{ap_id: ap_id}, emoji) do      %{data: %{"object" => object_ap_id}} = Activity.get_by_id(internal_activity_id) +    emoji = Pleroma.Emoji.maybe_quote(emoji)      "EmojiReact"      |> Activity.Queries.by_type()      |> where(actor: ^ap_id) -    |> where([activity], fragment("?->>'content' = ?", activity.data, ^emoji)) +    |> custom_emoji_discriminator(emoji)      |> Activity.Queries.by_object_id(object_ap_id)      |> order_by([activity], fragment("? desc nulls last", activity.id))      |> limit(1)      |> Repo.one()    end +  defp custom_emoji_discriminator(query, emoji) do +    if String.contains?(emoji, "@") do +      stripped = Pleroma.Emoji.maybe_strip_name(emoji) +      [name, domain] = String.split(stripped, "@") +      domain_pattern = "%/" <> domain <> "/%" +      emoji_pattern = Pleroma.Emoji.maybe_quote(name) + +      query +      |> where([activity], fragment("?->>'content' = ? +        AND EXISTS ( +          SELECT FROM jsonb_array_elements(?->'tag') elem +          WHERE elem->>'id' ILIKE ? +        )", activity.data, ^emoji_pattern, activity.data, ^domain_pattern)) +    else +      query +      |> where([activity], fragment("?->>'content' = ?", activity.data, ^emoji)) +    end +  end +    #### Announce-related helpers    @doc """ diff --git a/lib/pleroma/web/admin_api/controllers/frontend_controller.ex b/lib/pleroma/web/admin_api/controllers/frontend_controller.ex index b4dbb82fe..9e2ed4aac 100644 --- a/lib/pleroma/web/admin_api/controllers/frontend_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/frontend_controller.ex @@ -18,13 +18,24 @@ defmodule Pleroma.Web.AdminAPI.FrontendController do    def index(conn, _params) do      installed = installed() +    # FIrst get frontends from config, +    # then add frontends that are installed but not in the config      frontends = -      [:frontends, :available] -      |> Config.get([]) +      Config.get([:frontends, :available], [])        |> Enum.map(fn {name, desc} -> -        Map.put(desc, "installed", name in installed) +        desc +        |> Map.put("installed", name in installed) +        |> Map.put("installed_refs", installed_refs(name))        end) +    frontends = +      frontends ++ +        (installed +         |> Enum.filter(fn n -> not Enum.any?(frontends, fn f -> f["name"] == n end) end) +         |> Enum.map(fn name -> +           %{"name" => name, "installed" => true, "installed_refs" => installed_refs(name)} +         end)) +      render(conn, "index.json", frontends: frontends)    end @@ -43,4 +54,12 @@ defmodule Pleroma.Web.AdminAPI.FrontendController do        []      end    end + +  def installed_refs(name) do +    if name in installed() do +      File.ls!(Path.join(Pleroma.Frontend.dir(), name)) +    else +      [] +    end +  end  end diff --git a/lib/pleroma/web/admin_api/views/frontend_view.ex b/lib/pleroma/web/admin_api/views/frontend_view.ex index 0ca3d67cb..ae4016581 100644 --- a/lib/pleroma/web/admin_api/views/frontend_view.ex +++ b/lib/pleroma/web/admin_api/views/frontend_view.ex @@ -15,7 +15,8 @@ defmodule Pleroma.Web.AdminAPI.FrontendView do        git: frontend["git"],        build_url: frontend["build_url"],        ref: frontend["ref"], -      installed: frontend["installed"] +      installed: frontend["installed"], +      installed_refs: frontend["installed_refs"]      }    end  end diff --git a/lib/pleroma/web/api_spec.ex b/lib/pleroma/web/api_spec.ex index cae4241ff..2d56dc643 100644 --- a/lib/pleroma/web/api_spec.ex +++ b/lib/pleroma/web/api_spec.ex @@ -95,7 +95,8 @@ defmodule Pleroma.Web.ApiSpec do                "Relays",                "Report managment",                "Status administration", -              "User administration" +              "User administration", +              "Announcement management"              ]            },            %{"name" => "Applications", "tags" => ["Applications", "Push subscriptions"]}, @@ -110,10 +111,12 @@ defmodule Pleroma.Web.ApiSpec do                "Follow requests",                "Mascot",                "Markers", -              "Notifications" +              "Notifications", +              "Filters", +              "Settings"              ]            }, -          %{"name" => "Instance", "tags" => ["Custom emojis"]}, +          %{"name" => "Instance", "tags" => ["Custom emojis", "Instance misc"]},            %{"name" => "Messaging", "tags" => ["Chats", "Conversations"]},            %{              "name" => "Statuses", @@ -125,10 +128,21 @@ defmodule Pleroma.Web.ApiSpec do                "Retrieve status information",                "Scheduled statuses",                "Search", -              "Status actions" +              "Status actions", +              "Media attachments"              ]            }, -          %{"name" => "Miscellaneous", "tags" => ["Emoji packs", "Reports", "Suggestions"]} +          %{ +            "name" => "Miscellaneous", +            "tags" => [ +              "Emoji packs", +              "Reports", +              "Suggestions", +              "Announcements", +              "Remote interaction", +              "Others" +            ] +          }          ]        }      } diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index aabe988f7..f2897a3a3 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -452,7 +452,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do        operationId: "AccountController.blocks",        description: "View your blocks. See also accounts/:id/{block,unblock}",        security: [%{"oAuth" => ["read:blocks"]}], -      parameters: pagination_params(), +      parameters: [with_relationships_param() | pagination_params()],        responses: %{          200 => Operation.response("Accounts", "application/json", array_of_accounts())        } @@ -461,7 +461,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do    def lookup_operation do      %Operation{ -      tags: ["Account lookup"], +      tags: ["Retrieve account information"],        summary: "Find a user by nickname",        operationId: "AccountController.lookup",        parameters: [ diff --git a/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex b/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex index 58a039e72..49850e5d2 100644 --- a/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/announcement_operation.ex @@ -17,7 +17,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.AnnouncementOperation do    def index_operation do      %Operation{ -      tags: ["Announcement managment"], +      tags: ["Announcement management"],        summary: "Retrieve a list of announcements",        operationId: "AdminAPI.AnnouncementController.index",        security: [%{"oAuth" => ["admin:read"]}], @@ -46,7 +46,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.AnnouncementOperation do    def show_operation do      %Operation{ -      tags: ["Announcement managment"], +      tags: ["Announcement management"],        summary: "Display one announcement",        operationId: "AdminAPI.AnnouncementController.show",        security: [%{"oAuth" => ["admin:read"]}], @@ -69,7 +69,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.AnnouncementOperation do    def delete_operation do      %Operation{ -      tags: ["Announcement managment"], +      tags: ["Announcement management"],        summary: "Delete one announcement",        operationId: "AdminAPI.AnnouncementController.delete",        security: [%{"oAuth" => ["admin:write"]}], @@ -92,7 +92,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.AnnouncementOperation do    def create_operation do      %Operation{ -      tags: ["Announcement managment"], +      tags: ["Announcement management"],        summary: "Create one announcement",        operationId: "AdminAPI.AnnouncementController.create",        security: [%{"oAuth" => ["admin:write"]}], @@ -107,7 +107,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.AnnouncementOperation do    def change_operation do      %Operation{ -      tags: ["Announcement managment"], +      tags: ["Announcement management"],        summary: "Change one announcement",        operationId: "AdminAPI.AnnouncementController.change",        security: [%{"oAuth" => ["admin:write"]}], diff --git a/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex b/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex index 4bfe5ac5a..3e85c44d2 100644 --- a/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex @@ -51,8 +51,9 @@ defmodule Pleroma.Web.ApiSpec.Admin.FrontendOperation do            name: %Schema{type: :string},            git: %Schema{type: :string, format: :uri, nullable: true},            build_url: %Schema{type: :string, format: :uri, nullable: true}, -          ref: %Schema{type: :string}, -          installed: %Schema{type: :boolean} +          ref: %Schema{type: :string, nullable: true}, +          installed: %Schema{type: :boolean}, +          installed_refs: %Schema{type: :array, items: %Schema{type: :string}}          }        }      } diff --git a/lib/pleroma/web/api_spec/operations/admin/status_operation.ex b/lib/pleroma/web/api_spec/operations/admin/status_operation.ex index 229912dd7..17383f1d0 100644 --- a/lib/pleroma/web/api_spec/operations/admin/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/status_operation.ex @@ -70,7 +70,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do    def show_operation do      %Operation{ -      tags: ["Status adminitration)"], +      tags: ["Status administration"],        summary: "Get status",        operationId: "AdminAPI.StatusController.show",        parameters: [id_param() | admin_api_params()], @@ -84,7 +84,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do    def update_operation do      %Operation{ -      tags: ["Status adminitration)"], +      tags: ["Status administration"],        summary: "Change the scope of a status",        operationId: "AdminAPI.StatusController.update",        parameters: [id_param() | admin_api_params()], @@ -99,7 +99,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do    def delete_operation do      %Operation{ -      tags: ["Status adminitration)"], +      tags: ["Status administration"],        summary: "Delete status",        operationId: "AdminAPI.StatusController.delete",        parameters: [id_param() | admin_api_params()], @@ -143,7 +143,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.StatusOperation do            }          },          tags: %Schema{type: :string}, -        is_confirmed: %Schema{type: :string} +        is_confirmed: %Schema{type: :boolean}        }      }    end diff --git a/lib/pleroma/web/api_spec/operations/announcement_operation.ex b/lib/pleroma/web/api_spec/operations/announcement_operation.ex index 71be0002a..6f7031962 100644 --- a/lib/pleroma/web/api_spec/operations/announcement_operation.ex +++ b/lib/pleroma/web/api_spec/operations/announcement_operation.ex @@ -15,7 +15,7 @@ defmodule Pleroma.Web.ApiSpec.AnnouncementOperation do    def index_operation do      %Operation{ -      tags: ["Announcement"], +      tags: ["Announcements"],        summary: "Retrieve a list of announcements",        operationId: "MastodonAPI.AnnouncementController.index",        security: [%{"oAuth" => []}], @@ -28,7 +28,7 @@ defmodule Pleroma.Web.ApiSpec.AnnouncementOperation do    def mark_read_operation do      %Operation{ -      tags: ["Announcement"], +      tags: ["Announcements"],        summary: "Mark one announcement as read",        operationId: "MastodonAPI.AnnouncementController.mark_read",        security: [%{"oAuth" => ["write:accounts"]}], diff --git a/lib/pleroma/web/api_spec/operations/directory_operation.ex b/lib/pleroma/web/api_spec/operations/directory_operation.ex index 55752fa62..23fa84dff 100644 --- a/lib/pleroma/web/api_spec/operations/directory_operation.ex +++ b/lib/pleroma/web/api_spec/operations/directory_operation.ex @@ -17,7 +17,7 @@ defmodule Pleroma.Web.ApiSpec.DirectoryOperation do    def index_operation do      %Operation{ -      tags: ["Directory"], +      tags: ["Others"],        summary: "Profile directory",        operationId: "DirectoryController.index",        parameters: diff --git a/lib/pleroma/web/api_spec/operations/instance_operation.ex b/lib/pleroma/web/api_spec/operations/instance_operation.ex index 3c4b504fe..a07be7e40 100644 --- a/lib/pleroma/web/api_spec/operations/instance_operation.ex +++ b/lib/pleroma/web/api_spec/operations/instance_operation.ex @@ -13,7 +13,7 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do    def show_operation do      %Operation{ -      tags: ["Instance"], +      tags: ["Instance misc"],        summary: "Retrieve instance information",        description: "Information about the server",        operationId: "InstanceController.show", @@ -25,7 +25,7 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do    def peers_operation do      %Operation{ -      tags: ["Instance"], +      tags: ["Instance misc"],        summary: "Retrieve list of known instances",        operationId: "InstanceController.peers",        responses: %{ diff --git a/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex index d09c1c10e..b05bad197 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_emoji_file_operation.ex @@ -133,7 +133,11 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiFileOperation do    defp files_object do      %Schema{        type: :object, -      additionalProperties: %Schema{type: :string}, +      additionalProperties: %Schema{ +        type: :string, +        description: "Filename of the emoji", +        extensions: %{"x-additionalPropertiesName": "Emoji name"} +      },        description: "Object with emoji names as keys and filenames as values"      }    end diff --git a/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex index 6add3ff33..efa36ffdc 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_emoji_pack_operation.ex @@ -227,13 +227,29 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do    defp emoji_packs_response do      Operation.response( -      "Object with pack names as keys and pack contents as values", +      "Emoji packs and the count",        "application/json",        %Schema{          type: :object, -        additionalProperties: emoji_pack(), +        properties: %{ +          packs: %Schema{ +            type: :object, +            description: "Object with pack names as keys and pack contents as values", +            additionalProperties: %Schema{ +              emoji_pack() +              | extensions: %{"x-additionalPropertiesName": "Pack name"} +            } +          }, +          count: %Schema{ +            type: :integer, +            description: "Number of emoji packs" +          } +        },          example: %{ -          "emojos" => emoji_pack().example +          "packs" => %{ +            "emojos" => emoji_pack().example +          }, +          "count" => 1          }        }      ) @@ -274,7 +290,11 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do    defp files_object do      %Schema{        type: :object, -      additionalProperties: %Schema{type: :string}, +      additionalProperties: %Schema{ +        type: :string, +        description: "Filename", +        extensions: %{"x-additionalPropertiesName": "Emoji name"} +      },        description: "Object with emoji names as keys and filenames as values"      }    end diff --git a/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex index 82db4e1a8..e9319f3fb 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_instances_operation.ex @@ -13,7 +13,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaInstancesOperation do    def show_operation do      %Operation{ -      tags: ["Instance"], +      tags: ["Instance misc"],        summary: "Retrieve federation status",        description: "Information about instances deemed unreachable by the server",        operationId: "PleromaInstances.show", diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex index e921128c7..5d6e82f3c 100644 --- a/lib/pleroma/web/api_spec/operations/status_operation.ex +++ b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -440,7 +440,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do    def show_history_operation do      %Operation{ -      tags: ["Retrieve status history"], +      tags: ["Retrieve status information"],        summary: "Status history",        description: "View history of a status",        operationId: "StatusController.show_history", @@ -457,7 +457,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do    def show_source_operation do      %Operation{ -      tags: ["Retrieve status source"], +      tags: ["Retrieve status information"],        summary: "Status source",        description: "View source of a status",        operationId: "StatusController.show_source", @@ -474,7 +474,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do    def update_operation do      %Operation{ -      tags: ["Update status"], +      tags: ["Status actions"],        summary: "Update status",        description: "Change the content of a status",        operationId: "StatusController.update", diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex index 29df03e34..084329ad7 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -17,7 +17,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do    def emoji_operation do      %Operation{ -      tags: ["Emojis"], +      tags: ["Custom emojis"],        summary: "List all custom emojis",        operationId: "UtilController.emoji",        parameters: [], @@ -30,7 +30,8 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do                properties: %{                  image_url: %Schema{type: :string},                  tags: %Schema{type: :array, items: %Schema{type: :string}} -              } +              }, +              extensions: %{"x-additionalPropertiesName": "Emoji name"}              },              example: %{                "firefox" => %{ @@ -45,7 +46,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do    def frontend_configurations_operation do      %Operation{ -      tags: ["Configuration"], +      tags: ["Others"],        summary: "Dump frontend configurations",        operationId: "UtilController.frontend_configurations",        parameters: [], @@ -53,7 +54,12 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do          200 =>            Operation.response("List", "application/json", %Schema{              type: :object, -            additionalProperties: %Schema{type: :object} +            additionalProperties: %Schema{ +              type: :object, +              description: +                "Opaque object representing the instance-wide configuration for the frontend", +              extensions: %{"x-additionalPropertiesName": "Frontend name"} +            }            })        }      } @@ -132,7 +138,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do    def update_notificaton_settings_operation do      %Operation{ -      tags: ["Accounts"], +      tags: ["Settings"],        summary: "Update Notification Settings",        security: [%{"oAuth" => ["write:accounts"]}],        operationId: "UtilController.update_notificaton_settings", @@ -207,6 +213,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do      %Operation{        summary: "Get a captcha",        operationId: "UtilController.captcha", +      tags: ["Others"],        parameters: [],        responses: %{          200 => Operation.response("Success", "application/json", %Schema{type: :object}) @@ -356,7 +363,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do    def healthcheck_operation do      %Operation{ -      tags: ["Accounts"], +      tags: ["Others"],        summary: "Quick status check on the instance",        security: [%{"oAuth" => ["write:accounts"]}],        operationId: "UtilController.healthcheck", @@ -371,7 +378,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do    def remote_subscribe_operation do      %Operation{ -      tags: ["Accounts"], +      tags: ["Remote interaction"],        summary: "Remote Subscribe",        operationId: "UtilController.remote_subscribe",        parameters: [], @@ -381,7 +388,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do    def remote_interaction_operation do      %Operation{ -      tags: ["Accounts"], +      tags: ["Remote interaction"],        summary: "Remote interaction",        operationId: "UtilController.remote_interaction",        requestBody: request_body("Parameters", remote_interaction_request(), required: true), @@ -407,7 +414,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do    def show_subscribe_form_operation do      %Operation{ -      tags: ["Accounts"], +      tags: ["Remote interaction"],        summary: "Show remote subscribe form",        operationId: "UtilController.show_subscribe_form",        parameters: [], diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 698f11794..bc29cf4a6 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -144,7 +144,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do          properties: %{            content: %Schema{              type: :object, -            additionalProperties: %Schema{type: :string}, +            additionalProperties: %Schema{ +              type: :string, +              description: "Alternate representation in the MIME type specified", +              extensions: %{"x-additionalPropertiesName": "MIME type"} +            },              description:                "A map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`"            }, @@ -195,7 +199,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do            },            spoiler_text: %Schema{              type: :object, -            additionalProperties: %Schema{type: :string}, +            additionalProperties: %Schema{ +              type: :string, +              description: "Alternate representation in the MIME type specified", +              extensions: %{"x-additionalPropertiesName": "MIME type"} +            },              description:                "A map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`."            }, diff --git a/lib/pleroma/web/api_spec/scopes/compiler.ex b/lib/pleroma/web/api_spec/scopes/compiler.ex new file mode 100644 index 000000000..162edc9a3 --- /dev/null +++ b/lib/pleroma/web/api_spec/scopes/compiler.ex @@ -0,0 +1,82 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Scopes.Compiler do +  defmacro __before_compile__(_env) do +    strings = __MODULE__.extract_all_scopes() + +    quote do +      def placeholder do +        unquote do +          Enum.map( +            strings, +            fn string -> +              quote do +                Pleroma.Web.Gettext.dgettext_noop( +                  "oauth_scopes", +                  unquote(string) +                ) +              end +            end +          ) +        end +      end +    end +  end + +  def extract_all_scopes do +    extract_all_scopes_from(Pleroma.Web.ApiSpec.spec()) +  end + +  def extract_all_scopes_from(specs) do +    specs.paths +    |> Enum.reduce([], fn +      {_path, %{} = path_item}, acc -> +        extract_routes(path_item) +        |> Enum.flat_map(fn operation -> process_operation(operation) end) +        |> Kernel.++(acc) + +      {_, _}, acc -> +        acc +    end) +    |> Enum.uniq() +  end + +  defp extract_routes(path_item) do +    path_item +    |> Map.from_struct() +    |> Enum.map(fn {_method, path_item} -> path_item end) +    |> Enum.filter(fn +      %OpenApiSpex.Operation{} = _operation -> true +      _ -> false +    end) +  end + +  defp process_operation(operation) do +    operation.security +    |> Kernel.||([]) +    |> Enum.flat_map(fn +      %{"oAuth" => scopes} -> process_scopes(scopes) +      _ -> [] +    end) +  end + +  defp process_scopes(scopes) do +    scopes +    |> Enum.flat_map(fn scope -> +      process_scope(scope) +    end) +  end + +  def process_scope(scope) do +    hierarchy = String.split(scope, ":") + +    {_, list} = +      Enum.reduce(hierarchy, {"", []}, fn comp, {cur, list} -> +        {cur <> comp <> ":", [cur <> comp | list]} +      end) + +    list +  end +end diff --git a/lib/pleroma/web/api_spec/scopes/translator.ex b/lib/pleroma/web/api_spec/scopes/translator.ex new file mode 100644 index 000000000..54eea3593 --- /dev/null +++ b/lib/pleroma/web/api_spec/scopes/translator.ex @@ -0,0 +1,10 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Scopes.Translator do +  require Pleroma.Web.ApiSpec.Scopes.Compiler +  require Pleroma.Web.Gettext + +  @before_compile Pleroma.Web.ApiSpec.Scopes.Compiler +end diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index ff0814329..a93c97e1e 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -145,6 +145,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do        when is_list(options) do      limits = Config.get([:instance, :poll_limits]) +    options = options |> Enum.uniq() +      with :ok <- validate_poll_expiration(expires_in, limits),           :ok <- validate_poll_options_amount(options, limits),           :ok <- validate_poll_options_length(options, limits) do @@ -180,10 +182,15 @@ defmodule Pleroma.Web.CommonAPI.Utils do    end    defp validate_poll_options_amount(options, %{max_options: max_options}) do -    if Enum.count(options) > max_options do -      {:error, "Poll can't contain more than #{max_options} options"} -    else -      :ok +    cond do +      Enum.count(options) < 2 -> +        {:error, "Poll must contain at least 2 options"} + +      Enum.count(options) > max_options -> +        {:error, "Poll can't contain more than #{max_options} options"} + +      true -> +        :ok      end    end diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index ea6e593d9..c313a0e97 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -269,14 +269,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do    end    defp normalize_fields_attributes(fields) do -    if Enum.all?(fields, &is_tuple/1) do -      Enum.map(fields, fn {_, v} -> v end) -    else -      Enum.map(fields, fn -        %{} = field -> %{"name" => field.name, "value" => field.value} -        field -> field -      end) -    end +    if(Enum.all?(fields, &is_tuple/1), do: Enum.map(fields, fn {_, v} -> v end), else: fields) +    |> Enum.map(fn +      %{} = field -> %{"name" => field.name, "value" => field.value} +      field -> field +    end)    end    @doc "GET /api/v1/accounts/relationships" @@ -543,7 +540,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do      conn      |> add_link_headers(users) -    |> render("index.json", users: users, for: user, as: :user) +    |> render("index.json", +      users: users, +      for: user, +      as: :user, +      embed_relationships: embed_relationships?(params) +    )    end    @doc "GET /api/v1/accounts/lookup" diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index abf7f29ab..efd2a0af6 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -92,6 +92,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do          "safe_dm_mentions"        end,        "pleroma_emoji_reactions", +      "pleroma_custom_emoji_reactions",        "pleroma_chat_messages",        if Config.get([:instance, :show_reactions]) do          "exposable_reactions" diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex index b5b5b2376..2a51f3755 100644 --- a/lib/pleroma/web/mastodon_api/views/notification_view.ex +++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex @@ -17,6 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do    alias Pleroma.Web.MastodonAPI.AccountView    alias Pleroma.Web.MastodonAPI.NotificationView    alias Pleroma.Web.MastodonAPI.StatusView +  alias Pleroma.Web.MediaProxy    alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView    defp object_id_for(%{data: %{"object" => %{"id" => id}}}) when is_binary(id), do: id @@ -145,7 +146,9 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do    end    defp put_emoji(response, activity) do -    Map.put(response, :emoji, activity.data["content"]) +    response +    |> Map.put(:emoji, activity.data["content"]) +    |> Map.put(:emoji_url, MediaProxy.url(Pleroma.Emoji.emoji_url(activity.data)))    end    defp put_chat_message(response, activity, reading_user, opts) do diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 0a8c98b44..dea22f9c2 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -334,14 +334,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do        end      emoji_reactions = -      object.data -      |> Map.get("reactions", []) +      object +      |> Object.get_emoji_reactions()        |> EmojiReactionController.filter_allowed_users(          opts[:for],          Map.get(opts, :with_muted, false)        ) -      |> Stream.map(fn {emoji, users} -> -        build_emoji_map(emoji, users, opts[:for]) +      |> Stream.map(fn {emoji, users, url} -> +        build_emoji_map(emoji, users, url, opts[:for])        end)        |> Enum.to_list() @@ -702,11 +702,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do      end    end -  defp build_emoji_map(emoji, users, current_user) do +  defp build_emoji_map(emoji, users, url, current_user) do      %{ -      name: emoji, +      name: Pleroma.Web.PleromaAPI.EmojiReactionView.emoji_name(emoji, url),        count: length(users), -      me: !!(current_user && current_user.ap_id in users) +      url: MediaProxy.url(url), +      me: !!(current_user && current_user.ap_id in users), +      account_ids: Enum.map(users, fn user -> User.get_cached_by_ap_id(user).id end)      }    end diff --git a/lib/pleroma/web/metadata/providers/rel_me.ex b/lib/pleroma/web/metadata/providers/rel_me.ex index f0bee85c8..eabd8cb00 100644 --- a/lib/pleroma/web/metadata/providers/rel_me.ex +++ b/lib/pleroma/web/metadata/providers/rel_me.ex @@ -8,12 +8,20 @@ defmodule Pleroma.Web.Metadata.Providers.RelMe do    @impl Provider    def build_tags(%{user: user}) do -    bio_tree = Floki.parse_fragment!(user.bio) +    profile_tree = +      user.bio +      |> append_fields_tag(user.fields) +      |> Floki.parse_fragment!() -    (Floki.attribute(bio_tree, "link[rel~=me]", "href") ++ -       Floki.attribute(bio_tree, "a[rel~=me]", "href")) +    (Floki.attribute(profile_tree, "link[rel~=me]", "href") ++ +       Floki.attribute(profile_tree, "a[rel~=me]", "href"))      |> Enum.map(fn link ->        {:link, [rel: "me", href: link], []}      end)    end + +  defp append_fields_tag(bio, fields) do +    fields +    |> Enum.reduce(bio, fn %{"value" => v}, res -> res <> v end) +  end  end diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex index 78fd0b219..662cc15d6 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex @@ -28,8 +28,8 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do    def index(%{assigns: %{user: user}} = conn, %{id: activity_id} = params) do      with true <- Pleroma.Config.get([:instance, :show_reactions]),           %Activity{} = activity <- Activity.get_by_id_with_object(activity_id), -         %Object{data: %{"reactions" => reactions}} when is_list(reactions) <- -           Object.normalize(activity, fetch: false) do +         %Object{} = object <- Object.normalize(activity, fetch: false), +         reactions <- Object.get_emoji_reactions(object) do        reactions =          reactions          |> filter(params) @@ -50,29 +50,32 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do            if not with_muted, do: User.cached_muted_users_ap_ids(user), else: []        end -    filter_emoji = fn emoji, users -> +    filter_emoji = fn emoji, users, url ->        case Enum.reject(users, &(&1 in exclude_ap_ids)) do          [] -> nil -        users -> {emoji, users} +        users -> {emoji, users, url}        end      end      reactions      |> Stream.map(fn -      [emoji, users] when is_list(users) -> filter_emoji.(emoji, users) -      {emoji, users} when is_list(users) -> filter_emoji.(emoji, users) -      _ -> nil +      [emoji, users, url] when is_list(users) -> filter_emoji.(emoji, users, url)      end)      |> Stream.reject(&is_nil/1)    end    defp filter(reactions, %{emoji: emoji}) when is_binary(emoji) do -    Enum.filter(reactions, fn [e, _] -> e == emoji end) +    Enum.filter(reactions, fn [e, _, _] -> e == emoji end)    end    defp filter(reactions, _), do: reactions    def create(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) do +    emoji = +      emoji +      |> Pleroma.Emoji.fully_qualify_emoji() +      |> Pleroma.Emoji.maybe_quote() +      with {:ok, _activity} <- CommonAPI.react_with_emoji(activity_id, user, emoji) do        activity = Activity.get_by_id(activity_id) @@ -83,6 +86,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do    end    def delete(%{assigns: %{user: user}} = conn, %{id: activity_id, emoji: emoji}) do +    emoji = +      emoji +      |> Pleroma.Emoji.fully_qualify_emoji() +      |> Pleroma.Emoji.maybe_quote() +      with {:ok, _activity} <- CommonAPI.unreact_with_emoji(activity_id, user, emoji) do        activity = Activity.get_by_id(activity_id) diff --git a/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex b/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex index 68ebd8292..6df4ab9d0 100644 --- a/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex +++ b/lib/pleroma/web/pleroma_api/views/emoji_reaction_view.ex @@ -7,17 +7,30 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionView do    alias Pleroma.Web.MastodonAPI.AccountView +  def emoji_name(emoji, nil), do: emoji + +  def emoji_name(emoji, url) do +    url = URI.parse(url) + +    if url.host == Pleroma.Web.Endpoint.host() do +      emoji +    else +      "#{emoji}@#{url.host}" +    end +  end +    def render("index.json", %{emoji_reactions: emoji_reactions} = opts) do      render_many(emoji_reactions, __MODULE__, "show.json", opts)    end -  def render("show.json", %{emoji_reaction: {emoji, user_ap_ids}, user: user}) do +  def render("show.json", %{emoji_reaction: {emoji, user_ap_ids, url}, user: user}) do      users = fetch_users(user_ap_ids)      %{ -      name: emoji, +      name: emoji_name(emoji, url),        count: length(users),        accounts: render(AccountView, "index.json", users: users, for: user), +      url: Pleroma.Web.MediaProxy.url(url),        me: !!(user && user.ap_id in user_ap_ids)      }    end diff --git a/lib/pleroma/web/plugs/uploaded_media.ex b/lib/pleroma/web/plugs/uploaded_media.ex index ad8143234..8b3bc9acb 100644 --- a/lib/pleroma/web/plugs/uploaded_media.ex +++ b/lib/pleroma/web/plugs/uploaded_media.ex @@ -35,9 +35,9 @@ defmodule Pleroma.Web.Plugs.UploadedMedia do      conn =        case fetch_query_params(conn) do          %{query_params: %{"name" => name}} = conn -> -          name = String.replace(name, "\"", "\\\"") +          name = String.replace(name, ~s["], ~s[\\"]) -          put_resp_header(conn, "content-disposition", "filename=\"#{name}\"") +          put_resp_header(conn, "content-disposition", ~s[inline; filename="#{name}"])          conn ->            conn diff --git a/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex index 73115e92a..7585c4d3e 100644 --- a/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex +++ b/lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex @@ -8,7 +8,7 @@            <%= checkbox @form, :"scope_#{scope}", value: scope in @scopes && scope, checked_value: scope, unchecked_value: "", name: "authorization[scope][]" %>            <%= label @form, :"scope_#{scope}", String.capitalize(scope) %>            <%= if scope in @scopes && scope do %> -            <%= String.capitalize(scope) %> +            <code><%= scope %></code> <%= :"Elixir.Gettext".dgettext(Gettext, "oauth_scopes", scope) %>            <% end %>          </div>        <% else %> @@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do    def project do      [        app: :pleroma, -      version: version("2.5.2"), +      version: version("2.5.52"),        elixir: "~> 1.11",        elixirc_paths: elixirc_paths(Mix.env()),        compilers: [:phoenix, :gettext] ++ Mix.compilers(), @@ -78,8 +78,7 @@ defmodule Pleroma.Mixfile do          :comeonin,          :fast_sanitize,          :os_mon, -        :ssl, -        :esshd +        :ssl        ],        included_applications: [:ex_syslogger]      ] @@ -126,7 +125,8 @@ defmodule Pleroma.Mixfile do        {:telemetry_poller, "~> 1.0"},        {:tzdata, "~> 1.0.3"},        {:plug_cowboy, "~> 2.3"}, -      {:oban, "~> 2.13"}, +      # oban 2.14 requires Elixir 1.12+ +      {:oban, "~> 2.13.4"},        {:gettext,         git: "https://github.com/tusooa/gettext.git",         ref: "72fb2496b6c5280ed911bdc3756890e7f38a4808", @@ -148,7 +148,8 @@ defmodule Pleroma.Mixfile do        {:ex_aws, "~> 2.1.6"},        {:ex_aws_s3, "~> 2.0"},        {:sweet_xml, "~> 0.7.2"}, -      {:earmark, "~> 1.4.22"}, +      # earmark 1.4.23 requires Elixir 1.12+ +      {:earmark, "1.4.22"},        {:bbcode_pleroma, "~> 0.2.0"},        {:cors_plug, "~> 2.0"},        {:web_push_encryption, "~> 0.3.1"}, @@ -179,7 +180,6 @@ defmodule Pleroma.Mixfile do        {:joken, "~> 2.0"},        {:benchee, "~> 1.0"},        {:pot, "~> 1.0"}, -      {:esshd, "~> 0.1.0", runtime: Application.get_env(:esshd, :enabled, false)},        {:ex_const, "~> 0.2"},        {:plug_static_index_html, "~> 1.0.0"},        {:flake_id, "~> 0.1.0"}, @@ -193,7 +193,7 @@ defmodule Pleroma.Mixfile do        {:restarter, path: "./restarter"},        {:majic, "~> 1.0"},        {:eblurhash, "~> 1.2.2"}, -      {:open_api_spex, "~> 3.10"}, +      {:open_api_spex, "~> 3.16"},        {:ecto_psql_extras, "~> 0.6"},        # indirect dependency version override @@ -2,66 +2,65 @@    "accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm", "11b18c220bcc2eab63b5470c038ef10eb6783bcb1fcdb11aa4137defa5ac1bb8"},    "base62": {:hex, :base62, "1.2.2", "85c6627eb609317b70f555294045895ffaaeb1758666ab9ef9ca38865b11e629", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "d41336bda8eaa5be197f1e4592400513ee60518e5b9f4dcf38f4b4dae6f377bb"},    "bbcode_pleroma": {:hex, :bbcode_pleroma, "0.2.0", "d36f5bca6e2f62261c45be30fa9b92725c0655ad45c99025cb1c3e28e25803ef", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "19851074419a5fedb4ef49e1f01b30df504bb5dbb6d6adfc135238063bebd1c3"}, -  "bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.0", "6cb662d5c1b0a8858801cf20997bd006e7016aa8c52959c9ef80e0f34fb60b7a", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2c81d61d4f6ed0e5cf7bf27a9109b791ff216a1034b3d541327484f46dd43769"}, -  "benchee": {:hex, :benchee, "1.0.1", "66b211f9bfd84bd97e6d1beaddf8fc2312aaabe192f776e8931cb0c16f53a521", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}], "hexpm", "3ad58ae787e9c7c94dd7ceda3b587ec2c64604563e049b2a0e8baafae832addb"}, +  "bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.1", "5114d780459a04f2b4aeef52307de23de961b69e13a5cd98a911e39fda13f420", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "42182d5f46764def15bf9af83739e3bf4ad22661b1c34fc3e88558efced07279"}, +  "benchee": {:hex, :benchee, "1.1.0", "f3a43817209a92a1fade36ef36b86e1052627fd8934a8b937ac9ab3a76c43062", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}], "hexpm", "7da57d545003165a012b587077f6ba90b89210fd88074ce3c60ce239eb5e6d93"},    "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, -  "cachex": {:hex, :cachex, "3.3.0", "6f2ebb8f27491fe39121bd207c78badc499214d76c695658b19d6079beeca5c2", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "d90e5ee1dde14cef33f6b187af4335b88748b72b30c038969176cd4e6ccc31a1"}, +  "cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"},    "calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},    "captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]}, -  "castore": {:hex, :castore, "0.1.18", "deb5b9ab02400561b6f5708f3e7660fc35ca2d51bfc6a940d2f513f89c2975fc", [:mix], [], "hexpm", "61bbaf6452b782ef80b33cdb45701afbcf0a918a45ebe7e73f1130d661e66a06"}, +  "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"},    "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},    "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, -  "comeonin": {:hex, :comeonin, "5.3.2", "5c2f893d05c56ae3f5e24c1b983c2d5dfb88c6d979c9287a76a7feb1e1d8d646", [:mix], [], "hexpm", "d0993402844c49539aeadb3fe46a3c9bd190f1ecf86b6f9ebd71957534c95f04"}, +  "comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"},    "concurrent_limiter": {:hex, :concurrent_limiter, "0.1.1", "43ae1dc23edda1ab03dd66febc739c4ff710d047bb4d735754909f9a474ae01c", [:mix], [{:telemetry, "~> 0.3", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "53968ff238c0fbb4d7ed76ddb1af0be6f3b2f77909f6796e249e737c505a16eb"},    "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},    "cors_plug": {:hex, :cors_plug, "2.0.3", "316f806d10316e6d10f09473f19052d20ba0a0ce2a1d910ddf57d663dac402ae", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ee4ae1418e6ce117fc42c2ba3e6cbdca4e95ecd2fe59a05ec6884ca16d469aea"},    "covertool": {:hex, :covertool, "2.0.4", "54acff6cddd88d28dea663cd2e1fe20dd32fcf5f5d3aff7d59031ce44ce39efa", [:rebar3], [], "hexpm", "5c9568ba4308fda2082172737c80c31d991ea83961eb10791f06106a870d0cdc"},    "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},    "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, -  "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, -  "credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"}, +  "cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, +  "credo": {:hex, :credo, "1.7.0", "6119bee47272e85995598ee04f2ebbed3e947678dee048d10b5feca139435f75", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "6839fcf63d1f0d1c0f450abc8564a57c43d644077ab96f2934563e68b8a769d7"},    "crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},    "custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"}, -  "db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"}, +  "db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},    "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},    "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},    "earmark": {:hex, :earmark, "1.4.22", "ea3e45c6359446dc308be0a64ce82a03260d973de7d0625a762e6d352ff57958", [:mix], [{:earmark_parser, "~> 1.4.23", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "1caf5145665a42fd76d5317286b0c171861fb1c04f86ab103dde76868814fdfb"}, -  "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"}, +  "earmark_parser": {:hex, :earmark_parser, "1.4.31", "a93921cdc6b9b869f519213d5bc79d9e218ba768d7270d46fdcf1c01bacff9e2", [:mix], [], "hexpm", "317d367ee0335ef037a87e46c91a2269fef6306413f731e8ec11fc45a7efd059"},    "eblurhash": {:hex, :eblurhash, "1.2.2", "7da4255aaea984b31bb71155f673257353b0e0554d0d30dcf859547e74602582", [:rebar3], [], "hexpm", "8c20ca00904de023a835a9dcb7b7762fed32264c85a80c3cafa85288e405044c"}, -  "ecto": {:hex, :ecto, "3.9.2", "017db3bc786ff64271108522c01a5d3f6ba0aea5c84912cfb0dd73bf13684108", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "21466d5177e09e55289ac7eade579a642578242c7a3a9f91ad5c6583337a9d15"}, +  "ecto": {:hex, :ecto, "3.9.5", "9f0aa7ae44a1577b651c98791c6988cd1b69b21bc724e3fd67090b97f7604263", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d4f3115d8cbacdc0bfa4b742865459fb1371d0715515842a1fb17fe31920b74c"},    "ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"}, -  "ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.4", "5d43fd088d39a158c860b17e8d210669587f63ec89ea122a4654861c8c6e2db4", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.15.7", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "311db02f1b772e3d0dc7f56a05044b5e1499d78ed6abf38885e1ca70059449e5"}, -  "ecto_sql": {:hex, :ecto_sql, "3.9.0", "2bb21210a2a13317e098a420a8c1cc58b0c3421ab8e3acfa96417dab7817918c", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a8f3f720073b8b1ac4c978be25fa7960ed7fd44997420c304a4a2e200b596453"}, +  "ecto_psql_extras": {:hex, :ecto_psql_extras, "0.7.10", "e14d400930f401ca9f541b3349212634e44027d7f919bbb71224d7ac0d0e8acd", [:mix], [{:ecto_sql, "~> 3.4", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.15.7 or ~> 0.16.0", [hex: :postgrex, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "505e8cd81e4f17c090be0f99e92b1b3f0fd915f98e76965130b8ccfb891e7088"}, +  "ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"},    "eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"}, -  "elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"}, +  "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},    "esbuild": {:hex, :esbuild, "0.5.0", "d5bb08ff049d7880ee3609ed5c4b864bd2f46445ea40b16b4acead724fb4c4a3", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "f183a0b332d963c4cfaf585477695ea59eef9a6f2204fdd0efa00e099694ffe5"}, -  "esshd": {:hex, :esshd, "0.1.1", "d4dd4c46698093a40a56afecce8a46e246eb35463c457c246dacba2e056f31b5", [:mix], [], "hexpm", "d73e341e3009d390aa36387dc8862860bf9f874c94d9fd92ade2926376f49981"},    "eternal": {:hex, :eternal, "1.2.2", "d1641c86368de99375b98d183042dd6c2b234262b8d08dfd72b9eeaafc2a1abd", [:mix], [], "hexpm", "2c9fe32b9c3726703ba5e1d43a1d255a4f3f2d8f8f9bc19f094c7cb1a7a9e782"},    "ex_aws": {:hex, :ex_aws, "2.1.9", "dc4865ecc20a05190a34a0ac5213e3e5e2b0a75a0c2835e923ae7bfeac5e3c31", [:mix], [{:configparser_ex, "~> 4.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 3.0", [hex: :jsx, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "3e6c776703c9076001fbe1f7c049535f042cb2afa0d2cbd3b47cbc4e92ac0d10"}, -  "ex_aws_s3": {:hex, :ex_aws_s3, "2.2.0", "07a09de557070320e264893c0acc8a1d2e7ddf80155736e0aed966486d1988e6", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "15175c613371e29e1f88b78ec8a4327389ca1ec5b34489744b175727496b21bd"}, +  "ex_aws_s3": {:hex, :ex_aws_s3, "2.4.0", "ce8decb6b523381812798396bc0e3aaa62282e1b40520125d1f4eff4abdff0f4", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "85dda6e27754d94582869d39cba3241d9ea60b6aa4167f9c88e309dc687e56bb"},    "ex_const": {:hex, :ex_const, "0.2.4", "d06e540c9d834865b012a17407761455efa71d0ce91e5831e86881b9c9d82448", [:mix], [], "hexpm", "96fd346610cc992b8f896ed26a98be82ac4efb065a0578f334a32d60a3ba9767"}, -  "ex_doc": {:hex, :ex_doc, "0.24.2", "e4c26603830c1a2286dae45f4412a4d1980e1e89dc779fcd0181ed1d5a05c8d9", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "e134e1d9e821b8d9e4244687fb2ace58d479b67b282de5158333b0d57c6fb7da"}, +  "ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"},    "ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},    "ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"},    "fast_html": {:hex, :fast_html, "2.0.5", "c61760340606c1077ff1f196f17834056cb1dd3d5cb92a9f2cabf28bc6221c3c", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "605f4f4829443c14127694ebabb681778712ceecb4470ec32aa31012330e6506"}, -  "fast_sanitize": {:hex, :fast_sanitize, "0.2.2", "3cbbaebaea6043865dfb5b4ecb0f1af066ad410a51470e353714b10c42007b81", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "69f204db9250afa94a0d559d9110139850f57de2b081719fbafa1e9a89e94466"}, +  "fast_sanitize": {:hex, :fast_sanitize, "0.2.3", "67b93dfb34e302bef49fec3aaab74951e0f0602fd9fa99085987af05bd91c7a5", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e8ad286d10d0386e15d67d0ee125245ebcfbc7d7290b08712ba9013c8c5e56e2"},    "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},    "finch": {:hex, :finch, "0.10.2", "9ad27d68270d879f73f26604bb2e573d40f29bf0e907064a9a337f90a16a0312", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dd8b11b282072cec2ef30852283949c248bd5d2820c88d8acc89402b81db7550"},    "flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"}, -  "floki": {:hex, :floki, "0.30.1", "75d35526d3a1459920b6e87fdbc2e0b8a3670f965dd0903708d2b267e0904c55", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "e9c03524447d1c4cbfccd672d739b8c18453eee377846b119d4fd71b1a176bb8"}, +  "floki": {:hex, :floki, "0.34.2", "5fad07ef153b3b8ec110b6b155ec3780c4b2c4906297d0b4be1a7162d04a7e02", [:mix], [], "hexpm", "26b9d50f0f01796bc6be611ca815c5e0de034d2128e39cc9702eee6b66a4d1c8"},    "gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},    "gettext": {:git, "https://github.com/tusooa/gettext.git", "72fb2496b6c5280ed911bdc3756890e7f38a4808", [ref: "72fb2496b6c5280ed911bdc3756890e7f38a4808"]}, -  "gun": {:hex, :gun, "2.0.0-rc.2", "7c489a32dedccb77b6e82d1f3c5a7dadfbfa004ec14e322cdb5e579c438632d2", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "6b9d1eae146410d727140dbf8b404b9631302ecc2066d1d12f22097ad7d254fc"}, +  "gun": {:hex, :gun, "2.0.0", "2326bc0fd6d9cf628419708270d6fe8b02b8d002cf992e4165a77d997b1defd0", [:make, :rebar3], [{:cowlib, "2.12.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "6613cb7c62930dc8d58263c44dda72f8556346ba88358fc929dcbc5f76d04569"},    "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},    "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},    "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},    "http_signatures": {:hex, :http_signatures, "0.1.1", "ca7ebc1b61542b163644c8c3b1f0e0f41037d35f2395940d3c6c7deceab41fd8", [:mix], [], "hexpm", "cc3b8a007322cc7b624c0c15eec49ee58ac977254ff529a3c482f681465942a3"}, -  "httpoison": {:hex, :httpoison, "1.8.0", "6b85dea15820b7804ef607ff78406ab449dd78bed923a49c7160e1886e987a3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "28089eaa98cf90c66265b6b5ad87c59a3729bea2e74e9d08f9b51eb9729b3c3a"}, +  "httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},    "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},    "inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"},    "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, -  "joken": {:hex, :joken, "2.3.0", "62a979c46f2c81dcb8ddc9150453b60d3757d1ac393c72bb20fc50a7b0827dc6", [:mix], [{:jose, "~> 1.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "57b263a79c0ec5d536ac02d569c01e6b4de91bd1cb825625fe90eab4feb7bc1e"}, -  "jose": {:hex, :jose, "1.11.1", "59da64010c69aad6cde2f5b9248b896b84472e99bd18f246085b7b9fe435dcdb", [:mix, :rebar3], [], "hexpm", "078f6c9fb3cd2f4cfafc972c814261a7d1e8d2b3685c0a76eb87e158efff1ac5"}, +  "joken": {:hex, :joken, "2.6.0", "b9dd9b6d52e3e6fcb6c65e151ad38bf4bc286382b5b6f97079c47ade6b1bcc6a", [:mix], [{:jose, "~> 1.11.5", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5a95b05a71cd0b54abd35378aeb1d487a23a52c324fa7efdffc512b655b5aaa7"}, +  "jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"},    "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},    "linkify": {:hex, :linkify, "0.5.3", "5f8143d8f61f5ff08d3aeeff47ef6509492b4948d8f08007fbf66e4d2246a7f2", [:mix], [], "hexpm", "3ef35a1377d47c25506e07c1c005ea9d38d700699d92ee92825f024434258177"},    "majic": {:hex, :majic, "1.0.0", "37e50648db5f5c2ff0c9fb46454d034d11596c03683807b9fb3850676ffdaab3", [:make, :mix], [{:elixir_make, "~> 0.6.1", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "7905858f76650d49695f14ea55cd9aaaee0c6654fa391671d4cf305c275a0a9e"}, @@ -72,51 +71,53 @@    "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},    "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},    "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, -  "mint": {:hex, :mint, "1.4.2", "50330223429a6e1260b2ca5415f69b0ab086141bc76dc2fbf34d7c389a6675b2", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "ce75a5bbcc59b4d7d8d70f8b2fc284b1751ffb35c7b6a6302b5192f8ab4ddd80"}, +  "mint": {:hex, :mint, "1.5.1", "8db5239e56738552d85af398798c80648db0e90f343c8469f6c6d8898944fb6f", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "4a63e1e76a7c3956abd2c72f370a0d0aecddc3976dea5c27eccbecfa5e7d5b1e"},    "mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},    "mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"}, -  "mogrify": {:hex, :mogrify, "0.9.1", "a26f107c4987477769f272bd0f7e3ac4b7b75b11ba597fd001b877beffa9c068", [:mix], [], "hexpm", "134edf189337d2125c0948bf0c228fdeef975c594317452d536224069a5b7f05"}, -  "mox": {:hex, :mox, "1.0.0", "4b3c7005173f47ff30641ba044eb0fe67287743eec9bd9545e37f3002b0a9f8b", [:mix], [], "hexpm", "201b0a20b7abdaaab083e9cf97884950f8a30a1350a1da403b3145e213c6f4df"}, +  "mogrify": {:hex, :mogrify, "0.9.2", "b360984adea7dd6a55f18028e6327973c58de7f548fdb86c9859848aa904d5b0", [:mix], [], "hexpm", "c18d10fd70ca20e2585301616c89f6e4f7159d92efc9cc8ee579e00c886f699d"}, +  "mox": {:hex, :mox, "1.0.2", "dc2057289ac478b35760ba74165b4b3f402f68803dd5aecd3bfd19c183815d64", [:mix], [], "hexpm", "f9864921b3aaf763c8741b5b8e6f908f44566f1e427b2630e89e9a73b981fef2"},    "nimble_options": {:hex, :nimble_options, "0.4.0", "c89babbab52221a24b8d1ff9e7d838be70f0d871be823165c94dd3418eea728f", [:mix], [], "hexpm", "e6701c1af326a11eea9634a3b1c62b475339ace9456c1a23ec3bc9a847bca02d"}, -  "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm", "5c040b8469c1ff1b10093d3186e2e10dbe483cd73d79ec017993fb3985b8a9b3"}, +  "nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"},    "nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},    "nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]}, -  "oban": {:hex, :oban, "2.13.4", "b4c4f48f4c89cc01036670eefa28aa9c03d09aadd402655475b936983d597006", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a7d26f82b409e2d7928fbb75a17716e06ad3f783ebe9af260e3dd23abed7f124"}, -  "open_api_spex": {:hex, :open_api_spex, "3.10.0", "94e9521ad525b3fcf6dc77da7c45f87fdac24756d4de588cb0816b413e7c1844", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "2dbb2bde3d2b821f06936e8dfaf3284331186556291946d84eeba3750ac28765"}, +  "oban": {:hex, :oban, "2.13.6", "a0cb1bce3bd393770512231fb5a3695fa19fd3af10d7575bf73f837aee7abf43", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c1c5eb16f377b3cbbf2ea14be24d20e3d91285af9d1ac86260b7c2af5464887"}, +  "open_api_spex": {:hex, :open_api_spex, "3.16.1", "8137c338129d63060b4b04947c6c57429f86267045c479c703a38a6d3f98dee1", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "ef6fd778ac121af866b48b75ad4ad256b6ff33949113ea4aa1629af8bfdfdbfb"},    "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},    "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"}, -  "phoenix": {:hex, :phoenix, "1.6.15", "0a1d96bbc10747fd83525370d691953cdb6f3ccbac61aa01b4acb012474b047d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d70ab9fbf6b394755ea88b644d34d79d8b146e490973151f248cacd122d20672"}, +  "phoenix": {:hex, :phoenix, "1.6.16", "e5bdd18c7a06da5852a25c7befb72246de4ddc289182285f8685a40b7b5f5451", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e15989ff34f670a96b95ef6d1d25bad0d9c50df5df40b671d8f4a669e050ac39"},    "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"}, -  "phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"}, -  "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.6.2", "0769470265eb13af01b5001b29cb935f4710d6adaa1ffc18417a570a337a2f0f", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.3", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.17.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5bc6c6b38a2ca8b5020b442322fcee6afd5e641637a0b1fb059d4bd89bc58e7b"}, +  "phoenix_html": {:hex, :phoenix_html, "3.3.1", "4788757e804a30baac6b3fc9695bf5562465dd3f1da8eb8460ad5b404d9a2178", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bed1906edd4906a15fd7b412b85b05e521e1f67c9a85418c55999277e553d0d3"}, +  "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.6.5", "1495bb014be12c9a9252eca04b9af54246f6b5c1e4cd1f30210cd00ec540cf8e", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.3", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.17.7", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "ef4fa50dd78364409039c99cf6f98ab5209b4c5f8796c17f4db118324f0db852"},    "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"}, -  "phoenix_live_view": {:hex, :phoenix_live_view, "0.17.5", "63f52a6f9f6983f04e424586ff897c016ecc5e4f8d1e2c22c2887af1c57215d8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.9 or ~> 1.6.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c5586e6a3d4df71b8214c769d4f5eb8ece2b4001711a7ca0f97323c36958b0e3"}, +  "phoenix_live_view": {:hex, :phoenix_live_view, "0.17.14", "5ec615d4d61bf9d4755f158bd6c80372b715533fe6d6219e12d74fb5eedbeac1", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.0 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "afeb6ba43ce329a6f7fc1c9acdfc6d3039995345f025febb7f409a92f6faebd3"},    "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"}, -  "phoenix_swoosh": {:hex, :phoenix_swoosh, "1.1.0", "f8e4780705c9f254cc853f7a40e25f7198ba4d91102bcfad2226669b69766b35", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "aa82f10afd9a4b6080fdf3274dbb9432b25b210d42b4b6b55308f6e59cd87c3d"}, -  "phoenix_template": {:hex, :phoenix_template, "1.0.0", "c57bc5044f25f007dc86ab21895688c098a9f846a8dda6bc40e2d0ddc146e38f", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "1b066f99a26fd22064c12b2600a9a6e56700f591bf7b20b418054ea38b4d4357"}, -  "phoenix_view": {:hex, :phoenix_view, "2.0.1", "a653e3d9d944aace0a064e4a13ad473ffa68f7bc4ca42dbf83cc1d464f1fb295", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "6c358e2cefc5f341c728914b867c556bbfd239fed9e881bac257d70cb2b8a6f6"}, +  "phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.0", "a544d83fde4a767efb78f45404a74c9e37b2a9c5ea3339692e65a6966731f935", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "e88d117251e89a16b92222415a6d87b99a96747ddf674fc5c7631de734811dba"}, +  "phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"}, +  "phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"},    "plug": {:hex, :plug, "1.10.4", "41eba7d1a2d671faaf531fa867645bd5a3dce0957d8e2a3f398ccff7d2ef017f", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ad1e233fe73d2eec56616568d260777b67f53148a999dc2d048f4eb9778fe4a0"}, -  "plug_cowboy": {:hex, :plug_cowboy, "2.6.0", "d1cf12ff96a1ca4f52207c5271a6c351a4733f413803488d75b70ccf44aebec2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "073cf20b753ce6682ed72905cd62a2d4bd9bad1bf9f7feb02a1b8e525bd94fa6"}, -  "plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"}, +  "plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"}, +  "plug_crypto": {:hex, :plug_crypto, "1.2.5", "918772575e48e81e455818229bf719d4ab4181fcbf7f85b68a35620f78d89ced", [:mix], [], "hexpm", "26549a1d6345e2172eb1c233866756ae44a9609bd33ee6f99147ab3fd87fd842"},    "plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "79fd4fcf34d110605c26560cbae8f23c603ec4158c08298bd4360fdea90bb5cf"},    "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"},    "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm", "dad79704ce5440f3d5a3681c8590b9dc25d1a561e8f5a9c995281012860901e3"},    "postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"}, -  "pot": {:hex, :pot, "1.0.1", "81b511b1fa7c3123171c265cb7065a1528cebd7277b0cbc94257c50a8b2e4c17", [:rebar3], [], "hexpm", "ed87f5976531d91528452faa1138a5328db7f9f20d8feaae15f5051f79bcfb6d"}, +  "pot": {:hex, :pot, "1.0.2", "13abb849139fdc04ab8154986abbcb63bdee5de6ed2ba7e1713527e33df923dd", [:rebar3], [], "hexpm", "78fe127f5a4f5f919d6ea5a2a671827bd53eb9d37e5b4128c0ad3df99856c2e0"},    "prom_ex": {:hex, :prom_ex, "1.7.1", "39331ee3fe6f9a8587d8208bf9274a253bb80281700e127dd18786cda5e08c37", [:mix], [{:absinthe, ">= 1.6.0", [hex: :absinthe, repo: "hexpm", optional: true]}, {:broadway, ">= 1.0.2", [hex: :broadway, repo: "hexpm", optional: true]}, {:ecto, ">= 3.5.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:finch, "~> 0.10.2", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:oban, ">= 2.4.0", [hex: :oban, repo: "hexpm", optional: true]}, {:phoenix, ">= 1.5.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_live_view, ">= 0.14.0", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}, {:plug, ">= 1.12.1", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 2.5.1", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6.1", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}, {:telemetry_metrics_prometheus_core, "~> 1.0.2", [hex: :telemetry_metrics_prometheus_core, repo: "hexpm", optional: false]}, {:telemetry_poller, "~> 1.0.0", [hex: :telemetry_poller, repo: "hexpm", optional: false]}], "hexpm", "4c978872b88a929833925a0f4d0561824804c671fdd04581e765509ed0a6ed08"}, -  "prometheus": {:hex, :prometheus, "4.8.0", "1ce1e1002b173c336d61f186b56263346536e76814edd9a142e12aeb2d6c1ad2", [:mix, :rebar3], [], "hexpm", "0fc2e17103073edb3758a46a5d44b006191bf25b73cbaa2b779109de396afcb5"}, +  "prometheus": {:hex, :prometheus, "4.10.0", "792adbf0130ff61b5fa8826f013772af24b6e57b984445c8d602c8a0355704a1", [:mix, :rebar3], [{:quantile_estimator, "~> 0.2.1", [hex: :quantile_estimator, repo: "hexpm", optional: false]}], "hexpm", "2a99bb6dce85e238c7236fde6b0064f9834dc420ddbd962aac4ea2a3c3d59384"},    "prometheus_ecto": {:hex, :prometheus_ecto, "1.4.3", "3dd4da1812b8e0dbee81ea58bb3b62ed7588f2eae0c9e97e434c46807ff82311", [:mix], [{:ecto, "~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm", "8d66289f77f913b37eda81fd287340c17e61a447549deb28efc254532b2bed82"},    "prometheus_ex": {:git, "https://github.com/lanodan/prometheus.ex.git", "31f7fbe4b71b79ba27efc2a5085746c4011ceb8f", [branch: "fix/elixir-1.14"]},    "prometheus_phoenix": {:hex, :prometheus_phoenix, "1.3.0", "c4b527e0b3a9ef1af26bdcfbfad3998f37795b9185d475ca610fe4388fdd3bb5", [:mix], [{:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.3 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}], "hexpm", "c4d1404ac4e9d3d963da601db2a7d8ea31194f0017057fabf0cfb9bf5a6c8c75"},    "prometheus_phx": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/prometheus-phx.git", "9cd8f248c9381ffedc799905050abce194a97514", [branch: "no-logging"]},    "prometheus_plugs": {:hex, :prometheus_plugs, "1.1.5", "25933d48f8af3a5941dd7b621c889749894d8a1082a6ff7c67cc99dec26377c5", [:mix], [{:accept, "~> 0.1", [hex: :accept, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:prometheus_ex, "~> 1.1 or ~> 2.0 or ~> 3.0", [hex: :prometheus_ex, repo: "hexpm", optional: false]}, {:prometheus_process_collector, "~> 1.1", [hex: :prometheus_process_collector, repo: "hexpm", optional: true]}], "hexpm", "0273a6483ccb936d79ca19b0ab629aef0dba958697c94782bb728b920dfc6a79"}, +  "quantile_estimator": {:hex, :quantile_estimator, "0.2.1", "ef50a361f11b5f26b5f16d0696e46a9e4661756492c981f7b2229ef42ff1cd15", [:rebar3], [], "hexpm", "282a8a323ca2a845c9e6f787d166348f776c1d4a41ede63046d72d422e3da946"},    "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, -  "recon": {:hex, :recon, "2.5.1", "430ffa60685ac1efdfb1fe4c97b8767c92d0d92e6e7c3e8621559ba77598678a", [:mix, :rebar3], [], "hexpm", "5721c6b6d50122d8f68cccac712caa1231f97894bab779eff5ff0f886cb44648"}, +  "recon": {:hex, :recon, "2.5.3", "739107b9050ea683c30e96de050bc59248fd27ec147696f79a8797ff9fa17153", [:mix, :rebar3], [], "hexpm", "6c6683f46fd4a1dfd98404b9f78dcabc7fcd8826613a89dcb984727a8c3099d7"},    "remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]}, -  "sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"}, +  "sleeplocks": {:hex, :sleeplocks, "1.1.2", "d45aa1c5513da48c888715e3381211c859af34bee9b8290490e10c90bb6ff0ca", [:rebar3], [], "hexpm", "9fe5d048c5b781d6305c1a3a0f40bb3dfc06f49bf40571f3d2d0c57eaa7f59a5"},    "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, -  "sweet_xml": {:hex, :sweet_xml, "0.7.2", "4729f997286811fabdd8288f8474e0840a76573051062f066c4b597e76f14f9f", [:mix], [], "hexpm", "6894e68a120f454534d99045ea3325f7740ea71260bc315f82e29731d570a6e8"}, -  "swoosh": {:hex, :swoosh, "1.8.2", "af9a22ab2c0d20b266f61acca737fa11a121902de9466a39e91bacdce012101c", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d058ba750eafadb6c09a84a352c14c5d1eeeda6e84945fcc95785b7f3067b7db"}, +  "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, +  "sweet_xml": {:hex, :sweet_xml, "0.7.3", "debb256781c75ff6a8c5cbf7981146312b66f044a2898f453709a53e5031b45b", [:mix], [], "hexpm", "e110c867a1b3fe74bfc7dd9893aa851f0eed5518d0d7cad76d7baafd30e4f5ba"}, +  "swoosh": {:hex, :swoosh, "1.9.1", "0a5d7bf9954eb41d7e55525bc0940379982b090abbaef67cd8e1fd2ed7f8ca1a", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "76dffff3ffcab80f249d5937a592eaef7cc49ac6f4cdd27e622868326ed6371e"},    "syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},    "table_rex": {:hex, :table_rex, "3.1.1", "0c67164d1714b5e806d5067c1e96ff098ba7ae79413cc075973e17c38a587caa", [:mix], [], "hexpm", "678a23aba4d670419c23c17790f9dcd635a4a89022040df7d5d772cb21012490"},    "telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"}, @@ -124,10 +125,10 @@    "telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.0.2", "c98b1c580de637bfeac00db41b9fb91fb4c3548ee3d512a8ed7299172312eaf3", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "48351a0d56f80e38c997b44232b1043e0a081670d16766eee920e6254175b730"},    "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},    "tesla": {:hex, :tesla, "1.4.4", "bb89aa0c9745190930366f6a2ac612cdf2d0e4d7fff449861baa7875afd797b2", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.3", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "d5503a49f9dec1b287567ea8712d085947e247cb11b06bc54adb05bfde466457"}, -  "timex": {:hex, :timex, "3.7.5", "3eca56e23bfa4e0848f0b0a29a92fa20af251a975116c6d504966e8a90516dfd", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "a15608dca680f2ef663d71c95842c67f0af08a0f3b1d00e17bbd22872e2874e4"}, +  "timex": {:hex, :timex, "3.7.7", "3ed093cae596a410759104d878ad7b38e78b7c2151c6190340835515d4a46b8a", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "0ec4b09f25fe311321f9fc04144a7e3affe48eb29481d7a5583849b6c4dfa0a7"},    "trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},    "tzdata": {:hex, :tzdata, "1.0.5", "69f1ee029a49afa04ad77801febaf69385f3d3e3d1e4b56b9469025677b89a28", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "55519aa2a99e5d2095c1e61cc74c9be69688f8ab75c27da724eb8279ff402a5a"}, -  "ueberauth": {:hex, :ueberauth, "0.6.3", "d42ace28b870e8072cf30e32e385579c57b9cc96ec74fa1f30f30da9c14f3cc0", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "afc293d8a1140d6591b53e3eaf415ca92842cb1d32fad3c450c6f045f7f91b60"}, +  "ueberauth": {:hex, :ueberauth, "0.10.5", "806adb703df87e55b5615cf365e809f84c20c68aa8c08ff8a416a5a6644c4b02", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3efd1f31d490a125c7ed453b926f7c31d78b97b8a854c755f5c40064bf3ac9e1"},    "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},    "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"},    "web_push_encryption": {:hex, :web_push_encryption, "0.3.1", "76d0e7375142dfee67391e7690e89f92578889cbcf2879377900b5620ee4708d", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.1", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "4f82b2e57622fb9337559058e8797cb0df7e7c9790793bdc4e40bc895f70e2a2"}, diff --git a/priv/gettext/config_descriptions.pot b/priv/gettext/config_descriptions.pot index 1c3a98d3e..53b81fa41 100644 --- a/priv/gettext/config_descriptions.pot +++ b/priv/gettext/config_descriptions.pot @@ -1914,12 +1914,6 @@ msgstr ""  #, elixir-autogen, elixir-format  #: lib/pleroma/docs/translator.ex:5 -msgctxt "config description at :pleroma-:instance > :privileged_staff" -msgid "Let moderators access sensitive data (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)" -msgstr "" - -#, elixir-autogen, elixir-format -#: lib/pleroma/docs/translator.ex:5  msgctxt "config description at :pleroma-:instance > :profile_directory"  msgid "Enable profile directory."  msgstr "" @@ -4326,12 +4320,6 @@ msgstr ""  #, elixir-autogen, elixir-format  #: lib/pleroma/docs/translator.ex:5 -msgctxt "config label at :pleroma-:instance > :privileged_staff" -msgid "Privileged staff" -msgstr "" - -#, elixir-autogen, elixir-format -#: lib/pleroma/docs/translator.ex:5  msgctxt "config label at :pleroma-:instance > :profile_directory"  msgid "Profile directory"  msgstr "" @@ -6021,3 +6009,39 @@ msgstr ""  msgctxt "config label at :pleroma-:instance > :report_strip_status"  msgid "Report strip status"  msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/docs/translator.ex:5 +msgctxt "config description at :pleroma-:instance > :admin_privileges" +msgid "What extra privileges to allow admins (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/docs/translator.ex:5 +msgctxt "config description at :pleroma-:instance > :moderator_privileges" +msgid "What extra privileges to allow moderators (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/docs/translator.ex:5 +msgctxt "config label at :pleroma-:instance > :admin_privileges" +msgid "Admin privileges" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/docs/translator.ex:5 +msgctxt "config label at :pleroma-:instance > :moderator_privileges" +msgid "Moderator privileges" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/docs/translator.ex:5 +msgctxt "config description at :pleroma-:instance > :languages" +msgid "Languages to be exposed in /api/v1/instance. Should be in the format of BCP47 language codes." +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/docs/translator.ex:5 +msgctxt "config label at :pleroma-:instance > :languages" +msgid "Languages" +msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/oauth_scopes.po b/priv/gettext/en/LC_MESSAGES/oauth_scopes.po new file mode 100644 index 000000000..105ca022b --- /dev/null +++ b/priv/gettext/en/LC_MESSAGES/oauth_scopes.po @@ -0,0 +1,264 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Free Software Foundation, Inc. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"PO-Revision-Date: 2023-05-02 17:02-0400\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin" +msgstr "All admin access" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read" +msgstr "Read all using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write" +msgstr "Write all using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "follow" +msgstr "Read and write user relationships" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "push" +msgstr "Push notifications" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read" +msgstr "Read everything" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:accounts" +msgstr "Read information of all accounts" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:backups" +msgstr "Read your backups" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:blocks" +msgstr "Read block relationships" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:bookmarks" +msgstr "Read your bookmarks" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:chats" +msgstr "Read your chats" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:favourites" +msgstr "Read your favourites" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:filters" +msgstr "Read your filtering settings" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:follows" +msgstr "Read follow relationships" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:lists" +msgstr "Read your lists" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:notifications" +msgstr "Read your notifications" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:reports" +msgstr "Read your reports" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:search" +msgstr "Perform searches" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:statuses" +msgstr "Read all statuses you can see" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write" +msgstr "Write everything" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:accounts" +msgstr "Change your account information" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:blocks" +msgstr "Block or unblock someone" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:bookmarks" +msgstr "Add to or remove from your bookmarks" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:chats" +msgstr "Create or delete chats or chat messages, or mark them as read" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:conversations" +msgstr "Change recipients of, mark as read, or delete conversations" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:favourites" +msgstr "Favourite or unfavourite statuses" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:filters" +msgstr "Change your filtering settings" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:follow" +msgstr "Follow or unfollow someone" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:follows" +msgstr "Follow or unfollow someone" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:lists" +msgstr "Create, change or delete your lists" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:media" +msgstr "Upload media files or modify those you uploaded" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:mutes" +msgstr "Mute or unmute someone" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:notifications" +msgstr "Mark notifications as read" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:reports" +msgstr "Submit reports" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:statuses" +msgstr "Post, edit, reblog or react to statuses" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read:accounts" +msgstr "Read all accounts using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read:chats" +msgstr "Read all chats using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read:invites" +msgstr "Read all invites using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read:media_proxy_caches" +msgstr "Read media proxy caches using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read:reports" +msgstr "Read all reports using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read:statuses" +msgstr "Read all statuses using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:accounts" +msgstr "Change all accounts using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:chats" +msgstr "Change all chats using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:follows" +msgstr "Change follow relationships using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:invites" +msgstr "Invite or revoke an invite using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:media_proxy_caches" +msgstr "Change media proxy caches using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:reports" +msgstr "Handle reports using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:statuses" +msgstr "Delete, change scope of, or mark as sensitive statuses using admin API" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:media" +msgstr "Read media attachments" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:mutes" +msgstr "Read mute relationships" diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot index 19a0039ca..fa61d509e 100644 --- a/priv/gettext/errors.pot +++ b/priv/gettext/errors.pot @@ -111,7 +111,7 @@ msgid "Can't display this activity"  msgstr ""  #, elixir-autogen, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:337 +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:334  msgid "Can't find user"  msgstr "" @@ -236,7 +236,7 @@ msgid "Poll's author can't vote"  msgstr ""  #, elixir-autogen, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:502 +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:499  #: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20  #: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:39  #: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:51 @@ -558,12 +558,11 @@ msgid "Access denied"  msgstr ""  #, elixir-autogen, elixir-format -#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:334 +#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:331  msgid "This API requires an authenticated user"  msgstr ""  #, elixir-autogen, elixir-format -#: lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex:26  #: lib/pleroma/web/plugs/user_is_admin_plug.ex:21  msgid "User is not an admin."  msgstr "" @@ -586,7 +585,6 @@ msgid "Too many attachments"  msgstr ""  #, elixir-autogen, elixir-format -#: lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex:33  #: lib/pleroma/web/plugs/user_is_staff_plug.ex:20  msgid "User is not a staff member."  msgstr "" @@ -602,3 +600,10 @@ msgstr ""  #: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:264  msgid "File is too large"  msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/plugs/ensure_privileged_plug.ex:21 +#: lib/pleroma/web/plugs/ensure_privileged_plug.ex:34 +#: lib/pleroma/web/plugs/ensure_privileged_plug.ex:41 +msgid "User isn't privileged." +msgstr "" diff --git a/priv/gettext/oauth_scopes.pot b/priv/gettext/oauth_scopes.pot new file mode 100644 index 000000000..5f7b425f3 --- /dev/null +++ b/priv/gettext/oauth_scopes.pot @@ -0,0 +1,261 @@ +## This file is a PO Template file. +## +## "msgid"s here are often extracted from source code. +## Add new translations manually only if they're dynamic +## translations that can't be statically extracted. +## +## Run "mix gettext.extract" to bring this file up to +## date. Leave "msgstr"s empty as changing them here has no +## effect: edit them in PO (.po) files instead. +msgid "" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "follow" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "push" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:accounts" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:backups" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:blocks" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:bookmarks" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:chats" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:favourites" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:filters" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:follows" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:lists" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:notifications" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:reports" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:search" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:statuses" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:accounts" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:blocks" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:bookmarks" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:chats" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:conversations" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:favourites" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:filters" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:follow" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:follows" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:lists" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:media" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:mutes" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:notifications" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:reports" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "write:statuses" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read:accounts" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read:chats" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read:invites" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read:media_proxy_caches" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read:reports" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:read:statuses" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:accounts" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:chats" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:follows" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:invites" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:media_proxy_caches" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:reports" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "admin:write:statuses" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:media" +msgstr "" + +#, elixir-autogen, elixir-format +#: lib/pleroma/web/api_spec/scopes/translator.ex:5 +msgid "read:mutes" +msgstr "" diff --git a/priv/scrubbers/default.ex b/priv/scrubbers/default.ex index e10e3ec87..d1215d2e0 100644 --- a/priv/scrubbers/default.ex +++ b/priv/scrubbers/default.ex @@ -33,35 +33,35 @@ defmodule Pleroma.HTML.Scrubber.Default do      "ugc"    ]) -  Meta.allow_tag_with_these_attributes(:a, ["name", "title"]) - -  Meta.allow_tag_with_these_attributes(:abbr, ["title"]) - -  Meta.allow_tag_with_these_attributes(:b, []) -  Meta.allow_tag_with_these_attributes(:blockquote, []) -  Meta.allow_tag_with_these_attributes(:br, []) -  Meta.allow_tag_with_these_attributes(:code, []) -  Meta.allow_tag_with_these_attributes(:del, []) -  Meta.allow_tag_with_these_attributes(:em, []) -  Meta.allow_tag_with_these_attributes(:hr, []) -  Meta.allow_tag_with_these_attributes(:i, []) -  Meta.allow_tag_with_these_attributes(:li, []) -  Meta.allow_tag_with_these_attributes(:ol, []) -  Meta.allow_tag_with_these_attributes(:p, []) -  Meta.allow_tag_with_these_attributes(:pre, []) -  Meta.allow_tag_with_these_attributes(:strong, []) -  Meta.allow_tag_with_these_attributes(:sub, []) -  Meta.allow_tag_with_these_attributes(:sup, []) -  Meta.allow_tag_with_these_attributes(:ruby, []) -  Meta.allow_tag_with_these_attributes(:rb, []) -  Meta.allow_tag_with_these_attributes(:rp, []) -  Meta.allow_tag_with_these_attributes(:rt, []) -  Meta.allow_tag_with_these_attributes(:rtc, []) -  Meta.allow_tag_with_these_attributes(:u, []) -  Meta.allow_tag_with_these_attributes(:ul, []) +  Meta.allow_tag_with_these_attributes(:a, ["name", "title", "lang"]) + +  Meta.allow_tag_with_these_attributes(:abbr, ["title", "lang"]) + +  Meta.allow_tag_with_these_attributes(:b, ["lang"]) +  Meta.allow_tag_with_these_attributes(:blockquote, ["lang"]) +  Meta.allow_tag_with_these_attributes(:br, ["lang"]) +  Meta.allow_tag_with_these_attributes(:code, ["lang"]) +  Meta.allow_tag_with_these_attributes(:del, ["lang"]) +  Meta.allow_tag_with_these_attributes(:em, ["lang"]) +  Meta.allow_tag_with_these_attributes(:hr, ["lang"]) +  Meta.allow_tag_with_these_attributes(:i, ["lang"]) +  Meta.allow_tag_with_these_attributes(:li, ["lang"]) +  Meta.allow_tag_with_these_attributes(:ol, ["lang"]) +  Meta.allow_tag_with_these_attributes(:p, ["lang"]) +  Meta.allow_tag_with_these_attributes(:pre, ["lang"]) +  Meta.allow_tag_with_these_attributes(:strong, ["lang"]) +  Meta.allow_tag_with_these_attributes(:sub, ["lang"]) +  Meta.allow_tag_with_these_attributes(:sup, ["lang"]) +  Meta.allow_tag_with_these_attributes(:ruby, ["lang"]) +  Meta.allow_tag_with_these_attributes(:rb, ["lang"]) +  Meta.allow_tag_with_these_attributes(:rp, ["lang"]) +  Meta.allow_tag_with_these_attributes(:rt, ["lang"]) +  Meta.allow_tag_with_these_attributes(:rtc, ["lang"]) +  Meta.allow_tag_with_these_attributes(:u, ["lang"]) +  Meta.allow_tag_with_these_attributes(:ul, ["lang"])    Meta.allow_tag_with_this_attribute_values(:span, "class", ["h-card", "recipients-inline"]) -  Meta.allow_tag_with_these_attributes(:span, []) +  Meta.allow_tag_with_these_attributes(:span, ["lang"])    Meta.allow_tag_with_this_attribute_values(:code, "class", ["inline"]) @@ -77,29 +77,30 @@ defmodule Pleroma.HTML.Scrubber.Default do        "width",        "height",        "title", -      "alt" +      "alt", +      "lang"      ])    end    if Pleroma.Config.get([:markup, :allow_tables]) do -    Meta.allow_tag_with_these_attributes(:table, []) -    Meta.allow_tag_with_these_attributes(:tbody, []) -    Meta.allow_tag_with_these_attributes(:td, []) -    Meta.allow_tag_with_these_attributes(:th, []) -    Meta.allow_tag_with_these_attributes(:thead, []) -    Meta.allow_tag_with_these_attributes(:tr, []) +    Meta.allow_tag_with_these_attributes(:table, ["lang"]) +    Meta.allow_tag_with_these_attributes(:tbody, ["lang"]) +    Meta.allow_tag_with_these_attributes(:td, ["lang"]) +    Meta.allow_tag_with_these_attributes(:th, ["lang"]) +    Meta.allow_tag_with_these_attributes(:thead, ["lang"]) +    Meta.allow_tag_with_these_attributes(:tr, ["lang"])    end    if Pleroma.Config.get([:markup, :allow_headings]) do -    Meta.allow_tag_with_these_attributes(:h1, []) -    Meta.allow_tag_with_these_attributes(:h2, []) -    Meta.allow_tag_with_these_attributes(:h3, []) -    Meta.allow_tag_with_these_attributes(:h4, []) -    Meta.allow_tag_with_these_attributes(:h5, []) +    Meta.allow_tag_with_these_attributes(:h1, ["lang"]) +    Meta.allow_tag_with_these_attributes(:h2, ["lang"]) +    Meta.allow_tag_with_these_attributes(:h3, ["lang"]) +    Meta.allow_tag_with_these_attributes(:h4, ["lang"]) +    Meta.allow_tag_with_these_attributes(:h5, ["lang"])    end    if Pleroma.Config.get([:markup, :allow_fonts]) do -    Meta.allow_tag_with_these_attributes(:font, ["face"]) +    Meta.allow_tag_with_these_attributes(:font, ["face", "lang"])    end    Meta.strip_everything_not_covered() diff --git a/test/fixtures/custom-emoji-reaction.json b/test/fixtures/custom-emoji-reaction.json new file mode 100644 index 000000000..003de0511 --- /dev/null +++ b/test/fixtures/custom-emoji-reaction.json @@ -0,0 +1,28 @@ +{ +  "@context": [ +    "https://www.w3.org/ns/activitystreams", +    "https://w3id.org/security/v1", +    { +      "Hashtag": "as:Hashtag" +    } +  ], +  "type": "Like", +  "id": "https://misskey.local.live/likes/917ocsybgp", +  "actor": "https://misskey.local.live/users/8x8yep20u2", +  "object": "https://pleroma.local.live/objects/89937a53-2692-4631-bb62-770091267391", +  "content": ":hanapog:", +  "_misskey_reaction": ":hanapog:", +  "tag": [ +    { +      "id": "https://misskey.local.live/emojis/hanapog", +      "type": "Emoji", +      "name": ":hanapog:", +      "updated": "2022-06-07T12:00:05.773Z", +      "icon": { +        "type": "Image", +        "mediaType": "image/png", +        "url": "https://misskey.local.live/files/webpublic-8f8a9768-7264-4171-88d6-2356aabeadcd" +      } +    } +  ] +} diff --git a/test/fixtures/fep-e232.json b/test/fixtures/fep-e232.json new file mode 100644 index 000000000..e9d12ae35 --- /dev/null +++ b/test/fixtures/fep-e232.json @@ -0,0 +1,31 @@ +{ +  "@context": "https://www.w3.org/ns/activitystreams", +  "type": "Create", +  "actor": "https://example.org/users/alice", +  "object": { +    "id": "https://example.org/objects/10", +    "type": "Note", +    "attributedTo": "https://example.org/users/alice", +    "content": "<p>test <a href=\"https://example.org/objects/9\">https://example.org/objects/9</a></p>", +    "published": "2022-10-01T21:30:05.211215Z", +    "tag": [ +      { +        "name": "@bob@example.net", +        "type": "Mention", +        "href": "https://example.net/users/bob" +      }, +      { +        "name": "https://example.org/objects/9", +        "type": "Link", +        "href": "https://example.org/objects/9", +        "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" +      } +    ], +    "to": [ +      "https://www.w3.org/ns/activitystreams#Public" +    ], +    "cc": [ +      "https://example.org/users/alice/followers" +    ] +  } +} diff --git a/test/fixtures/hubzilla-actor.json b/test/fixtures/hubzilla-actor.json new file mode 100644 index 000000000..445d6413c --- /dev/null +++ b/test/fixtures/hubzilla-actor.json @@ -0,0 +1 @@ +{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1","https://hub.somaton.com/apschema/v1.9"],"type":"Person","id":"https://hub.somaton.com/channel/testc6","preferredUsername":"testc6","name":"testc6 lala","updated":"2021-08-29T10:07:23Z","icon":{"type":"Image","mediaType":"image/png","updated":"2021-10-09T04:54:35Z","url":"https://hub.somaton.com/photo/profile/l/33","height":300,"width":300},"url":"https://hub.somaton.com/channel/testc6","publicKey":{"id":"https://hub.somaton.com/channel/testc6","owner":"https://hub.somaton.com/channel/testc6","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq5ep+6MhhaAiqZSd8nXe\nUAokXNgqTr/DjUic5VDudjQgvetchaiBUieBnqpJSPNNAvvf6Qs4eDW4w2JQeA6y\nqEplKrmb8l1EyhwXeFLDUGQdf0f6hg++x5mIrO6uX0tlQGU6nutvhItn6JMZc5GU\nv3C/UW0OfHCCdHSGZ/1nIqq1P98FqF0+PA1pvTHCkLr4kcKzfpmkLjsccUSq0FGh\nQF+paW9FU89o4hkaH/X3E/Ac7DL8zgcyt29KSj4eUIvjBIEPAMdRno345fiZ+QYr\nlYQYaBC2gvozjxtxl9MyfqjBRzfl9VDHzoDvMn5+LD5dCRB1zOESv/b3EpiHYqXl\nwiPzP9az8e8cw6D72n/Mlrf27yIuVAdwaGdbAwekjIQZHIDoP0XNnA5i31RLpEMI\nbNpH47ChtjxeilQZ3va6qIShYfGlndpy/rx4i4Yt4xIG+BbGb/dWo3AbtHi64fPZ\nMoLuR71sEBe7uAvalJ+lopxuQ2qLJpCInukQ13p/G/n9tVDwbfGyumzr5hHk7JoY\nN+JqH737MCZqb9dRDof+fju58GY1VzFjBph38sHYJh0ykA+2BzYU2+nT7CDXfKWA\nsmHhizp7haoPjl/yclZG5FJwg3oqHTD14dASUs+OI4K+Q//74wfb4/6E3CDyOkW3\nUj+8TPZooKulxtQ9ezergr0CAwEAAQ==\n-----END PUBLIC KEY-----\n"},"outbox":"https://hub.somaton.com/outbox/testc6","inbox":"https://hub.somaton.com/inbox/testc6","followers":"https://hub.somaton.com/followers/testc6","following":"https://hub.somaton.com/following/testc6","endpoints":{"sharedInbox":"https://hub.somaton.com/inbox"},"discoverable":false,"signature":{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],"type":"RsaSignature2017","nonce":"8d6dea03f04cbb7faaf43958a4cf39a115ff1c61c7febaa6154c463eab9a42c8","creator":"https://hub.somaton.com/channel/testc6","created":"2021-10-13T18:21:48Z","signatureValue":"N4CJBO2K/8v7KI97REyJXaSYOlLWscuEDlODDnjNYD1fbVQFO3s2JtqPcN2lVJvNTlW5HUze+owaAYNcvZe3mNm1iz05Xru3s8yRA8bNCdKBuWd/3zb3/JQVkbSb09D2PloeuoKBQmPIn+dNiTyFR0jxLsxCXXTomGKigWPtTOUIt52Dv9MFJ3jRZmfoykT9bHrAIVCASHoiluhTkPAzc6pt0lSyZd0D3X4J1K4/sLXa8HRoooMFu2dHWfqV4tyLU9WzofAhvnYg9tEbKCH42DIAbwDfjAeC4qL8xkqAlYWLvXYVGH76cZLdp9Zuv1p3NHqaPEJ85MbuaUkfnU75Bx/Fcfoi0pEieWRdFvMx5b/UFwGbJd6iSAO1zRbGYTPEMPWHzh0AEAaLeyY+g3ZmpNu88ujrIr8iJ1U4EkjOBn8ooxA5LaI2fXDiYC2NwRiAbY+xVtgJgvHDi9tXCdvzjZWfU/cgiwF/cYMbsB2BCyPRd+XZhudfXSOysFC4WYnawhiRVevba9lQ6rEP4FMepOGq4ZOSGzxgw2xNIXpu0IkrxX5mEv/ahEhDy1KGRIFc0GnPJrv3kMVxJrZ7SF8PNAGqftQBLkqQR+SEygs3XB4cd2DQ2lPeiMd8+Xv+lBjtzZtZAM/Y4CZCOdV9DHXDGNSKKFDzzna4QcUzQ+KRc8w="}}
\ No newline at end of file diff --git a/test/fixtures/hubzilla-create-image.json b/test/fixtures/hubzilla-create-image.json new file mode 100644 index 000000000..9f0669bb7 --- /dev/null +++ b/test/fixtures/hubzilla-create-image.json @@ -0,0 +1 @@ +{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1","https://hub.somaton.com/apschema/v1.9"],"type":"Create","id":"https://hub.somaton.com/activity/452583b2-7e1f-4ac3-8334-ff666f134afe","diaspora:guid":"452583b2-7e1f-4ac3-8334-ff666f134afe","name":"daf82c18ef92a84cda72(1).jpg","published":"2021-10-12T21:28:26Z","actor":"https://hub.somaton.com/channel/testc6","object":{"type":"Image","name":"daf82c18ef92a84cda72(1).jpg","published":"2021-10-12T21:28:23Z","updated":"2021-10-12T21:28:23Z","attributedTo":"https://hub.somaton.com/channel/testc6","id":"https://hub.somaton.com/photo/452583b2-7e1f-4ac3-8334-ff666f134afe","url":[{"type":"Link","mediaType":"image/jpeg","href":"https://hub.somaton.com/photo/452583b2-7e1f-4ac3-8334-ff666f134afe-0.jpg","width":2200,"height":2200},{"type":"Link","mediaType":"image/jpeg","href":"https://hub.somaton.com/photo/452583b2-7e1f-4ac3-8334-ff666f134afe-1.jpg","width":1024,"height":1024},{"type":"Link","mediaType":"image/jpeg","href":"https://hub.somaton.com/photo/452583b2-7e1f-4ac3-8334-ff666f134afe-2.jpg","width":640,"height":640},{"type":"Link","mediaType":"image/jpeg","href":"https://hub.somaton.com/photo/452583b2-7e1f-4ac3-8334-ff666f134afe-3.jpg","width":320,"height":320},{"type":"Link","mediaType":"text/html","href":"https://hub.somaton.com/photos/testc6/image/452583b2-7e1f-4ac3-8334-ff666f134afe"}],"source":{"content":"[footer][zrl=https://hub.somaton.com/channel/testc6]testc6 lala[/zrl] posted [zrl=https://hub.somaton.com/photos/testc6/image/452583b2-7e1f-4ac3-8334-ff666f134afe]a new photo[/zrl] to [zrl=https://hub.somaton.com/photos/testc6/album/1e9b0d74-633e-4bd0-b37f-694bb0ed0145]test[/zrl][/footer]","mediaType":"text/bbcode"},"content":"<div class=\"wall-item-footer\"><a class=\"zrl\" href=\"https://hub.somaton.com/channel/testc6\"  target=\"_blank\"  rel=\"nofollow noopener\" >testc6 lala</a> posted <a class=\"zrl\" href=\"https://hub.somaton.com/photos/testc6/image/452583b2-7e1f-4ac3-8334-ff666f134afe\"  target=\"_blank\"  rel=\"nofollow noopener\" >a new photo</a> to <a class=\"zrl\" href=\"https://hub.somaton.com/photos/testc6/album/1e9b0d74-633e-4bd0-b37f-694bb0ed0145\"  target=\"_blank\"  rel=\"nofollow noopener\" >test</a></div>","to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://hub.somaton.com/followers/testc6"]},"target":{"type":"orderedCollection","name":"test","id":"https://hub.somaton.com/album/testc6/test"},"to":["https://www.w3.org/ns/activitystreams#Public"],"cc":["https://hub.somaton.com/followers/testc6"],"signature":{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],"type":"RsaSignature2017","nonce":"e0d077edccf262f02ed59ff67e91a5324ccaffc3d2b3f23793b4bd24cdbe70bb","creator":"https://hub.somaton.com/channel/testc6","created":"2021-10-13T18:39:05Z","signatureValue":"YYU0/17PqqUmLCn4oVS2N62rV1G9WQ+wLax2cI+EpMw/WOWKuVvtGrvhzciQ5ITXoh3scrZRYH8Bke1jDWkjL9YtjVD6TjMsv6f3OoO1vvMNgEfQfgZJ78QQt5MoLrT2mkRa35lSmVHkTDROKJPrwIAnpN6bDb577wZ63BsuBjqW7ca/E6oXSIr+meCXv3kqkyYDSz0ImYvVmki+OfX97xbYkQlzM06EgK1LZTHfuf4sk09hVfDDqVB9tHO4ObYQCYNiOWRHjA5S1Cw8WX1OQJ+GCQ8yxHmtiU3tJsxeYhxGs7VEmTLUvf/QZ0VTPumkd1CewdxzNGvAP3f9JCakuV7eyk88oqF+p7xxfxmBjLYbMTuhrcZIdUdMcjW9pENOYBbt+a+FhPsjbm8zVU3iKPqe/8UAvo01hGW7jiKJUm4qdcX3H3MExTLEFuz0NTeqxl4djlyGTT9KBqNouD+/oSSgwm6qeRZ5y3RsC27N0HRbg74qNXhhWQZVWQtHdSCHjAfHVPOSpjxpSPs7qkMLQ0vPsVsCsukZz8JCoXRo+JoKuaiaRgfiIRGNBO/XEicKMyu2JCU+UmkroiDJHy+4IfZRevnlneRa1jmu5KA/4xk5KU8l0I0Inap7TSPhv14Ex2sF89LkT8MbcDM3S3QL4urYsQj37zOKRDTFzE96TmI="}}
\ No newline at end of file diff --git a/test/mix/tasks/pleroma/openapi_spec_test.exs b/test/mix/tasks/pleroma/openapi_spec_test.exs new file mode 100644 index 000000000..01437187a --- /dev/null +++ b/test/mix/tasks/pleroma/openapi_spec_test.exs @@ -0,0 +1,62 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Mix.Tasks.Pleroma.OpenapiSpecTest do +  use Pleroma.DataCase, async: true + +  alias Mix.Tasks.Pleroma.OpenapiSpec + +  @spec_base %{ +    "paths" => %{ +      "/cofe" => %{ +        "get" => %{ +          "operationId" => "Some.operation", +          "tags" => [] +        } +      }, +      "/mew" => %{ +        "post" => %{ +          "operationId" => "Another.operation", +          "tags" => ["mew mew"] +        } +      } +    }, +    "x-tagGroups" => [ +      %{ +        "name" => "mew", +        "tags" => ["mew mew", "abc"] +      }, +      %{ +        "name" => "lol", +        "tags" => ["lol lol", "xyz"] +      } +    ] +  } + +  describe "check_specs/1" do +    test "Every operation must have a tag" do +      assert {:error, ["Some.operation (get /cofe): No tags specified"]} == +               OpenapiSpec.check_specs(@spec_base) +    end + +    test "Every tag must be in tag groups" do +      spec = +        @spec_base +        |> put_in(["paths", "/cofe", "get", "tags"], ["abc", "def", "not specified"]) + +      assert {:error, +              [ +                "Some.operation (get /cofe): Tags #{inspect(["def", "not specified"])} not available. Please add it in \"x-tagGroups\" in Pleroma.Web.ApiSpec" +              ]} == OpenapiSpec.check_specs(spec) +    end + +    test "No errors if ok" do +      spec = +        @spec_base +        |> put_in(["paths", "/cofe", "get", "tags"], ["abc", "mew mew"]) + +      assert :ok == OpenapiSpec.check_specs(spec) +    end +  end +end diff --git a/test/pleroma/bbs/handler_test.exs b/test/pleroma/bbs/handler_test.exs deleted file mode 100644 index aea3b6ead..000000000 --- a/test/pleroma/bbs/handler_test.exs +++ /dev/null @@ -1,89 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.BBS.HandlerTest do -  use Pleroma.DataCase, async: true -  alias Pleroma.Activity -  alias Pleroma.BBS.Handler -  alias Pleroma.Object -  alias Pleroma.Repo -  alias Pleroma.User -  alias Pleroma.Web.CommonAPI - -  import ExUnit.CaptureIO -  import Pleroma.Factory -  import Ecto.Query - -  test "getting the home timeline" do -    user = insert(:user) -    followed = insert(:user) - -    {:ok, user, followed} = User.follow(user, followed) - -    {:ok, _first} = CommonAPI.post(user, %{status: "hey"}) -    {:ok, _second} = CommonAPI.post(followed, %{status: "hello"}) - -    output = -      capture_io(fn -> -        Handler.handle_command(%{user: user}, "home") -      end) - -    assert output =~ user.nickname -    assert output =~ followed.nickname - -    assert output =~ "hey" -    assert output =~ "hello" -  end - -  test "posting" do -    user = insert(:user) - -    output = -      capture_io(fn -> -        Handler.handle_command(%{user: user}, "p this is a test post") -      end) - -    assert output =~ "Posted" - -    activity = -      Repo.one( -        from(a in Activity, -          where: fragment("?->>'type' = ?", a.data, "Create") -        ) -      ) - -    assert activity.actor == user.ap_id -    object = Object.normalize(activity, fetch: false) -    assert object.data["content"] == "this is a test post" -  end - -  test "replying" do -    user = insert(:user) -    another_user = insert(:user) - -    {:ok, activity} = CommonAPI.post(another_user, %{status: "this is a test post"}) -    activity_object = Object.normalize(activity, fetch: false) - -    output = -      capture_io(fn -> -        Handler.handle_command(%{user: user}, "r #{activity.id} this is a reply") -      end) - -    assert output =~ "Replied" - -    reply = -      Repo.one( -        from(a in Activity, -          where: fragment("?->>'type' = ?", a.data, "Create"), -          where: a.actor == ^user.ap_id -        ) -      ) - -    assert reply.actor == user.ap_id - -    reply_object_data = Object.normalize(reply, fetch: false).data -    assert reply_object_data["content"] == "this is a reply" -    assert reply_object_data["inReplyTo"] == activity_object.data["id"] -  end -end diff --git a/test/pleroma/object_test.exs b/test/pleroma/object_test.exs index d536e0b16..7bc5c9928 100644 --- a/test/pleroma/object_test.exs +++ b/test/pleroma/object_test.exs @@ -444,4 +444,42 @@ defmodule Pleroma.ObjectTest do                 Enum.sort_by(object.hashtags, & &1.name)      end    end + +  describe "get_emoji_reactions/1" do +    test "3-tuple current format" do +      object = %Object{ +        data: %{ +          "reactions" => [ +            ["x", ["https://some/user"], "https://some/emoji"] +          ] +        } +      } + +      assert Object.get_emoji_reactions(object) == object.data["reactions"] +    end + +    test "2-tuple legacy format" do +      object = %Object{ +        data: %{ +          "reactions" => [ +            ["x", ["https://some/user"]] +          ] +        } +      } + +      assert Object.get_emoji_reactions(object) == [["x", ["https://some/user"], nil]] +    end + +    test "Map format" do +      object = %Object{ +        data: %{ +          "reactions" => %{ +            "x" => ["https://some/user"] +          } +        } +      } + +      assert Object.get_emoji_reactions(object) == [["x", ["https://some/user"], nil]] +    end +  end  end diff --git a/test/pleroma/user/import_test.exs b/test/pleroma/user/import_test.exs index b4efd4bb0..f75305e0e 100644 --- a/test/pleroma/user/import_test.exs +++ b/test/pleroma/user/import_test.exs @@ -3,7 +3,6 @@  # SPDX-License-Identifier: AGPL-3.0-only  defmodule Pleroma.User.ImportTest do -  alias Pleroma.Repo    alias Pleroma.Tests.ObanHelpers    alias Pleroma.User diff --git a/test/pleroma/user_search_test.exs b/test/pleroma/user_search_test.exs index 1deab6888..1af9a1493 100644 --- a/test/pleroma/user_search_test.exs +++ b/test/pleroma/user_search_test.exs @@ -3,7 +3,6 @@  # SPDX-License-Identifier: AGPL-3.0-only  defmodule Pleroma.UserSearchTest do -  alias Pleroma.Repo    alias Pleroma.User    use Pleroma.DataCase diff --git a/test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs b/test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs index bbdb09c4c..9bb291a38 100644 --- a/test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/emoji_react_handling_test.exs @@ -38,16 +38,70 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactHandlingTest do        assert {:content, {"can't be blank", [validation: :required]}} in cng.errors      end -    test "it is not valid with a non-emoji content field", %{valid_emoji_react: valid_emoji_react} do +    test "it is valid when custom emoji is used", %{valid_emoji_react: valid_emoji_react} do        without_emoji_content =          valid_emoji_react -        |> Map.put("content", "x") +        |> Map.put("content", ":hello:") +        |> Map.put("tag", [ +          %{ +            "type" => "Emoji", +            "name" => ":hello:", +            "icon" => %{"url" => "http://somewhere", "type" => "Image"} +          } +        ]) + +      {:ok, _, _} = ObjectValidator.validate(without_emoji_content, []) +    end + +    test "it is not valid when custom emoji don't have a matching tag", %{ +      valid_emoji_react: valid_emoji_react +    } do +      without_emoji_content = +        valid_emoji_react +        |> Map.put("content", ":hello:") +        |> Map.put("tag", [ +          %{ +            "type" => "Emoji", +            "name" => ":whoops:", +            "icon" => %{"url" => "http://somewhere", "type" => "Image"} +          } +        ]) + +      {:error, cng} = ObjectValidator.validate(without_emoji_content, []) + +      refute cng.valid? + +      assert {:tag, {"does not contain an Emoji tag", []}} in cng.errors +    end + +    test "it is not valid when custom emoji have no tags", %{ +      valid_emoji_react: valid_emoji_react +    } do +      without_emoji_content = +        valid_emoji_react +        |> Map.put("content", ":hello:") +        |> Map.put("tag", []) + +      {:error, cng} = ObjectValidator.validate(without_emoji_content, []) + +      refute cng.valid? + +      assert {:tag, {"does not contain an Emoji tag", []}} in cng.errors +    end + +    test "it is not valid when custom emoji doesn't match a shortcode format", %{ +      valid_emoji_react: valid_emoji_react +    } do +      without_emoji_content = +        valid_emoji_react +        |> Map.put("content", "hello") +        |> Map.put("tag", [])        {:error, cng} = ObjectValidator.validate(without_emoji_content, [])        refute cng.valid? -      assert {:content, {"must be a single character emoji", []}} in cng.errors +      assert {:tag, {"does not contain an Emoji tag", []}} in cng.errors      end    end  end diff --git a/test/pleroma/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs index b24831e85..6820e23d0 100644 --- a/test/pleroma/web/activity_pub/side_effects_test.exs +++ b/test/pleroma/web/activity_pub/side_effects_test.exs @@ -453,7 +453,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do        object = Object.get_by_ap_id(emoji_react.data["object"])        assert object.data["reaction_count"] == 1 -      assert ["👌", [user.ap_id]] in object.data["reactions"] +      assert ["👌", [user.ap_id], nil] in object.data["reactions"]      end      test "creates a notification", %{emoji_react: emoji_react, poster: poster} do diff --git a/test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs index 9d99df27c..f2e1cefa3 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/emoji_react_handling_test.exs @@ -34,7 +34,56 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.EmojiReactHandlingTest do      object = Object.get_by_ap_id(data["object"])      assert object.data["reaction_count"] == 1 -    assert match?([["👌", _]], object.data["reactions"]) +    assert match?([["👌", _, nil]], object.data["reactions"]) +  end + +  test "it works for incoming custom emoji reactions" do +    user = insert(:user) +    other_user = insert(:user, local: false) +    {:ok, activity} = CommonAPI.post(user, %{status: "hello"}) + +    data = +      File.read!("test/fixtures/custom-emoji-reaction.json") +      |> Jason.decode!() +      |> Map.put("object", activity.data["object"]) +      |> Map.put("actor", other_user.ap_id) + +    {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + +    assert data["actor"] == other_user.ap_id +    assert data["type"] == "EmojiReact" +    assert data["id"] == "https://misskey.local.live/likes/917ocsybgp" +    assert data["object"] == activity.data["object"] +    assert data["content"] == ":hanapog:" + +    assert data["tag"] == [ +             %{ +               "id" => "https://misskey.local.live/emojis/hanapog", +               "type" => "Emoji", +               "name" => "hanapog", +               "updated" => "2022-06-07T12:00:05.773Z", +               "icon" => %{ +                 "type" => "Image", +                 "url" => +                   "https://misskey.local.live/files/webpublic-8f8a9768-7264-4171-88d6-2356aabeadcd" +               } +             } +           ] + +    object = Object.get_by_ap_id(data["object"]) + +    assert object.data["reaction_count"] == 1 + +    assert match?( +             [ +               [ +                 "hanapog", +                 _, +                 "https://misskey.local.live/files/webpublic-8f8a9768-7264-4171-88d6-2356aabeadcd" +               ] +             ], +             object.data["reactions"] +           )    end    test "it works for incoming unqualified emoji reactions" do @@ -65,7 +114,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.EmojiReactHandlingTest do      object = Object.get_by_ap_id(data["object"])      assert object.data["reaction_count"] == 1 -    assert match?([[emoji, _]], object.data["reactions"]) +    assert match?([[^emoji, _, _]], object.data["reactions"])    end    test "it reject invalid emoji reactions" do diff --git a/test/pleroma/web/activity_pub/transmogrifier/image_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/image_handling_test.exs new file mode 100644 index 000000000..b85f0a477 --- /dev/null +++ b/test/pleroma/web/activity_pub/transmogrifier/image_handling_test.exs @@ -0,0 +1,50 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.ImageHandlingTest do +  use Oban.Testing, repo: Pleroma.Repo +  use Pleroma.DataCase + +  alias Pleroma.Activity +  alias Pleroma.Object +  alias Pleroma.Web.ActivityPub.Transmogrifier + +  test "Hubzilla Image object" do +    Tesla.Mock.mock(fn +      %{url: "https://hub.somaton.com/channel/testc6"} -> +        %Tesla.Env{ +          status: 200, +          body: File.read!("test/fixtures/hubzilla-actor.json"), +          headers: HttpRequestMock.activitypub_object_headers() +        } +    end) + +    data = File.read!("test/fixtures/hubzilla-create-image.json") |> Poison.decode!() + +    {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + +    assert object = Object.normalize(activity, fetch: false) + +    assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] + +    assert object.data["cc"] == ["https://hub.somaton.com/followers/testc6"] + +    assert object.data["attachment"] == [ +             %{ +               "mediaType" => "image/jpeg", +               "type" => "Link", +               "url" => [ +                 %{ +                   "height" => 2200, +                   "href" => +                     "https://hub.somaton.com/photo/452583b2-7e1f-4ac3-8334-ff666f134afe-0.jpg", +                   "mediaType" => "image/jpeg", +                   "type" => "Link", +                   "width" => 2200 +                 } +               ] +             } +           ] +  end +end diff --git a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs index 7c406fbd0..a9ad3e9c8 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs @@ -104,6 +104,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do        end      end +    @tag capture_log: true      test "it does not crash if the object in inReplyTo can't be fetched" do        data =          File.read!("test/fixtures/mastodon-post-activity.json") @@ -723,6 +724,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do      assert modified.data["context"] == object.data["id"]    end +  @tag capture_log: true    test "the reply note uses its parent's ID when context is missing and reply is unreachable" do      insert(:user, ap_id: "https://mk.absturztau.be/users/8ozbzjs3o8") diff --git a/test/pleroma/web/activity_pub/transmogrifier_test.exs b/test/pleroma/web/activity_pub/transmogrifier_test.exs index 6b4636d22..f76606479 100644 --- a/test/pleroma/web/activity_pub/transmogrifier_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier_test.exs @@ -123,6 +123,20 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do        assert activity.data["context"] == object.data["context"]      end + +    test "it drops link tags" do +      insert(:user, ap_id: "https://example.org/users/alice") + +      message = File.read!("test/fixtures/fep-e232.json") |> Jason.decode!() + +      assert {:ok, activity} = Transmogrifier.handle_incoming(message) + +      object = Object.normalize(activity) +      assert length(object.data["tag"]) == 1 + +      tag = object.data["tag"] |> List.first() +      assert tag["type"] == "Mention" +    end    end    describe "prepare outgoing" do diff --git a/test/pleroma/web/activity_pub/utils_test.exs b/test/pleroma/web/activity_pub/utils_test.exs index e7d1e01c4..3f93c872b 100644 --- a/test/pleroma/web/activity_pub/utils_test.exs +++ b/test/pleroma/web/activity_pub/utils_test.exs @@ -587,15 +587,38 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do    end    describe "get_cached_emoji_reactions/1" do -    test "returns the data or an emtpy list" do +    test "returns the normalized data or an emtpy list" do        object = insert(:note)        assert Utils.get_cached_emoji_reactions(object) == []        object = insert(:note, data: %{"reactions" => [["x", ["lain"]]]}) -      assert Utils.get_cached_emoji_reactions(object) == [["x", ["lain"]]] +      assert Utils.get_cached_emoji_reactions(object) == [["x", ["lain"], nil]]        object = insert(:note, data: %{"reactions" => %{}})        assert Utils.get_cached_emoji_reactions(object) == []      end    end + +  describe "add_emoji_reaction_to_object/1" do +    test "works with legacy 2-tuple format" do +      user = insert(:user) +      other_user = insert(:user) +      third_user = insert(:user) + +      note = +        insert(:note, +          user: user, +          data: %{ +            "reactions" => [["😿", [other_user.ap_id]]] +          } +        ) + +      _activity = insert(:note_activity, user: user, note: note) + +      Utils.add_emoji_reaction_to_object( +        %Activity{data: %{"content" => "😿", "actor" => third_user.ap_id}}, +        note +      ) +    end +  end  end diff --git a/test/pleroma/web/admin_api/controllers/config_controller_test.exs b/test/pleroma/web/admin_api/controllers/config_controller_test.exs index 9ef7c0c46..19ce3681c 100644 --- a/test/pleroma/web/admin_api/controllers/config_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/config_controller_test.exs @@ -316,6 +316,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do        assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}      end +    @tag capture_log: true      test "save configs setting without explicit key", %{conn: conn} do        adapter = Application.get_env(:http, :adapter)        send_user_agent = Application.get_env(:http, :send_user_agent) @@ -1501,15 +1502,14 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do        clear_config(:database_config_whitelist, [          {:pleroma, :instance},          {:pleroma, :activitypub}, -        {:pleroma, Pleroma.Upload}, -        {:esshd} +        {:pleroma, Pleroma.Upload}        ])        conn = get(conn, "/api/pleroma/admin/config/descriptions")        children = json_response_and_validate_schema(conn, 200) -      assert length(children) == 4 +      assert length(children) == 3        assert Enum.count(children, fn c -> c["group"] == ":pleroma" end) == 3 @@ -1521,9 +1521,6 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do        web_endpoint = Enum.find(children, fn c -> c["key"] == "Pleroma.Upload" end)        assert web_endpoint["children"] - -      esshd = Enum.find(children, fn c -> c["group"] == ":esshd" end) -      assert esshd["children"]      end    end  end diff --git a/test/pleroma/web/admin_api/controllers/frontend_controller_test.exs b/test/pleroma/web/admin_api/controllers/frontend_controller_test.exs index 38a23b224..0d1a4999e 100644 --- a/test/pleroma/web/admin_api/controllers/frontend_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/frontend_controller_test.exs @@ -89,6 +89,7 @@ defmodule Pleroma.Web.AdminAPI.FrontendControllerTest do                   "build_url" => "http://gensokyo.2hu/builds/${ref}",                   "git" => nil,                   "installed" => true, +                 "installed_refs" => ["fantasy"],                   "name" => "pleroma",                   "ref" => "fantasy"                 } diff --git a/test/pleroma/web/api_spec/scopes/compiler_test.exs b/test/pleroma/web/api_spec/scopes/compiler_test.exs new file mode 100644 index 000000000..99e1d343a --- /dev/null +++ b/test/pleroma/web/api_spec/scopes/compiler_test.exs @@ -0,0 +1,56 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.Scopes.CompilerTest do +  use ExUnit.Case, async: true + +  alias Pleroma.Web.ApiSpec.Scopes.Compiler + +  @dummy_response %{} + +  @data %{ +    paths: %{ +      "/mew" => %OpenApiSpex.PathItem{ +        post: %OpenApiSpex.Operation{ +          security: [%{"oAuth" => ["a:b:c"]}], +          responses: @dummy_response +        }, +        get: %OpenApiSpex.Operation{security: nil, responses: @dummy_response} +      }, +      "/mew2" => %OpenApiSpex.PathItem{ +        post: %OpenApiSpex.Operation{ +          security: [%{"oAuth" => ["d:e", "f:g"]}], +          responses: @dummy_response +        }, +        get: %OpenApiSpex.Operation{security: nil, responses: @dummy_response} +      } +    } +  } + +  describe "process_scope/1" do +    test "gives all higher-level scopes" do +      scopes = Compiler.process_scope("admin:read:accounts") + +      assert [_, _, _] = scopes +      assert "admin" in scopes +      assert "admin:read" in scopes +      assert "admin:read:accounts" in scopes +    end +  end + +  describe "extract_all_scopes_from/1" do +    test "extracts scopes" do +      scopes = Compiler.extract_all_scopes_from(@data) + +      assert [_, _, _, _, _, _, _] = scopes +      assert "a" in scopes +      assert "a:b" in scopes +      assert "a:b:c" in scopes +      assert "d" in scopes +      assert "d:e" in scopes +      assert "f" in scopes +      assert "f:g" in scopes +    end +  end +end diff --git a/test/pleroma/web/common_api_test.exs b/test/pleroma/web/common_api_test.exs index 5c9103e9f..968d826a2 100644 --- a/test/pleroma/web/common_api_test.exs +++ b/test/pleroma/web/common_api_test.exs @@ -527,6 +527,17 @@ defmodule Pleroma.Web.CommonAPITest do      assert Object.tags(object) == ["ساٴينس"]    end +  test "allows lang attribute" do +    user = insert(:user) +    text = ~s{<span lang="en">something</span><p lang="diaetuitech_rpyhpgc">random</p>} + +    {:ok, activity} = CommonAPI.post(user, %{status: text, content_type: "text/html"}) + +    object = Object.normalize(activity, fetch: false) + +    assert object.data["content"] == text +  end +    test "double dot in link is allowed" do      user = insert(:user)      text = "https://example.to/something..mp3" diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index 958b7f76f..128e60b0a 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -2031,6 +2031,39 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do      assert [%{"id" => ^id1}] = result    end +  test "list of blocks with with_relationships parameter" do +    %{user: user, conn: conn} = oauth_access(["read:blocks"]) +    %{id: id1} = other_user1 = insert(:user) +    %{id: id2} = other_user2 = insert(:user) +    %{id: id3} = other_user3 = insert(:user) + +    {:ok, _, _} = User.follow(other_user1, user) +    {:ok, _, _} = User.follow(other_user2, user) +    {:ok, _, _} = User.follow(other_user3, user) + +    {:ok, _} = User.block(user, other_user1) +    {:ok, _} = User.block(user, other_user2) +    {:ok, _} = User.block(user, other_user3) + +    assert [ +             %{ +               "id" => ^id3, +               "pleroma" => %{"relationship" => %{"blocking" => true, "followed_by" => false}} +             }, +             %{ +               "id" => ^id2, +               "pleroma" => %{"relationship" => %{"blocking" => true, "followed_by" => false}} +             }, +             %{ +               "id" => ^id1, +               "pleroma" => %{"relationship" => %{"blocking" => true, "followed_by" => false}} +             } +           ] = +             conn +             |> get("/api/v1/blocks?with_relationships=true") +             |> json_response_and_validate_schema(200) +  end +    test "account lookup", %{conn: conn} do      %{nickname: acct} = insert(:user, %{nickname: "nickname"})      %{nickname: acct_two} = insert(:user, %{nickname: "nickname@notlocaldoma.in"}) diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs index 5bae2cd00..1e8979127 100644 --- a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -626,7 +626,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do          |> put_req_header("content-type", "application/json")          |> post("/api/v1/statuses", %{            "status" => "desu~", -          "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1} +          "poll" => %{ +            "options" => Enum.map(0..limit, fn num -> "desu #{num}" end), +            "expires_in" => 1 +          }          })        %{"error" => error} = json_response_and_validate_schema(conn, 422) @@ -642,7 +645,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do          |> post("/api/v1/statuses", %{            "status" => "...",            "poll" => %{ -            "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)], +            "options" => [String.duplicate(".", limit + 1), "lol"],              "expires_in" => 1            }          }) @@ -724,6 +727,32 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do        assert object.data["type"] == "Question"        assert length(object.data["oneOf"]) == 3      end + +    test "cannot have only one option", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/v1/statuses", %{ +          "status" => "desu~", +          "poll" => %{"options" => ["mew"], "expires_in" => 1} +        }) + +      %{"error" => error} = json_response_and_validate_schema(conn, 422) +      assert error == "Poll must contain at least 2 options" +    end + +    test "cannot have only duplicated options", %{conn: conn} do +      conn = +        conn +        |> put_req_header("content-type", "application/json") +        |> post("/api/v1/statuses", %{ +          "status" => "desu~", +          "poll" => %{"options" => ["mew", "mew"], "expires_in" => 1} +        }) + +      %{"error" => error} = json_response_and_validate_schema(conn, 422) +      assert error == "Poll must contain at least 2 options" +    end    end    test "get a status" do diff --git a/test/pleroma/web/mastodon_api/update_credentials_test.exs b/test/pleroma/web/mastodon_api/update_credentials_test.exs index 57fa0f047..6c63d53c2 100644 --- a/test/pleroma/web/mastodon_api/update_credentials_test.exs +++ b/test/pleroma/web/mastodon_api/update_credentials_test.exs @@ -375,7 +375,9 @@ defmodule Pleroma.Web.MastodonAPI.UpdateCredentialsTest do            "pleroma_background_image" => new_background_oversized          }) -      assert user_response = json_response_and_validate_schema(res, 413) +      assert %{"error" => "File is too large"} == json_response_and_validate_schema(res, 413) + +      user = Repo.get(User, user.id)        assert user.background == %{}        clear_config([:instance, :upload_limit], upload_limit) diff --git a/test/pleroma/web/mastodon_api/views/notification_view_test.exs b/test/pleroma/web/mastodon_api/views/notification_view_test.exs index 6ea894691..ddbe4557f 100644 --- a/test/pleroma/web/mastodon_api/views/notification_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/notification_view_test.exs @@ -190,7 +190,47 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do        emoji: "☕",        account: AccountView.render("show.json", %{user: other_user, for: user}),        status: StatusView.render("show.json", %{activity: activity, for: user}), -      created_at: Utils.to_masto_date(notification.inserted_at) +      created_at: Utils.to_masto_date(notification.inserted_at), +      emoji_url: nil +    } + +    test_notifications_rendering([notification], user, [expected]) +  end + +  test "EmojiReact custom emoji notification" do +    user = insert(:user) +    other_user = insert(:user) + +    note = +      insert(:note, +        user: user, +        data: %{ +          "reactions" => [ +            ["👍", [user.ap_id], nil], +            ["dinosaur", [user.ap_id], "http://localhost:4001/emoji/dino walking.gif"] +          ] +        } +      ) + +    activity = insert(:note_activity, note: note, user: user) + +    {:ok, _activity} = CommonAPI.react_with_emoji(activity.id, other_user, "dinosaur") + +    activity = Repo.get(Activity, activity.id) + +    [notification] = Notification.for_user(user) + +    assert notification + +    expected = %{ +      id: to_string(notification.id), +      pleroma: %{is_seen: false, is_muted: false}, +      type: "pleroma:emoji_reaction", +      emoji: ":dinosaur:", +      account: AccountView.render("show.json", %{user: other_user, for: user}), +      status: StatusView.render("show.json", %{activity: activity, for: user}), +      created_at: Utils.to_masto_date(notification.inserted_at), +      emoji_url: "http://localhost:4001/emoji/dino walking.gif"      }      test_notifications_rendering([notification], user, [expected]) diff --git a/test/pleroma/web/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs index f76b115b7..b93335190 100644 --- a/test/pleroma/web/mastodon_api/views/status_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/status_view_test.exs @@ -35,16 +35,26 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      {:ok, activity} = CommonAPI.post(user, %{status: "dae cofe??"})      {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "☕") +    {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, ":dinosaur:")      {:ok, _} = CommonAPI.react_with_emoji(activity.id, third_user, "🍵")      {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") +    {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, ":dinosaur:") +      activity = Repo.get(Activity, activity.id)      status = StatusView.render("show.json", activity: activity)      assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())      assert status[:pleroma][:emoji_reactions] == [ -             %{name: "☕", count: 2, me: false}, -             %{name: "🍵", count: 1, me: false} +             %{name: "☕", count: 2, me: false, url: nil, account_ids: [other_user.id, user.id]}, +             %{ +               count: 2, +               me: false, +               name: "dinosaur", +               url: "http://localhost:4001/emoji/dino walking.gif", +               account_ids: [other_user.id, user.id] +             }, +             %{name: "🍵", count: 1, me: false, url: nil, account_ids: [third_user.id]}             ]      status = StatusView.render("show.json", activity: activity, for: user) @@ -52,8 +62,36 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      assert_schema(status, "Status", Pleroma.Web.ApiSpec.spec())      assert status[:pleroma][:emoji_reactions] == [ -             %{name: "☕", count: 2, me: true}, -             %{name: "🍵", count: 1, me: false} +             %{name: "☕", count: 2, me: true, url: nil, account_ids: [other_user.id, user.id]}, +             %{ +               count: 2, +               me: true, +               name: "dinosaur", +               url: "http://localhost:4001/emoji/dino walking.gif", +               account_ids: [other_user.id, user.id] +             }, +             %{name: "🍵", count: 1, me: false, url: nil, account_ids: [third_user.id]} +           ] +  end + +  test "works with legacy-formatted reactions" do +    user = insert(:user) +    other_user = insert(:user) + +    note = +      insert(:note, +        user: user, +        data: %{ +          "reactions" => [["😿", [other_user.ap_id]]] +        } +      ) + +    activity = insert(:note_activity, user: user, note: note) + +    status = StatusView.render("show.json", activity: activity, for: user) + +    assert status[:pleroma][:emoji_reactions] == [ +             %{name: "😿", count: 1, me: false, url: nil, account_ids: [other_user.id]}             ]    end @@ -66,11 +104,10 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      |> Object.update_data(%{"reactions" => %{"☕" => [user.ap_id], "x" => 1}})      activity = Activity.get_by_id(activity.id) -      status = StatusView.render("show.json", activity: activity, for: user)      assert status[:pleroma][:emoji_reactions] == [ -             %{name: "☕", count: 1, me: true} +             %{name: "☕", count: 1, me: true, url: nil, account_ids: [user.id]}             ]    end @@ -90,7 +127,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      status = StatusView.render("show.json", activity: activity)      assert status[:pleroma][:emoji_reactions] == [ -             %{name: "☕", count: 1, me: false} +             %{name: "☕", count: 1, me: false, url: nil, account_ids: [other_user.id]}             ]      status = StatusView.render("show.json", activity: activity, for: user) @@ -102,19 +139,25 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do      status = StatusView.render("show.json", activity: activity)      assert status[:pleroma][:emoji_reactions] == [ -             %{name: "☕", count: 2, me: false} +             %{ +               name: "☕", +               count: 2, +               me: false, +               url: nil, +               account_ids: [third_user.id, other_user.id] +             }             ]      status = StatusView.render("show.json", activity: activity, for: user)      assert status[:pleroma][:emoji_reactions] == [ -             %{name: "☕", count: 1, me: false} +             %{name: "☕", count: 1, me: false, url: nil, account_ids: [third_user.id]}             ]      status = StatusView.render("show.json", activity: activity, for: other_user)      assert status[:pleroma][:emoji_reactions] == [ -             %{name: "☕", count: 1, me: true} +             %{name: "☕", count: 1, me: true, url: nil, account_ids: [other_user.id]}             ]    end diff --git a/test/pleroma/web/metadata/providers/rel_me_test.exs b/test/pleroma/web/metadata/providers/rel_me_test.exs index cce4f3607..793669037 100644 --- a/test/pleroma/web/metadata/providers/rel_me_test.exs +++ b/test/pleroma/web/metadata/providers/rel_me_test.exs @@ -11,11 +11,24 @@ defmodule Pleroma.Web.Metadata.Providers.RelMeTest do      bio =        ~s(<a href="https://some-link.com">https://some-link.com</a> <a rel="me" href="https://another-link.com">https://another-link.com</a> <link href="http://some.com"> <link rel="me" href="http://some3.com">) -    user = insert(:user, %{bio: bio}) +    fields = [ +      %{ +        "name" => "profile", +        "value" => ~S(<a rel="me" href="http://profile.com">http://profile.com</a>) +      }, +      %{ +        "name" => "like", +        "value" => ~S(<a href="http://cofe.io">http://cofe.io</a>) +      }, +      %{"name" => "foo", "value" => "bar"} +    ] + +    user = insert(:user, %{bio: bio, fields: fields})      assert RelMe.build_tags(%{user: user}) == [               {:link, [rel: "me", href: "http://some3.com"], []}, -             {:link, [rel: "me", href: "https://another-link.com"], []} +             {:link, [rel: "me", href: "https://another-link.com"], []}, +             {:link, [rel: "me", href: "http://profile.com"], []}             ]    end  end diff --git a/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs index 77c75b560..21e7d4839 100644 --- a/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/emoji_reaction_controller_test.exs @@ -17,23 +17,113 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do      user = insert(:user)      other_user = insert(:user) +    note = insert(:note, user: user, data: %{"reactions" => [["👍", [other_user.ap_id], nil]]}) +    activity = insert(:note_activity, note: note, user: user) + +    result = +      conn +      |> assign(:user, other_user) +      |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) +      |> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/\u26A0") +      |> json_response_and_validate_schema(200) + +    assert %{"id" => id} = result +    assert to_string(activity.id) == id + +    assert result["pleroma"]["emoji_reactions"] == [ +             %{ +               "name" => "👍", +               "count" => 1, +               "me" => true, +               "url" => nil, +               "account_ids" => [other_user.id] +             }, +             %{ +               "name" => "\u26A0\uFE0F", +               "count" => 1, +               "me" => true, +               "url" => nil, +               "account_ids" => [other_user.id] +             } +           ] +      {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) +    ObanHelpers.perform_all() + +    # Reacting with a custom emoji      result =        conn        |> assign(:user, other_user)        |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) -      |> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/☕") +      |> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/:dinosaur:")        |> json_response_and_validate_schema(200) -    # We return the status, but this our implementation detail.      assert %{"id" => id} = result      assert to_string(activity.id) == id      assert result["pleroma"]["emoji_reactions"] == [ -             %{"name" => "☕", "count" => 1, "me" => true} +             %{ +               "name" => "dinosaur", +               "count" => 1, +               "me" => true, +               "url" => "http://localhost:4001/emoji/dino walking.gif", +               "account_ids" => [other_user.id] +             } +           ] + +    # Reacting with a remote emoji +    note = +      insert(:note, +        user: user, +        data: %{ +          "reactions" => [ +            ["👍", [other_user.ap_id], nil], +            ["wow", [other_user.ap_id], "https://remote/emoji/wow"] +          ] +        } +      ) + +    activity = insert(:note_activity, note: note, user: user) + +    result = +      conn +      |> assign(:user, user) +      |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:statuses"])) +      |> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/:wow@remote:") +      |> json_response(200) + +    assert result["pleroma"]["emoji_reactions"] == [ +             %{ +               "account_ids" => [other_user.id], +               "count" => 1, +               "me" => false, +               "name" => "👍", +               "url" => nil +             }, +             %{ +               "name" => "wow@remote", +               "count" => 2, +               "me" => true, +               "url" => "https://remote/emoji/wow", +               "account_ids" => [user.id, other_user.id] +             }             ] +    # Reacting with a remote custom emoji that hasn't been reacted with yet +    note = +      insert(:note, +        user: user +      ) + +    activity = insert(:note_activity, note: note, user: user) + +    assert conn +           |> assign(:user, user) +           |> assign(:token, insert(:oauth_token, user: user, scopes: ["write:statuses"])) +           |> put("/api/v1/pleroma/statuses/#{activity.id}/reactions/:wow@remote:") +           |> json_response(400) +      # Reacting with a non-emoji      assert conn             |> assign(:user, other_user) @@ -46,8 +136,21 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do      user = insert(:user)      other_user = insert(:user) -    {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"}) +    note = +      insert(:note, +        user: user, +        data: %{"reactions" => [["wow", [user.ap_id], "https://remote/emoji/wow"]]} +      ) + +    activity = insert(:note_activity, note: note, user: user) + +    ObanHelpers.perform_all() +      {:ok, _reaction_activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") +    {:ok, _reaction_activity} = CommonAPI.react_with_emoji(activity.id, other_user, ":dinosaur:") + +    {:ok, _reaction_activity} = +      CommonAPI.react_with_emoji(activity.id, other_user, ":wow@remote:")      ObanHelpers.perform_all() @@ -60,11 +163,47 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do      assert %{"id" => id} = json_response_and_validate_schema(result, 200)      assert to_string(activity.id) == id +    # Remove custom emoji + +    result = +      conn +      |> assign(:user, other_user) +      |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) +      |> delete("/api/v1/pleroma/statuses/#{activity.id}/reactions/:dinosaur:") + +    assert %{"id" => id} = json_response_and_validate_schema(result, 200) +    assert to_string(activity.id) == id +      ObanHelpers.perform_all()      object = Object.get_by_ap_id(activity.data["object"]) -    assert object.data["reaction_count"] == 0 +    assert object.data["reaction_count"] == 2 + +    # Remove custom remote emoji +    result = +      conn +      |> assign(:user, other_user) +      |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) +      |> delete("/api/v1/pleroma/statuses/#{activity.id}/reactions/:wow@remote:") +      |> json_response(200) + +    assert result["pleroma"]["emoji_reactions"] == [ +             %{ +               "name" => "wow@remote", +               "count" => 1, +               "me" => false, +               "url" => "https://remote/emoji/wow", +               "account_ids" => [user.id] +             } +           ] + +    # Remove custom remote emoji that hasn't been reacted with yet +    assert conn +           |> assign(:user, other_user) +           |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["write:statuses"])) +           |> delete("/api/v1/pleroma/statuses/#{activity.id}/reactions/:zoop@remote:") +           |> json_response(400)    end    test "GET /api/v1/pleroma/statuses/:id/reactions", %{conn: conn} do @@ -106,6 +245,38 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do               result    end +  test "GET /api/v1/pleroma/statuses/:id/reactions with legacy format", %{conn: conn} do +    user = insert(:user) +    other_user = insert(:user) + +    note = +      insert(:note, +        user: user, +        data: %{ +          "reactions" => [["😿", [other_user.ap_id]]] +        } +      ) + +    activity = insert(:note_activity, user: user, note: note) + +    result = +      conn +      |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions") +      |> json_response_and_validate_schema(200) + +    other_user_id = other_user.id + +    assert [ +             %{ +               "name" => "😿", +               "count" => 1, +               "me" => false, +               "url" => nil, +               "accounts" => [%{"id" => ^other_user_id}] +             } +           ] = result +  end +    test "GET /api/v1/pleroma/statuses/:id/reactions?with_muted=true", %{conn: conn} do      user = insert(:user)      user2 = insert(:user) @@ -181,7 +352,15 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do      {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")      {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "☕") -    assert [%{"name" => "🎅", "count" => 1, "accounts" => [represented_user], "me" => false}] = +    assert [ +             %{ +               "name" => "🎅", +               "count" => 1, +               "accounts" => [represented_user], +               "me" => false, +               "url" => nil +             } +           ] =               conn               |> get("/api/v1/pleroma/statuses/#{activity.id}/reactions/🎅")               |> json_response_and_validate_schema(200) diff --git a/test/pleroma/web/plugs/uploaded_media_plug_test.exs b/test/pleroma/web/plugs/uploaded_media_plug_test.exs index ec46b0537..8323ff6ab 100644 --- a/test/pleroma/web/plugs/uploaded_media_plug_test.exs +++ b/test/pleroma/web/plugs/uploaded_media_plug_test.exs @@ -33,11 +33,11 @@ defmodule Pleroma.Web.Plugs.UploadedMediaPlugTest do    test "sends Content-Disposition header when name param is set", %{      attachment_url: attachment_url    } do -    conn = get(build_conn(), attachment_url <> "?name=\"cofe\".gif") +    conn = get(build_conn(), attachment_url <> ~s[?name="cofe".gif])      assert Enum.any?(               conn.resp_headers, -             &(&1 == {"content-disposition", "filename=\"\\\"cofe\\\".gif\""}) +             &(&1 == {"content-disposition", ~s[inline; filename="\\"cofe\\".gif"]})             )    end  end diff --git a/test/test_helper.exs b/test/test_helper.exs index 60a61484f..7727cffbc 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -2,6 +2,8 @@  # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>  # SPDX-License-Identifier: AGPL-3.0-only +Code.put_compiler_option(:warnings_as_errors, true) +  os_exclude = if :os.type() == {:unix, :darwin}, do: [skip_on_mac: true], else: []  ExUnit.start(exclude: [:federated, :erratic] ++ os_exclude) diff --git a/tools/check-changelog b/tools/check-changelog new file mode 100644 index 000000000..60692033f --- /dev/null +++ b/tools/check-changelog @@ -0,0 +1,18 @@ +#!/bin/sh + +echo "looking for change log" + +git remote add upstream https://git.pleroma.social/pleroma/pleroma.git +git fetch upstream ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}:refs/remotes/upstream/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME + +git diff --raw --no-renames upstream/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME HEAD -- changelog.d | \ +    grep ' A\t' | grep '\.\(skip\|add\|remove\|fix\|security\)$' +ret=$? + +if [ $ret -eq 0 ]; then +    echo "found a changelog entry" +    exit 0 +else +    echo "changelog entry not found" +    exit 1 +fi  | 
