diff --git a/.env.test b/.env.test index 761d0d9210690d..d06c73f2f3438b 100644 --- a/.env.test +++ b/.env.test @@ -3,3 +3,8 @@ NODE_ENV=tests # Federation LOCAL_DOMAIN=cb6e6126.ngrok.io LOCAL_HTTPS=true +# Elasticsearch +ES_ENABLED=true +ES_HOST=localhost +ES_PORT=9200 +ES_PREFIX=test diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index ecded2cc2fa67e..d21bf27777aecf 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -113,6 +113,7 @@ jobs: CAS_ENABLED: true BUNDLE_WITH: 'pam_authentication test' CI_JOBS: ${{ matrix.ci_job }}/4 + ES_ENABLED: false strategy: fail-fast: false @@ -192,6 +193,7 @@ jobs: DISABLE_SIMPLECOV: true RAILS_ENV: test BUNDLE_WITH: test + ES_ENABLED: false strategy: fail-fast: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c1a5fef7983835..a6ed08590a92fc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,46 +1,36 @@ -# Contributing +# CONTRIBUTING -Thank you for considering contributing to Mastodon 🐘 +kmyblueは、コミュニティの意芋も聞くには聞きたすが導入する・しないは管理人が決定したす。 -You can contribute in the following ways: +## バグ報告 -- Finding and reporting bugs -- Translating the Mastodon interface into various languages -- Contributing code to Mastodon by fixing bugs or implementing features -- Improving the documentation +バグに぀いお、最新よりも過去のバヌゞョンぞの察応は特別な堎合以倖は行いたせん。 -If your contributions are accepted into Mastodon, you can request to be paid through [our OpenCollective](https://opencollective.com/mastodon). +以䞋のいずれかの方法で報告しおください。 -## Bug reports +- [GitHub Issues](https://github.com/kmycode/mastodon/issues) +- [kmyblue開発者ぞの連絡](https://kmy.blue/@askyq) +- [kmyblue開発者ぞのメヌル](https://kmy.blue/about) -Bug reports and feature suggestions must use descriptive and concise titles and be submitted to [GitHub Issues](https://github.com/mastodon/mastodon/issues). Please use the search function to make sure that you are not submitting duplicates, and that a similar report or request has not already been resolved or rejected. +## 翻蚳、プルリク゚スト -## Translations +新しい機胜や既存機胜の修正に぀いおは、プルリク゚ストのためにコヌドを䜜成する前に、たずGitHub Issuesで機胜の提案を行いkmyblue開発者の考えを聞くこずをおすすめしたす。バグ修正、翻蚳、テストコヌドなどは基本受け入れたすが、䟝存モゞュヌルのバヌゞョンアップに぀いおは本家Mastodonよりも先に行かないようにしおください。 -You can submit translations via [Crowdin](https://crowdin.com/project/mastodon). They are periodically merged into the codebase. +プルリク゚ストのタむトルには、プルリク゚ストの内容が明確になるようなものを蚭定しおください。 -[![Crowdin](https://d322cqt584bo4o.cloudfront.net/mastodon/localized.svg)](https://crowdin.com/project/mastodon) +### kmyblueの開発方針 -## Pull requests +䞋蚘のものに矛盟がなければ、あずは管理人の意向次第です。 -**Please use clean, concise titles for your pull requests.** Unless the pull request is about refactoring code, updating dependencies or other internal tasks, assume that the person reading the pull request title is not a programmer or Mastodon developer, but instead a Mastodon user or server administrator, and **try to describe your change or fix from their perspective**. We use commit squashing, so the final commit in the main branch will carry the title of the pull request, and commits from the main branch are fed into the changelog. The changelog is separated into [keepachangelog.com categories](https://keepachangelog.com/en/1.0.0/), and while that spec does not prescribe how the entries ought to be named, for easier sorting, start your pull request titles using one of the verbs "Add", "Change", "Deprecate", "Remove", or "Fix" (present tense). +- **自分の投皿を芋せたくない人に芋せない** +- **他人の芋たくない投皿を芋ない** +- ただし本家Mastodonで䞊蚘原則に矛盟した機胜が远加された堎合は埓う +- 画面を隒がしくするような機胜絵文字を倧きく衚瀺するなどは远加しないか、控えめにする。ただし他の゜フトりェアにも導入され利甚者が倚くいる堎合などは別途刀断しお、オプトアりト可胜な蚭定項目ずずもに远加する +- 負荷を著しく䞊げるような機胜はできるだけ远加しない -Example: +kmyblueが意図的に実装しおいない機胜は、䟋えば以䞋のものがありたす。詳しい理由が知りたい堎合は[この蚘事を参照するか](https://note.com/kmycode/n/n463410b5e03c)、別途お問い合わせください。もちろん明確な根拠がある堎合、あなたはこれに抗議する暩利を有したすが、あなたがこのkmyblueをフォヌクしお新しいリポゞトリを䜜るほうがより自由でしょう。 -| Not ideal | Better | -| ------------------------------------ | ------------------------------------------------------------- | -| Fixed NoMethodError in RemovalWorker | Fix nil error when removing statuses caused by race condition | - -It is not always possible to phrase every change in such a manner, but it is desired. - -**The smaller the set of changes in the pull request is, the quicker it can be reviewed and merged.** Splitting tasks into multiple smaller pull requests is often preferable. - -**Pull requests that do not pass automated checks may not be reviewed**. In particular, you need to keep in mind: - -- Unit and integration tests (rspec, jest) -- Code style rules (rubocop, eslint) -- Normalization of locale files (i18n-tasks) - -## Documentation - -The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/documentation](https://github.com/mastodon/documentation). +- 匕甚機胜 +- お気に入り䞀芧の公開 +- ブックマヌク分類の公開 +- 他のサヌバヌの投皿に察しお他のサヌバヌのアカりントが行った絵文字リアクションの受け入れ diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 00000000000000..f3f43742d0c2aa --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,60 @@ +# kmyblueむンストヌル手順 + +## 共通の泚意事項 + +### 必須゜フトりェアのバヌゞョン + +Ruby、ElasticSearch、ImageMagick、PostgreSQLなど必須゜フトりェアのバヌゞョンは、本家Mastodonに準じたす。リリヌスノヌトに察応する本家Mastodonバヌゞョンが蚘茉されおいたすので、本家Mastodonのリリヌスノヌトから察応するバヌゞョンを探しお調べおください。 + +### 䞀般的な泚意事項 + +kmyblueは頻繁にバヌゞョンアップを行いたす。 + +- 本家Mastodonの開発䞭のバヌゞョンを平然ず取り蟌みたす +- バグが含たれおいるこずがありたす +- 特に最新コミットでは、デバッグ甚コヌドや、`kmy.blue`本番サヌバヌで動䜜確認を行うためのコヌドが含たれおいる堎合がありたす。ブランチの最新コミットではなく最新タグを取り蟌むこずを匷くおすすめしたす + +### ElasticSearchを䜿甚する堎合 + +kmyblueでは、sudachiの䜿甚を前提ずしおいたす。 + +䞋蚘URLより、ElasticSearchにSudachiプラグむンを远加しおください。 +ただし蟞曞ファむルsudachi dictionary archiveは手順曞で指瀺されたパスではなく`/etc/elasticsearch/sudachi`に栌玍しおください。 + +https://github.com/WorksApplications/elasticsearch-sudachi + +Sudachiむンストヌル終了埌、远加で`/etc/elasticsearch/sudachi/config.json`に䞋蚘を蚘述しお保存しおください。`system_full.dic`を䜿甚する堎合は適宜`systemDict`プロパティの内容を眮き換えおください。 + +```json +{ + "systemDict": "system_core.dic" +} +``` + +## 新芏むンストヌルの堎合 + +1. 本家Mastodonずセットアップ手順はほずんど䞀緒です。kmyblueが独自に必須゜フトりェアを远加したわけではありたせん。ただしkmyblueはMastodonの開発䞭コヌドを取り蟌んでいるので、Rubyなどのバヌゞョンアップ䜜業が必芁になる堎合がありたす。Mastodon公匏のセットアップ手順を盲信せず、画面の指瀺に埓っおむンストヌルを進めおください。CloudFlareを組み合わせおセットアップしたずき、サヌバヌに接続するず400が出るなどのトラブルが出るこずがありたすが、倧抵はMastodon本家由来のトラブルだず思われるので基本サポヌトはしたせん +2. ただひず぀差異がありたす。Gitリポゞトリはこのkmyblueに向けおください。`kb_development`ブランチの最新コミットではなく、`kb`で始たる最新のタグを取り蟌むこずを匷くおすすめしたす + +## 本家Mastodonからのマむグレヌションの堎合 + +kmyblueから本家Mastodonに戻りたい堎合もあるず思いたすので、**必ずデヌタベヌスのバックアップをずっおください**。 + +1. kmyblueのリリヌスノヌトに、kmyblueバヌゞョンに察応した本家Mastodonのバヌゞョンが蚘茉されおいたす。それを参照しお、たず本家Mastodonをそのバヌゞョンたでバヌゞョンアップしおください +2. Gitのリモヌトにkmyblueを远加しお、そのたたチェックアりトしおください +3. デヌタベヌスのマむグレヌションなどを行っおください + +``` +sudo systemctl stop mastodon-* + +bundle install +yarn install +RAILS_ENV=production bin/rails db:migrate +RAILS_ENV=production bin/rails assets:clobber +RAILS_ENV=production bin/rails assets:precompile + +# ElasticSearchを䜿甚する堎合 +RAILS_ENV=production bin/tootctl search deploy + +sudo systemctl start mastodon-web mastodon-streaming@4000 mastodon-sidekiq +``` diff --git a/README.md b/README.md index 768a1e876421e1..13885c96f80fc6 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,19 @@ kmyblueは[Mastodon](https://github.com/mastodon/mastodon)のフォヌクです kmyblueはフォヌク名であり、同時に[サヌバヌ名](https://kmy.blue)でもありたす。以䞋は特に蚘述がない限り、フォヌクずしおのkmyblueをさしたす。 -kmyblueは AGPL ラむセンスで公開されおいるため、どなたでも自由にフォヌクし、この゜ヌスコヌドを元に自分でサヌバヌを立おお公開するこずができたす。たた ActivityPub に参加するこずもできたす。サヌバヌkmyblueは創䜜䜜家向けのものですが、フォヌクずしおのkmyblueは䜜者の嫌いな政治に関する過激な話を取り扱うコミュニティ、創䜜掻動の䞀郚゚ロ関係含むたたは党䜓を吊定するコミュニティなども平等にお䜿いいただけたすし、サヌバヌkmyblueのルヌルを適甚する必芁もありたせん。 -ただしkmyblueにおいおテストコヌドは食りでしかないため、䞍具合が発生しおも自己責任になりたす。既知のバグもいく぀かありたすし、盎す予定のないものも含たれたす。 +kmyblueは AGPL ラむセンスで公開されおいるため、どなたでも自由にフォヌクし、この゜ヌスコヌドを元に自分でサヌバヌを立おお公開するこずができたす。たた ActivityPub に参加するこずもできたす。確かにサヌバヌkmyblueは創䜜䜜家向けのものですが、フォヌクずしおのkmyblueは䜜者ず政治的に察立するコミュニティ、創䜜掻動の䞀郚゚ロ関係含むたたは党䜓を吊定するコミュニティなどにも平等にお䜿いいただけたす。いかなるコミュニティがkmyblueフォヌクを䜿甚しおも、それ自䜓に䜜者が抗議するこずもありたせん。サヌバヌkmyblueのルヌルを適甚する必芁もなく、「Anyone But Kmyblue」なルヌルを蚭定するこずすら蚱容されたす。 +ただしkmyblueにおいお**テストコヌドは食り**でしかありたせん。独自機胜のテストを蚘述するだけでなく、本家のテストコヌドの補匷も行っおおりたすが、確認挏れは必ず発生するものです。䞍具合が発生しおも自己責任になりたす。既知のバグもいく぀かありたすし、盎す予定のないものも含たれたす。 テストコヌド、Lint どちらも動いおいたす。 +## むンストヌル方法 + +INSTALL.mdを参照しおください。 + +## 開発ぞの参加方法 + +CONTRIBUTING.mdを参照しおください。 + ## kmyblueの匷み ### 本家Mastodonぞの積極的远埓 diff --git a/app/chewy/accounts_index.rb b/app/chewy/accounts_index.rb index c854cdf01c3a5f..ad3816e4d63d75 100644 --- a/app/chewy/accounts_index.rb +++ b/app/chewy/accounts_index.rb @@ -1,7 +1,72 @@ # frozen_string_literal: true class AccountsIndex < Chewy::Index - settings index: index_preset(refresh_interval: '30s'), analysis: { + DEVELOPMENT_SETTINGS = { + filter: { + english_stop: { + type: 'stop', + stopwords: '_english_', + }, + + english_stemmer: { + type: 'stemmer', + language: 'english', + }, + + english_possessive_stemmer: { + type: 'stemmer', + language: 'possessive_english', + }, + }, + + analyzer: { + natural: { + tokenizer: 'standard', + filter: %w( + lowercase + asciifolding + cjk_width + elision + english_possessive_stemmer + english_stop + english_stemmer + ), + }, + + sudachi_analyzer: { + tokenizer: 'standard', + filter: %w( + lowercase + asciifolding + cjk_width + elision + english_possessive_stemmer + english_stop + english_stemmer + ), + }, + + verbatim: { + tokenizer: 'standard', + filter: %w(lowercase asciifolding cjk_width), + }, + + edge_ngram: { + tokenizer: 'edge_ngram', + filter: %w(lowercase asciifolding cjk_width), + }, + }, + + tokenizer: { + edge_ngram: { + type: 'edge_ngram', + min_gram: 1, + max_gram: 15, + }, + }, + }.freeze + + PRODUCTION_SETTINGS = { filter: { english_stop: { type: 'stop', @@ -77,7 +142,9 @@ class AccountsIndex < Chewy::Index discard_punctuation: 'true', }, }, - } + }.freeze + + settings index: index_preset(refresh_interval: '30s'), analysis: Rails.env.test? ? DEVELOPMENT_SETTINGS : PRODUCTION_SETTINGS index_scope ::Account.searchable.includes(:account_stat) diff --git a/app/chewy/public_statuses_index.rb b/app/chewy/public_statuses_index.rb index 4bcbe817d9af1f..d152b520390077 100644 --- a/app/chewy/public_statuses_index.rb +++ b/app/chewy/public_statuses_index.rb @@ -38,6 +38,19 @@ class PublicStatusesIndex < Chewy::Index ), }, + sudachi_analyzer: { + tokenizer: 'standard', + filter: %w( + lowercase + asciifolding + cjk_width + elision + english_possessive_stemmer + english_stop + english_stemmer + ), + }, + hashtag: { tokenizer: 'keyword', filter: %w( @@ -126,7 +139,7 @@ class PublicStatusesIndex < Chewy::Index }, }.freeze - settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: PRODUCTION_SETTINGS + settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: Rails.env.test? ? DEVELOPMENT_SETTINGS : PRODUCTION_SETTINGS index_scope ::Status.unscoped .kept diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index 5c993422de8421..3b83e2f31c1a51 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -37,6 +37,19 @@ class StatusesIndex < Chewy::Index ), }, + sudachi_analyzer: { + tokenizer: 'standard', + filter: %w( + lowercase + asciifolding + cjk_width + elision + english_possessive_stemmer + english_stop + english_stemmer + ), + }, + hashtag: { tokenizer: 'keyword', filter: %w( @@ -129,7 +142,7 @@ class StatusesIndex < Chewy::Index }, }.freeze - settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: PRODUCTION_SETTINGS + settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: Rails.env.test? ? DEVELOPMENT_SETTINGS : PRODUCTION_SETTINGS index_scope ::Status.unscoped.kept.without_reblogs.includes( :media_attachments, diff --git a/app/controllers/api/v1/antennas_controller.rb b/app/controllers/api/v1/antennas_controller.rb index c4b00c7784856d..37bfb7f55242f5 100644 --- a/app/controllers/api/v1/antennas_controller.rb +++ b/app/controllers/api/v1/antennas_controller.rb @@ -42,6 +42,6 @@ def set_antenna end def antenna_params - params.permit(:title, :list_id, :insert_feeds, :stl, :with_media_only, :ignore_reblog) + params.permit(:title, :list_id, :insert_feeds, :stl, :ltl, :with_media_only, :ignore_reblog) end end diff --git a/app/controllers/settings/preferences/other_controller.rb b/app/controllers/settings/preferences/other_controller.rb index a19fbf5c48c3c7..77b4fe10f832a9 100644 --- a/app/controllers/settings/preferences/other_controller.rb +++ b/app/controllers/settings/preferences/other_controller.rb @@ -1,6 +1,13 @@ # frozen_string_literal: true class Settings::Preferences::OtherController < Settings::Preferences::BaseController + include DtlHelper + + def show + @dtl_enabled = DTL_ENABLED + @dtl_tag = DTL_TAG + end + private def after_update_redirect_path diff --git a/app/helpers/dtl_helper.rb b/app/helpers/dtl_helper.rb new file mode 100644 index 00000000000000..d3c3a8c6621d00 --- /dev/null +++ b/app/helpers/dtl_helper.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module DtlHelper + DTL_ENABLED = ENV.fetch('DTL_ENABLED', 'false') == 'true' + DTL_TAG = ENV.fetch('DTL_TAG', 'kmyblue') +end diff --git a/app/javascript/icons/android-chrome-144x144.png b/app/javascript/icons/android-chrome-144x144.png old mode 100644 new mode 100755 index 698fb4a260b136..d636e94c43bf42 Binary files a/app/javascript/icons/android-chrome-144x144.png and b/app/javascript/icons/android-chrome-144x144.png differ diff --git a/app/javascript/icons/android-chrome-192x192.png b/app/javascript/icons/android-chrome-192x192.png old mode 100644 new mode 100755 index 2b6b632648f6b1..4a2681ffb93418 Binary files a/app/javascript/icons/android-chrome-192x192.png and b/app/javascript/icons/android-chrome-192x192.png differ diff --git a/app/javascript/icons/android-chrome-256x256.png b/app/javascript/icons/android-chrome-256x256.png old mode 100644 new mode 100755 index 51e3849a263062..8fab493ede6d3d Binary files a/app/javascript/icons/android-chrome-256x256.png and b/app/javascript/icons/android-chrome-256x256.png differ diff --git a/app/javascript/icons/android-chrome-36x36.png b/app/javascript/icons/android-chrome-36x36.png old mode 100644 new mode 100755 index 925f69c4fc7109..335d012db1c00c Binary files a/app/javascript/icons/android-chrome-36x36.png and b/app/javascript/icons/android-chrome-36x36.png differ diff --git a/app/javascript/icons/android-chrome-384x384.png b/app/javascript/icons/android-chrome-384x384.png old mode 100644 new mode 100755 index 9d256a83cb3af2..02b1e6fcedd9fd Binary files a/app/javascript/icons/android-chrome-384x384.png and b/app/javascript/icons/android-chrome-384x384.png differ diff --git a/app/javascript/icons/android-chrome-48x48.png b/app/javascript/icons/android-chrome-48x48.png old mode 100644 new mode 100755 index bcfe7475d0750b..43cf411b8c0680 Binary files a/app/javascript/icons/android-chrome-48x48.png and b/app/javascript/icons/android-chrome-48x48.png differ diff --git a/app/javascript/icons/android-chrome-512x512.png b/app/javascript/icons/android-chrome-512x512.png old mode 100644 new mode 100755 index bffacfb699c6b1..1856b80c7cb972 Binary files a/app/javascript/icons/android-chrome-512x512.png and b/app/javascript/icons/android-chrome-512x512.png differ diff --git a/app/javascript/icons/android-chrome-72x72.png b/app/javascript/icons/android-chrome-72x72.png old mode 100644 new mode 100755 index 16679d5731a6d6..335008bf852f7b Binary files a/app/javascript/icons/android-chrome-72x72.png and b/app/javascript/icons/android-chrome-72x72.png differ diff --git a/app/javascript/icons/android-chrome-96x96.png b/app/javascript/icons/android-chrome-96x96.png old mode 100644 new mode 100755 index 9ade87cf32c06b..d1cb0958220556 Binary files a/app/javascript/icons/android-chrome-96x96.png and b/app/javascript/icons/android-chrome-96x96.png differ diff --git a/app/javascript/icons/apple-touch-icon-1024x1024.png b/app/javascript/icons/apple-touch-icon-1024x1024.png old mode 100644 new mode 100755 index 8ec371eb27ddbe..c2a2d516ef5b03 Binary files a/app/javascript/icons/apple-touch-icon-1024x1024.png and b/app/javascript/icons/apple-touch-icon-1024x1024.png differ diff --git a/app/javascript/icons/apple-touch-icon-114x114.png b/app/javascript/icons/apple-touch-icon-114x114.png old mode 100644 new mode 100755 index e1563f51e5758b..218b4154390789 Binary files a/app/javascript/icons/apple-touch-icon-114x114.png and b/app/javascript/icons/apple-touch-icon-114x114.png differ diff --git a/app/javascript/icons/apple-touch-icon-120x120.png b/app/javascript/icons/apple-touch-icon-120x120.png old mode 100644 new mode 100755 index e9a5f5b0e57595..be53bc7c1015b9 Binary files a/app/javascript/icons/apple-touch-icon-120x120.png and b/app/javascript/icons/apple-touch-icon-120x120.png differ diff --git a/app/javascript/icons/apple-touch-icon-144x144.png b/app/javascript/icons/apple-touch-icon-144x144.png old mode 100644 new mode 100755 index 698fb4a260b136..cbb055732f186a Binary files a/app/javascript/icons/apple-touch-icon-144x144.png and b/app/javascript/icons/apple-touch-icon-144x144.png differ diff --git a/app/javascript/icons/apple-touch-icon-152x152.png b/app/javascript/icons/apple-touch-icon-152x152.png old mode 100644 new mode 100755 index 0cc93cc2888c02..3a7975c0541c75 Binary files a/app/javascript/icons/apple-touch-icon-152x152.png and b/app/javascript/icons/apple-touch-icon-152x152.png differ diff --git a/app/javascript/icons/apple-touch-icon-167x167.png b/app/javascript/icons/apple-touch-icon-167x167.png old mode 100644 new mode 100755 index 9bbbf53120cc00..25be4eb5f5bba5 Binary files a/app/javascript/icons/apple-touch-icon-167x167.png and b/app/javascript/icons/apple-touch-icon-167x167.png differ diff --git a/app/javascript/icons/apple-touch-icon-180x180.png b/app/javascript/icons/apple-touch-icon-180x180.png old mode 100644 new mode 100755 index 329b803b918992..dc0e9bc20b8086 Binary files a/app/javascript/icons/apple-touch-icon-180x180.png and b/app/javascript/icons/apple-touch-icon-180x180.png differ diff --git a/app/javascript/icons/apple-touch-icon-57x57.png b/app/javascript/icons/apple-touch-icon-57x57.png old mode 100644 new mode 100755 index e00e142c640eca..bb0dc957cd896b Binary files a/app/javascript/icons/apple-touch-icon-57x57.png and b/app/javascript/icons/apple-touch-icon-57x57.png differ diff --git a/app/javascript/icons/apple-touch-icon-60x60.png b/app/javascript/icons/apple-touch-icon-60x60.png old mode 100644 new mode 100755 index 011285b564703b..9143a0bf07360d Binary files a/app/javascript/icons/apple-touch-icon-60x60.png and b/app/javascript/icons/apple-touch-icon-60x60.png differ diff --git a/app/javascript/icons/apple-touch-icon-72x72.png b/app/javascript/icons/apple-touch-icon-72x72.png old mode 100644 new mode 100755 index 16679d5731a6d6..2b7d19484c9e3a Binary files a/app/javascript/icons/apple-touch-icon-72x72.png and b/app/javascript/icons/apple-touch-icon-72x72.png differ diff --git a/app/javascript/icons/apple-touch-icon-76x76.png b/app/javascript/icons/apple-touch-icon-76x76.png old mode 100644 new mode 100755 index 83c87488768532..0985e33bcb20f7 Binary files a/app/javascript/icons/apple-touch-icon-76x76.png and b/app/javascript/icons/apple-touch-icon-76x76.png differ diff --git a/app/javascript/icons/favicon-16x16.png b/app/javascript/icons/favicon-16x16.png old mode 100644 new mode 100755 index eed8e0035c34b5..1326ba04627bcb Binary files a/app/javascript/icons/favicon-16x16.png and b/app/javascript/icons/favicon-16x16.png differ diff --git a/app/javascript/icons/favicon-32x32.png b/app/javascript/icons/favicon-32x32.png old mode 100644 new mode 100755 index 9165746bcfa5f7..f5058cb0a56e41 Binary files a/app/javascript/icons/favicon-32x32.png and b/app/javascript/icons/favicon-32x32.png differ diff --git a/app/javascript/icons/favicon-48x48.png b/app/javascript/icons/favicon-48x48.png old mode 100644 new mode 100755 index 259676c0a9217e..6253d054c7f48f Binary files a/app/javascript/icons/favicon-48x48.png and b/app/javascript/icons/favicon-48x48.png differ diff --git a/app/javascript/mastodon/actions/antennas.js b/app/javascript/mastodon/actions/antennas.js index 51002b6c5891cb..4716897586fe5e 100644 --- a/app/javascript/mastodon/actions/antennas.js +++ b/app/javascript/mastodon/actions/antennas.js @@ -236,10 +236,10 @@ export const createAntennaFail = error => ({ error, }); -export const updateAntenna = (id, title, shouldReset, list_id, stl, with_media_only, ignore_reblog, insert_feeds) => (dispatch, getState) => { +export const updateAntenna = (id, title, shouldReset, list_id, stl, ltl, with_media_only, ignore_reblog, insert_feeds) => (dispatch, getState) => { dispatch(updateAntennaRequest(id)); - api(getState).put(`/api/v1/antennas/${id}`, { title, list_id, stl, with_media_only, ignore_reblog, insert_feeds }).then(({ data }) => { + api(getState).put(`/api/v1/antennas/${id}`, { title, list_id, stl, ltl, with_media_only, ignore_reblog, insert_feeds }).then(({ data }) => { dispatch(updateAntennaSuccess(data)); if (shouldReset) { diff --git a/app/javascript/mastodon/features/antenna_setting/index.jsx b/app/javascript/mastodon/features/antenna_setting/index.jsx index 83f21fd066ec3b..086c1de6b63f84 100644 --- a/app/javascript/mastodon/features/antenna_setting/index.jsx +++ b/app/javascript/mastodon/features/antenna_setting/index.jsx @@ -198,31 +198,37 @@ class AntennaSetting extends PureComponent { onStlToggle = ({ target }) => { const { dispatch } = this.props; const { id } = this.props.params; - dispatch(updateAntenna(id, undefined, false, undefined, target.checked, undefined, undefined, undefined)); + dispatch(updateAntenna(id, undefined, false, undefined, target.checked, undefined, undefined, undefined, undefined)); + }; + + onLtlToggle = ({ target }) => { + const { dispatch } = this.props; + const { id } = this.props.params; + dispatch(updateAntenna(id, undefined, false, undefined, undefined, target.checked, undefined, undefined, undefined)); }; onMediaOnlyToggle = ({ target }) => { const { dispatch } = this.props; const { id } = this.props.params; - dispatch(updateAntenna(id, undefined, false, undefined, undefined, target.checked, undefined, undefined)); + dispatch(updateAntenna(id, undefined, false, undefined, undefined, undefined, target.checked, undefined, undefined)); }; onIgnoreReblogToggle = ({ target }) => { const { dispatch } = this.props; const { id } = this.props.params; - dispatch(updateAntenna(id, undefined, false, undefined, undefined, undefined, target.checked, undefined)); + dispatch(updateAntenna(id, undefined, false, undefined, undefined, undefined, undefined, target.checked, undefined)); }; onNoInsertFeedsToggle = ({ target }) => { const { dispatch } = this.props; const { id } = this.props.params; - dispatch(updateAntenna(id, undefined, false, undefined, undefined, undefined, undefined, target.checked)); + dispatch(updateAntenna(id, undefined, false, undefined, undefined, undefined, undefined, undefined, target.checked)); }; onSelect = value => { const { dispatch } = this.props; const { id } = this.props.params; - dispatch(updateAntenna(id, undefined, false, value.value, undefined, undefined, undefined, undefined)); + dispatch(updateAntenna(id, undefined, false, value.value, undefined, undefined, undefined, undefined, undefined)); }; onHomeSelect = () => this.onSelect({ value: '0' }); @@ -293,6 +299,7 @@ class AntennaSetting extends PureComponent { const pinned = !!columnId; const title = antenna ? antenna.get('title') : id; const isStl = antenna ? antenna.get('stl') : undefined; + const isLtl = antenna ? antenna.get('ltl') : undefined; const isMediaOnly = antenna ? antenna.get('with_media_only') : undefined; const isIgnoreReblog = antenna ? antenna.get('ignore_reblog') : undefined; const isInsertFeeds = antenna ? antenna.get('insert_feeds') : undefined; @@ -312,7 +319,7 @@ class AntennaSetting extends PureComponent { } let columnSettings; - if (!isStl) { + if (!isStl && !isLtl) { columnSettings = ( <>
@@ -339,6 +346,12 @@ class AntennaSetting extends PureComponent {

); + } else if (isLtl) { + stlAlert = ( +
+

+
+ ); } const rangeRadioValues = ImmutableList([ @@ -384,12 +397,23 @@ class AntennaSetting extends PureComponent { -
- - -
+ {!isLtl && ( +
+ + +
+ )} + + {!isStl && ( +
+ + +
+ )}
@@ -429,7 +453,7 @@ class AntennaSetting extends PureComponent { )} - {!isStl && ( + {!isStl && !isLtl && ( <>

diff --git a/app/javascript/mastodon/features/lists/index.jsx b/app/javascript/mastodon/features/lists/index.jsx index e947a40b83990d..fb52973d23d149 100644 --- a/app/javascript/mastodon/features/lists/index.jsx +++ b/app/javascript/mastodon/features/lists/index.jsx @@ -22,6 +22,7 @@ import NewListForm from './components/new_list_form'; const messages = defineMessages({ heading: { id: 'column.lists', defaultMessage: 'Lists' }, subheading: { id: 'lists.subheading', defaultMessage: 'Your lists' }, + with_antenna: { id: 'lists.with_antenna', defaultMessage: 'Antenna' }, }); const getOrderedLists = createSelector([state => state.get('lists')], lists => { @@ -76,7 +77,8 @@ class Lists extends ImmutablePureComponent { bindToDocument={!multiColumn} > {lists.map(list => - , + ( 0) ? intl.formatMessage(messages.with_antenna) : undefined} />), )} diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx index 07e0980d213fcb..5b412ba14735f0 100644 --- a/app/javascript/mastodon/features/ui/components/navigation_panel.jsx +++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx @@ -7,7 +7,7 @@ import { Link } from 'react-router-dom'; import { WordmarkLogo } from 'mastodon/components/logo'; import NavigationPortal from 'mastodon/components/navigation_portal'; -import { enableDtlMenu, timelinePreview, trendsEnabled } from 'mastodon/initial_state'; +import { enableDtlMenu, timelinePreview, trendsEnabled, dtlTag } from 'mastodon/initial_state'; import { transientSingleColumn } from 'mastodon/is_mobile'; import ColumnLink from './column_link'; @@ -93,8 +93,8 @@ class NavigationPanel extends Component { )} - {signedIn && enableDtlMenu && ( - + {signedIn && enableDtlMenu && dtlTag && ( + )} {!signedIn && explorer} diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js index 356f7515b2bbd1..3e84866233aaf6 100644 --- a/app/javascript/mastodon/initial_state.js +++ b/app/javascript/mastodon/initial_state.js @@ -58,6 +58,7 @@ * @property {string} display_media * @property {boolean} display_media_expand * @property {string} domain + * @property {string} dtl_tag * @property {boolean} enable_login_privacy * @property {boolean} enable_dtl_menu * @property {boolean=} expand_spoilers @@ -124,6 +125,7 @@ export const disabledAccountId = getMeta('disabled_account_id'); export const displayMedia = getMeta('display_media'); export const displayMediaExpand = getMeta('display_media_expand'); export const domain = getMeta('domain'); +export const dtlTag = getMeta('dtl_tag'); export const enableLoginPrivacy = getMeta('enable_login_privacy'); export const enableDtlMenu = getMeta('enable_dtl_menu'); export const expandSpoilers = getMeta('expand_spoilers'); diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 717b51443fd89a..a73bfe2d7e6727 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -206,7 +206,7 @@ "compose_form.direct_message_warning_learn_more": "もっず詳しく", "compose_form.encryption_warning": "Mastodonの投皿ぱンドツヌ゚ンド暗号化に察応しおいたせん。安党に送受信されるべき情報をMastodonで共有しないでください。", "compose_form.hashtag_warning": "この投皿は公開蚭定ではないのでハッシュタグの䞀芧に衚瀺されたせん。公開投皿だけがハッシュタグで怜玢できたす。", - "compose_form.limited_post_warning": "限定投皿は珟状、ごく䞀郚のMastodonサヌバヌにしか届きたせん2023幎8月時点でFedibird、kmyblue、Pawooなど䞀郚のみです", + "compose_form.limited_post_warning": "限定投皿は珟状、ごく䞀郚のMastodonサヌバヌにしか届きたせん2023幎9月時点でFedibird、kmyblueなど䞀郚のみです", "compose_form.lock_disclaimer": "あなたのアカりントは{locked}になっおいたせん。誰でもあなたをフォロヌするこずができ、フォロワヌ限定の投皿を芋るこずができたす。", "compose_form.lock_disclaimer.lock": "承認制", "compose_form.markdown.marked": "Markdown有効", diff --git a/app/models/antenna.rb b/app/models/antenna.rb index 29872be182594a..c14892e138bac4 100644 --- a/app/models/antenna.rb +++ b/app/models/antenna.rb @@ -25,6 +25,7 @@ # stl :boolean default(FALSE), not null # ignore_reblog :boolean default(FALSE), not null # insert_feeds :boolean default(FALSE), not null +# ltl :boolean default(FALSE), not null # class Antenna < ApplicationRecord include Expireable @@ -45,16 +46,19 @@ class Antenna < ApplicationRecord belongs_to :list, optional: true scope :stls, -> { where(stl: true) } + scope :ltls, -> { where(ltl: true) } scope :all_keywords, -> { where(any_keywords: true) } scope :all_domains, -> { where(any_domains: true) } scope :all_accounts, -> { where(any_accounts: true) } scope :all_tags, -> { where(any_tags: true) } scope :availables, -> { where(available: true).where(Arel.sql('any_keywords = FALSE OR any_domains = FALSE OR any_accounts = FALSE OR any_tags = FALSE')) } scope :available_stls, -> { where(available: true, stl: true) } + scope :available_ltls, -> { where(available: true, stl: false, ltl: true) } validate :list_owner validate :validate_limit validate :validate_stl_limit + validate :validate_ltl_limit def list_owner raise Mastodon::ValidationError, I18n.t('antennas.errors.invalid_list_owner') if !list_id.zero? && list.present? && list.account != account @@ -235,6 +239,22 @@ def validate_stl_limit stls = account.antennas.where(stl: true).where.not(id: id) - errors.add(:base, I18n.t('antennas.errors.over_stl_limit', limit: 1)) if list_id.zero? ? stls.any? { |tl| tl.list_id.zero? } : stls.any? { |tl| tl.list_id != 0 } + errors.add(:base, I18n.t('antennas.errors.over_stl_limit', limit: 1)) if if insert_feeds + list_id.zero? ? stls.any? { |tl| tl.list_id.zero? } : stls.any? { |tl| tl.list_id != 0 } + else + stls.any? { |tl| !tl.insert_feeds } + end + end + + def validate_ltl_limit + return unless ltl + + ltls = account.antennas.where(ltl: true).where.not(id: id) + + errors.add(:base, I18n.t('antennas.errors.over_ltl_limit', limit: 1)) if if insert_feeds + list_id.zero? ? ltls.any? { |tl| tl.list_id.zero? } : ltls.any? { |tl| tl.list_id != 0 } + else + ltls.any? { |tl| !tl.insert_feeds } + end end end diff --git a/app/models/status.rb b/app/models/status.rb index 786daa142c47f9..45e221d71c27a1 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -43,6 +43,7 @@ class Status < ApplicationRecord include RateLimitable include StatusSafeReblogInsert include StatusSearchConcern + include DtlHelper rate_limit by: :account, family: :statuses @@ -291,7 +292,7 @@ def reported? end def dtl? - tags.where(name: 'kmyblue').exists? + tags.where(name: DTL_TAG).exists? end def emojis diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index edc6bfe7136b85..45888d7572d32d 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -2,6 +2,7 @@ class InitialStateSerializer < ActiveModel::Serializer include RoutingHelper + include DtlHelper attributes :meta, :compose, :accounts, :media_attachments, :settings, @@ -35,6 +36,7 @@ def meta trends_as_landing_page: Setting.trends_as_landing_page, status_page_url: Setting.status_page_url, sso_redirect: sso_redirect, + dtl_tag: DTL_ENABLED ? DTL_TAG : nil, } if object.current_account diff --git a/app/serializers/rest/antenna_serializer.rb b/app/serializers/rest/antenna_serializer.rb index 0d449a208931da..c0d03c7282f5c0 100644 --- a/app/serializers/rest/antenna_serializer.rb +++ b/app/serializers/rest/antenna_serializer.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class REST::AntennaSerializer < ActiveModel::Serializer - attributes :id, :title, :stl, :insert_feeds, :with_media_only, :ignore_reblog, :accounts_count, :domains_count, :tags_count, :keywords_count + attributes :id, :title, :stl, :ltl, :insert_feeds, :with_media_only, :ignore_reblog, :accounts_count, :domains_count, :tags_count, :keywords_count class ListSerializer < ActiveModel::Serializer attributes :id, :title diff --git a/app/services/delivery_antenna_service.rb b/app/services/delivery_antenna_service.rb index 9c4914515d7ab1..e494321917ce22 100644 --- a/app/services/delivery_antenna_service.rb +++ b/app/services/delivery_antenna_service.rb @@ -2,16 +2,21 @@ class DeliveryAntennaService include FormattingHelper + include DtlHelper - def call(status, update, stl_home) + def call(status, update, **options) @status = status @account = @status.account @update = update - if stl_home - delivery_stl! - else + mode = options[:mode] || :home + case mode + when :home delivery! + when :stl + delivery_stl! + when :ltl + delivery_ltl! end end @@ -19,6 +24,8 @@ def call(status, update, stl_home) def delivery! must_dtl_tag = @account.dissubscribable + return if must_dtl_tag && !DTL_ENABLED + tag_ids = @status.tags.pluck(:id) domain = @account.domain || Rails.configuration.x.local_domain follower_ids = @status.unlisted_visibility? ? @status.account.followers.pluck(:id) : [] @@ -31,7 +38,7 @@ def delivery! antennas = Antenna.where(id: antennas.select(:id)) if must_dtl_tag - dtl_tag = Tag.find_or_create_by_names('kmyblue').first + dtl_tag = Tag.find_or_create_by_names(DTL_TAG).first return if !dtl_tag || tag_ids.exclude?(dtl_tag.id) antennas = antennas.left_joins(:antenna_tags).where(antenna_tags: { tag_id: dtl_tag.id }) @@ -44,7 +51,7 @@ def delivery! antennas = antennas.where(account: @status.mentioned_accounts) if @status.visibility.to_sym == :limited antennas = antennas.where(with_media_only: false) unless @status.with_media? antennas = antennas.where(ignore_reblog: false) if @status.reblog? - antennas = antennas.where(stl: false) + antennas = antennas.where(stl: false, ltl: false) collection = AntennaCollection.new(@status, @update, false) content = extract_status_plain_text_with_spoiler_text(@status) @@ -72,7 +79,8 @@ def delivery_stl! antennas = antennas.where(account_id: Account.without_suspended.joins(:user).select('accounts.id').where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago)) home_post = !@account.domain.nil? || @status.reblog? || [:public, :public_unlisted, :login].exclude?(@status.visibility.to_sym) - antennas = antennas.where(account: @account.followers).or(antennas.where(account: @account)).where.not(list_id: 0) if home_post + antennas = antennas.where(account: @account.followers).or(antennas.where(account: @account)).where('insert_feeds IS FALSE OR list_id > 0') if home_post && !@status.limited_visibility? + antennas = antennas.where(account: @status.mentioned_accounts).or(antennas.where(account: @account)).where('insert_feeds IS FALSE OR list_id > 0') if @status.limited_visibility? collection = AntennaCollection.new(@status, @update, home_post) @@ -87,6 +95,27 @@ def delivery_stl! collection.deliver! end + def delivery_ltl! + return if %i(public public_unlisted login).exclude?(@status.visibility.to_sym) + return unless @account.local? + return if @status.reblog? + + antennas = Antenna.available_ltls + antennas = antennas.where(account_id: Account.without_suspended.joins(:user).select('accounts.id').where('users.current_sign_in_at > ?', User::ACTIVE_DURATION.ago)) + + collection = AntennaCollection.new(@status, @update, false) + + antennas.in_batches do |ans| + ans.each do |antenna| + next if antenna.expired? + + collection.push(antenna) + end + end + + collection.deliver! + end + class AntennaCollection def initialize(status, update, stl_home = false) # rubocop:disable Style/OptionalBooleanParameter @status = status diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index 156a811f51727f..a2e2653813ab4d 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -2,6 +2,7 @@ class FanOutOnWriteService < BaseService include Redisable + include DtlHelper # Push a status into home and mentions feeds # @param [Status] status @@ -51,11 +52,13 @@ def fan_out_to_local_recipients! when :public, :unlisted, :public_unlisted, :login, :private deliver_to_all_followers! deliver_to_lists! - deliver_to_antennas! if !@account.dissubscribable || (@status.dtl? && @account.user&.setting_dtl_force_subscribable && @status.tags.exists?(name: 'kmyblue')) + deliver_to_antennas! if !@account.dissubscribable || (@status.dtl? && DTL_ENABLED && @account.user&.setting_dtl_force_subscribable && @status.tags.exists?(name: DTL_TAG)) deliver_to_stl_antennas! + deliver_to_ltl_antennas! when :limited deliver_to_lists_mentioned_accounts_only! deliver_to_antennas! unless @account.dissubscribable + deliver_to_stl_antennas! deliver_to_mentioned_followers! else deliver_to_mentioned_followers! @@ -135,11 +138,15 @@ def deliver_to_lists_mentioned_accounts_only! end def deliver_to_stl_antennas! - DeliveryAntennaService.new.call(@status, @options[:update], true) + DeliveryAntennaService.new.call(@status, @options[:update], mode: :stl) + end + + def deliver_to_ltl_antennas! + DeliveryAntennaService.new.call(@status, @options[:update], mode: :ltl) end def deliver_to_antennas! - DeliveryAntennaService.new.call(@status, @options[:update], false) + DeliveryAntennaService.new.call(@status, @options[:update], mode: :home) end def deliver_to_mentioned_followers! diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index bd0e2a462aaf81..2e5b765d865f02 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -3,6 +3,7 @@ class PostStatusService < BaseService include Redisable include LanguagesHelper + include DtlHelper MIN_SCHEDULE_OFFSET = 5.minutes.freeze @@ -101,8 +102,10 @@ def load_circle end def overwrite_dtl_post + return unless DTL_ENABLED + raw_tags = Extractor.extract_hashtags(@text) - return if raw_tags.exclude?('kmyblue') + return if raw_tags.exclude?(DTL_TAG) return unless %i(public public_unlisted unlisted).include?(@visibility) @visibility = :unlisted if @account.user&.setting_dtl_force_with_tag == :full diff --git a/app/views/admin/ng_words/show.html.haml b/app/views/admin/ng_words/show.html.haml index be8b56ac400a52..d76757e4c0d9fb 100644 --- a/app/views/admin/ng_words/show.html.haml +++ b/app/views/admin/ng_words/show.html.haml @@ -8,7 +8,7 @@ = render 'shared/error_messages', object: @admin_settings .fields-group - = f.input :ng_words, wrapper: :with_label, as: :text, input_html: { rows: 12 }, label: t('admin.ng_words.keywords') + = f.input :ng_words, wrapper: :with_label, as: :text, input_html: { rows: 12 }, label: t('admin.ng_words.keywords'), hint: t('admin.ng_words.keywords_hint') .fields-group = f.input :post_hash_tags_max, wrapper: :with_label, as: :integer, label: t('admin.ng_words.post_hash_tags_max') diff --git a/app/views/settings/preferences/other/show.html.haml b/app/views/settings/preferences/other/show.html.haml index 55bf9c6ddacc10..4ac61240418ebc 100644 --- a/app/views/settings/preferences/other/show.html.haml +++ b/app/views/settings/preferences/other/show.html.haml @@ -42,18 +42,20 @@ .fields-group = ff.input :'web.enable_login_privacy', wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_enable_login_privacy'), hint: false - %h4= t 'preferences.dtl' + - if @dtl_enabled - %p.hint= t 'preferences.dtl_hint' + %h4= t 'preferences.dtl' - .fields-group - = ff.input :'web.enable_dtl_menu', wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_dtl_menu'), hint: I18n.t('simple_form.hints.defaults.setting_dtl_menu') + %p.hint= t 'preferences.dtl_hint', tag: @dtl_tag - .fields-group - = ff.input :dtl_force_with_tag, kmyblue: true, collection: ['full', 'searchability', 'none'], label_method: lambda { |item| safe_join([t("simple_form.labels.dtl_force_with_tag.#{item}")]) }, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label, label: I18n.t('simple_form.labels.defaults.setting_dtl_force_with_tag'), hint: I18n.t('simple_form.hints.defaults.setting_dtl_force_with_tag') + .fields-group + = ff.input :'web.enable_dtl_menu', wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_dtl_menu') - .fields-group - = ff.input :dtl_force_subscribable, wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_dtl_force_subscribable'), hint: I18n.t('simple_form.hints.defaults.setting_dtl_force_subscribable') + .fields-group + = ff.input :dtl_force_with_tag, kmyblue: true, collection: ['full', 'searchability', 'none'], label_method: lambda { |item| safe_join([t("simple_form.labels.dtl_force_with_tag.#{item}")]) }, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label, label: I18n.t('simple_form.labels.defaults.setting_dtl_force_with_tag'), hint: I18n.t('simple_form.hints.defaults.setting_dtl_force_with_tag', tag: @dtl_tag) + + .fields-group + = ff.input :dtl_force_subscribable, wrapper: :with_label, kmyblue: true, label: I18n.t('simple_form.labels.defaults.setting_dtl_force_subscribable'), hint: I18n.t('simple_form.hints.defaults.setting_dtl_force_subscribable') %h4= t 'preferences.public_timelines' diff --git a/config/locales/en.yml b/config/locales/en.yml index 9f87e2c6c462d7..b05633ad47935e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -600,6 +600,7 @@ en: enable_block_emoji_reaction_settings: Enable block emoji reactions settings for users hide_local_users_for_anonymous: Hide timeline local user posts from anonymous keywords: Reject keywords + keywords_hint: The first character of the line is "?". to use regular expressions post_hash_tags_max: Hash tags max for posts test_error: Testing is returned any errors title: NG words and against spams @@ -780,6 +781,7 @@ en: hint: This keywords is applied to public posts only.. keywords: Sensitive keywords keywords_for_all: Sensitive keywords (Contains CW alert) + keywords_for_all_hint: The first character of the line is "?". to use regular expressions title: Sensitive words and moderation options settings: about: @@ -1111,6 +1113,7 @@ en: invalid_context: None or invalid context supplied invalid_list_owner: This list is not yours over_limit: You have exceeded the limit of %{limit} antennas + over_ltl_limit: You have exceeded the limit of %{limit} ltl antennas over_stl_limit: You have exceeded the limit of %{limit} stl antennas index: contexts: Antennas in %{contexts} @@ -1642,7 +1645,7 @@ en: too_many_options: can't contain more than %{max} items preferences: dtl: Deep timeline - dtl_hint: "You can join deep timeline with #kmyblue tag. Following settings make convenient to use deep timeline." + dtl_hint: "You can join deep timeline with #%{tag} tag. Following settings make convenient to use deep timeline." other: Other posting_defaults: Posting defaults public_timelines: Public timelines diff --git a/config/locales/ja.yml b/config/locales/ja.yml index f219a97112a799..0b36b6dc423929 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -598,6 +598,7 @@ ja: enable_block_emoji_reaction_settings: 各ナヌザヌにスタンプ機胜のブロック蚭定項目を解攟する hide_local_users_for_anonymous: ログむンしおいない状態でロヌカルナヌザヌの投皿をタむムラむンから取埗できないようにする keywords: 投皿できないキヌワヌド + keywords_hint: 行を「?」で始めるず、正芏衚珟が䜿えたす post_hash_tags_max: 投皿に蚭定可胜なハッシュタグの最倧数 test_error: NGワヌドのテストに倱敗したした。正芏衚珟のミスが含たれおいるかもしれたせん title: NGワヌドずスパム @@ -775,7 +776,7 @@ ja: hint: センシティブなキヌワヌドの蚭定は、圓サヌバヌのロヌカルナヌザヌによる公開範囲「公開」「ロヌカル公開」「ログむンナヌザヌのみ」に察しお適甚されたす。 keywords: センシティブなキヌワヌド譊告文は陀倖 keywords_for_all: センシティブなキヌワヌド譊告文にも適甚 - keywords_for_all_hint: ここで指定したキヌワヌドを含む投皿は匷制的にCWになりたす。譊告文にも含たれおいればCWになりたす + keywords_for_all_hint: ここで指定したキヌワヌドを含む投皿は匷制的にCWになりたす。譊告文にも含たれおいればCWになりたす。行が「?」で始たっおいれば正芏衚珟が䜿えたす keywords_hint: ここで指定したキヌワヌドを含む投皿は匷制的にCWになりたす。ただし譊告文に䜿甚しおいた堎合は無芖されたす title: センシティブ単語ず蚭定 settings: @@ -1031,6 +1032,7 @@ ja: keywords: 登録できるキヌワヌド数の䞊限に達しおいたす tags: 登録できるタグ数の䞊限に達しおいたす over_limit: 所持できるアンテナ数 %{limit}を超えおいたす + over_ltl_limit: 所持できるLTLモヌド付きアンテナ数 (ホヌム/リストそれぞれに぀き%{limit}) を超えおいたす over_stl_limit: 所持できるSTLモヌド付きアンテナ数 (ホヌム/リストそれぞれに぀き%{limit}) を超えおいたす too_short_keyword: キヌワヌドが短すぎたす edit: @@ -1108,7 +1110,7 @@ ja: help_html: CAPTCHAの解決に問題がある堎合は、 %{email} たでお問い合わせください。お手䌝いいたしたす。 hint_html: もう䞀぀だけあなたが人間であるこずを確認する必芁がありたす(スパムを防ぐためです)。 以䞋のCAPTCHAを解き、「続ける」をクリックしたす。 title: セキュリティチェック - cloudflare_with_registering: 登録時にCloudflareの画面が衚瀺されたす。登録できないずきは tt@kmycode.net たでご連絡ください + cloudflare_with_registering: 登録時にCloudflareの画面が衚瀺されたす。登録できないずきは管理者ぞご連絡ください confirmations: wrong_email_hint: メヌルアドレスが正しくない堎合は、アカりント蚭定で倉曎できたす。 delete_account: アカりントの削陀 @@ -1583,7 +1585,7 @@ ja: too_many_options: は%{max}個たでです preferences: dtl: ディヌプタむムラむン - dtl_hint: "#kmyblue ハッシュタグに参加するこずで、ディヌプタむムラむンに投皿できたす。ここではディヌプタむムラむンを利甚しやすくするための蚭定ができたす。" + dtl_hint: "#%{tag} ハッシュタグに参加するこずで、ディヌプタむムラむンに投皿できたす。ここではディヌプタむムラむンを利甚しやすくするための蚭定ができたす。" other: その他 posting_defaults: デフォルトの投皿蚭定 public_timelines: 公開タむムラむン diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 24d5981302b986..4a927ea211c1e2 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -67,7 +67,7 @@ en: setting_display_media_hide_all: Always hide media setting_display_media_show_all: Always show media setting_dtl_force_subscribable: Your post can be detected local user's antenna to subscribe deep timeline - setting_dtl_force_with_tag: "With using #kmyblue tag, your post settings will be changed forcibly" + setting_dtl_force_with_tag: "With using #%{tag} tag, your post settings will be changed forcibly" setting_dtl_menu: Show DTL menu on web setting_use_blurhash: Gradients are based on the colors of the hidden visuals but obfuscate any details setting_use_pending_items: Hide timeline updates behind a click instead of automatically scrolling the feed diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index ccda4fa8cf4b18..699173a95119ec 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -69,7 +69,7 @@ ja: setting_display_media_hide_all: メディアを垞に隠す setting_display_media_show_all: メディアを垞に衚瀺する setting_dtl_force_subscribable: 賌読拒吊蚭定に関係なく、ディヌプタむムラむンに向けた投皿はアンテナに掲茉されたす。ディヌプタむムラむンをアンテナ経由で閲芧しおいる人にあなたの発蚀が届きたす - setting_dtl_force_with_tag: "ハッシュタグ #kmyblue を぀けお投皿するずき、公開範囲ず怜玢蚱可を匷制的に眮き換えるかを蚭定したす" + setting_dtl_force_with_tag: "ハッシュタグ #%{tag} を぀けお投皿するずき、公開範囲ず怜玢蚱可を匷制的に眮き換えるかを蚭定したす" setting_emoji_reaction_streaming_notify_impl2: 圓該サヌバヌの独自機胜に察応したアプリを利甚時に、スタンプ機胜を利甚できたす。動䜜確認しおいないためそもそもそのようなアプリ自䜓を確認できおいないため正しく動かない堎合がありたす setting_hide_network: フォロヌずフォロワヌの情報がプロフィヌルペヌゞで芋られないようにしたす setting_link_preview: プレビュヌ生成を停止するこずは、センシティブなサむトぞのリンクを頻繁に投皿する人にも有効かもしれたせん diff --git a/db/migrate/20230911022527_add_ltl_to_antennas.rb b/db/migrate/20230911022527_add_ltl_to_antennas.rb new file mode 100644 index 00000000000000..18eeb19a3ccde1 --- /dev/null +++ b/db/migrate/20230911022527_add_ltl_to_antennas.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class AddLtlToAntennas < ActiveRecord::Migration[7.0] + include Mastodon::MigrationHelpers + + disable_ddl_transaction! + + def change + safety_assured do + add_column_with_default :antennas, :ltl, :boolean, default: false, allow_null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 1ade0757828a37..f322d5e715336d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_09_07_150100) do +ActiveRecord::Schema[7.0].define(version: 2023_09_11_022527) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -50,6 +50,15 @@ t.index ["account_id", "domain"], name: "index_account_domain_blocks_on_account_id_and_domain", unique: true end + create_table "account_groups", force: :cascade do |t| + t.bigint "account_id", null: false + t.bigint "group_account_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["account_id"], name: "index_account_groups_on_account_id" + t.index ["group_account_id"], name: "index_account_groups_on_group_account_id" + end + create_table "account_migrations", force: :cascade do |t| t.bigint "account_id" t.string "acct", default: "", null: false @@ -313,6 +322,7 @@ t.boolean "stl", default: false, null: false t.boolean "ignore_reblog", default: false, null: false t.boolean "insert_feeds", default: false, null: false + t.boolean "ltl", default: false, null: false t.index ["account_id"], name: "index_antennas_on_account_id" t.index ["any_accounts"], name: "index_antennas_on_any_accounts" t.index ["any_domains"], name: "index_antennas_on_any_domains" @@ -1357,6 +1367,7 @@ add_foreign_key "account_conversations", "conversations", on_delete: :cascade add_foreign_key "account_deletion_requests", "accounts", on_delete: :cascade add_foreign_key "account_domain_blocks", "accounts", name: "fk_206c6029bd", on_delete: :cascade + add_foreign_key "account_groups", "accounts", on_delete: :cascade add_foreign_key "account_migrations", "accounts", column: "target_account_id", on_delete: :nullify add_foreign_key "account_migrations", "accounts", on_delete: :cascade add_foreign_key "account_moderation_notes", "accounts" diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index fac76c2045ef22..c00ed740a2b4f7 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -4,6 +4,14 @@ module Mastodon module Version module_function + def kmyblue_major + 3 + end + + def kmyblue_minor + 0 + end + def major 4 end @@ -24,8 +32,17 @@ def prerelease ENV['MASTODON_VERSION_PRERELEASE'].presence || default_prerelease end + def to_a_of_kmyblue + [kmyblue_major, kmyblue_minor].compact + end + + def to_s_of_kmyblue + components = [to_a_of_kmyblue.join('.')] + components.join + end + def build_metadata - ENV.fetch('MASTODON_VERSION_METADATA', nil) + ['kmyblue', to_s_of_kmyblue, ENV.fetch('MASTODON_VERSION_METADATA', nil)].compact.join('.') end def to_a diff --git a/public/favicon.ico b/public/favicon.ico old mode 100644 new mode 100755 index b09a98bb9b0649..fc5e475d425f5e Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/spec/models/concerns/account_interactions_spec.rb b/spec/models/concerns/account_interactions_spec.rb index 84e2c91a85dfd4..0b8825135d1f68 100644 --- a/spec/models/concerns/account_interactions_spec.rb +++ b/spec/models/concerns/account_interactions_spec.rb @@ -9,6 +9,8 @@ let(:target_account) { Fabricate(:account, username: 'target') } let(:target_account_id) { target_account.id } let(:target_account_ids) { [target_account_id] } + let(:follower_account) { Fabricate(:account, username: 'follower') } + let(:followee_account) { Fabricate(:account, username: 'followee') } describe '.following_map' do subject { Account.following_map(target_account_ids, account_id) } @@ -711,4 +713,20 @@ expect(account.lists_for_local_distribution.to_a).to contain_exactly(follower_list, self_list) end end + + describe '#mutuals' do + subject { account.mutuals } + + context 'when following target_account' do + it 'mutual one' do + account.follow!(target_account) + target_account.follow!(account) + follower_account.follow!(account) + account.follow!(followee_account) + + expect(subject.count).to eq 1 + expect(subject.first.id).to eq target_account.id + end + end + end end diff --git a/spec/services/delivery_antenna_service_spec.rb b/spec/services/delivery_antenna_service_spec.rb index 961ccc6a5c0bc8..aa63aed74cb8d3 100644 --- a/spec/services/delivery_antenna_service_spec.rb +++ b/spec/services/delivery_antenna_service_spec.rb @@ -27,7 +27,7 @@ let!(:antenna) { nil } let!(:empty_antenna) { nil } - let(:stl_home) { false } + let(:mode) { :home } before do bob.follow!(alice) @@ -35,7 +35,7 @@ allow(redis).to receive(:publish) - subject.call(status, false, stl_home) + subject.call(status, false, mode: mode) end def home_feed_of(account) @@ -323,4 +323,40 @@ def list(owner) end end end + + context 'when stl mode keyword is not working' do + let(:mode) { :stl } + let!(:antenna) { antenna_with_keyword(bob, 'anime', stl: true) } + + it 'detecting antenna' do + expect(antenna_feed_of(antenna)).to include status.id + end + end + + context 'when ltl mode keyword is not working' do + let(:mode) { :ltl } + let!(:antenna) { antenna_with_keyword(bob, 'anime', ltl: true) } + + it 'detecting antenna' do + expect(antenna_feed_of(antenna)).to include status.id + end + end + + context 'when stl mode exclude_keyword is not working' do + let(:mode) { :stl } + let!(:antenna) { antenna_with_keyword(bob, 'anime', exclude_keywords: ['body'], stl: true) } + + it 'detecting antenna' do + expect(antenna_feed_of(antenna)).to include status.id + end + end + + context 'when ltl mode exclude_keyword is not working' do + let(:mode) { :ltl } + let!(:antenna) { antenna_with_keyword(bob, 'anime', exclude_keywords: ['body'], ltl: true) } + + it 'detecting antenna' do + expect(antenna_feed_of(antenna)).to include status.id + end + end end diff --git a/spec/services/fan_out_on_write_service_spec.rb b/spec/services/fan_out_on_write_service_spec.rb index e3d8c952dc2840..c15253c879cb5b 100644 --- a/spec/services/fan_out_on_write_service_spec.rb +++ b/spec/services/fan_out_on_write_service_spec.rb @@ -56,6 +56,10 @@ def antenna_with_account(owner, target_account) antenna end + def antenna_with_options(owner, **options) + Fabricate(:antenna, account: owner, **options) + end + context 'when status is public' do let(:visibility) { 'public' } @@ -97,6 +101,26 @@ def antenna_with_account(owner, target_account) expect(antenna_feed_of(empty_antenna)).to_not include status.id end end + + context 'with STL antenna' do + let!(:antenna) { antenna_with_options(bob, stl: true) } + let!(:empty_antenna) { antenna_with_options(tom) } + + it 'is added to the antenna feed of antenna follower' do + expect(antenna_feed_of(antenna)).to include status.id + expect(antenna_feed_of(empty_antenna)).to_not include status.id + end + end + + context 'with LTL antenna' do + let!(:antenna) { antenna_with_options(bob, ltl: true) } + let!(:empty_antenna) { antenna_with_options(tom) } + + it 'is added to the antenna feed of antenna follower' do + expect(antenna_feed_of(antenna)).to include status.id + expect(antenna_feed_of(empty_antenna)).to_not include status.id + end + end end context 'when status is limited' do @@ -138,6 +162,24 @@ def antenna_with_account(owner, target_account) expect(antenna_feed_of(empty_antenna)).to_not include status.id end end + + context 'with STL antenna' do + let!(:antenna) { antenna_with_options(bob, stl: true) } + let!(:empty_antenna) { antenna_with_options(tom, stl: true) } + + it 'is added to the antenna feed of antenna follower' do + expect(antenna_feed_of(antenna)).to include status.id + expect(antenna_feed_of(empty_antenna)).to_not include status.id + end + end + + context 'with LTL antenna' do + let!(:empty_antenna) { antenna_with_options(bob, ltl: true) } + + it 'is added to the antenna feed of antenna follower' do + expect(antenna_feed_of(empty_antenna)).to_not include status.id + end + end end context 'when status is private' do @@ -176,6 +218,24 @@ def antenna_with_account(owner, target_account) expect(antenna_feed_of(empty_antenna)).to_not include status.id end end + + context 'with STL antenna' do + let!(:antenna) { antenna_with_options(bob, stl: true) } + let!(:empty_antenna) { antenna_with_options(ohagi, stl: true) } + + it 'is added to the antenna feed of antenna follower' do + expect(antenna_feed_of(antenna)).to include status.id + expect(antenna_feed_of(empty_antenna)).to_not include status.id + end + end + + context 'with LTL antenna' do + let!(:empty_antenna) { antenna_with_options(bob, ltl: true) } + + it 'is added to the antenna feed of antenna follower' do + expect(antenna_feed_of(empty_antenna)).to_not include status.id + end + end end context 'when status is unlisted' do @@ -215,6 +275,24 @@ def antenna_with_account(owner, target_account) end end + context 'with STL antenna' do + let!(:antenna) { antenna_with_options(bob, stl: true) } + let!(:empty_antenna) { antenna_with_options(ohagi, stl: true) } + + it 'is added to the antenna feed of antenna follower' do + expect(antenna_feed_of(antenna)).to include status.id + expect(antenna_feed_of(empty_antenna)).to_not include status.id + end + end + + context 'with LTL antenna' do + let!(:empty_antenna) { antenna_with_options(bob, ltl: true) } + + it 'is added to the antenna feed of antenna follower' do + expect(antenna_feed_of(empty_antenna)).to_not include status.id + end + end + context 'with non-public searchability' do let(:searchability) { 'direct' } diff --git a/spec/services/post_status_service_spec.rb b/spec/services/post_status_service_spec.rb index 91fee989ab7f25..3b953b9fc1fb65 100644 --- a/spec/services/post_status_service_spec.rb +++ b/spec/services/post_status_service_spec.rb @@ -173,12 +173,37 @@ it 'mutual visibility' do account = Fabricate(:account) + mutual_account = Fabricate(:account) + other_account = Fabricate(:account) text = 'This is an English text.' + mutual_account.follow!(account) + account.follow!(mutual_account) + other_account.follow!(account) status = subject.call(account, text: text, visibility: 'mutual') expect(status.visibility).to eq 'limited' expect(status.limited_scope).to eq 'mutual' + expect(status.mentioned_accounts.count).to eq 1 + expect(status.mentioned_accounts.first.id).to eq mutual_account.id + end + + it 'circle visibility' do + account = Fabricate(:account) + circle_account = Fabricate(:account) + other_account = Fabricate(:account) + circle = Fabricate(:circle, account: account) + text = 'This is an English text.' + + circle_account.follow!(account) + other_account.follow!(account) + circle.accounts << circle_account + status = subject.call(account, text: text, visibility: 'circle', circle_id: circle.id) + + expect(status.visibility).to eq 'limited' + expect(status.limited_scope).to eq 'circle' + expect(status.mentioned_accounts.count).to eq 1 + expect(status.mentioned_accounts.first.id).to eq circle_account.id end it 'safeguards mentions' do